Skip to content

feat(runtime)!: action ctx.session 双发 positions(权威)+ roles(弃用别名) (#5613) - #5991

Merged
baozhoutao merged 3 commits into
mainfrom
claude/issue-5613-action-session-positions-rename
Aug 6, 2026
Merged

feat(runtime)!: action ctx.session 双发 positions(权威)+ roles(弃用别名) (#5613)#5991
baozhoutao merged 3 commits into
mainfrom
claude/issue-5613-action-session-positions-rename

Conversation

@baozhoutao

@baozhoutao baozhoutao commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #5613

#5613 phase 2 的 runtime 半边(spec 半边为 #5779 / PR #5849,已在主干 d4e080937)。维护者裁定「C 骨架 + A 语义,contract-first 两阶段」:契约先立、生产者随后。本 PR 就是那个「随后」。

前提复核(实测,非照抄 issue)

以最新 origin/main(5582e1821)为基线逐条实测:

  • packages/spec/src/ui/action-params.zod.tsActionSessionSchema 已含 positions(权威键)+ 弃用别名对,ADR-0087 语义迁移条目 action-session-*-to-positions 已在 packages/spec/src/migrations/registry.tsspec-changes.jsondocs/protocol-upgrade-guide.md 三处就位 —— 契约面确实已解锁;
  • buildActionSession() 现址 packages/runtime/src/action-execution.ts:768(issue 正文的 689-695 行号已漂),仍只发别名键,值取自 ec.positions;docstring 仍自称 "mirroring the hook ctx.session shape (Unify the developer-facing org identifier: hooks expose session.tenantId while RLS/seed/columns use organizationId (add organizationId as the blessed name) #3280)";
  • 两处调用点仍在:packages/runtime/src/domains/actions.ts:333action-execution.ts:999(均只是 session: buildActionSession(deps, ec),形状变化直接流过,无需改动);
  • 预埋翻转点(键集 toEqual 断言)仍在 packages/runtime/src/action-session-shape-contract.test.ts

前提成立。

改了什么

1. buildActionSession() 双发(packages/runtime/src/action-execution.ts)

...(Array.isArray(ec.positions) && ec.positions.length
    ? { positions: ec.positions, roles: ec.positions }
    : {}),

一个数组、两个键名,由构造保证同值(不是两次可能漂开的读取)。「非空才写」语义原样保留:无 positions(或 positions 非数组)时两个键都不出现 —— 'positions' in ctx.session 与别名键的 in 判定同真同假;无身份信封时整个 session 仍是 undefined 而非 {}(#3712)。

2. 两句错注释修正

3. 翻转预埋断言 + 承载双发同值(action-session-shape-contract.test.ts)

键集断言翻为含 positions 的新形态,并把窗口期的承重事实钉住:两个键都对着 ec.positions 断言(而不是互相比对),所以「其中一个改从别处取值」也过不了;新增一条「别名绝不单独出现」,是窗口关闭那天删别名的方向性保险。

4. 真实 dispatch 验证(http-dispatcher.test.ts)

迁移条目的验收口径写明「Verify against a real dispatch, not a fixture」,故补了一条走 dispatcher.handleActions 的用例:持有 positions 的调用者发起 action,断言 body 实际收到的 ctx.session.positions,以及同值的别名。

5. ScriptContext.session 收窄 —— 收的是联合,不是 ActionSession 单型

issue 第 4 条要求用 spec 的 ActionSession 收窄。实测后按「缩回最小面并说明」处理:这个 seam 确实同时承载 hook session 与 action session(body-runner.tsbuildSandboxContext / buildActionSandboxContext 两个写入方),把它声明成 ActionSession 单型,正是本 issue 要消灭的「一个键名两种现实」的同型错误 —— 只是换到类型面上。因此新增并导出:

export type ScriptSession = ActionSession | HookContext['session'];

session?: unknownsession?: ScriptSession#5697 当初留 unknown 的理由(收窄会逼 seam 的每个消费者去判别 body 种类)这次是实测而非再假设:两个写入方从 any 赋值,唯一读取方 quickjs-runnersetObjectJsonunknown,全仓(objectstack / objectui / cloud)ScriptContext 无 runtime 包外引用。所以今天没有任何站点需要判别,而联合类型正是让将来需要判别的站点被 tsc 抓住。收窄面止于 runtime 包内。

6. changeset(major)+ 文档

.changeset/action-session-positions-runtime-dual-emit.md,@objectstack/runtime: major,正文带 FROM → TO 迁移处方(改读 positions;别名在窗口内仍解析,窗口关闭按 #3280#3290 路径移除;不要…includes('admin') 的访问判断改名成 positions.includes('admin') —— 那是把缺陷改名而不是把读改对),并把 ScriptContext.session 的收窄作为第二处 breaking 明写。

文档面实测:全仓没有任何 hooks/actions 文档在教 action 侧的旧拼法(skills/objectstack-data 讲的是 hook 侧已退役的那个键,content/docs/references/ui/action-params.mdx#5849 已重生的生成物)。唯一真正教 action body ctx 的手写面是 skills/objectstack-ui/SKILL.md,在其「Action body context (ctx)」小节补了 ctx.session.positions 的权威读法 + 「不是授权输入」的警示 + 指向升级指南的迁移指路。

该小节改过一次,原因值得记录:初版在技能文档里直接写出弃用别名的示例,被 check:role-word(ADR-0090 D3 的 shrink-only 棘轮)判红 —— 保留词计数 2 → 5。棘轮是对的:技能文档是授权写法的教学面,只该教权威拼法;别名与迁移处方留在其本来的渠道(changeset + docs/protocol-upgrade-guide.md,均不在棘轮扫描面内)。已按此重写,node scripts/check-role-word.mjs 现报 OK (43 baselined file(s), no new occurrences)

验证

反向验证 —— 方向事先预测,结果与预测一致。 预测:这是最常见的「红」向,而非 #5046 式的「诊断变多」或 #5009 式的「反转」—— 因为断言的是产出对象的精确键集,而被撤掉的正是产出该键的那条肢,缺席可直接观测(不存在「计数归零导致断言因空而绿」的陷阱)。把双发撤回只留别名后实测:

× builds exactly the declared keys …
  → expected 3 keys to deeply equal [ 'organizationId', 'positions', …(2) ]
× carries `ec.positions` verbatim under BOTH …
  → expected undefined to deeply equal [ 'sales_rep', 'org_admin' ]
× emits the alias only alongside the canonical key …
  → expected the built key list to include 'positions'
× omits `userId` entirely for an org-scoped call with no user
  → expected 2 keys to deeply equal [ 'organizationId', 'positions', …(1) ]
 Tests  4 failed | 7 passed (11)

真实 dispatch 用例同向变红:expected undefined to deeply equal [ 'sales_rep', 'org_admin' ](http-dispatcher.test.ts:3659)。随后已还原。

正向(均在共享 verify 锁内、--max-old-space-size=4096):

  • pnpm --filter @objectstack/runtime test(--maxWorkers=2):Test Files 102 passed (102) / Tests 1476 passed (1476);
  • turbo run test --concurrency=2(全仓):135 successful, 135 total;
  • turbo run typecheck --concurrency=2(全仓):125 successful, 125 total —— 收窄没有溢出 runtime 包;
  • turbo run build --filter='!@objectstack/docs' --concurrency=2:71 successful, 71 total;
  • pnpm lint(eslint --no-inline-config,全仓):无输出即绿;lint job 的 24 个 check:* 逐条本地跑过,全 OK;
  • spec 侧 check:skill-docs / check:skill-refs / check:skill-examples / check:docs / check:spec-changes / check:upgrade-guide:全部 in sync;
  • node scripts/check-nul-bytes.mjs:OK (scanned 5738 tracked text file(s))

⛔ 未动 packages/spec,未动 content/docs/releases/

窗口关闭时要做什么(留给后来者)

移除别名的那一次改动,应当同时:producer 去掉别名键、action-session-shape-contract.test.ts 的键集期望与「双发同值」断言随之收缩、http-dispatcher.test.ts 的别名断言删除、spec 侧按迁移条目处置(条目已明说窗口期内立 tombstone)。技能文档无需改动 —— 它从一开始就只教权威拼法。


🤖 Generated with Claude Code

https://claude.ai/code/session_01DWUR56YsttL5sTF72Q75TQ

buildActionSession() 现同时输出 `positions`(ADR-0090 D3 权威拼法)与
`roles`(同值弃用别名),弃用窗口由 ADR-0087 语义迁移
`action-session-roles-to-positions` 声明;并修正 docblock 中「mirroring hook
ctx.session」的失实自述(hook 侧该键已于 #5050 退役),改为如实指向
ActionSessionSchema 与迁移条目。

- 翻转 action-session-shape-contract.test.ts 的预埋键集断言,并断言双发同值;
- 新增真实 dispatch 验证(http-dispatcher.test.ts),按迁移条目的验收口径;
- ScriptContext.session 由 `unknown` 收窄为 ScriptSession = ActionSession |
  HookContext['session'] 的联合(两个真实生产者形状),不收窄为 ActionSession
  单型——该 seam 确实承载 hook session。

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

vercel Bot commented Aug 6, 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 6, 2026 2:30pm

Request Review

@github-actions github-actions Bot added the size/m label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/api/client-sdk.mdx (via packages/runtime)
  • content/docs/api/index.mdx (via @objectstack/runtime)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/runtime)
  • content/docs/concepts/north-star.mdx (via packages/runtime)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/vercel.mdx (via @objectstack/runtime)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/runtime)
  • content/docs/kernel/cluster.mdx (via @objectstack/runtime)
  • content/docs/permissions/authentication.mdx (via @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/runtime)
  • content/docs/plugins/packages.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime)
  • content/docs/releases/implementation-status.mdx (via @objectstack/runtime)
  • content/docs/releases/v17.mdx (via @objectstack/runtime)

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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Aug 6, 2026
claude added 2 commits August 6, 2026 14:20
CI 的 check:role-word 是 shrink-only 棘轮:skills/objectstack-ui/SKILL.md
的保留词计数由 2 涨到 5(弃用别名示例 + 迁移条目 id + 反例代码各一)。
按 ADR-0090 D3 的本意,技能文档只教权威拼法 `ctx.session.positions`,
别名与迁移处方留在其本来的渠道(changeset + protocol-upgrade-guide,
均不在该棘轮扫描面内),正文以 `action-session-*-to-positions` 指路。

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

Copy link
Copy Markdown
Contributor Author

范围外发现(已单独立单,未在本 PR 修)

CI(head 7b73a6186,已并入最新 origin/main 后重跑):23 个 check 全绿,0 失败 —— ESLint / TypeScript Type Check / Build Core / Test Core (1-3/3) / Dogfood Regression Gate (1-3/3) / Dogfood Verify CLI / Temporal Conformance (live PG + MySQL) / Check Changeset / Check PR Size / Console Pin Freshness 等均 success,Build Docs 与 Console Pin Gate 按路径过滤 skipped。


Generated by Claude Code


Generated by Claude Code

@baozhoutao
baozhoutao marked this pull request as ready for review August 6, 2026 14:49
@baozhoutao
baozhoutao added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 739f496 Aug 6, 2026
24 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-5613-action-session-positions-rename branch August 6, 2026 15:06
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

2 participants