Skip to content

feat(spec)!: mapping/agent/page 收紧,strictObject 不再把作者指向已删除的键(#4001 批 6a) - #4530

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

feat(spec)!: mapping/agent/page 收紧,strictObject 不再把作者指向已删除的键(#4001 批 6a)#4530
os-zhuang merged 1 commit into
mainfrom
claude/strict-schema-authz-surface-s8vnok

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

#4001 批 6a(清单)。批 1–5 = #4514 / #4519 / #4522 / #4527 / #4528 / #4529,均已合并。

先修这场战役自己捅的娄子

上一批 skill 关闭时,它还带着 retiredKey 墓碑。而 strictObject 的「你是不是想写」候选表取自整个 shape——墓碑也在里面。于是:

Unrecognized key(s) on this skill: `triggerPhrase`. …
Did you mean `triggerPhrase` → `triggerPhrases`?

triggerPhrases是被删掉的键。 照做的作者会撞上墓碑,拿到第二条拒绝,告诉他把刚被指示写下的东西删掉。

账本里已经记了两次的形状,这是第三次——而且第一次出现在共享 helper 里,也就是说它会流向之后每一次转换。两个 helper 各自都是对的,只有组合起来是错的——这种缺陷没有任何单 schema 的测试会去找。

修法不是给墓碑开特例,而是一条更窄也更稳的规则:永远不要建议一个 schema 根本不接受的键。 候选表剔除接受 never 的键(不管包了几层)。

两者是互补的,不是重复:retiredKeyguidance 更强——它把键的类型变成 never,所以即使配置是从变量传进来的也会 tsc 报错,而这正是 excess-property 检查够不到的地方。

agent 有两个安全形状的删除,没有墓碑

visibilitytenantId 当初作为未被强制执行的安全属性被删除——删得对,它们确实什么都没做——但删的时候没留处方,因为那时 shape 是 .strip,根本没有一条拒绝可以挂。

一个写下 visibility: 'private' 的作者,以为这个 agent 被隐藏了。它对所有人可见,而且一直如此。

又是 skill.permissions 那一类:读起来像安全控制、实际不是、写下去还一声不吭。现在两条都指向真正门控 agent 的东西(access / permissions,自 #1884 起在 chat 路由上真正生效)。

route —— 平台自己的测试套件里养了多年的虚构键

stack.test.ts 里写着 route: '/landing'PageSchema从来没有声明过 route——页面由 name 路由,而在这个测试用的 map 格式里,name就是 map 的键这正是同一个测试在六行之后断言的东西

第五个被发现「把 strip 时代的假象固化成预期」的测试,而且是最容易被重新发明的一个——route 是任何人在页面上第一个会去写的键。已连同 pathurl 一起立碑。

测试断言的实际是
position.parent 被接受层级根本不存在
object.namespace 被剥离是对的对象会以错误的名字发布
退役的 compactLayout 被剥离是对的作者拿不到任何提示
skill.permissions 被剥离是对的一个不存在的权限门
page.route 被剥离是对的一个从未存在的路由键

顺带立碑(因为现在终于有地方放了)

  • agent.memory.shortTerm —— 声明了一个运行时无人消费的工作记忆窗口(ADR-0013 D3)
  • page.recordReview / page.blankLayout —— 没有渲染器的页面类型(framework#2265)
  • interfaceConfig.visualization —— 显示方式是 visualization,不是 page type,从 appearance.allowedVisualizations 里运行时选
  • guardrails.allowedTopics —— 没有白名单,只有 blockedTopics 黑名单

闸门

mappingpage 补上 ADR-0010 保护信封。信封欠债从 8 降到 2(只剩 actionfield)。

头条数字改成推导,不再手数。 它已经飘了 1 ——在一场反复发现「手工维护的覆盖率度量会腐烂」的战役里。STILL_STRIP 带反向钉,和信封欠债表一样;并且我先验证了它能双向报红才敢信它:把已关闭的 agent 塞进去 → 红;把仍 strip 的 view 拿出来 → 红;恢复 → 绿。

注册类型顶层已关闭:21 / 25。仍剥离:action · dashboard · field · view

告警层覆盖的根从 6 降到 3 —— 验证过是交接不是窟窿agent.zzzpage.zzz、嵌套的 page.regions[0].zzz 现在都被解析拒绝。一个坏掉的遍历和一次成功的毕业,会让这个数字以完全相同的方式变小,所以这条检查就钉在数字旁边。

验证

  • @objectstack/spec284 文件 / 7239 用例通过tsc --noEmit 干净
  • 8 个生成物 up-to-date,spec 全部 check:* 绿
  • CRM / Todo / showcase / platform-objects 构建全部通过

授权影响:这些形状没声明的键从「静默丢弃」变成「拒绝」——本来就已经被忽略,没有在工作的元数据会变。

参考

🤖 Generated with Claude Code

https://claude.ai/code/session_01WnqGjQFQMqd5k81LYV8SCY


Generated by Claude Code

…ing removed keys (#4001)
The helper bug first, because this campaign put it there. `skill` closed last
batch while still carrying `retiredKey` tombstones, and `strictObject` built its
"did you mean" candidates from the whole shape — tombstones included. So a
`triggerPhrase` typo was answered with "Did you mean `triggerPhrases`?", a key
that had been REMOVED, and the author who complied got a second rejection
telling them to delete what they had just been told to write.
Third occurrence of a shape the ledger already records twice, and the first one
in a SHARED helper, where it would have reached every conversion after it. Both
helpers were correct alone; only the combination was wrong. Fixed structurally
rather than by special-casing tombstones: never suggest a key the schema cannot
accept. They stay complementary — `retiredKey` is stronger than `guidance`,
since typing the key as `never` also fails `tsc` through a variable, where
excess-property checking would not fire.
`agent` had two security-shaped removals with no tombstone. `visibility` and
`tenantId` were deleted as unenforced security properties — correctly — but
without a prescription, because the shape was `.strip` and there was no
rejection to hang one on. An author who wrote `visibility: 'private'` believed
the agent was hidden; it was listed to everyone, and always had been. The
`skill.permissions` class again. Both now name what actually gates an agent.
`route` on a page was a fiction the platform's own suite carried. `stack.test.ts`
authored `route: '/landing'` for years; `PageSchema` never declared it, and a
page is routed by its `name` — which in the map format under test IS the map
key, which the test asserted six lines below the key contradicting it. Fifth
test found codifying a strip-era fiction, and the likeliest to be reinvented.
`mapping` and `page` gain their ADR-0010 envelope. The undeclared-envelope debt
list is down to two, from eight.
The headline number is now derived instead of tallied — it had already drifted
by one, in a campaign whose recurring finding is that hand-maintained coverage
measurements go stale. `STILL_STRIP` carries a reverse pin like the envelope
list; verified it goes red in both directions before being trusted.
Registered types closed: 21 of 25. Still open: action, dashboard, field, view.
The warning layer's covered roots drop 6 → 3, verified as a hand-off rather than
a hole: `agent.zzz`, `page.zzz` and nested `page.regions[0].zzz` are each now
rejected by the parse. A broken walk and a graduation shrink that count
identically, so the check is pinned next to the number.
Verified: 284 files / 7239 tests, tsc clean, 8 generated artifacts current,
all spec gates green, and the CRM/Todo/showcase/platform-objects builds pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WnqGjQFQMqd5k81LYV8SCY
@os-zhuang
os-zhuang marked this pull request as ready for review August 1, 2026 18:17
@vercel

vercelBot commented Aug 1, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 1, 2026 6:17pm

Request Review

@github-actions

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.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:aiprotocol:dataprotocol:uiteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-zhuang@claude