在做 #5613 (action body ctx.session 的 roles → positions 更名,PR #5991 )时顺带测到的同族面,不在该 PR 范围内 ,按 Prime Directive #10 单独记录。
事实
packages/runtime/src/security/actor-user.ts 的 ActorUser 同时声明两个键,值完全相同:
第 95-97 行:positions: string[] —— /** ADR-0090 position names held by the caller (canonical; EvalUser.positions). */
第 98 行:roles: string[] —— /** Legacy alias of positions (pre-ADR-0090 spelling, kept for the REST/AI shapes). */
构造处(约 208 行)是一次赋值两次:
positions : core . positions ,
roles : core . positions ,
这个形状被 packages/runtime/src/action-ctx-user-shape.test.ts 明确钉住(the ADR-0090 position aliases stay in lockstep),并在 http-dispatcher.test.ts 的 action body 用例里断言过。所以它是有意的、被测试保护的、正在被消费的 别名 —— 不是漏网的死代码。
为什么仍然值得记一笔
它与 #5613 修掉的那个面是同一个形状,唯独少了后者刚装上的那一半:
ctx.session(#5613 后)
ctx.user(现状)
权威键
positions
positions
旧拼法
存在,已标 deprecated
存在,注释写 "Legacy alias"
ADR-0087 迁移条目
有(action-session-*-to-positions)
无
关闭日期 / 移除路径
有(#3280 → #3290 那条路)
无
契约声明
ActionSessionSchema(spec)
仅 runtime 的 TS interface
ADR-0090 D3 把该拼法列为保留禁用词,而 check:role-word 只扫 content/docs 与 skills,扫不到 .ts;packages/lint 的 security-role-word 只判作者写的元数据 ,也判不到运行时构造的 ctx.user。所以这个别名今天没有任何闸门在看,也没有任何东西约定它何时结束 —— 「两个拼法长期共存」正是 #5613 的裁定里被认定为缺陷而非终点的那个状态。
为什么标 finding 而不是缺陷
今天没有用户会撞上:两个键恒等,读哪个都对。它是方向问题 ,不是故障 —— 因此按观察类记录,不挂 pm:queue,由 PM 分诊定级(严重度在立单时判断本来就不可靠,#1004 / #897 两个方向都出过错)。
可能的处置(供分诊,不预判)
照 [runtime] action body 的 ctx.session 仍在生产 roles(值是 ec.positions)—— 自称「mirroring hook ctx.session」,而 hook 侧该键已按 ADR-0049 退役 #5613 的模子办 :给 ctx.user 面立 spec 契约(它今天连 schema 都没有,只有 runtime 的 interface),把 roles 标 deprecated,加 ADR-0087 语义迁移条目和关闭日期;
直接退役 :先测消费者(REST / AI 路由 / objectui / cloud)是否真的还在读旧拼法,若无 → ADR-0049 enforce-or-remove;
明确保留 :若 REST/AI 的对外契约确实需要这个拼法,把「为什么它豁免 ADR-0090 D3」写进 ADR,而不是留在一行代码注释里(Prime Directive [WIP] Add Chinese version of the documentation #13 :代码里找不到的决定,就是会被推翻的决定)。
现场
查重:按 actor-user.ts、ctx.user roles、ADR-0090 D3 alias 三组关键词搜过 open issues,未见同题(唯一命中的 #5521 是同一接缝的另一个字段 ScriptContext.user 的类型问题,已单独记录)。
Generated by Claude Code
在做 #5613(action body
ctx.session的roles→positions更名,PR #5991)时顺带测到的同族面,不在该 PR 范围内,按 Prime Directive #10 单独记录。事实
packages/runtime/src/security/actor-user.ts的ActorUser同时声明两个键,值完全相同:positions: string[]——/** ADR-0090 position names held by the caller (canonical; EvalUser.positions). */roles: string[]——/** Legacy alias of positions (pre-ADR-0090 spelling, kept for the REST/AI shapes). */构造处(约 208 行)是一次赋值两次:
这个形状被
packages/runtime/src/action-ctx-user-shape.test.ts明确钉住(the ADR-0090 position aliases stay in lockstep),并在http-dispatcher.test.ts的 action body 用例里断言过。所以它是有意的、被测试保护的、正在被消费的别名 —— 不是漏网的死代码。为什么仍然值得记一笔
它与 #5613 修掉的那个面是同一个形状,唯独少了后者刚装上的那一半:
ctx.session(#5613 后)ctx.user(现状)positionspositionsaction-session-*-to-positions)ActionSessionSchema(spec)ADR-0090 D3 把该拼法列为保留禁用词,而
check:role-word只扫content/docs与skills,扫不到.ts;packages/lint的security-role-word只判作者写的元数据,也判不到运行时构造的ctx.user。所以这个别名今天没有任何闸门在看,也没有任何东西约定它何时结束 —— 「两个拼法长期共存」正是 #5613 的裁定里被认定为缺陷而非终点的那个状态。为什么标
finding而不是缺陷今天没有用户会撞上:两个键恒等,读哪个都对。它是方向问题,不是故障 —— 因此按观察类记录,不挂
pm:queue,由 PM 分诊定级(严重度在立单时判断本来就不可靠,#1004 / #897 两个方向都出过错)。可能的处置(供分诊,不预判)
ctx.session仍在生产roles(值是ec.positions)—— 自称「mirroring hook ctx.session」,而 hook 侧该键已按 ADR-0049 退役 #5613 的模子办:给ctx.user面立 spec 契约(它今天连 schema 都没有,只有 runtime 的 interface),把roles标 deprecated,加 ADR-0087 语义迁移条目和关闭日期;现场
packages/runtime/src/security/actor-user.ts:95-98(声明)、:207-208(构造)packages/runtime/src/action-ctx-user-shape.test.ts:324-328(lockstep 钉子)positions/preserveAudit—— 引擎在生产、消费方在读、文档在教,契约里没有(#5050 的镜像方向) #5605(hook 侧补positions)、[spec] 为 action body 的ctx.session立契约 schema(按现状声明)—— #5613 契约先行裁定的第一阶段 #5697 / [#5613 第二阶段 · spec 半边] ActionSessionSchema 补positions键 +roles弃用对 + ADR-0087 语义迁移条目 #5779 / [runtime] action body 的ctx.session仍在生产roles(值是ec.positions)—— 自称「mirroring hook ctx.session」,而 hook 侧该键已按 ADR-0049 退役 #5613(actionctx.session契约 + 更名)查重:按
actor-user.ts、ctx.user roles、ADR-0090 D3 alias三组关键词搜过 open issues,未见同题(唯一命中的 #5521 是同一接缝的另一个字段ScriptContext.user的类型问题,已单独记录)。Generated by Claude Code