Skip to content

[spec] HookContext.session 少声明了 positions / preserveAudit —— 引擎在生产、消费方在读、文档在教,契约里没有(#5050 的镜像方向) #5605

Description

@os-zhuang

背景

#5050 退役的是「声明了、没人生产」的 session.roles。在核查它的生产方时,发现同一个 session 块存在方向相反的漂移:有两个键是引擎真的在生产、消费方真的在读、文档真的在教,但 packages/spec/src/data/hook.zod.ts 的 HookContextSchema.session 里根本没有声明。

这不是同一个 bug 的另一半措辞 —— roles 是 declared-never-produced,这两个是 produced-never-declared,修法完全不同(前者删,后者补声明),所以单独立单。

证据(均对 origin/main 核实)

生产方 —— packages/objectql/src/engine.ts 的 buildSession():

消费方:

  • packages/objectql/src/plugin.ts:782 —— const preserveAudit = session?.preserveAudit === true;(审计戳 hook 的核心分支;该处 session 形参标注为 any,所以类型层看不见这条读)

文档在教(两页,都把 positions 当作喂给 sharing service 的正规写法):

  • content/docs/kernel/runtime-services/examples.mdx:37 —— positions: ctx.session?.positions,
  • content/docs/kernel/runtime-services/sharing-service.mdx:67 —— 同上

契约里没有 —— HookContextSchema.session 声明的键是 userId / actor / organizationId / accessToken / isSystem / skipTriggers / skipAutomations(#5050 后再加一个 roles 墓碑)。没有 positions,没有 preserveAudit。

为什么这是缺陷而不是无害省略

  1. 文档教的代码在类型层编译不过。 一个按 content/docs/automation/index.mdx 的写法把 handler 标注成 (ctx: HookContext) 的作者,写 ctx.session?.positions 会吃 TS2339;上面两页示例之所以看不出来,是因为它们把 ctx 标成了 any。照文档抄 + 照文档标类型 = 编译失败,这是作者第一次写 hook 就会撞上的。
  2. positions 恰恰是 ADR-0090 D3 之后的权限词汇本体。 ExecutionContext 已经把 roles 改名成 positions(packages/spec/src/kernel/execution-context.zod.ts:118,注释原话 "Formerly roles")。[spec] 退役 HookContext session.roles —— #4839 双删后零消费方零生产方(ADR-0049) #5050 把 hook 侧最后一处 roles 拼法退役之后,hook 能看到的唯一任职词汇就是这个未声明的 positions —— 契约上等于说「hook 拿不到任职信息」,而运行时明明给了。
  3. Zod 非 strict 掩盖了它。 HookContextSchema 刻意不 strict(见文件头注释),所以 positions 在 parse 时被静默 strip。也就是说:谁真的去 HookContextSchema.parse(ctx) 一把,谁就把 positions 弄丢了 —— 而生成的 reference 页(content/docs/references/data/hook.mdx)恰恰以 HookContextSchema.parse(data) 作为示例。

需要裁定的点(不要直接猜)

补声明的语义要维护者定,两条路差别很大:

  • A. 两个键都补进 session 声明(positions: z.array(z.string()).optional()、preserveAudit: z.boolean().optional()),配 .describe() 写清「仅供 hook 读取,授权由 security service 裁决,这里不是授权输入」。契约与运行时对齐,文档示例可以正常标类型。风险:positions 出现在 hook 上下文里,可能被下一个作者读成「可以拿它做授权判断」—— 需要用 roles 退役同款的措辞把边界钉死。
  • B. 认为 hook 本就不该看到任职信息,那么该删的是 buildSession() 的 positions 写入 + 两页文档的教法,而不是补声明(即对 positions 走 enforce-or-remove 的 remove 一侧)。但注意 preserveAudit 有真实消费方(plugin.ts:782),它只能走补声明。

我的倾向是 A,理由:preserveAudit 无论如何都得补(有活的消费方,删不掉),而 positions 有两页文档 + sharing service 的真实用法在背书,删它是砍掉一个在用的能力;A 同时消灭「文档教的代码编译不过」这个当下就会撞到的问题。但 positions 是否应当出现在 hook 契约上是安全语义判断,按 ADR-0095 D3 的口径应由维护者拍板,故不擅自实现。

与其他单的关系

发现于 #5050 的生产方核查过程(Prime Directive #10)。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions