Repository navigation
[runtime] action body 的 ctx.session 仍在生产 roles(值是 ec.positions)—— 自称「mirroring hook ctx.session」,而 hook 侧该键已按 ADR-0049 退役 #5613
Description
Activity
分诊:
needs-user-decision+domain:cli(预挂)。- 落点锚定:
packages/runtime/src/action-execution.ts的buildActionSession()(runtime → cli 车道);C 选项会外溢到 spec(立 action ctx schema),届时按 contract-first 拆分、spec 半边归 spec 座位。 - 为什么进决策箱:A(改名
positions)/ B(删)/ C(先立 schema 再做 A 或 B)三路都是 action body 公开契约 + 权限词汇(ADR-0090 D3)的双重决定,且 breaking 皆为静默(裸any,无任何闸门可见),按 Prime Directive Add comprehensive test suite for Zod schema validation #12/[WIP] Add Chinese version of the documentation #13 归维护者。正文作者倾向「C 骨架 + A 语义」,供拍板参考。 - 过时前提:
action-execution.ts近两笔合并(fix(runtime): callData 的 ObjectQL 兜底对「记录不存在」统一答 404 RECORD_NOT_FOUND (#5138) #5584 calldata 404、fix(runtime):ctx.user.name交付真实 display name —— 三条 dispatch 路径统一 user 形状 (#5372) #5518 ctx.user.name)均不触buildActionSession段,前提成立。 - 查重:与 [spec] HookContext.session 少声明了
positions/preserveAudit—— 引擎在生产、消费方在读、文档在教,契约里没有(#5050 的镜像方向) #5605(hook 契约补声明)、[spec] 退役 HookContext session.roles —— #4839 双删后零消费方零生产方(ADR-0049) #5050(hook roles 退役,在飞)同族不同面,互不阻塞;三仓无重复。
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- 落点锚定:
维护者裁定(2026-08-06,经 spec 车道 PM
session_018fxLGQdatPbBUvCgiVxg6D誊写):契约先行(C 骨架 + A 语义)——先给 action body 的ctx.session立 schema(按现状声明,裸 any 面是本类漂移的根因),再在有类型面的前提下做roles → positions更名(带弃用窗口 + ADR-0087 语义迁移)。执行拆分(contract-first):
- 第一阶段(spec 车道):action ctx.session 契约 schema,按现状声明(含
roles现状键,describe 标注 deprecated → positions)——另立单派发,见下方关联。 - 第二阶段(本单,cli 车道):
Blocked-by:第一阶段;schema 落地后做更名 + 弃用窗口 + 迁移处方(changeset/文档),buildActionSession注释同步修正。
摘
needs-user-decision转pm:blocked等第一阶段。
Generated by Claude Code
- 第一阶段(spec 车道):action ctx.session 契约 schema,按现状声明(含
解锁通知(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,2026-08-06):第一阶段 #5697 已落地(PR #5721,02:4xZ 合并)——action ctx.session 契约已按现状声明(ActionSessionSchema@packages/spec/src/ui/action-params.zod.ts,roles键已标 deprecated 并在.describe()里写明值为 ADR-0090 D3positions)。第二阶段(更名 + 弃用窗口 + ADR-0087 语义迁移 +buildActionSession()两句错注释修正)可依的类型面已就位:- 承重翻转点已预埋:
packages/runtime/src/action-session-shape-contract.test.ts中expect(Object.keys(built!).sort()).toEqual(['organizationId', 'roles', 'userId'])即第二阶段要翻的断言,文件内已注明。 ScriptContext.session维持unknown的理由与代价记录在sandbox/script-runner.ts注释里,收窄属第二阶段下游自然工作,未重复立单。
标签
pm:blocked→pm:queue,归 cli 车道自扫排程。
Generated by Claude Code
- 承重翻转点已预埋:
第二阶段再拆分(spec 半边转座)(cli 车道 PM,
session_01DWUR56YsttL5sTF72Q75TQ,2026-08-06 05:3xZ):派发前实读ActionSessionSchema发现其rolesdocstring 自带约定——「do NOT add apositionskey here ahead of it,The rename is #5613 phase 2」——即第二阶段必然触碰packages/spec(补positions键、roles弃用对、ADR-0087 语义迁移条目、生成物重生)。按「shared contract surfaces have one owner」,spec 半边已立 #5779 转 spec 座位队列(contract-first,spec 先行);本单收缩为 runtime 半边(buildActionSession()双发改造 + 注释修正 + 翻action-session-shape-contract.test.ts键集断言 + changeset major + 文档),正文已补Blocked-by: #5779,挂pm:blocked,#5779 落地后即派。
Generated by Claude Code
前置解除(spec 车道 PM,
session_018fxLGQdatPbBUvCgiVxg6D,2026-08-06):Blocked-by: #5779已随 PR #5849 MERGED 解除,pm:blocked→pm:queue,归 cli 车道队列自扫。runtime 半边可依的契约面(全部已上主干):
ActionSessionSchema.positions权威键 +roles弃用别名对(ui/action-params.zod.ts)、ADR-0087 语义迁移条目action-session-roles-to-positions(step17)。半边工序清单照 #5779 PR 正文「后续」节:buildActionSession()双发同值、两句错注释修正、翻转packages/runtime/src/action-session-shape-contract.test.ts的键集断言(锚点未动,失败信息即指引)、major changeset、文档。窗口期读法(读positions回落roles,回落随别名过期)已写进 schema JSDoc——runtime 半边落地越快,该临时回落固化风险越小。
Generated by Claude Code
解锁(cli 车道 PM,
session_01DWUR56YsttL5sTF72Q75TQ):Blocked-by: #5779已解除——spec 半边 PR #5849 已合入 origin/main(d4e080937,ActionSessionSchema已有positions权威键 +roles弃用别名对)。摘pm:blocked回pm:queue。派发时点:等 #5563(独占窗口,占 runtime 包)落地后,runtime 半边即派——双发改造 + 注释修正 + 翻action-session-shape-contract.test.ts键集断言(预埋的翻转点)+ changeset major + 文档。
Generated by Claude Code
认领:PM 循环第 7 轮(cli 车道)
会话:session_01DWUR56YsttL5sTF72Q75TQ
分支:claude/issue-5613-action-session-positions-rename
Worktree:objectstack-issue-5613
域:domain:cli(第二阶段 runtime 半边)
文件面:packages/runtime/src/action-execution.ts(buildActionSession()双发改造 + 两句错注释修正)+packages/runtime/src/action-session-shape-contract.test.ts(预埋键集断言翻转)+(如需)sandbox/script-runner.ts的ScriptContext.session收窄 + changeset(major)+ 文档;⛔ 不动packages/spec(spec 半边 #5779/PR #5849 已合)执行依据:维护者裁「C 骨架 + A 语义」;spec 侧已就位(
ActionSessionSchema含positions权威键 +roles弃用别名对,弃用窗口 = runtime 双发同值)。churn:runtime 近日被 #5774/#5895/#5951 多轮动过(均不触 action-execution.ts,以最新 origin/main 为准)。
Generated by Claude Code
验收:ACCEPT(第二阶段 runtime 半边,三阶段接力收官)(cli 车道 PM,
session_01DWUR56YsttL5sTF72Q75TQ,2026-08-06 15:0xZ)→ PR #5991- 裁决「C 骨架 + A 语义」全链兑现:feat(spec): 为 action body 的 ctx.session 立契约 schema(按现状声明,#5697) #5721 立契约 → feat(spec): ActionSession 补
positions权威键,roles降为弃用别名 (#5779) #5849 双键 schema → 本 PR runtime 双发(positions权威 + 别名同值,构造保证;非空才写语义保持);「mirroring hook ctx.session」失实注释删除,改指 schema 与 ADR-0087 条目;spec 座位预埋的键集断言承重翻转 + 「别名绝不单独出现」方向钉 + 真实handleActions派发用例。 ScriptContext.session收窄的类型选择正确:联合ScriptSession = ActionSession | HookContext['session']而非单型——单型化会把本单要消灭的「一个键名两种现实」搬到类型面重演,理由写进 PR;实测不外溢 runtime。- 中途 ESLint 红的处置是本单最佳注脚:
check:role-word棘轮抓到初版技能文档写了弃用别名示例——棘轮在保卫本单自己的原则;dev 重写为只教ctx.session.positions,别名与迁移处方留在 changeset + upgrade-guide(棘轮扫描面外)。探活催修信号后自愈,验收线未被污染。 - 边界:spec 零触碰;
skills/objectstack-ui/SKILL.md+19 行为更名的文档半径(同 content/docs 随码先例,devx 座位零在飞无撞面),验收记录在案。 - CI 亲核(head
7b73a61):23 检查 0 failure,ESLintsuccess;major changeset 带 FROM→TO 迁移处方(含「不要把includes('admin')判权改名」反例)。 - 界外:[runtime]
ctx.user的roles别名(值是positions)没有关闭日期 —— #5613 给 ctx.session 装了迁移窗口,同名同值的 ctx.user 面仍是无限期别名(observation) #6011(ctx.user.roles同族别名无迁移条目、棘轮盲区)规范立单——且本次未自打 domain 标签,上一单的纪律提醒已生效;ScriptContext.user是unknown—— 沙箱接缝上没有任何类型把 dispatcher 的 user 形状钉住(observation) #5521 未重复立单、改留言共享收窄方法。
转 ready + 挂 auto-merge,两读数跟到 MERGED——落地即 cli 车道队列见底。
Generated by Claude Code
- 裁决「C 骨架 + A 语义」全链兑现:feat(spec): 为 action body 的 ctx.session 立契约 schema(按现状声明,#5697) #5721 立契约 → feat(spec): ActionSession 补
- added a commit that references this issue
on Oct 7, 2026
事实
packages/runtime/src/action-execution.ts的buildActionSession()(第 689-695 行)构造 action body 的ctx.session:它的文档注释原话是:"Build the action-body
ctx.sessionfrom the request ExecutionContext, mirroring the hookctx.sessionshape (#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:296与packages/runtime/src/action-execution.ts:920。三个问题叠在一起
它把 ADR-0090 D3 明令禁止的拼法重新引入了。 值取自
ec.positions——ExecutionContext已经在 ADR-0090 D3 把roles改名为positions(packages/spec/src/kernel/execution-context.zod.ts:118,注释原话 "Formerlyroles"),这里等于在边界上又把positions翻译回roles交给作者。ADR-0090 D3 禁的正是这个拼法;plugin-approvals 的 admin 豁免读session.roles,而 ObjectQL 的buildSession()从不填充它 —— 记录锁/delegation 守卫的 admin 覆盖在真实引擎路径上永不生效 #4839 删掉 plugin-approvals 两处roles.includes('admin')时,理由也正是「不要第二套权限方言」。「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 名。没有任何 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 的公开契约:
positions,值不变。 与ExecutionContext/ sharing service / ADR-0090 D3 词汇统一,一处方言消失。代价:breaking —— 现存 action body 里ctx.session.roles的读会静默变undefined(裸any,没有任何东西会报错),所以必须配 ADR-0087 语义迁移 + changeset;而且因为没有 schema,连墓碑都无处可挂,处方只能落在 changeset 和文档上。我倾向 C 的骨架 + A 的语义(先声明 action 上下文契约,同时把键正名为
positions),理由是两条轴都指向它:长期看,一个每天被客户 action body 读、却没有任何 schema 的运行时上下文,就是下一个 #5050 的温床;而「让 AI 写的元数据/代码难写错」这条更直接 —— 现在一个模型作者在 hook 里被硬拒、在 action 里被放行,它学到的是「roles有时候能用」,这正是最坏的一课。但这是公开契约 + 安全词汇的双重决定,按 AGENTS.md Prime Directive #12 / #13 应由维护者裁定,故只登记不实现。与其他单的关系
HookContext.session.roles)的消费半径扫描。[spec] 退役 HookContext session.roles —— #4839 双删后零消费方零生产方(ADR-0049) #5050 不受影响、也不被本单证伪:action 的ctx.session是另一个对象,永远不会变成 HookContext,没有任何 schema 读它。[spec] 退役 HookContext session.roles —— #4839 双删后零消费方零生产方(ADR-0049) #5050 的墓碑注释、changeset、语义迁移条目里都已显式点名这个邻居,免得后人拿它来「推翻」hook 侧的零生产方结论([移交自 objectui · v17 阻塞]app.homePageId退役墓碑写着「no shell ever read it」,但 objectui 的 AppContent 正在读它 #4865 的教训)。positions/preserveAudit—— 引擎在生产、消费方在读、文档在教,契约里没有(#5050 的镜像方向) #5605(HookContext.session少声明positions/preserveAudit)、[docs-gen] 生成的 reference 把retiredKey()墓碑渲染成any—— 嵌套两层时连[REMOVED]处方都没有,退役键读起来像自由槽 #5606(生成的 reference 把墓碑渲染成any)。按 Prime Directive #10 登记,不在 #5050 的 PR 内修。
Blocked-by: #5779