refactor(plugin-security): make sys_permission_set a pure projection of the metadata layer (ADR-0094)#2901
Merged
Conversation
…of the metadata layer (ADR-0094) Retire the two-store split-brain behind the #2857 display-freshness class (#2875): the metadata layer (packaged declarations + sys_metadata overlay, merged overlay-wins) is now the ONE authoritative store for permission-set definitions, and the queryable sys_permission_set record is a derived read-model — enforced structurally, not by a subscriber a new write path might forget. - metadata-protocol: new registerMutationProjector(type, fn) — an awaited, best-effort per-type hook run after persistence inside saveMetaItem / publishMetaItem / deleteMetaItem, surfaced as `projectionApplied` on the response. A derived read-model is consistent before the write returns (the #2867 onMetadataMutation subscriber was fire-and-forget). - plugin-security: new permission-set-projection module: * data-door write-through — every non-system CRUD write on sys_permission_set is redirected into the metadata store at the engine middleware choke point (after the two-doors + delegated-admin + CRUD/FLS gates); the record is written only by the projector; * the projector re-reads the layered effective body, upserts/creates the env record (Studio-authored sets finally appear in Setup), retires it when the definition is gone, resets it to the declared body when an overlay tombstone reveals the artifact baseline, and syncs the metadata manager's in-memory `permission` entry so evaluator resolution (registry-first list) can no longer disagree with the display; * boot reconciliation — env overlays project onto records, legacy data-door-only records are backfilled into metadata once, and records that drifted from an existing definition are re-projected (metadata wins; the drift was never enforced); * declared (artifact) baselines are read from the engine SchemaRegistry, skipping runtime shadows and projection echoes, so a deleted runtime-only definition cannot zombie back as "declared". Behavior changes: deleting an artifact-backed set through the data door now RESETS it to its declared body instead of removing the row; renames through the data door are rejected (400); pre-existing record drift shadowed by a metadata definition is discarded loudly at first boot. Follow-up: rejecting env-scope overlays of package-owned sets at authoring time is tracked in #2898. Closes #2875 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SXjD7g2JkEsdqhZFZgAo1Q
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 12 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…authorization.mdx Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SXjD7g2JkEsdqhZFZgAo1Q
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.
Closes #2875(根因修复;#2867 的投影补丁是最终一致的胶水,本 PR 用结构性手段替代它)。
方案(ADR-0094,随 PR 一同提交)
权限集定义(label / description / 六组 facet / adminScope / active)从此只有一个权威存储:元数据层(包声明 +
sys_metadataoverlay,按 layered read 的 overlay-wins 合并)。sys_permission_set数据记录降级为纯投影(只读派生物),并且是结构性保证,不再依赖"新写入路径可能忘记触发"的订阅器:sys_permission_set数据面写入(Setup 通用 CRUD、批量导入、任何走 ObjectQL 的未来 API)在引擎中间件咽喉点被重定向为元数据写入(saveMetaItem/deleteMetaItem),驱动层写入不再发生。中间件注册在安全中间件之内,two-doors 门、delegated-admin 门、CRUD/FLS 检查全部先行生效。metadata-protocol新增registerMutationProjector(type, fn):在saveMetaItem/publishMetaItem/deleteMetaItem持久化之后、返回之前 await 执行,结果以projectionApplied呈现在响应上。Studio 保存返回时记录已一致——无投影竞态(fix(plugin-security): project env-scope permission-set edits onto sys_permission_set (#2857) #2867 的订阅器是 fire-and-forget)。投影器是记录的唯一写入者。顺带修复的一致性缺陷(审计中发现)
list()registry-first vs layered read overlay-wins:声明集的 env overlay 显示生效但 evaluator 仍用声明体。投影器现在同步 manager 内存注册表条目(带_envProjection标记,防止冒充 artifact),显示与强制执行不再分叉。deleteMetaItem的 registry 自愈会把 manager 视图重新注册回引擎注册表,产生"僵尸声明"导致定义删不掉——声明基线改从引擎 SchemaRegistry 读取,并跳过 runtime shadow(_packageId: 'sys_metadata')与投影回声。行为变化(changeset 已注明)
验证
plugin-security326 通过(新增投影/写穿/收敛套件),metadata-protocol23 通过(新增 projector 契约),全仓pnpm test通过。--preset=full实跑):创建→记录由投影器生成(managed_by:'user'+ overlay 落盘);PATCH→overlay 更新、记录同步、未提及 facet 保留;删除 runtime-only→定义与记录同退;编辑/删除member_default(声明集)→生成 overlay / 重置为声明体;package-owned 集仍被 two-doors 门拒绝。遗留
🤖 Generated with Claude Code
https://claude.ai/code/session_01SXjD7g2JkEsdqhZFZgAo1Q
Generated by Claude Code