Skip to content

[spec] hook.zod.ts 的 positions JSDoc 仍把 services.sharing.canEdit(…) 举为「hook 可以这么读」的正例 —— #5720 已证实 hook 拿不到 services #6001

Closed

Description

@hotlong

事实(对 origin/main 核实,628b028)

packages/spec/src/data/hook.zod.ts,session.positions 的 JSDoc(约 415-418 行):

 * ⚠️ **Descriptive, NOT an authorization input.** A hook may READ this to
 * describe the caller — forwarding it as the sharing service's evaluation
 * context (`services.sharing.canEdit(..., { positions })`, the shape both
 * `content/docs/kernel/runtime-services/` pages teach), tailoring a
 * message, logging — and nothing more.

这段注释在两个层面都已过时:

  1. 它举的动作在 hook 里做不到。 [docs] runtime-services 的 hook 示例在教 ctx.services —— hook 上下文从来没有这个键,照抄的 hook 会拒掉每一次写入 #5720 已实测钉死:引擎逐键构造 hook 上下文(object / event / input / session / provenance / user / api / transaction / ql,见 packages/objectql/src/engine.ts 4796 / 4929 / 5505 / 6059),buildSandboxContext(packages/runtime/src/sandbox/body-runner.ts)同样只产 input / previous / user / session / event / object / result / api / log / crypto —— 两条路径都没有 services 键。所以「a hook may READ positions … forwarding it as services.sharing.canEdit(…)」举的正例,恰恰是 [docs] runtime-services 的 hook 示例在教 ctx.services —— hook 上下文从来没有这个键,照抄的 hook 会拒掉每一次写入 #5720 认定会无条件拒掉每一次写入的那个形状(ctx.services?.sharing?.canEdit(…) 短路成 undefined,if (!ok) throw 全拒)。

  2. 它引用的文档现状已反转。 注释说这是「both content/docs/kernel/runtime-services/ pages teach」的形状。PR docs(kernel): runtime-services 的 hook 示例改教真实通道 —— 去掉不存在的 ctx.services (#5720) #5938 之后:sharing-service.mdx 新增了一整节 "Enforcement is automatic — do not re-check it in a hook",并把 Example 改成由持有该服务的代码(ISharingService 入参)调用;examples.mdx 第 2 节改教 ctx.api。也就是说这两页现在教的正好相反。

为什么值得记一笔

packages/spec 的 JSDoc 是 AI 与人照抄的语料(它还会被 /api/v1/meta/types/hook 与 Studio 表单读到,见同文件 22 行附近的说明)。这里留着的不是措辞旧,而是一个被点名修掉的缺陷形状仍被当作正例在契约里挂着 —— 与 #5720 / #5944 同族,只是藏在 spec 的注释里,不在 content/docs/ 下,所以 #5938、#5995 的派发面都够不着。

注意本单不主张改 positions 的语义结论(「描述性、非授权输入」是对的,不动);要改的只是它选用的那个举例通道。

建议修法

把举例通道换成 hook 真实拿得到的:保留「可以读来描述调用者」的结论,例子改为 ctx.api 侧的业务判断 / 定制消息 / 日志,并删掉「the shape both content/docs/kernel/runtime-services/ pages teach」这句已反转的交叉引用(或改指 sharing-service.mdx 的 "do not re-check it in a hook" 一节)。

关联

未认领、未标 pm:queue,交 PM 分诊定级。

Activity

  1. added
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    on Aug 6, 2026
  2. claude commented on Aug 6, 2026

    @claude
    Contributor

    分诊结论:入队 pm:queue + domain:spec

    分类 — 具体缺陷,落点与修法在正文里都已锚死,无需拍板 ⇒ pm:queue。⛔ 不判 finding:这不是「措辞旧」的观察项,而是一个已被 #5720 判定为「会无条件拒掉每一次写入」的形状,仍作为正例挂在契约注释里;packages/spec 的 JSDoc 是人与 AI 照抄的语料,且经 /api/v1/meta/types/hook 与 Studio 表单外露,错误示例在这里是活的传播面,不是休眠代码。

    域/路由 — 修复落点 packages/spec/src/data/hook.zod.ts ⇒ domain:spec。依据 SKILL「shared contract surfaces have one owner」:触 packages/spec 的一律归 spec 座位,与谁发现无关。

    落点锚定理由(逐条对 origin/main 44106d9 复核 —— 注意正文引用的是 628b028,已前移,故重核而非采信):

    1. 注释仍在:hook.zod.ts:420-423 命中 services.sharing.canEdit(..., { positions }) 以及「the shape both content/docs/kernel/runtime-services/ pages teach」这句交叉引用。反查对照:同文件 positions 共 12 处命中,证伪「路径错 / 扫描器坏了」这个解释。
    2. [docs] runtime-services 的 hook 示例在教 ctx.services —— hook 上下文从来没有这个键,照抄的 hook 会拒掉每一次写入 #5720 的实测结论在 main 上仍成立:buildSandboxContext(packages/runtime/src/sandbox/body-runner.ts:307)逐键构造的 hook 上下文是 input / previous / user / session / event / object / result / api / log / crypto —— 没有 services 键。⚠️ 该文件内唯一的 services 提及在 328 行,属 buildActionSandboxContext 的 action 上下文约定注释,不在 hook 路径上,不构成反例(这一条特意查过,避免把裸名命中读成反证)。

    查重 — 三仓 open issue/PR 全量缓存 + 本地正则(REST search/issues 在本会话被网关按仓库范围拒绝,沿用前轮处置)。hook.zod / canEdit / sharing.canEdit 的命中逐条排除:#5945(HookContext.api 声明为 z.unknown(),已在决策箱,另一属性)、#5817(ISharingService 模块头的 canEdit/canDelete 职责漂移,另一文件)、#5997(HookContext 契约表的 input.options,finding,另一行)。无重复,本单是 #5720 / #5944 家族在 spec 注释里的剩余面 —— 正如正文所说,#5938 / #5995 的派发面(content/docs/)够不着这里。

    给 spec 座位的两条并发提示:

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. added
    target:v17Release blocker for v17 — on the board until fixed, dropped as no longer valid, or accepted for GA
    on Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:specDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatch

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions