Skip to content

fix(objectql,spec): app 声明的 capability 经注册缝获得 registry provenance (#5870) - #5965

Merged
baozhoutao merged 2 commits into
mainfrom
claude/issue-5870-capabilities-provenance-seam
Aug 6, 2026
Merged

fix(objectql,spec): app 声明的 capability 经注册缝获得 registry provenance (#5870)#5965
baozhoutao merged 2 commits into
mainfrom
claude/issue-5870-capabilities-provenance-seam

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes #5870
Part of #4967(Part 2;父单由 identity 车道跟踪关单)

前提复核(先于实现,结论:成立)

issue 正文的四条断言逐条对 origin/main 复核,全部成立:

  1. bootstrapDeclaredCapabilitiescap._packageId ?? cap.packageId 解析归属(packages/plugins/plugin-security/src/bootstrap-declared-capabilities.ts);
  2. AppPluginmetadata.registerInMemory(type, item.name, item)(packages/runtime/src/app-plugin.ts ~653),而 MetadataManager.registerInMemory 只做 registry.get(type).set(name, data)(packages/metadata/src/metadata-manager.ts:772)—— 不盖任何章;
  3. 唯一盖章路径是 SchemaRegistry.registerItem(type, item, 'name', packageId)applyProtection(item, { packageId })(packages/objectql/src/registry.ts:1375);
  4. 其入口 metadataArrayKeys 仓内共两处(engine.ts 的 manifest 缝与嵌套 registerPlugin 缝;issue 写的 ~1735 是今日 4 个 PR 搅动前的行号,已按内容定位到 2183 / 2351),Security Protocol 组含 roles / permissions / profiles / sharingRules / policies,独缺 capabilities

一处 issue 未提及、但不修就兑现不了验收的结构点

只把 'capabilities' 加进两处清单还不够。该缝注册用的是 pluralToSingular(key),而 PLURAL_TO_SINGULAR(packages/spec/src/shared/metadata-collection.zod.ts)没有 capabilities 条目 —— 未命中时原样返回复数键,项会被注册成类型 'capabilities',而 readDeclared(ql, 'capability') 读的是单数 'capability',等于换了一个同样没人读的库位。

这不是新造映射:AppPlugin 的安全元数据桥早已用 ['capabilities', 'capability'] 这一对(app-plugin.ts:643),sharingRules → sharing_rulepermissions → permission 也都在这张表里 —— 补的是这张边界表上唯一缺的那格。

改动

  • packages/objectql/src/engine.ts —— 两处 metadataArrayKeys 的 Security Protocol 组各加 'capabilities'(manifest 缝 + 嵌套 plugin 缝);
  • packages/spec/src/shared/metadata-collection.zod.ts —— PLURAL_TO_SINGULARcapabilities: 'capability';
  • examples/app-showcase/src/security/capabilities.ts —— 只改注释:原文写着「registry 章永远到不了 app 声明的 capability……那是 PLATFORM gap,已立 A refused capability declaration still suppresses the back-compat derivation, so the capability exists nowhere — and an app-declared capability can never get registry provenance #4967」,本 PR 落地后这段自述失效。改为说明 packageId 恢复成 schema 所写的「fallback」身份、保留是为了演示该字段,且它与 stack id 同为 com.example.showcase,与如今生效的章一致。未改任何声明值
  • .changeset/capabilities-registry-provenance-seam.md —— @objectstack/objectql + @objectstack/spec 均 patch,正文写清 FROM → TO。

⛔ 未改 packages/runtime(cli 车道)与 packages/plugins/plugin-security(identity 车道):实测二者无需同步改动即可兑现验收,仅作只读验证。

修复的行为(FROM → TO)

FROM:CapabilityDeclarationSchema.packageId.optional(),自述为「registry 未盖 _packageId 时的 ADR-0086 D3 fallback」。因为 capabilities 不在盖章缝里,_packageId 永不存在,这个「fallback」实为必填。漏写的失败形态只有一条 boot warn 加一条永不生效的授权 —— permission set 有行、能物化,capability 无章可达,readDeclared(ql, 'capability') 恒返回 []。授权面上的 declared ≠ enforced。

TO:包(以及包内嵌套 plugin)声明的 capability 携带 _packageId / _provenance: 'package' 到达 registry,可经 registry.listItems('capability') 读到,并以 managed_by:'package' + 真实 package_id 落进 sys_capability。作者侧 packageId 恢复成真正的可选 fallback;两者并存时按 schema 一贯所述以 registry 章为准。

运行时可创建性未变:capabilityDEFAULT_METADATA_TYPE_REGISTRY 无条目,而 isRuntimeCreateAllowed() 读的是那张注册表、不是 item store,所以该类型的判定改动前后一致(见下方越界发现)。

反向验证 —— 方向为标准「红」,且是先红后绿测得的

本改动是对缝的纯增补,预判方向即:去掉增补 → 新 pin 全红。实测按此顺序执行:

  • 修改前(仅新增测试):Test Files 1 failed | 128 passed,Tests 7 failed | 2115 passed。7 条新例全红,失败点分别为 listItems('capability') 返回 []pluralToSingular('capabilities')'capabilities';
  • 修改后:Test Files 129 passed,Tests 2122 passed(合并 main 后 2123)。

无「诊断增多」或「倒置」情形:本改动不删除任何 ?? 支路,也不改变 schema 对任何拼写的判决。

测试

新增 packages/objectql/src/engine-capability-provenance.test.ts(7 例,真引擎 + 真 registry,无 mock,沿用 engine-cross-package-collision.test.ts 的形态):

  1. manifest 里的 capability 注册到单数 capability 类型下;
  2. 未写作者侧 packageId 时,cap._packageId ?? cap.packageId 仍解析出归属(并断言 packageId 确为 undefined —— 证明 fallback 真的是 fallback);
  3. permissions 同条件平价(_packageId / _provenance 相同);
  4. 两个包的同名 capability 各归其主(getItem(type, name, pkgId) 分别解析);
  5. 嵌套 plugin 缝同样盖章(覆盖第二处 metadataArrayKeys,防止只修一处);
    6-7. 一致性 pin:三条被 boot seeder 按单数类型读回的安全集合(permissions/permissioncapabilities/capabilitysharingRules/sharing_rule)—— 既钉 pluralToSingular 映射,又钉「经 manifest 缝注册后确实带章」的实际行为。两半同时成立才算接进了缝。

测试断言的是 readDeclared 所消费的 registry 状态,而非 import 该函数:@objectstack/plugin-security@objectstack/objectql 互不依赖,为测试新增一条包依赖会凭空发明运行时并不存在的边。所读表达式在测试文件头部注明出处(bootstrap-declared-permissions.ts:61-69)并收在单个 readDeclaredShape() 里。

命令与实测(均在容器共享锁下前台阻塞执行,--maxWorkers=2),合并 origin/main 后重跑:

objectql          Test Files 129 passed   Tests 2123 passed
spec              Test Files 323 passed   Tests 8267 passed
plugin-security   Test Files  35 passed   Tests  768 passed
metadata-protocol Test Files  49 passed   Tests  486 passed
rest              Test Files  60 passed   Tests  833 passed
runtime           Test Files 102 passed   Tests 1468 passed
metadata          Test Files  25 passed   Tests  508 passed

typecheck: objectql / spec / runtime / plugin-security 全 Done
spec check:generated: 10/10 up to date
node scripts/check-nul-bytes.mjs: OK

消费半径已按「规则被谁读」而非「改了哪个包」清扫:PLURAL_TO_SINGULAR 的全部消费者(metadata/loaders/database-loaderrest/rest-serverruntime/domains/packagesspec/conversions/storedmetadata-protocol/protocolspec/kernel/metadata-authoring-lint)逐个跑过。其中 authoring-lint 的覆盖面未变:它以 getMetadataTypeSchema(type) 取 schema,capability 无 schema 故直接 continue,不影响那条被钉住的覆盖数。

与本 PR 同窗合入的 #5934(plugin-security 派生半边只 reconcile 自家行)与本改动互补:本 PR 让 capability 首次产生 managed_by:'package' 行,而 #5934 恰好阻止派生占位符每次启动覆盖这些行的 label/description。二者合并后 plugin-security 全绿。

决策箱核对(#4636)

不交叠、不预判。#4636loadMetaFromDbobject 分支从 snake_case 行上读 record.packageId 导致落到 'sys_metadata' 哨兵,发生在 DB 再水化路径、经由 registerObject。本 PR 只走 manifest 装配路径的 registerItem,packageId 取的是 manifest 自己的 id —— 与 permissions 完全同一个取值,不涉及「capability 该登记在哪个 owner key 下」的任何选择。

越界发现(已按 Prime Directive #10 立单,未在本 PR 修)

#5961 —— capabilityDEFAULT_METADATA_TYPE_REGISTRY / BUILTIN_METADATA_TYPE_SCHEMAS / HAND_CRAFTED_SCHEMAS 三处皆无条目,于是 isRuntimeCreateAllowed() 走「无静态注册条目 ⇒ 允许」的兜底,saveMetaItem 又走「未注册类型 ⇒ 不校验直接存」,PUT /api/v1/meta/capability/:name 收任意 JSON;/meta/types 则合成一个 allowRuntimeCreate: true、无 schema 的描述符。与 #5271api 修掉的是同一形态。

这不是本 PR 引入的:写入门只读注册表、不读 item store,该路在本 PR 之前已敞开。本 PR 改变的只是可见性 —— capabilities 现在真的进 registry,capability 于是开始出现在 getMetaTypes() 的枚举里。已在 issue 正文中写明这一因果,避免下一位读者误判为回归。修法有三案(补 schema + allowRuntimeCreate:false / 只补 schema / 改合成逻辑),取舍不属本单范围,留 PM 分诊。


Generated by Claude Code

claude added 2 commits August 6, 2026 13:02
… package provenance (#5870)

`ObjectQL.registerApp()` is the only seam that stamps ADR-0010 provenance
(`registerItem` -> `applyProtection` -> `_packageId` / `_provenance`), and the
`metadataArrayKeys` list driving it — twice in engine.ts, the manifest seam and
the nested-plugin seam — carried every Security-Protocol collection except
`capabilities`. `bootstrapDeclaredCapabilities` resolves the owner as
`cap._packageId ?? cap.packageId` and reads `readDeclared(ql, 'capability')`,
which was therefore always empty: the author-side `packageId` documented as the
ADR-0086 D3 fallback was in fact mandatory, and omitting it produced one boot
warn plus one authorization declaration that never took effect.

Register `capabilities` through the same seam as `permissions` at both sites,
and map `capabilities` -> `capability` in `PLURAL_TO_SINGULAR` so the items land
under the singular type the seeder reads back (the mapping AppPlugin's security
bridge already used).

Part of #4967 (Part 2).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Q7oc7ASjh8yxyS3Yz78We
@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 1:18pm

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 2 package(s): @objectstack/objectql, @objectstack/spec.

114 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 @objectstack/objectql, 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 packages/objectql, @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/migration-from-objectql.mdx (via @objectstack/objectql)
  • 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/deployment/vercel.mdx (via @objectstack/objectql)
  • 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/examples.mdx (via packages/objectql, @objectstack/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 @objectstack/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/objectql, @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/objectql, @objectstack/spec)
  • content/docs/permissions/authentication.mdx (via @objectstack/objectql)
  • 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/objectql, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/objectql, @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/objectql, @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 packages/objectql, @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/objectql, @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/objectql, @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 tooling labels Aug 6, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review August 6, 2026 13:52
@baozhoutao
baozhoutao added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 1b49eaf Aug 6, 2026
25 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-5870-capabilities-provenance-seam branch August 6, 2026 14:18
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

Development

Successfully merging this pull request may close these issues.

capabilities 不在 ObjectQL metadataArrayKeys 注册缝里 —— app 声明的 capability 永远拿不到 registry provenance(#4967 Part 2 拆出)

2 participants