Skip to content

refactor(spec)!: 退役 HookContext.session.roles —— 声明过、被两条死分支读过、从未被生产 (#5050) - #5621

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-5050-retire-session-roles
Aug 5, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/issue-5050-retire-session-roles

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5050

前提复核(动手前对 origin/main 实测)

问题 结论 证据
键还在吗 在 packages/spec/src/data/hook.zod.ts:374 roles: z.array(z.string()).optional()
还有消费方吗 零 全仓 session.roles 命中只剩 #4839 留下的注释/pin/changeset,以及 spec 自己的夹具与技能文档
有生产方吗 零(hook 路径) buildSession()(packages/objectql/src/engine.ts:1479)逐字段构造 —— userId / organizationId / positions / accessToken / isSystem / actor / skip 标记 —— 没有 roles 写入点

跨仓按 #4895 的双方向做(#4865 的教训:退役前必须有阳性对照):

  • cloud:session.roles 零命中;同一轮反查阳性 —— 它的 hook 消费方确实在读 hookContext?.session?.userId(service-cloud/src/marketplace-visibility-plugin.ts:98、control-plane-org-scope-plugin.ts:237-248)。即「grep 能找到东西,只是找不到这个键」。
  • objectui:零命中;反查阳性(roles 在该仓存在,但都是 /auth/me 的 user 载荷 —— app-shell/src/layout/AppHeader.tsx:457 等,另一张面,不受影响)。

结论:前提成立。

退役路线

HookContextSchema 刻意不是 .strict()(文件头注释写明理由:引擎给上下文加字段——如 #3712 的 provenance——不应变成消费方的破坏性变更)。所以按 playbook §2:

ADR-0087 处置:语义迁移,不是 conversion(重点,请审这一条)

没有做 D2 conversion,是有意的:HookContext 是引擎每次操作现建的运行时上下文,从不落库 —— 没有任何 sys_metadata 行、example 或 template 能携带这个键,os migrate meta 无源可改。造一个没有对象的 conversion 只会让升级指南宣传一层不存在的覆盖。

按仓内既有判例走 SemanticMigration(MIGRATIONS_BY_MAJOR[17].semantic[]),与 openApi31(#4579)、activationEvents(#4657)、workflow 服务槽(#4451)同形:

  • 新条目 hook-context-session-roles-retired,surface: data.hookContext.session.roles
  • 处方因此仍然进 spec-changes.json、生成的升级指南、spec_changes MCP 工具
  • 墓碑的 guidance 里没有 os migrate meta 那句(playbook 约定:只有 conversion 真的改源码时才写)

闸门这边也自洽:build-schemas.ts 的 (b) 闸只走顶层键,session 是内联嵌套对象(快照里只有 data/HookContext:session 一行,没有 session.roles),所以它既不要求也不阻拦 —— 判据与闸门给出同一个答案。

四张 ratchet 零变化 —— 这是正常读数

按 playbook「先定路线再决定该期待什么读数」:本次是内联嵌套键收窄,def 还在、导出面不变,所以 api-surface / authorable-surface / api-surface-signatures / json-schema.manifest 全部字节不变(与 #4391 枚举值收窄同类,而非 #4834 整 def 删除)。check:authorable-surface 前后皆绿即为此。

authorable-surface.base.json 的变动是 gen:schema 的机械重锚(baseRev 指向本分支的 merge base),同 a9f32df / cdfbee2 等 spec PR 的既有行为。

台账

packages/spec/liveness/hook.json 治理的是 HookSchema(可授权元数据类型),HookContextSchema 不在 walk 里,session.roles 从来没有台账行 —— 所以既没有要留的墓碑行,也没有要删的孤儿行。已核对,无改动。

一处真实的坑:文档把墓碑宣传成 any

墓碑一开始留在原位(session 的第 4 个键),重生成后 content/docs/references/data/hook.mdx 变成:

session | { userId?: string; actor?: string; organizationId?: string; roles?: any; … }

z.never() 没有 JSON-Schema type,渲染器落到 prop.type || 'any';而内联 shape 摘要只印前 4 个键、放不下 [REMOVED] 处方。退役反而把这个键宣传成"随便写"的自由槽,正是 ADR-0033 陷阱对着文档的一面。

本 PR 的处理:把墓碑挪到 shape 底部,摘要因此只展示 4 个活键(userId / actor / organizationId / accessToken),并在源码注释里写明为什么,防止后人"整理"回去。两个真实通道(tsc + parse)完全不受影响。

⚠️ 这是绕开,不是修好,只对「墓碑不在前 4 位」的情况有效。渲染器缺陷本身已另开 #5606 —— 它现在就在伤 references/ui/theme.mdx:130({ base?: string; heading?: any; mono?: any },两个 #5021 的墓碑嵌套两层、整页没有任何一处出现它们的处方,描述列还是空的)。修它要全仓重生成 references,不该搭在本 PR 上。

消费半径扫描的收获(#5046 的教训:按规则被谁消费扫,不是按改了哪个包扫)

扫 HookContext 的全部导入方时发现 packages/runtime/src/action-execution.ts:694 的 buildActionSession() 确实写了 roles: ec.positions,而且它的注释自称 "mirroring the hook ctx.session shape"。

这不证伪本次退役:那是 action body 的 ctx.session,另一个对象,裸 any,不经任何 schema,永远不会变成 HookContext(沙箱侧 ScriptContext.session?: unknown)。但它确实会让后来者拿着「我在 action 里明明读到了 session.roles」来推翻这里的零生产方结论 —— 这正是 #4865 的形状。所以:

测试与反向自证

pin 测试 5 条(packages/spec/src/data/hook.test.ts),方向在跑之前就先定好:还原 roles: z.array(z.string()).optional() 应当让 parse 断言转红、并让两条 @ts-expect-error 报 TS2578(#5478 之后 spec 测试层真的进 tsc,类型 pin 是活的)。实测:

还原后 vitest run src/data/hook.test.ts:

Tests  3 failed | 63 passed (66)
 FAIL  > session.roles retirement (#5050) > REJECTS an authored `roles`, with the prescription in the message
 FAIL  > session.roles retirement (#5050) > names the live vocabulary rather than only refusing
 FAIL  > session.roles retirement (#5050) > fails tsc at the producer — the channel that outranks the parse here

还原后 tsc --noEmit --project tsconfig.test.json:

src/data/hook.test.ts(952,9): error TS2578: Unused '@ts-expect-error' directive.
src/data/hook.test.ts(964,7): error TS2578: Unused '@ts-expect-error' directive.

恢复墓碑后两者皆绿。预测方向 = 实测方向。

其中一条 pin 值得单独说:「墓碑 ≠ 变 strict」 —— 断言一个未知键仍然被静默 strip,只有退役键才响。这条是把「为什么不能直接删」和「为什么不能顺手加 .strict()」两个判断一起钉住。

正式验证(worktree 内,统一走 flock /tmp/os-heavy-verify.lock):

pnpm --filter @objectstack/spec test        → Test Files 314 passed (314) / Tests 8015 passed (8015)
pnpm --filter @objectstack/spec typecheck   → tsc --noEmit 通过;check:test-typecheck: OK
check:generated                             → ✓ All 10 generated artifacts are up to date.
check:liveness / check:empty-state / check:authorable-surface / check:api-surface /
check:spec-changes / check:upgrade-guide / check:skill-refs / check:skill-docs /
check:skill-examples                        → 9/9 PASS
node scripts/check-nul-bytes.mjs            → OK(另对本次全部改动文件自查 0x00-0x1f,零命中)

改了什么

  • packages/spec/src/data/hook.zod.ts —— 墓碑 + 就地注释(含"墓碑放底部"的理由与 [runtime] action body 的 ctx.session 仍在生产 roles(值是 ec.positions)—— 自称「mirroring hook ctx.session」,而 hook 侧该键已按 ADR-0049 退役 #5613 邻居声明)
  • packages/spec/src/migrations/registry.ts —— 语义迁移条目 + step17 rationale 段落
  • packages/spec/src/data/hook.test.ts —— 2 处夹具重判 + 5 条 pin
  • skills/objectstack-data/references/data-hooks.md —— 3 处:字段表、类型清单、以及那个用死键做脱敏判断的示例(原写法 isAdmin 恒为 undefined,改为按 isSystem 豁免,并写明按角色豁免应当落在字段级权限)
  • packages/plugins/plugin-approvals/src/admin-exemption-retired.test.ts —— 仅注释:原文说"spec 现在声明 roles",退役后改为过去式(该文件的 pin 与仍然拼 roles: ['admin'] 的夹具故意保留,它们证明这个拼法在运行时同样什么都不授予)
  • 生成物:spec-changes.json、docs/protocol-upgrade-guide.md、content/docs/references/data/hook.mdx、authorable-surface.base.json
  • changeset:@objectstack/spec major

顺带登记的三个 issue(Prime Directive #10,均未实现、未指派)

🤖 Generated with Claude Code

https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D


Generated by Claude Code

… two dead branches, never produced (#5050)

`session.roles` on the runtime hook context had neither end: declared in
`data/hook.zod.ts`, read only by the two plugin-approvals admin exemptions
deleted in #4839 (PR #5049), and never written by `buildSession()` or anything
else feeding a HookContext. ADR-0049 enforce-or-remove disposition: REMOVE.

- tombstoned with `retiredKey()` (HookContextSchema is deliberately not
  `.strict()`, so a plain delete would strip the key silently — #3733/ADR-0104)
- placed BELOW the live keys: the reference generator renders a `z.never()` as
  `any` inside an inline shape summary, so in its original 4th position it made
  `references/data/hook.mdx` advertise `roles?: any` (renderer gap filed #5606)
- ADR-0087: a SemanticMigration (`hook-context-session-roles-retired`), NOT a
  D2 conversion — a HookContext is built per operation and never stored, so no
  source exists to rewrite (the `openApi31` / `activationEvents` shape)
- pins both channels: the parse prescription and two `@ts-expect-error`
  directives, live since #5286/#5478 put the test layer in front of tsc
- skills/objectstack-data hook reference no longer teaches the dead key

Cross-repo consumer check ran in both directions (cloud/objectui, #4895's
discipline). The action body's `ctx.session` is a different, untyped object
that does carry `roles` — named explicitly here and filed as #5613 so it is
not mistaken for a producer of this key.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
@vercel

vercel Bot commented Aug 5, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 5, 2026 9:26pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling size/m labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

109 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Copy link
Copy Markdown
Contributor Author

PM 预记(session_018fxLGQdatPbBUvCgiVxg6D):本 PR 的 ESLint job 将因 #5604(main 侧 check:engine-double-contract 断裂,#5584 遗留,与本 diff 无关)而红——验收时不计入本单质量账,#5604 修复落地后合 main 重跑。

「ADR-0087 处置:语义迁移而非 conversion」一条 PM 初审认可(否决窗口开放):HookContext 是运行时现建、从不落库,conversion 无源可改,MIGRATIONS_BY_MAJOR[17].semantic[] 与 openApi31/activationEvents/workflow 槽三判例同形,且 build-schemas (b) 闸(仅顶层键)给出同一答案——判据与闸门自洽。维护者如要求 conversion 形式,回一句即改。

changeset 为 major(退役),与 v17 pre 窗口内既有退役单(如 #5293)同待遇,check-changeset-no-major 结果以 CI 为准。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 5, 2026 22:14
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit cbb6a5c Aug 5, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5050-retire-session-roles branch August 5, 2026 22:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[spec] 退役 HookContext session.roles —— #4839 双删后零消费方零生产方(ADR-0049)

2 participants