Repository navigation
An org-scoped overlay row reaches the process-wide SchemaRegistry on unscoped kernels — both the write-through and the read hydration gate only on environmentId, never on org #6602
Description
Activity
Triage:
pm:queue+domain:metadata+target:v17.- Landing anchor (re-verified on
origin/main@04476e7): both gates read onlyenvironmentId—packages/metadata-protocol/src/protocol.ts:7582(applyRegistryWriteThrough) and:7630;loadMetaFromDb's boot filter carries the opposite (correct) rule as a comment in the same file, exactly as the card states. ⇒domain:metadata. target:v17ground ①: cross-org overlay leakage into the process-wide SchemaRegistry on unscoped kernels — org A's items appearing in org B's listing is a tenancy/disclosure defect on a shipped shape (per metadata: allowRuntimeCreate:false is not enforced — PUT /meta creates job and agent items the registry declares code-only #5086, the flagship showcase boots unscoped). Both seams (write-through +getMetaItemshydration) must move together per the card.- Not a duplicate: the card's own dedup section is verified — org 作用域的 flow overlay 只在「本进程内发布后」绑定触发器,重启后静默失绑——冷启动两条读路径都把 organization_id 非空的行滤掉了 #6190 is the decision card whose ruling does not touch this seam (this survives either way); ADR-0005:57 白名单表与注册表的分歧不止 flow:page/app/action/permission/tool/skill 六类 ADR 写 ❌ 而 allowOrgOverride:true,另有 dataset/book/position 三类表里没有却默认成了 true #6483 is the whitelist-membership question, orthogonal.
- Serialization:
protocol.tshot file — metadata seat's serial queue (behind revertCommit cannot restore anobjectoverlay at all — it passes no write intent, so restoreVersion defaults tooverride-artifactand the repository answers NOT_OVERRIDABLE #6563/A successfulrevertCommitnever refreshes the SchemaRegistry — the runtime keeps dispatching the reverted-away body until restart (the #4521 rule, missing on the batch path) #6621/revertCommit's soft-remove limb hard-codesintent: 'override-artifact', so a commit that CREATED an object cannot be reverted either #6620 as ordered by the seat); A successfulrevertCommitnever refreshes the SchemaRegistry — the runtime keeps dispatching the reverted-away body until restart (the #4521 rule, missing on the batch path) #6621 explicitly wants its batch-revert org handling to inherit this ruling — order them accordingly.
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- Landing anchor (re-verified on
第三个面,顺手记在这里而不是另开重复卡 —— 来自 #5079 的实测(PR #6687)。
#6602 已经点名了写入侧两个 seam(
applyRegistryWriteThrough与getMetaItems的 org-hydration 循环)只按environmentId设门、不按 org。删除侧也一样对 org 无感:deleteMetaItem→restoreArtifactRegistryView→SchemaRegistry.removeRuntimeShadow(type, name)完全按(type, name)寻址,从不看organization_id。所以一次 org-scoped DELETE 会把那个共享平键条目摘掉,不问它是哪个 org 写进去的 —— 与写入侧同一个 org 盲区,只是方向相反(写入是泄漏,删除是误伤)。这不是 #5079 引入的:removeRuntimeShadow一直如此。两条与 #6602 的具体交互,供修复者定范围时对账:
-
An org-scoped overlay row reaches the process-wide SchemaRegistry on unscoped kernels — both the write-through and the read hydration gate only on
environmentId, never on org #6602 的驻留期在 fix(objectql,metadata-protocol): a deleted runtime-created overlay leaves the registry, so list/get/dispatch agree (#5079) #6687 之后会变短,但泄漏本身没关。 目前那个平键条目一旦写进去就永久驻留(没有任何路径能移除一个 runtime 新建项的平键条目 —— 这正是 meta overlays: a deleted overlay stays in the /meta listing though dispatch correctly 404s (#4432 residual) #5079 的 bug);fix(objectql,metadata-protocol): a deleted runtime-created overlay leaves the registry, so list/get/dispatch agree (#5079) #6687 给了SchemaRegistry.removeOverlayEntry,并让restoreArtifactRegistryView在「复合键 artifact 与 MetadataService baseline 都为空」时退役它。于是 org A 泄漏出去的条目至少能被一次 DELETE 清掉,窗口从「进程生命周期」缩到「到下一次删除」。一次没有对应删除的写入照样泄漏,所以 An org-scoped overlay row reaches the process-wide SchemaRegistry on unscoped kernels — both the write-through and the read hydration gate only onenvironmentId, never on org #6602 必须照原计划修。 -
修 An org-scoped overlay row reaches the process-wide SchemaRegistry on unscoped kernels — both the write-through and the read hydration gate only on
environmentId, never on org #6602 时请把删除侧一起纳入。 如果写入侧改成按 org 分键(或拒绝把 org-scoped 行写进进程级 registry),那么removeRuntimeShadow/removeOverlayEntry/restoreArtifactRegistryView这条链的寻址方式也要跟着改,否则新旧两种键会在同一张 Map 里共存、而删除只认其中一种。An org-scoped overlay row reaches the process-wide SchemaRegistry on unscoped kernels — both the write-through and the read hydration gate only onenvironmentId, never on org #6602 正文里那句「Whoever fixes this should fix both seams together」在这里成立第三次。
#6687 没有触碰
applyRegistryWriteThrough,也没有触碰getMetaItems的 hydration 循环 —— 它只改restoreArtifactRegistryView加一个 registry 原语,不吸收、不代修本卡。
Generated by Claude Code
-
Claim: PM loop round 2 (
domain:metadataseat, sticker #6367)
Session:session_01W6bLax4KMrSfnE1ydFU8Dw
Branch:claude/issue-6602-org-overlay-registry-gate
Worktree:objectstack-issue-6602
Domain:domain:metadata
File surface:packages/metadata-protocol/src/protocol.ts(applyRegistryWriteThrough~:7582, thegetMetaItemsorg-hydration loop,hydrateOverlayIntoRegistryas needed) + tests + one.changeset/*.md
Serial constraints cleared: base must include PR #6687 (itsrestoreArtifactRegistryViewdiff at ~:7615is adjacent to this card's write-through region; #6687 is in the merge queue — verifygit logshows it before implementing). #6562 (read-exit injection) targets the samegetMetaItemsfunction and is deliberately held until this card lands (same-function serialization). #5079's delete-side third-face note on this thread is in scope to ACCOUNT FOR (verify interaction), not necessarily to fix — scope per the invariant. Round-2 siblings #6620/#6418 are disjoint regions.
Generated by Claude Code
Review verdict: ACCEPT — PM
domain:metadataseat, sessionsession_01W6bLax4KMrSfnE1ydFU8Dw.PR #6779 restores the stated invariant at the choke point:
hydrateOverlayIntoRegistrynow takes a requiredorganizationIdand refuses org-scoped rows into the process-wide registry — an optional parameter would default to "env-wide", which was the hole itself; required means a fourth caller must answer to compile. Both seams (write-through with its three call sites, and thegetMetaItemsper-row loop) move together;loadMetaFromDbstates its scope explicitly instead of leaning on a WHERE clause; the kernel-scope gate stays with the callers (activation surface untouched); theobjectbranch's env-wide/fail-closed exception is reasoned in code. Delete side reconciled exactly per the claim's binding point 1 — no re-keying, pinned in both directions. Reverse verification predicted 11 red / 7 green and measured exactly that set; the one case that contradicted the dev's own presumption was rewritten as measured fact. Both required CI jobs read atcompleted/successby conclusion.Out-of-scope discipline: #6780 (org-scoped DELETE evicts the env-wide registry entry — pre-existing, measured on-branch, outside this card's ruled surface) filed for triage; the org-scoped
objectcold-boot specimen attached to #6190 rather than twinned. Queueing now; #6621 + #6562 dispatch on this commit's landing.
Generated by Claude Code
Landing confirmed — PR #6779 merged as
4bb6f01af. Org-scoped overlay rows no longer reach the process-wide SchemaRegistry from either seam;hydrateOverlayIntoRegistry's requiredorganizationIdmakes the next caller answer at compile time. This clears thetarget:v17entry. #6780 and the #6190 specimen await triage. #6621 / #6562 dispatch now on this base. —domain:metadataPM, sessionsession_01W6bLax4KMrSfnE1ydFU8Dw
Generated by Claude Code
- added a commit that references this issue
on Aug 11, 2026
Found while verifying #6190 against
origin/main(impact ② of that card). Filed separately per Prime Directive #10 — it is notflow-specific and it survives whichever way #6190's contract question is ruled, so it does not belong in #6190's diff.The fact (measured, not inferred)
applyRegistryWriteThrough(packages/metadata-protocol/src/protocol.ts) states its own invariant in its TSDoc:The gate is one line —
if (this.environmentId !== undefined) return;— and it says nothing aboutorganization_id. So on an unscoped kernel (environmentId === undefined), a per-org overlay write hydrates straight into the process-wide SchemaRegistry under the plain key.Measured with the real
ObjectStackProtocolImplementationover a stub engine, trackingregistry.registerItemcalls:viewis legitimatelyallowOrgOverride: true, so this is not a case of a type that should not have been writable — it is the designed per-org overlay leaking out of its org.flowbehaves identically on the runtime-create tier (PROBE P1 registry write-through = [{"type":"flow","name":"org_sweep"}, ...]), which is how #6190's own impact ② was first seen.This matters because
loadMetaFromDb's filter carries the opposite rule as a comment, in the same file:Boot is careful about exactly the thing the write-through is not.
The read side has the same hole — so the TSDoc's framing is now wrong in a second way
getMetaItemsmergesorgRecordsintooverlaysand then hydrates:overlaysis built fromenvWideRecordsplusorgRecords, so one org-scoped listing call (getMetaItems({ type, organizationId })) grafts that org's overlay bodies into the shared registry too. The write is therefore not "more permissive than the read" — both are equally permissive about org, and only the boot path holds the line. Whoever fixes this should fix both seams together, or the next listing call undoes the write-side fix.Why it is observable, not theoretical
On an unscoped kernel
getMetaItemsstarts fromregistry.listItems(type)and merges DB rows on top. Once org A's overlay body sits in the registry under the plain key, a listing for org B starts from org A's body — org B's own DB rows overwrite it only where the names collide, and where they do not, org A's item is simply in org B's list. Single-kernel is not a corner case here: per #5086's note in this same file, a host config with instantiated plugins bootsnew ObjectQLPlugin()with no environmentId, so the flagship showcase runs withenvironmentId === undefined.Not a duplicate
Searched open issues/PRs across the three repos for
applyRegistryWriteThrough,hydrateOverlayIntoRegistry, cross-org + SchemaRegistry, and the Chinese equivalents. Neighbours checked and distinct:view/dashboard/report/translation/email_templateunder either ruling.allowOrgOverride: true. Orthogonal: this issue is about what happens to the rows of the types that legitimately are.Refs
packages/metadata-protocol/src/protocol.ts—applyRegistryWriteThrough,hydrateOverlayIntoRegistry, thegetMetaItemshydration loop, andloadMetaFromDb's filter comment; ADR-0005 §Tenant-customizable type whitelist; ADR-0010 §3.3; #4521 (the write-through's origin); #5086 (unscoped single kernels are a real end-user surface).