Skip to content

[runtime] action body 的 ctx.session 仍在生产 roles(值是 ec.positions)—— 自称「mirroring hook ctx.session」,而 hook 侧该键已按 ADR-0049 退役 #5613

Description

@os-zhuang

事实

packages/runtime/src/action-execution.tsbuildActionSession()(第 689-695 行)构造 action body 的 ctx.session:

return {
    ...(ec.userId != null ? { userId: String(ec.userId) } : {}),
    ...(ec.tenantId != null ? { organizationId: String(ec.tenantId) } : {}),
    ...(Array.isArray(ec.positions) && ec.positions.length ? { roles: ec.positions } : {}),
};

它的文档注释原话是:"Build the action-body ctx.session from the request ExecutionContext, mirroring the hook ctx.session shape (#3280) so an action author reads the caller's active org under the SAME blessed name as a hook author."

调用点两处:packages/runtime/src/domains/actions.ts:296packages/runtime/src/action-execution.ts:920

三个问题叠在一起

  1. 它把 ADR-0090 D3 明令禁止的拼法重新引入了。 值取自 ec.positions —— ExecutionContext 已经在 ADR-0090 D3 把 roles 改名为 positions(packages/spec/src/kernel/execution-context.zod.ts:118,注释原话 "Formerly roles"),这里等于在边界上又把 positions 翻译回 roles 交给作者。ADR-0090 D3 禁的正是这个拼法;plugin-approvals 的 admin 豁免读 session.roles,而 ObjectQL 的 buildSession() 从不填充它 —— 记录锁/delegation 守卫的 admin 覆盖在真实引擎路径上永不生效 #4839 删掉 plugin-approvals 两处 roles.includes('admin') 时,理由也正是「不要第二套权限方言」。

  2. 「mirroring hook ctx.session」这句注释现在是错的,而且是危险方向的错。 [spec] 退役 HookContext session.roles —— #4839 双删后零消费方零生产方(ADR-0049) #5050 已按 ADR-0049 把 HookContext.session.roles 退役(墓碑 + 语义迁移)。所以现在两张面对同一个键名给出两种现实:hook 侧 —— 键已退役、写它会 tsc 报错 + parse 拒收;action 侧 —— 键还在,而且真的有值。同一个平台里同名同位的字段,一个作者在 hook 里读到 undefined(以前)/ 报错(现在),在 action 里读到一串 position 名。

  3. 没有任何 schema 声明 action ctx。 actionContext 是裸 any(两处调用点都是 const actionContext: any = {...}),沙箱侧 ScriptContext.session?: unknown(packages/runtime/src/sandbox/script-runner.ts:71)。所以这个键既没有契约、没有文档表、也没有闸门 —— 它只存在于运行时对象里,是 declared-nowhere / produced-anyway,连 liveness 台账都够不着它。

需要裁定(不要直接猜)

三条路,选哪条影响 action body 的公开契约:

  • A. 改名为 positions,值不变。ExecutionContext / sharing service / ADR-0090 D3 词汇统一,一处方言消失。代价:breaking —— 现存 action body 里 ctx.session.roles 的读会静默变 undefined(裸 any,没有任何东西会报错),所以必须配 ADR-0087 语义迁移 + changeset;而且因为没有 schema,连墓碑都无处可挂,处方只能落在 changeset 和文档上。
  • B. 直接删。 如果结论是「action body 本就不该拿到任职信息、要判权限就走 security service」,那就删掉这条 spread。同样 breaking、同样静默。
  • C. 先给 action ctx 立 schema(声明 = 强制),再在其上做 A 或 B。 最贵,但这是唯一能让下一次同类漂移被闸门看见的路 —— 现状之所以能长这么久,正是因为 action ctx 完全在契约之外。

我倾向 C 的骨架 + A 的语义(先声明 action 上下文契约,同时把键正名为 positions),理由是两条轴都指向它:长期看,一个每天被客户 action body 读、却没有任何 schema 的运行时上下文,就是下一个 #5050 的温床;而「让 AI 写的元数据/代码难写错」这条更直接 —— 现在一个模型作者在 hook 里被硬拒、在 action 里被放行,它学到的是「roles 有时候能用」,这正是最坏的一课。但这是公开契约 + 安全词汇的双重决定,按 AGENTS.md Prime Directive #12 / #13 应由维护者裁定,故只登记不实现。

与其他单的关系

按 Prime Directive #10 登记,不在 #5050 的 PR 内修。

Blocked-by: #5779

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