Skip to content

feat(spec)!: action 收紧,ADR-0010 信封欠债归零(#4001 批 6d) - #4533

Merged
os-zhuang merged 3 commits into
mainfrom
claude/strict-schema-authz-surface-s8vnok
Aug 1, 2026
Merged

feat(spec)!: action 收紧,ADR-0010 信封欠债归零(#4001 批 6d)#4533
os-zhuang merged 3 commits into
mainfrom
claude/strict-schema-authz-surface-s8vnok

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

#4001 批 6d(清单)。批 1–6c = #4514#4532,均已合并。

模板自己一直没被应用到

ActionParamSchema#3746 起就是 strict——这场战役就是从它推广出来的,而且它贡献了最锋利的那条教训:

visibleWhenvisible 证明了,别名表里最值钱的那条从来不是错别字,而是一个读起来像控制、实际不是的键。

而 param 外面的那个 action,又开了三个 release。

为什么这一个特别要紧:AI 暴露块

ActionAiSchema治理闸门。它自己的文档写着:平台的价值在于「一个人类能精确治理 agent 队伍被允许调用哪些能力」,以及「一个半成品或未经审阅的 action 绝不能被静默武装」。

然而 —— requireConfirmation(比 requiresConfirmation 少一个字母)会被静默丢弃

一个作者要求在 AI 调用这个 action 时插入人类审批环节,他没有得到,而且没有人告诉他

现在这个块和 action 根都会拒绝,并且对作者最容易写错层级的两个键给出处方(exposedrequiresConfirmation 属于 ai 之下)。


⚠️ 这个 PR 里我自己犯的两个错

两个形状相同:对着「我记得的 schema」判断,而不是对着 schema。都已修正(commit 2、commit 3)。

错误一:一条会让人删掉权限门的处方

第一版 guidance 写的是「permissions 写在 action 上不是权限门」,并把作者引向对象的权限集。

这是错的,而且错在最要命的方向。

requiredPermissions(ADR-0066 D4)一个已声明的键,在平台 action 路由上以 403 强制执行,而 content/docs/ui/actions.mdx 就在教它:

locations: [],                            // no UI surface
requiredPermissions: ['finance.admin'],   // still gated

任何照着我那条处方做的人,都会被引导去删掉一个正在工作的权限门。

现在
permissions / capabilities / acl 改名到 requiredPermissions
requiredPermissions 的 guidance 条目 删除——键是声明过的,strict 根本到不了它;那条既错误又不可达
locationlocations 方向反了:locations 才是声明的键,原别名会建议把正确的键改成不存在的键
visible/disabled 那句警告 移到 hidden 上——隐藏不等于门控,无 UI 的 action 是 locations: []依然有门

账本第 7 条的第四次这场战役自己的处方,本身就是一个可能自信地写错的表面。

抓到它的是那份 docs-drift 报告——那份我一直当例行公事的「107 个文件、仅供参考」清单,逐条对着 schema 核的时候,ui/actions.mdx 正好教了那个键。

错误二:本地只跑了 packages/spec

CI 的 Test Core 挂了 —— @objectstack/metadata-protocol 两个 fixture 在 action 上写了 object: 'crm_invoice',而 ActionSchema 从未声明过 object,键是 objectNamegit log -S 确认没有任何 commit 曾声明过它,所以不是缺转换链,就是打错的键。

值得记的是它在哪儿:这两个 fixture 扮演「数据库里的 pre-17 行」,所以一个打错的键读起来像一个关于真实历史数据的断言——「存量 action 带 object」——而那个键从未存在过

第六个 strip 时代的假象,第一个伪装成「存量行」而不是「作者写的字面量」的。

教训直白:关闭一个注册类型的影响半径,是每一个解析那个类型的包,不是 packages/spec 一个。


🎯 ADR-0010 信封欠债归零

结构化遍历当初打开这张表时有八个名字action book field job mapping page translation validation)——而它替换掉的那个探针,藏了其中七个

action 是最后一个。

空集合被保留,而不是删掉。 没有豁免项之后,DECLARES the protection envelope 这条会跑遍每一个注册类型——新类型漏了那个 spread 会立刻报红,而不是被悄悄加进一张豁免表

往回加一个名字,是在提一个 bug,不是在授予一个豁免

进度

注册类型顶层已关闭:24 / 25。只剩 view

两件记下来而不是抹掉的事

1. 数组下标那个测试已经没有测试对象了。 action 关闭之后,整个注册面上再没有任何仍是 strip 的「对象数组」。遍历器的数组处理没变、依然正确——消失的是能触发它的元数据类型。测试改为断言交接 + 已关闭根下的逐节点下降(#4522),并在原地写明:将来出现新的 strip 表面就把带下标的断言加回来。

2. view 是最后一个开着的根。 它关闭后这一层在根层面就没有可警告的了。测试里写明:把下限改成 0 并「刻意」断言空集,不要删掉这个测试——因为一个没人选择的空结果,和一个坏掉的推导,长得一模一样

验证

  • @objectstack/spec284 文件 / 7240 用例tsc --noEmit 干净
  • 全仓测试套件通过(exit 0,零失败)—— 这次不再只跑 spec
  • 8 个生成物 up-to-date,spec check:* 全绿
  • CRM / Todo / showcase / platform-objects 构建通过
  • 修正后单独验证:requiredPermissions + locations: [] 被接受permissions/capabilities 改名到 requiredPermissionslocation 改名到 locations

授权影响:ActionSchema 没声明的键从「静默丢弃」变成「拒绝」——本来就已经被忽略。

参考

🤖 Generated with Claude Code

https://claude.ai/code/session_01WnqGjQFQMqd5k81LYV8SCY

…es zero (#4001)

`ActionParamSchema` has been strict since #3746 — the template this campaign was
generalized from, and the source of its sharpest lesson: `visibleWhen` →
`visible` showed the most valuable alias entry is rarely a typo but a key that
reads as a control and silently is not one. The action AROUND the param stayed
open for three more releases.

The AI exposure block is why this one mattered. `ActionAiSchema` is the
governance gate — its own doc says a half-finished or unreviewed action must
never be silently armed — yet `requireConfirmation`, one letter off, was dropped
in silence, so an author who asked for a human-in-the-loop gate on an
AI-invoked action did not get one and was not told.

The root tombstones the key this campaign cares most about. `permissions` on an
action is NOT a permission gate: authorization comes from the object's
permission sets and, on the AI surface, from the agent's access/permissions
(#1884). The rejection says so and names the trap beside it — `visible` and
`disabled` are UI predicates, they hide or grey a button, they do not stop a
request. Third time in this campaign after skill.permissions and
agent.visibility, always a different surface, always reading like a control.

The undeclared-envelope debt list is now EMPTY. The structural walk opened it
with eight names after replacing a probe that had been hiding seven; `action`
was the last. The empty set is kept, not deleted: with no exemptions the
DECLARES case runs over every registered type, so a new type shipping without
the spread fails immediately instead of being quietly added to a list.

Registered types closed: 24 of 25. Only `view` remains.

Two lint-layer facts recorded rather than papered over: the array-index test has
run out of subject (no strip-mode array-of-objects remains anywhere), and `view`
is the last open root — when it closes, change the floor to 0 and assert the
empty set deliberately rather than deleting the test, because an empty result
nobody chose is indistinguishable from a derivation that broke.

Verified: 284 files / 7240 tests, tsc clean, 8 generated artifacts current, all
spec gates green, and CRM/Todo/showcase/platform-objects build.

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

vercel Bot commented Aug 1, 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 1, 2026 8:02pm

Request Review

@os-zhuang
os-zhuang marked this pull request as ready for review August 1, 2026 19:41
@os-zhuang
os-zhuang enabled auto-merge August 1, 2026 19:42
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ui tooling labels Aug 1, 2026
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

107 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 packages/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/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/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/kernel/runtime-capabilities.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/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.

@github-actions github-actions Bot added the size/m label Aug 1, 2026
claude added 2 commits August 1, 2026 19:45
…s AT it (#4001)

The first draft of this batch's guidance claimed `permissions` on an action is
not a permission gate and sent authors to the object's permission sets. That is
wrong: `requiredPermissions` (ADR-0066 D4) is a declared key enforced with a 403
on the platform action route, and `ui/actions.mdx` teaches it by name. Anyone
following the prescription would have been invited to delete a working gate.

  - `permissions` / `capabilities` / `requiresPermissions` / `acl` now RENAME
    onto `requiredPermissions` instead of pointing away from it.
  - The dead `requiredPermissions` guidance entry is removed — the key is
    declared, so strict never reaches it; the entry was wrong AND unreachable.
  - `location` → `locations` was backwards: `locations` is the declared key, so
    the alias would have suggested renaming a correct key to a nonexistent one.
  - The `visible`/`disabled` warning survives on `hidden`, where it belongs:
    hiding is not gating, and a headless action is `locations: []`.

Ledger finding 7 for the fourth time — this campaign's own prescriptions are a
surface that can be confidently wrong. Caught by checking the docs the drift
report flagged against the schema rather than against memory of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnqGjQFQMqd5k81LYV8SCY
…e key is `objectName` (#4001)

`ActionSchema` has never declared `object` — the key is `objectName`. Two
stored-row fixtures wrote it anyway, `.strip` ate it, and the tests passed. With
the shape closed the row now fails spec validation after conversion and the
protocol reports `invalid: 1`.

Worth naming because of where it was: these fixtures stand in for pre-17 rows in
a database, so a typo in one reads as a claim about real legacy data — "stored
actions carry `object`" — when no such key ever existed. There is no missing
conversion here; `git log -S` finds no commit that ever declared it.

Sixth strip-era fiction this campaign has found in a test, and the first dressed
as a stored ROW rather than an authored literal.

Also the reason this was caught by CI rather than locally: the previous pass ran
only `packages/spec`. The blast radius of closing a registered type is every
package that parses that type.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnqGjQFQMqd5k81LYV8SCY
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 1, 2026
Merged via the queue into main with commit 80334c7 Aug 1, 2026
21 checks passed
@os-zhuang
os-zhuang deleted the claude/strict-schema-authz-surface-s8vnok branch August 1, 2026 20:23
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.

2 participants