Skip to content

feat(spec): ActionSession 补 positions 权威键,roles 降为弃用别名 (#5779) - #5849

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-5779-action-session-positions
Aug 6, 2026
Merged

feat(spec): ActionSession 补 positions 权威键,roles 降为弃用别名 (#5779)#5849
os-zhuang merged 3 commits into
mainfrom
claude/issue-5779-action-session-positions

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #5779

#5613 第二阶段的 spec 半边,依维护者「C 骨架 + A 语义」裁定。⛔ packages/runtime 零文件触碰(已实测确认,见下)。

前提核验(实测 origin/main @ 1624f4ad2)

issue 前提成立:ActionSessionSchemapackages/spec/src/ui/action-params.zod.ts:206,roles 已标 deprecated,其 docstring 明写「The rename is #5613 phase 2; do NOT add a positions key here ahead of it」——本单正是那条禁令等的东西,随本 PR 解除并改写。

做了什么

  1. positions 为权威键(z.array(z.string()).optional())。describe 指向 ADR-0090 D3 / ExecutionContext.positions,并把现 roles describe 里「Never gate PRIVILEGE on this array——问 security service(ADR-0095)」的告诫原样带过来:告诫属于而不属于拼法,所以更名必须原样保留。JSDoc 写明双发窗口语义,以及一句必须说破的话——把 roles.includes('admin') 改写成 positions.includes('admin')迁移了缺陷,不是迁移了代码。
  2. roles 改写为弃用别名对:describe 改为「DEPRECATED alias of positions」,点名 ADR-0087 条目 id 与 Unify the developer-facing org identifier: hooks expose session.tenantId while RLS/seed/columns use organizationId (add organizationId as the blessed name) #3280Remove the deprecated hook/action ctx.session.tenantId alias in the next major (converge on organizationId) #3290 session.tenantId 别名下线先例路径;@deprecated JSDoc 保留并更新指向。
  3. ADR-0087 语义迁移条目 action-session-roles-to-positions(step17,紧邻 refactor(spec)!: 退役 HookContext.session.roles —— 声明过、被两条死分支读过、从未被生产 (#5050) #5621 落的 hook-context-session-roles-retired)。定性为语义迁移而非 D2 数据转换,两条独立理由:action ctx 每次派发现造、从不落库,无存量源可改写(照 [spec] 退役 HookContext session.roles —— #4839 双删后零消费方零生产方(ADR-0049) #5050 / RestServerConfig.openApi31(OpenApi31Extensions / Callback / OpenApiWebhookEvent)declared ≠ enforced:没有任何运行时读取它 —— ADR-0049 enforce-or-remove 候选 #4579 / ADR-0049:两份 activationEvents 声明四仓零 reader —— declared-but-unenforced,且 studio 侧 z.string() 零校验 #4657 先例);且这个键唯一被拼写的地方是 action body 里的自由代码,声明式 transform 不能安全改写代码中的标识符——正是 ADR-0090 那一波在 step 13 把 current_user.roles 交还作者(cel-current-user-roles-to-positions)而非做文本替换的同一理由。条目里另写明故意不立墓碑:retiredKey()拒收该键,而弃用窗口的全部意义就是让旧拼法继续可用——窗口期立墓碑等于把它要推迟的移除提前执行了。
  4. 生成物走 os-regen:authorable-surface.json +1 键(ui/ActionSession:positions,与预期一致)、spec-changes.jsondocs/protocol-upgrade-guide.md、reference 页。
  5. changeset:@objectstack/spec minor,正文是作者视角的迁移处方。

兼容性核验(先证红方向,再实测)

预测写在跑之前:本单只加 optional 键,runtime 的 action-session-shape-contract.test.ts(钉 parse(built) 深等于 built + 键集断言)应当保持绿——加可选键既不能拒收原本合法的对象,也不会在缺该键的输入上把键凭空造出来;而生产方(仍只发 roles)根本没动。

时点 结果
改动基线 Test Files 1 passed (1) / Tests 10 passed (10)
改动 Test Files 1 passed (1) / Tests 10 passed (10)

方向与预测一致。这里不是「先绿后红」那类反向验证,而是一次不动的预测——说明它有意义的,是方向和机制都写在跑之前:若它红了,就意味着契约把生产方带崩了,而弃用窗口存在的全部目的正是不让这件事发生。已在测试块注释里如实标注这一点,没有硬套模板。

spec 侧先证红(把 positions 声明删掉后实测):

× PRESERVES `positions` through a parse instead of stripping it
    AssertionError: expected { userId: 'usr_1', …(1) } to have property "positions"
× accepts the DUAL-EMIT shape #5613's runtime half will produce — both keys, one value
    AssertionError: expected { userId: 'usr_1', …(2) } to deeply equal { userId: 'usr_1', …(3) }
× pins `positions` as CANONICAL and carries the ADR-0095 privilege boundary
    TypeError: Cannot read properties of undefined (reading 'description')
 Tests  3 failed | 15 passed (18)

三条按预测转红,已恢复。

钉什么的判据:ActionSessionSchema 刻意非 strict(平台交给 body 的运行时形状),所以 safeParse().success 在这里毫无价值——未声明的键照样「解析成功」然后被静默剥掉。承重断言因此是「parse 保住了这个键」。这是键可达性问题而非值裁决问题(schema 对 positions 的值除 string[] 外不作判断),所以要求更多就是钉了契约没主张的东西。

消费半径扫描

ActionSessionSchema 的消费方:runtime 的 pin 测试 + action-execution.ts 的类型标注 + sandbox/script-runner.ts 的注释——均不改。全仓 grep session.roles 的其余命中都在 hook 面(skills/objectstack-data/references/data-hooks.mdplugin-approvals#4839 pin、hook.zod/hook.test),是 #5050 已退役的另一张面,与本单无关且未触碰。

验证

命令 结果
pnpm --filter @objectstack/spec check:generated ✓ All 10 generated artifacts are up to date.
pnpm --filter @objectstack/spec typecheck tsc --noEmit 通过;check:test-typecheck: OK
pnpm --filter @objectstack/spec test Test Files 319 passed (319) / Tests 8152 passed (8152)
runtime pin(兼容性核验) 10 passed (10),改动前后各一次
node scripts/check-nul-bytes.mjs OK (scanned 5678 tracked text file(s));另对本单所有改动文件做了 grep -naP 越界自扫,干净

两处需要 reviewer 知道的判断

后续

#5613 的 runtime 半边(buildActionSession() 双发、两句错注释修正、翻 action-session-shape-contract.test.ts 键集断言、major changeset)本单落地后可派。承重翻转点未动,仍在原处。


Generated by Claude Code

…as its deprecated alias (#5779)

The spec half of #5613 phase 2, under the maintainer's contract-first ruling
("C skeleton + A semantics"). Phase 1 (#5697) declared the action-body
`ctx.session` shape exactly as `buildActionSession()` built it and deliberately
withheld a `positions` key — "minting one before the migration would ship two
live spellings of one value". That prohibition existed to stop a two-spelling
window opening without a closing date; this change opens it WITH one, and
rewrites the docstring that carried the ban.

`positions` is now the canonical key: the ADR-0090 D3 vocabulary the execution
context, the sharing service and (since #5605 / PR #5722) the hook `ctx.session`
already speak. `roles` becomes a deprecated alias of it, removed after one
window on the path `session.tenantId` already walked (#3280 deprecated, #3290
removed in v11).

The ADR-0087 semantic migration `action-session-roles-to-positions` is the
reader-facing channel. It is a D3 semantic TODO rather than a D2 conversion on
two independent grounds: an action `ctx.session` is constructed per dispatch and
never persisted, so there is no source for the chain to rewrite; and the only
place the key is ever spelled is inside an action body, i.e. free-form
author-written code, which is why the ADR-0090 wave delegated
`current_user.roles` at step 13 instead of substituting text. The alias is
deliberately NOT tombstoned — a tombstone rejects the key, which is the removal
a deprecation window exists to defer.

Contract leads producer: `buildActionSession()` dual-emitting both keys, and the
two wrong sentences already tracked in its docblock, are #5613's runtime half
and are not in this change. `packages/runtime` is untouched. Both keys are
optional, so the runtime consistency pin
(`action-session-shape-contract.test.ts`) is unchanged and stays green — a
non-strict parse of a session without `positions` neither gains the key nor
rejects the object. Measured before and after: 10/10 passing both times.

Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D

Co-authored-by: Claude <noreply@anthropic.com>
@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 8:51am

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/spec.

110 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/field-grouping-and-order.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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ui tooling labels Aug 6, 2026
claude added 2 commits August 6, 2026 08:02
… describe text (#5779)

`check:org-identifier` is a hard-fail guard, and it skips COMMENT lines but not
code — so naming the removed alias in a JSDoc note is documentation, while
naming it inside a `.describe()` string or a migration `reason` is a published
string the gate rightly refuses. Those strings are not incidental: a
`.describe()` lands verbatim in `content/docs/references/`, which is exactly the
author-facing prose an AI copies a hook or action body from, and the guard
exists because a `session.tenantId` read there resolves to `undefined` (#3290
removed it in v11).

Three occurrences reworded to "the v11 session-alias removal path", keeping the
#3280#3290 anchor that makes the precedent findable: two `.describe()`
strings on ActionSession (`positions`, `roles`) and the migration entry's
`reason`. The JSDoc mentions are left as they are — the gate permits them by
design, and the pre-existing `organizationId` docblock already names the alias
the same way.

`os-allow-tenant-id` was deliberately NOT used: it is documented for the rare
genuine driver-layer read, and spending it on prose would launder a
documentation reference past a guard that has zero baselined occurrences.

Generated artifacts regenerated from the reworded source; the `#3290` assertion
in the description pins still holds.

Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D

Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Contributor Author

org-identifier 措辞修订(575a11812,含 origin/main 合并 ae33b159a)

check:org-identifier 红,三处命中,已修:

文件 位置
packages/spec/src/ui/action-params.zod.ts positions.describe()
packages/spec/src/ui/action-params.zod.ts roles.describe()
packages/spec/src/migrations/registry.ts 语义迁移条目的 reason

闸门为什么对:它跳过注释行但不跳过代码行——所以 JSDoc 里点名这个已移除别名是「文档」(现存 organizationId 的 docblock 本来就这么写,一直是绿的),而写进 .describe() 字符串里就不是了。.describe()原样落进 content/docs/references/,正是 AI 作者照抄 hook/action body 的那张面;而 #3290 已在 v11 把 session.tenantId 从该面移除,照抄过去的读解析为 undefined。这条闸门是零基线硬失败,不是 ratchet。

没有用 os-allow-tenant-id:该逃生口的注释写明是给「rare genuine driver-layer session.tenantId」用的。把它花在一处散文引用上,等于把文档引用洗过一道零基线的闸门——正是这类闸门存在要拦的事。改为「the v11 session-alias removal path」,保留 #3280#3290 锚点,先例依然可检索;描述 pin 里的 expect(doc).toMatch(/#3290/) 未受影响,仍绿。

合并 origin/main 后重跑(#5358 本身也已在 main 落地,恰好把我手工剔除 base.json 重锚的处置变成了工具的默认行为):

命令 结果
pnpm check:org-identifier OK (1659 author-facing source file(s), no removed session.tenantId alias).
pnpm check:role-word OK (43 baselined file(s), no new occurrences).
pnpm --filter @objectstack/spec check:generated ✓ All 10 generated artifacts are up to date.
pnpm --filter @objectstack/spec test Test Files 321 passed (321) / Tests 8204 passed (8204)
pnpm --filter @objectstack/spec typecheck 通过
runtime pin(兼容性核验,第三次) Tests 10 passed (10)
node scripts/check-nul-bytes.mjs OK (scanned 5697 tracked text file(s))

合并时 docs/protocol-upgrade-guide.md 被 os-regen 驱动挂起(未做文本合并),已按纪律从合并后的树重生成;pre-commit 自查确认 ✓ current — marker cleared。分支相对 origin/main 的 delta 仍恰为 8 个文件,packages/runtime 零触碰


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review August 6, 2026 09:16
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit d4e0809 Aug 6, 2026
25 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-5779-action-session-positions branch August 6, 2026 09:29
os-zhuang pushed a commit that referenced this pull request Aug 6, 2026
…ble-surface)

#5861 被合并队列踢出,签名是 `strictness-ledger-doc.test.ts > is checked in current`。
归因:#5849 / #5857 在入队前合入 main(#5857 给 `api/` 新增了一个 z.object 站点),
本分支的 counts.md 是在旧树上渲染的,合并树上不再等于现渲染 —— 单体生成物的串行税
(#5837 正在治的病),不是实现回归。

在合并树上重跑 os-regen:`api/` 站点数 395 → 396(main 的 +1 与本单 `projectionApplied`
嵌套对象的 +1),docs 与 authorable-surface 一并重渲。相对 origin/main 的净增量仍只有
本单四个键。`authorable-surface.base.json` 本轮无重锚漂移(已核对与 main 一致)。

Co-Authored-By: Claude <noreply@anthropic.com>
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 protocol:ui size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[#5613 第二阶段 · spec 半边] ActionSessionSchema 补 positions 键 + roles 弃用对 + ADR-0087 语义迁移条目

2 participants