From ebdb9d6ae7d7e853d2df0dccdf4f684fe928d72d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 6 Sep 2026 01:47:53 +0000 Subject: [PATCH] docs(metadata-protocol): the getMetaItems gate enumerates six rest-server call sites, not five, and names the transitive diagnostics door The `[#14683]` gate docblock above `getMetaItems`' `organizationIdForMetaRead` call called the `GET /meta/:type` list door "the only door that both gates and reaches this method", then accounted for "the four remaining" `organizationIdForMetaRead` call sites in `rest-server.ts`. One named plus four remaining is five; that file has SIX, and the missing one is `GET /meta/diagnostics?type=`, which both gates and reaches this method. It was missed because it does not call `getMetaItems`. It calls `getMetaDiagnostics`, whose `?type=` arm sets `targetTypes = [request.type]` and loops `getMetaItems({ type: t, organizationId, ... })` over it, so a grep for doors invoking `getMetaItems` cannot see it. That hop is also undeclared: `getMetaDiagnostics` is not a member of `MetadataProtocol`, neither required nor optional, so the door reaches it through a runtime cast behind a 501 feature-detect - real at runtime, invisible to the type system. The enumeration is what #14683 offers as evidence that its change is a no-op for every already-gating caller. That conclusion is measured and holds; only the enumeration backing it was short a caller. Comment only: no behaviour change, no schema change. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ --- packages/metadata-protocol/src/protocol.ts | 38 +++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/packages/metadata-protocol/src/protocol.ts b/packages/metadata-protocol/src/protocol.ts index 89436302d0..87087b7160 100644 --- a/packages/metadata-protocol/src/protocol.ts +++ b/packages/metadata-protocol/src/protocol.ts @@ -6797,8 +6797,8 @@ export class ObjectStackProtocolImplementation implements // caller that already gates therefore sees no change, provided it gated // on the same type this line gates on. Every such caller does: // - // • `packages/rest`'s `GET /meta/:type` list door — the only door - // that both gates and reaches this method — computes + // • `packages/rest`'s `GET /meta/:type` list door — one of TWO doors + // that both gate and reach this method — computes // `organizationIdForMetaRead(canonicalMetaUrlType(req.params.type), // ctx?.tenantId)` and then passes `type: req.params.type`, the RAW // segment. The first statement of this method folds that segment @@ -6806,14 +6806,44 @@ export class ObjectStackProtocolImplementation implements // `canonicalMetaUrlType` — so `request.type` here is the identical // STRING the door gated on, and the second application is the // algebraic no-op above. + // • `GET /meta/diagnostics?type=` — the SECOND such door, and the one + // a caller-side grep cannot see. It gates the same way + // (`organizationIdForMetaRead(canonicalMetaUrlType(diagnosticsType), + // ctx?.tenantId)`) and then passes its own RAW segment on — but it + // reaches this method TRANSITIVELY, through + // {@link getMetaDiagnostics}, whose `?type=` arm sets `targetTypes = + // [request.type]` and loops `getMetaItems({ type: t, + // organizationId, … })` over it. So the door's segment still arrives + // here as `request.type` and is still folded by the same first + // statement: the identical STRING, the same algebraic no-op, one hop + // further out. ⚠️ That hop is UNDECLARED — `getMetaDiagnostics` is + // not a member of `MetadataProtocol`, neither required nor optional, + // so the door reaches it through a `(p as any)` cast behind a 501 + // feature-detect. Real at runtime, invisible to the type system, and + // therefore something a caller census must be TOLD rather than left + // to derive. // • the search sweep's page read below gates on `'page'` and passes // `'page'`; `page` is non-overridable, so both readings are // `undefined` whatever the session holds. // • the four remaining `organizationIdForMetaRead` call sites in // `rest-server.ts` (`/layers`, the by-name read, `/history`, // `/diff`) reach `getMetaItemLayered` / `getMetaItem` / - // `historyMetaItem` / `diffMetaItem` — never this method — so this - // line cannot move them at all. + // `historyMetaItem` / `diffMetaItem` — never this method, at any + // depth — so this line cannot move them at all. Two doors named + // above plus these four IS that file's whole set of SIX; the + // enumeration that named one door and "four remaining" described + // five, and the door it dropped was the one that reaches here. + // + // ⭐ Read this list from the CALLEE side, which is how it is now built. + // A grep for doors that invoke `getMetaItems` answers only its own + // question: it cannot see a door that arrives through something else, + // and that is exactly how the diagnostics door went unlisted. The + // closed form is the other direction — `this.getMetaItems(` has THREE + // callers in this file: {@link getMetaDiagnostics}, + // {@link searchAll} and {@link findReferencesToMeta}. The third gates + // nothing, deliberately: its door spends the organization on the + // reference SOURCES while `req.params.type` is the TARGET, so it hands + // the tenant over RAW and is not a caller this paragraph is about. // // ⛔ Gate AFTER the fold, never before it. `declaresOrgOverride` // tolerates the MANIFEST plurals and not the URL-only ones