fix(objectql,spec): app 声明的 capability 经注册缝获得 registry provenance (#5870) - #5965
Merged
Merged
Conversation
… 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
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 114 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
baozhoutao
marked this pull request as ready for review
August 6, 2026 13:52
This was referenced Aug 6, 2026
This was referenced Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5870
Part of #4967(Part 2;父单由 identity 车道跟踪关单)
前提复核(先于实现,结论:成立)
issue 正文的四条断言逐条对
origin/main复核,全部成立:bootstrapDeclaredCapabilities以cap._packageId ?? cap.packageId解析归属(packages/plugins/plugin-security/src/bootstrap-declared-capabilities.ts);AppPlugin走metadata.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)—— 不盖任何章;SchemaRegistry.registerItem(type, item, 'name', packageId)→applyProtection(item, { packageId })(packages/objectql/src/registry.ts:1375);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_rule、permissions → permission也都在这张表里 —— 补的是这张边界表上唯一缺的那格。改动
packages/objectql/src/engine.ts—— 两处metadataArrayKeys的 Security Protocol 组各加'capabilities'(manifest 缝 + 嵌套 plugin 缝);packages/spec/src/shared/metadata-collection.zod.ts——PLURAL_TO_SINGULAR补capabilities: '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 章为准。运行时可创建性未变:
capability在DEFAULT_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的形态):capability类型下;packageId时,cap._packageId ?? cap.packageId仍解析出归属(并断言packageId确为undefined—— 证明 fallback 真的是 fallback);permissions同条件平价(_packageId/_provenance相同);getItem(type, name, pkgId)分别解析);metadataArrayKeys,防止只修一处);6-7. 一致性 pin:三条被 boot seeder 按单数类型读回的安全集合(
permissions/permission、capabilities/capability、sharingRules/sharing_rule)—— 既钉pluralToSingular映射,又钉「经 manifest 缝注册后确实带章」的实际行为。两半同时成立才算接进了缝。测试断言的是
readDeclared所消费的 registry 状态,而非 import 该函数:@objectstack/plugin-security与@objectstack/objectql互不依赖,为测试新增一条包依赖会凭空发明运行时并不存在的边。所读表达式在测试文件头部注明出处(bootstrap-declared-permissions.ts:61-69)并收在单个readDeclaredShape()里。命令与实测(均在容器共享锁下前台阻塞执行,
--maxWorkers=2),合并origin/main后重跑:消费半径已按「规则被谁读」而非「改了哪个包」清扫:
PLURAL_TO_SINGULAR的全部消费者(metadata/loaders/database-loader、rest/rest-server、runtime/domains/packages、spec/conversions/stored、metadata-protocol/protocol、spec/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)
不交叠、不预判。#4636 是
loadMetaFromDb的 object 分支从 snake_case 行上读record.packageId导致落到'sys_metadata'哨兵,发生在 DB 再水化路径、经由registerObject。本 PR 只走 manifest 装配路径的registerItem,packageId取的是 manifest 自己的id—— 与permissions完全同一个取值,不涉及「capability 该登记在哪个 owner key 下」的任何选择。越界发现(已按 Prime Directive #10 立单,未在本 PR 修)
#5961 ——
capability在DEFAULT_METADATA_TYPE_REGISTRY/BUILTIN_METADATA_TYPE_SCHEMAS/HAND_CRAFTED_SCHEMAS三处皆无条目,于是isRuntimeCreateAllowed()走「无静态注册条目 ⇒ 允许」的兜底,saveMetaItem又走「未注册类型 ⇒ 不校验直接存」,PUT /api/v1/meta/capability/:name收任意 JSON;/meta/types则合成一个allowRuntimeCreate: true、无 schema 的描述符。与 #5271 给api修掉的是同一形态。这不是本 PR 引入的:写入门只读注册表、不读 item store,该路在本 PR 之前已敞开。本 PR 改变的只是可见性 —— capabilities 现在真的进 registry,
capability于是开始出现在getMetaTypes()的枚举里。已在 issue 正文中写明这一因果,避免下一位读者误判为回归。修法有三案(补 schema +allowRuntimeCreate:false/ 只补 schema / 改合成逻辑),取舍不属本单范围,留 PM 分诊。Generated by Claude Code