Uh oh!
There was an error while loading. Please reload this page.
fix(metadata-protocol): apply the allowOrgOverride read gate inside getMetaItems, so multi-type sweeps are scoped per type - #14767
Conversation
Snapshot before verification. `organizationIdForMetaRead(request.type, request.organizationId)` is resolved once, after the canonical fold, and spent by both the active-overlay arm and the previewDrafts arm. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
Includes the idempotence proof the direction-A ruling was conditional on, mechanised over the complete accepted-spelling population. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
…n overridable type (#14683) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
…ridable type (#14683) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
… signal tokens (#14683) The body cites #13925's major-plus-marker precedent to REFUSE it. Spelled with the literal markers, check-adr-0087-registration reads the citation as this changeset's own declaration and demands an ADR-0087 disposition for a change that retires nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
…] for the probe sites (#14683) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
📓 Docs Drift CheckThis PR changes 1 package(s): 31 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 9 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin cb52c281cb890f79d76a18fdc7e227be37f4b66e && git checkout cb52c281cb890f79d76a18fdc7e227be37f4b66e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 224f8ea4a0776d72de0003a77695bfb98c2206cd fc986acdab8dddc2e351409cd0ade81d34d20893 && git checkout -B drift-repro 224f8ea4a0776d72de0003a77695bfb98c2206cd && git merge --no-ff fc986acdab8dddc2e351409cd0ade81d34d20893
node scripts/docs-audit/affected-docs.mjs --json 224f8ea4a0776d72de0003a77695bfb98c2206cd
|
os-musk
commented
Sep 3, 2026
Contract review — FAIL, adopted verbatim. Narrow: semver only, and it is repairable in prose.Performed by an isolated reviewer at the configured tier in its own worktree cut from The blocking finding — |
| commit | what changed on a published read door | level |
|---|---|---|
b6c769019 (#9454/#9727) | the row set every /meta read door returns (org rows added) | metadata-protocol: patch, rest: patch, metadata-core: patch |
26f3588fb (#10340/#10519) | which partition two spellings read (rows moved) | rest: patch, metadata-core: patch |
67ceb9aef (#11553) | the same, on the dispatcher door | runtime: patch |
Against the other rungs: minor ⇔ a new export (87ad30c10, 3c1bbd2a8); minor + **BREAKING** + adr-0087: ⇔ a published type narrowing (d8024f050). This PR is fix(, adds no export, narrows no type. ⇒ patch.
A second factual error, introduced by the wording-repair commit 9f3244830: the changeset now says #13925's shape "would carry a major bump plus an explicit incompatibility marker". #13925 shipped as "@objectstack/core": **minor** with **BREAKING** and an adr-0087: marker, and scripts/check-changeset-no-major.mjs forbids major outright under pre-1.0 semantics. cd9d3ee89 was accurate — the gate-driven repair broke a claim it was not aimed at. Worth recording as a class: a repair that satisfies a gate can damage a neighbouring sentence the gate does not read.
No BREAKING banner is owed, and the reviewer's reasoning is stronger than the changeset's: the rows a raw-org caller loses are the #6190 phantoms — refused at write since ac244ad09/6155c3c24, skipped by boot hydration, audited by reportUnhydratableOrgScopedRows, and already withheld by every REST /meta read door since b6c769019. The only doors still serving them were the dispatcher list and the runtime manifest/flip reads; this PR aligns them with REST. check-adr-0087-registration is green on the final tree — the repair laundered nothing, because nothing is owed.
What the review verified rather than accepted — the parts that PASS
- ⭐ The idempotence tripwire is genuinely discharged, not circular. The reviewer read the pre-change
protocol.ts(5258b63f8::6811const orgId = (request as any).organizationId,:6873-6874the twoqueryByOrgcalls) and confirmed §3b'sexpected—gated ? [null, gated] : [null]— is the old behaviour written out, computed from the input the door would pass, never referencing the internal gate,OVERRIDABLE, or the post-fold type. The "same type" half verified too:canonicalizeMetaRequestType→canonicalMetaType→canonicalMetaUrlType, the identical map the door gated on. - The ablation reproduced independently: 194 total, 54 failed / 140 passed, per section
§1:1 §2:1 §3c:50 §4:1 §5:1, and §3b 0 of 61 on both legs — exactly the shape the tripwire asked for. Restore proved by blob hash13b37b5d…and emptygit diff HEAD. - ⭐ Both re-spellings are legitimate, and this was measured rather than argued: the reviewer ran the pre-change versions of both test files against the new
protocol.tsand got exactly one failure each — the case that was re-spelled — and nothing else.appreally did roll back toallowOrgOverride: falseinee58392e1(ADR-0005:57 白名单表与注册表的分歧不止 flow:page/app/action/permission/tool/skill 六类 ADR 写 ❌ 而 allowOrgOverride:true,另有 dataset/book/position 三类表里没有却默认成了 true #6483/fix(spec): ADR-0005 白名单强制 —— 九类未获批 allowOrgOverride:true 回滚为 false (#6483) #6608). No coverage lost. - Call-site enumeration confirmed: five non-test
organizationIdForMetaReadsites inrest-server.ts, a sixth atprotocol.ts:11117, exactly two reachinggetMetaItems; the other sevengetMetaItemscallers inrest-server.tspass noorganizationId.⚠️ One caveat stated honestly: the reachability grep wasthis\.getMetaItems(, so an(this as any).getMetaItems(spelling would have evaded it — none seen, absence not exhaustively proven. - Third sweep verified (
packages.ts:1160,assemblePackageManifest) and covered by the callee-side gate with no runtime edit.Part ofis honest: neither door forwards an organization onorigin/maintoday, so neither door's answer changes with this PR. packages/rest/**andpackages/spec/**untouched — the fence held.
Advisory findings
- Clause ② is
yes, but on a different limb than declared. "Which rows a caller gets back, with a tenancy dimension" does not hold — a tenancy read-scope behaviour change sits on the human floor (SKILL.md:601-604) and the accept/refuse criterion at:547is unmet. The limb that holds: the change alters the runtime meaning of a spec-described request member without touching the spec (GetMetaItemsRequestSchema.organizationId'sdescribe()). ⭐ Per docs(pm-dispatch): rule how clause ② is graded — provisional claim, mechanical floor, judged conformance #14696 a provisional declaration reaching the right answer for the wrong reason is the mechanism working, not a fault. Being corrected in the PR body. GetMetaItemsRequestSchema.organizationId'sdescribe()should gain a registry clause. That is apackages/specchange ⇒ a separatedomain:speccard, ⛔ not a rider here. Being filed by this seat.- The ablation report's anchor "injected 0→1" cannot be literally true —
const orgId = request.organizationId;already appears twice inprotocol.ts, so the transition is 2→3. The reading is correct and was reproduced; only the anchor arithmetic is off. - The two out-of-scope findings the dev could not dedup were already filed by this seat:
getMetaItem(SINGULAR) has the same ungated-caller defect asgetMetaItemsand it is sharper — its overlay read is??PRECEDENCE, not a union, so a pre-#6190 phantom SHADOWS the live env-wide row and becomes the served document #14770 and [finding]runtime/domains/packages.ts:594says the package-export read is "left org-aware on purpose — a layered read is a superset, never a loss"; PR #14767 makes that false forapp#14771.
Generated by Claude Code
Contract review returned FAIL on the semver level alone. `minor` was derived from #13973, which is open, `pm:blocked`, never landed, and is about date-materialising drivers — it says nothing about read doors. The real precedents are this predicate's own lineage, and all three shipped `patch`: b6c7690 (#9454/#9727) added the org partition to every `/meta` read door; 26f3588 (#10340/#10519) moved which partition two spellings read; 67ceb9a (#11553) repaired the same on the dispatcher. Withholding a partition from types that never had a read channel is the same class. Also corrects the #13925 claim this file's previous wording-repair commit introduced: #13925 shipped `"@objectstack/core": minor` with a bolded banner and an `adr-0087:` marker, not a major bump — `check-changeset-no-major.mjs` refuses `major` outright, so `minor` in this repo MEANS breaking, which is why claiming it here would be wrong. ⛔ No code, test or pin changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
os-musk
commented
Sep 3, 2026
Delta re-review — FAIL again, on one sentence, and it vindicates sending it backThe rework met every mechanical part of the tier's PASS condition — this seat verified that independently before asking ( ⭐ That call is what caught this. The reworked changeset added an argument nobody asked for, and it is false. The false sentence, and the file that refutes it
Affirming the consequent.breaking ⇒ minor is true (header lines 25-27); minor ⇒ breaking is not, and the landed record carries the counter-examples: A false claim about the repo's versioning convention, in a release-notes input, contradicted by its own citation: the same class as the first FAIL. The conclusion ( Everything else in the delta — confirmed by the tier
One advisoryThe ⭐ Platform fact, confirmed and now recordedRoot Generated by Claude Code |
#14683) I wrote that `minor` "means breaking" here, and that claiming it would signal an incompatibility. That affirms the consequent: breaking => minor holds, minor => breaking does not. check-changeset-no-major.mjs says so in the header I cited — during the window the bump level "tells a consumer nothing about whether the release breaks them", and the carriers are the banner and the ADR-0087 disposition, "the only signal there is". The landed record carries the other half: 87ad30c ('@objectstack/types': minor) and 3c1bbd2 ("@objectstack/metadata": minor) are new-export bumps with zero banner tokens. So `minor` is the union, not a breaking marker. Corrected to what the gate actually says: the level carries nothing either way, this change owes neither carrier, and `patch` follows from the lineage and from no export being added. Lines 54-58 unchanged. ⛔ No code, test or pin changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
os-musk
commented
Sep 3, 2026
Contract review — PASS, adopted verbatim (round 3)The isolated reviewer at the configured tier returns PASS on the replacement sentence, judged on that and nothing else. Its words: "The fuse can be cleared in one stroke."
What the tier verified on the final headRe-derived at
⭐ The replacement was checked line by line against its own sourceThis is the part worth recording, because the two earlier FAILs were both citations that contradicted the file they cited:
One nit, explicitly not a fourth round" On the PR bodyThe tier judged it as found and confirms every statement in it matches something verified across the three rounds. ⭐ Three rounds, three FAILs, and the code was correct from the first one — Generated by Claude Code |
os-musk
commented
Sep 3, 2026
Landing provenance — |
Part of #14683
getMetaItemsnow applies theallowOrgOverrideread gate itself, so a metadata sweep that reads more than one type per request is scoped per type instead of per request.Clause-②: yes. No new exported symbol and no new key on a published payload, so the mechanical floor is not hit. The limb that holds is that this change alters the runtime meaning of a spec-described request member without touching the spec:
GetMetaItemsRequestSchema.organizationIdis described as "Selects the org partition in the ADR-0005 overlay read order…", and after this PR the org partition is not consulted at all for a registry-non-overridable type. The described contract and the served behaviour diverge, with the spec unchanged.needs:contract-reviewrequested either way.The change
One line of behaviour, in
packages/metadata-protocol/src/protocol.ts:The two per-arm re-reads of
request.organizationId(the active-overlay read and thepreviewDraftsread) are deleted and both now spend that one resolution — a gate threaded into only one arm would leave the draft preview resurrecting exactly what the active list had stopped serving.Three placement facts, each load-bearing:
declaresOrgOverridetolerates the MANIFEST plurals and not the URL-only ones (translations,email_templateshave no manifest key). Handed a raw URL segment it answers env-wide for two genuinely org-overridable types — one item in two partitions, addressed by spelling.organizationIdForMetaReadwas already imported intoprotocol.ts(line 57) from@objectstack/metadata-core, already aworkspace:*dependency ofpackages/metadata-protocol. Nothing moved between packages, no cycle. (Measured, not assumed — this was a declared stop condition.)The harm class is RESURRECTION, not concealment
Carried forward from triage deliberately, because a fix written against the opposite premise would aim at the wrong failure.
SysMetadataRepository.history()filtersorganization_idby strict equality, so naming the tenant therehides anallowOrgOverride: falsetype's rows. On this path the twoqueryByOrgreads are UNIONed, so naming it can only add — and what it adds are the pre-#6190 phantoms: org-scoped rows of types with no per-org read channel, whichloadMetaFromDbwalks past andreportUnhydratableOrgScopedRowsexists to warn about. Read back, they surface inside a clearance rendered before a destructive action, where a resurrected row is worse than an omission because it reads as evidence.⭐ The idempotence proof (the tripwire)
Direction A was ruled conditional on showing that moving the predicate inside does not change the scope any already-gating call site receives. Verdict: it does not. No gating call site's scope moves.
The measured call-site population
Counted on this branch's own tree, not taken on faith:
getMetaItems?rest-server.ts:3172/layersgetMetaItemLayeredrest-server.ts:4823GET /meta/:typelist:4839)rest-server.ts:5661by-name readgetMetaItem/getMetaItemCachedrest-server.ts:6517/historyhistoryMetaItemrest-server.ts:7085/diffdiffMetaItemprotocol.ts:11091search sweep'spageread:11118)pageis non-overridable, both readings areundefinedorganizationIdForMetaReadhas FIVE call sites inrest-server.tsonorigin/main, not six. The sixth is thepageread insidepackages/metadata-protocolitself. Both readings of "six" are now reconciled: five in that file, six in the tree. (PR #14677 would make it six in the file; it has not landed.)The argument
Let
f(t, o) = organizationIdForMetaRead(t, o).fanswers eitheroorundefined, sof(t, f(t, o)) === f(t, o)for everytando— a second application over the same type is an algebraic no-op. The load-bearing half is therefore "the same type", and it holds for both sites that reach the method:canonicalMetaUrlType(req.params.type)and then passestype: req.params.type, the raw segment. The first statement ofgetMetaItemsfolds that segment throughcanonicalizeMetaRequestType, which iscanonicalMetaUrlType— the identical map, sorequest.typeinside is the identical string the door gated on.'page'and passes'page'.The proof is executable, not prose
packages/metadata-protocol/src/get-meta-items-org-read-gate.test.ts§3 measures both halves over the complete accepted-spelling population — 61 spellings, derived fromMETA_URL_TO_SINGULARunioned with the registry, so a new type or a changed fold cannot slip past:⭐ §3b is green on both sides of the ablation, and that is its positive content — a case green with and without the change is exactly the demonstration the tripwire asked for.
What the change DOES move — and the semver derivation
Callers that pass a raw active organization are narrowed for non-overridable types. Enumerated mechanically from call sites rather than from the card's table:
runtime/domains/meta.ts:921— dispatcherGET /metadata/:typeruntime/domains/packages.ts:1160—assemblePackageManifestexport sweepruntime/domains/packages.ts:603— ADR-0045 publish visibility flip readapponlygetMetaDiagnosticsuntyped sweepfindReferencesToMetamatcher.fromTypeSemver:
patch— derived from this change's own lineage. A published/metaread door's row set changing is not a new class here; it is the class this predicate was born in, and all three landed instances shippedpatch:b6c769019(#9454 / #9727)/metaread door returns — org rows addedmetadata-core,metadata-protocol,rest: allpatch26f3588fb(#10340 / #10519)rest,metadata-core:patch67ceb9aef(#11553)runtime:patchThe first is the commit that introduced
organizationIdForMetaReaditself. Adding the org partition to every read door waspatch; moving which partition two spellings read waspatch; withholding the org partition from types that never had a read channel for it is the same class one verb further in, and takes the same level.⛔ Not
minor.scripts/check-changeset-no-major.mjsrefusesmajoroutright, so during the launch window a genuinely breaking change ships asminor(pre-1.0, whole-stack lockstep) — #13925 is exactly that:"@objectstack/core": minor, carrying a bolded incompatibility banner and anadr-0087:marker for a narrowed published accept set.But the implication runs one way only, and the gate's own header is explicit that it does: during the window
minoris the union of ordinary new-functionality bumps and banner-marked breaking ones —87ad30c10('@objectstack/types': minor) and3c1bbd2a8("@objectstack/metadata": minor) are new-export bumps carrying no banner at all — so the bump level "tells a consumer nothing about whether the release breaks them". The carriers of breaking-ness are the bolded banner in the changeset body and the ADR-0087 disposition: "during the window they are the only signal there is".⇒ So
minorhere would not claim an incompatibility — it would claim nothing about compatibility, which is precisely the cost the header names. This change carries neither carrier because it owes neither: nothing is retired, no accept set narrows, andcheck-adr-0087-registrationreads it as non-breaking. The level ispatchbecause the lineage above ispatchand no export is added, not becausepatchrebuts somethingminorwould have asserted.minormeans "breaking" here and that claiming it would signal an incompatibility. That affirmed the consequent — breaking ⇒ minor holds, minor ⇒ breaking does not — and contradicted the very file it cited. Corrected above; the conclusion (patch) is unchanged, only the reason it rests on.Nothing here is incompatible, and the reason is what the withheld rows are. They are the #6190 phantoms: org-scoped rows of types with no per-org read channel. The platform has refused to mint them since
ac244ad09/6155c3c24, boot hydration skips them,reportUnhydratableOrgScopedRowsaudits them, and every REST/metaread door has already withheld them sinceb6c769019. The only doors still serving them were the dispatcher list (runtime/src/domains/meta.ts:921) and the runtime manifest and publish-flip reads (packages.ts:1160,:603) — so this change aligns those three with the published/metasurface rather than departing from it. A consumer reading those rows was reading through a door inconsistent with/meta, on data the platform had already ruled dead.⛔ Not "only a refactor of where the predicate lives" either — triage refused that sentence and it is not the reason for the level. The predicate's new position does change which rows three doors serve; that is why this is a behaviour entry rather than an internal note, and the lineage above is why its level is
patch.minoron the strength of a precedent that does not exist: issue 13973 (named without a link so this PR does not pull an unrelated blocked card into its timeline — the withdrawal is checkable as written) is still open and blocked, never landed onmain, and its subject is driver date materialisation. It says nothing about read doors at any level. That citation is withdrawn rather than replaced; the three landed commits above are the real lineage. The same revision also mis-stated #13925 as carrying a major bump, whichcheck-changeset-no-major.mjsforbids outright; it shippedminor, as above.Reverse verification
Mutation: the gate line replaced by
const orgId = request.organizationId;— i.e. the pre-change behaviour restored, inpackages/metadata-protocol/src/protocol.ts.Predicted in writing before mutating: 54 red / 140 green, named — §1 phantom case (1), §2 draft case (1), §3c for the 50 spellings folding to a non-overridable type, §4 sweep (1), §5 sources (1).
Observed: exactly 54 failed / 140 passed (194), and the named set matched — §1×1, §2×1, §3×50, §4×1, §5×1, with zero §3b failures.
Discipline on both legs:
1 -> 0and the injected line0 -> 1, asserted before the run; a miss aborts and voids the reading.// ABLATION: gate removedsuffix, and it is that exact string which went0 -> 1. The bare statementconst orgId = request.organizationId;already occurs twice inprotocol.ts(getMetaItemat :7348 andgetMetaItemLayeredat :7760), so counted bare it went2 -> 3. The suffix is what made the anchor unambiguous; the first revision of this body quoted the count without saying so../protocol.js— same-package relative, so it reads source. Demonstrated positively:dist/index.jsstill contained the gate throughout the mutated run (grep count 1), so a dist-mediated test could not have gone red. It did.git checkout HEAD -- PATHwith PATH absolute (namingHEADso a written index cannot hand the mutation back), thengit hash-objectcompared against the HEAD blob —13b37b5d468e9bcff6c9edd588a2699bac36b157, matched — plusgit diff HEADempty. An empty hash is read as failure, never as "nothing to compare".trap ... EXIT INT TERMwith an absolute repo root resolved viagit rev-parse --show-toplevel, so a foreground-cap SIGTERM mid-mutation cannot leave the tree mutated.Fixture triage
Two existing cases asserted a union the platform must no longer perform. Both were re-spelled, not deleted — the invariant each pins is unchanged and simply has to be measured on a type that has an org partition:
packages/metadata-protocol/src/meta-overlay-cache.test.ts§7 "an org-scoped read does not answer from the env-wide entry" —objecttoview. The key-separation invariant survives; onobjectit would now be asserting a separation the platform deliberately does not have.packages/objectql/src/protocol-meta.test.ts"getMetaItems unions env-wide and org-specific rows" —apptoview. This is the read-side twin of the[#6190]re-spelling three cases up in the same file. Its twogetMetaItem(singular) siblings keepappon purpose: that verb is untouched here.Scanned by the rule's consumption radius rather than by the edited package: every
*.test.tsin the tree naming bothgetMetaItemsandorganizationId(22 files) was enumerated and the affected packages run.Verification
origin/mainmerge base5258b63f8. Union re-run at3bbeb09f0(git rev-parse --short HEADat the time of the run) — the commit carrying the whole code tree. The commits after it are changeset prose only and touch nothing underpackages/**, so the runs below still describe this PR's code exactly.@objectstack/metadata-protocolfull suiteget-meta-items-org-read-gate.test.ts@objectstack/objectql(4 affected files)@objectstack/rest(6 affected files)@objectstack/runtime(2 affected files)@objectstack/metadata-protocoltypecheck@objectstack/objectqltypecheckpnpm lint(repo-wideeslint . --no-inline-config)Gate families re-derived on the FINAL file list with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths passed — the script takes its own change set from the merge base): 38 families, harvested with--commandsso neither spelling nor the convention block could be dropped. The claim-time seed named 23; the extra families come from the changeset and from the "adds or edits a test file" convention block.36 of 38 green. Two remain NOT MEASURED, in the gates' own words — neither is a pass and neither is a red:
node scripts/check-test-completeness.mjs— exit 3: needs a savedturbo run testlog; "running the family locally, record this gate as NOT MEASURED".node scripts/pm/check-half-states.mjs— exit 3: the instrument refuses to run in an agent container.Two others exited 3 on the first pass and were converted into real readings by building the workspace closure (
turbo run build --filter='./packages/*' --filter='./packages/*/*', 71/71) rather than left unmeasured:pnpm check:type-check-debt— green: 22 ledger entries re-measured in 141.2s, 755 raw tsc errors, none above its recorded number, "surplus: none — every entry sits exactly at its measurement, so any new error is red". This is the ratchet family the new test file moves.pnpm check:dual-build-cjs-loads— green: 102 require entry points across 66 packages load, 610 CJS files parse.One gate went red and was repaired:
check-adr-0087-registrationread this changeset's prose citation of #13925's marker as this changeset's own breaking declaration (its detector is/\*\*BREAKING/iplus a line-initialBREAKING[ -]CHANGE). The change retires nothing and declares nothing breaking, so the repair is the wording — ⛔ not anadr-0087:disposition marker for a question this change does not raise.The changeset has since been re-levelled to⚠️ notes in the semver section above). After each, the three changeset-sensitive families were re-run on the final tree:
patchand its semver reasoning corrected twice (the twonode scripts/check-empty-changeset.mjsexit 0,node scripts/check-changeset-no-major.mjsexit 0,node scripts/check-adr-0087-registration.mjsexit 0. Nothing else was re-run, and nothing else needed to be: every commit after3bbeb09f0touches only.changeset/getmetaitems-org-read-gate.md, andpackages/metadata-protocol/src/protocol.tsis still blob13b37b5d468e9bcff6c9edd588a2699bac36b157— the same blob the runs and the ablation above measured.check:changeset-no-majornpm script (rootpackage.jsonhas onlycheck:changeset-gate-self-tests, which runs the--self-tests and not the gate itself).pnpm check:changeset-no-majortherefore exits 254 as a missing script, which is not a gate reading. The directnode scripts/check-changeset-no-major.mjsspelling — the onedispatch-gates --commandsemits — is the gate.Scope
packages/rest/**is not touched — the fence held, and it turned out to be a proof obligation rather than an edit, exactly as claimed.packages/spec/**andpackages/objectql/src/engine.tsare not touched either. The one file outsidepackages/metadata-protocolispackages/objectql/src/protocol-meta.test.ts, a forced fixture re-spelling.The half this PR does not do, hence
Part ofrather than a closing keyword: the two doors named on the card —GET /meta/diagnosticswith no?type=, andGET /meta/:type/:name/references— pass noorganizationIdat all onorigin/maintoday, so nothing about their answers changes here. The gate is now in the right place for whenever a caller does forward one; the forwarding itself lives inpackages/rest, which this card fences off. #13753 and #14677 are where that half belongs, and neither is addressed here.Generated by Claude Code