Skip to content

test(spec): 把「有单条写就必须有批量」做成全仓棘轮,防 #3745 复发 (#3026) - #3755

Merged
os-zhuang merged 1 commit into
mainfrom
claude/ui-button-api-whitelist-consistency-8ftbmc
Jul 28, 2026
Merged

test(spec): 把「有单条写就必须有批量」做成全仓棘轮,防 #3745 复发 (#3026)#3755
os-zhuang merged 1 commit into
mainfrom
claude/ui-button-api-whitelist-consistency-8ftbmc

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

背景

#3745 修掉了 8 个对象「单条写全开、批量路由 405」的缺口。根因不是判断错,而是审计范围:#3391 P1 把 bulk 门禁改成 bulk ∧ derived(child) 后,配套的「给显式白名单补 bulk 原语」被写成了「platform-objects 包审计」,而缺口在 plugin-security / plugin-approvals / metadata-core —— 三个没人想到要打开的包。

同类问题会再犯:今天全仓 58 处 apiMethods 声明散在多个包里,靠人再扫一遍不可靠。本 PR 把这次的人工排查换成 CI 每次都跑的扫描。

改动

新增 packages/spec/src/data/api-methods-batch-conformance.test.ts —— 扫描 packages/**每一个 *.object.ts,提取 apiMethods 数组字面量,断言三条不变量:

  1. 有单条写就必须有批量 —— 白名单授予 create/update/delete 任一,就必须同时授予 bulk;否则必须登记进 SINGLE_RECORD_WRITE_ONLY 并写明理由(今天为空:全仓要么授予了 bulk,要么根本不授予写)。
  2. 只能声明 6 个原语 —— P2:ApiMethod 枚举收缩至 6 原语(breaking,独立版本) #3543 收缩后,legacy 动词在 parse 期就被 strip、resolver 也忽略,声明了等于静默的死元数据,在源头拦掉。
  3. 豁免清单不许留陈旧条目 —— 一条不再对应真实"只单条写"对象的豁免,读起来像"有据可依"实则什么都没记录。

外加一条扫描量下限断言(> 40):扫不到文件的实现会让上面三条全部真空通过 —— 那正是本文件要防的失败模式。

为什么扫源码而不是 import

system/constants/platform-object-names.test.ts 同一套技术:import 全部对象包会反转 spec → * 的依赖方向,而扫源码能把检查放在派生表旁边,且新包新对象自动被覆盖(这次缺口的形状正是"新包没人扫")。

防假绿

不是绿橡皮图章 —— 把 sys_permission_set'bulk' 拿掉重跑,棘轮如实变红,失败信息直接给出对象名、白名单内容和文件路径:

sys_permission_set: [get, list, create, update, delete] grants single-record writes
but not 'bulk' — /batch and the *Many routes will 405
(packages/plugins/plugin-security/src/objects/sys-permission-set.object.ts)

随后已还原。

验证

  • @objectstack/spec 259 files / 6721 tests 全过(含新增 4 条)
  • 扫描当前 main:58 处声明,0 违规

changeset:空 changeset(test-only,不发布任何包)。

关联


Generated by Claude Code

…across every object (#3026)

#3745 fixed eight objects that 405'd `/batch` and the `*Many` routes while
exposing the same write verbs one record at a time. The bug was not a wrong
judgement — it was an audit whose SCOPE was a package name (`platform-objects`)
while the gap lived in three packages nobody thought to open.

This replaces that manual sweep with a scan of every `*.object.ts` under
`packages/`, so a new declaration anywhere is covered by construction. Three
invariants, plus a floor on the scan size so an empty sweep cannot pass
vacuously:

- a whitelist granting `create`/`update`/`delete` must also grant `bulk`,
  unless registered in `SINGLE_RECORD_WRITE_ONLY` with a reason (empty today);
- a whitelist may only author the six primitives — since the #3543 shrink a
  legacy verb is stripped at parse, so declaring one is silently dead metadata;
- the exemption list may not hold stale entries.

Scanning source rather than importing the packages keeps the check next to the
derivation table without inverting the spec → * dependency direction, the same
technique as `system/constants/platform-object-names.test.ts`.

Verified as real evidence, not a green rubber stamp: removing `'bulk'` from
`sys_permission_set` turns the ratchet red with the offending object, its
whitelist and its file path in the failure message. `@objectstack/spec` 259
files / 6721 tests pass with it in place.

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

vercel Bot commented Jul 28, 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 Jul 28, 2026 2:16am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling size/m labels Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

104 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 packages/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/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/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.

@os-zhuang
os-zhuang marked this pull request as ready for review July 28, 2026 02:33
@os-zhuang
os-zhuang merged commit b070943 into main Jul 28, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/ui-button-api-whitelist-consistency-8ftbmc branch July 28, 2026 02:34
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:data size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants