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

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