Skip to content

Commit e1d4f9e

Browse files
claude[bot]claude
andauthored
fix(metadata-protocol): getMetaItemLayered gates the org read, bound after the canonical fold (#16034)
* fix(metadata-protocol): getMetaItemLayered gates the org read after the canonical fold `getMetaItemLayered` — the third `/meta` read verb — applied no registry read gate of its own, so a caller could spend a raw active organization on a type with no per-org read channel and a pre-#6190 phantom org-scoped row became the `overlay` layer of the three-layer diagnostic. The binding moved BELOW `canonicalizeMetaRequestType` rather than being replaced in place: it sat above the fold, so the sibling verbs' one-liner would have gated on the RAW type, which #10340 measured splits one item across two partitions for the URL-only spellings (`translations` / `email_templates`). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ * test(metadata-protocol): pin the layered read gate, incl. the after-fold ordering Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ * test(metadata-protocol): the outage pin org arm moves to a tier-A type The layered read now gates its organization, so an allowOrgOverride:false type issues no org-scope read at all and the case would pass vacuously. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 51d59e4 commit e1d4f9e

4 files changed

Lines changed: 617 additions & 4 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@objectstack/metadata-protocol": patch
3+
---
4+
5+
`getMetaItemLayered` no longer reports a phantom org-scoped row as a tenant customization.
6+
7+
`getMetaItemLayered` is the three-layer diagnostic behind Studio's "Code default vs Overlay vs Effective" view, and the third `/meta` read verb in the series `getMetaItems` (plural) and `getMetaItem` (singular) were repaired in. Unlike those two it applied no registry read gate of its own: whatever organization a caller passed was spent on whatever type it passed. On a type the registry declares `allowOrgOverride: false` — everything outside the ADR-0005 tier-A five (`view`, `dashboard`, `report`, `translation`, `email_template`) — a deployment with history can hold pre-#6190 phantom org-scoped rows, which boot hydration deliberately walks past. Read back through this verb they surfaced as `overlay` with `overlayScope: 'org'`: an operator was shown a customization that does not exist, in the one surface built to be authoritative about customizations.
8+
9+
It was not only displayed. Two doors return that layer **as the response** when it is non-null — the runtime metadata dispatcher and REST `GET /meta/:type/:name/published` — so on those paths the phantom was served as the item.
10+
11+
The read now resolves its organization through `organizationIdForMetaRead`, the same registry-derived predicate the REST `/meta` doors have applied since #9454 and the twin of the write side's `organizationIdForMetaWrite`. A type with a per-org read channel still resolves the caller's organization and still reports `overlayScope: 'org'`; every other type reads env-wide, which is the partition that actually runs.
12+
13+
**The gate is bound after the canonical type fold, and that ordering is load-bearing.** In the two sibling verbs the binding already sat below `canonicalizeMetaRequestType`, so the fix there was a substitution. Here it sat above it, and dropping the same expression in place would have gated on the raw `/meta/:type` segment: `declaresOrgOverride` tolerates the manifest plurals but not the URL-only spellings (`translations` and `email_templates` have no manifest key), so a raw segment splits one item across two partitions, addressed by spelling. The repair is therefore a reorder, and it is pinned by a test that fails if the binding moves back above the fold.
14+
15+
Callers that name no organization — four of the five `plugin-security` invocations, and every import/analytics/auth reader — are unaffected, and a door that already computed the same predicate receives the scope it did before.

0 commit comments

Comments
 (0)