docs(adr): ADR-0094 D2 enumerates three projecting doors; the shipped code has seven (amendment) - #15249
Merged
os-zhuang merged 1 commit intoSep 4, 2026
Conversation
… has seven The mutation projector is awaited at seven call sites in the metadata protocol, not the three D2 enumerates: the recovery doors (rollbackMetaItem, both limbs of revertCommit) and deleteMetaItem's legacy raw-engine exit project too. Record it as a dated amendment in the ADR's own style, leaving the original sentences visible and marked rather than rewritten. Also corrects the D2 clause that reads as though boot reconciliation is the only thing that heals a failed projection, notes that the publish door is a shared helper reached by two callers, notes that three of the seven sites surface no projectionApplied, and adds the forward pointer to ADR-0131's v18 retirement. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Collaborator
Director review — APPROVE for hand-merge. Every factual claim in the amendment re-derived on
|
| claim in the amendment | reading on main |
|---|---|
seven this.runMutationProjector( call sites, one declaration |
✅ git grep -c = 7; declaration at :4981, sites at :15504 :16737 :19516 :19621 :20015 :20571 :20739 |
| enclosing methods per the table | ✅ saveMetaItem (decl :14568) · runPublishSideEffects (decl :16631) · revertCommit ×2 (decl :19178) · rollbackMetaItem (decl :19832) · deleteMetaItem ×2 (decl :20322, repository branch + legacy exit) |
state per door |
✅ mode === 'draft' ? 'draft' : 'active' · 'active' · 'deleted' / 'active' (revert limbs) · 'active' · 'deleted' / 'deleted' |
| the publish door is the shared helper with two callers | ✅ runPublishSideEffects is called from publishMetaItem (:16356) and publishPackageDrafts (:17863) |
projectionApplied surfaced at four of seven |
✅ surfaced at :15521 (save), :16367/:16744 (publish), :20587 and :20770 (both delete exits); no occurrence anywhere inside revertCommit / rollbackMetaItem |
| the count is pinned | ✅ protocol.recovery-doors-mutation-projector.test.ts:488 — "has exactly seven … three original, four recovery-door sites", toHaveLength(7) |
| the six pre-existing rollback/revert mentions are not about these methods | ✅ all six are ADR-0005 / #6483 / PR #6608 or "reverting an admin's reclassification"; none touched |
| "boot reconciliation heals on next start" is the sentence being qualified | ✅ D2's best-effort clause, verbatim on main |
| ADR-0131 pointers | ✅ D2 (:232) names "the sys_permission_set projector and reconciler of ADR-0094 D2/D4" at :269; D3 (:274) retires the object, :296 "ADR-0094 D1 — the metadata layer was already the sole …"; D13 retirements (:635); D14 v18 line (:659); C3 row cites D2/D3/D5/D13 and #15204 is C3 |
| style | ✅ same ## Amendment (date): title shape as the record's 2026-08-14 amendment; originals left verbatim and marked; D1 untouched; no file.ts:NNN anchor in any added line (0 matches) |
| out-of-scope finding | ✅ #15247 exists (the D3→D4 citation defect in two #14982 comments), correctly not fixed here |
CI on f2acdc322: Lint & Repo Gates (carries the three ADR gates), Type Check lanes, Test Core, Governed Surface Queue Guard, Check Changeset (skip-changeset) — all green.
Two non-blocking notes for the record
- The D2 heading now carries a parenthetical.
check:adr-anchorsis green, so no in-repo link keyed on the old heading text broke; out-of-repo deep links to that heading slug are the only thing that could, and none is known. - The amendment's "one major left to live" framing is exactly the sentence the card asked for; it stops the next reader building on the seven doors without pretending they are gone.
Ready to hand-merge as-is. Squash, no changeset, Fixes #15244 closes the card.
Generated by Claude Code
os-zhuang
approved these changes
Sep 4, 2026
os-zhuang
marked this pull request as ready for review
September 4, 2026 14:02
os-zhuang
enabled auto-merge
September 4, 2026 14:03
os-zhuang
deleted the
claude/issue-15244-adr-0094-recovery-doors-amendment
branch
September 4, 2026 14:48
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.
Fixes #15244
What this changes
docs/adr/0094-sys-permission-set-pure-projection.mdonly. No code, no changeset, norelease notes.
PR #14982 (merged 2026-09-03 as
95464ed65) put the ADR-0094 mutation projector behindthe recovery doors. Its documentation half, PR #14980, was voided on the maintainer's
instruction, so the code shipped and the record did not follow it. D2 still enumerated
three projecting doors while the tree has seven. Nothing D2 said was false — it was
incomplete, which is the harder kind of error to notice.
This is a dated amendment in the ADR's own established style (the same
## Amendment (date): titleshape the record already carries for 2026-08-14), plus twoinline markers so a reader who lands on the stale sentences is told. The original
sentences are left verbatim and marked, never rewritten, and no decision changes: D1 is
untouched, and D2's rule is exactly what the four new call sites implement.
Re-check command a reviewer can run
git grep -c "this.runMutationProjector(" -- packages/metadata-protocol/src/protocol.tsExpected output on this branch:
The count is structurally pinned, not incidental —
packages/metadata-protocol/src/protocol.recovery-doors-mutation-projector.test.tsasserts"exactly seven" call sites with the declaration excluded as a non-vacuity control. To see
the enclosing methods:
git grep -n "runMutationProjector" -- packages/metadata-protocol/src/protocol.tswhich prints the one declaration plus the seven call sites.
What I measured, rather than repeated from the card
The card's attributions were re-verified against the seven call sites by computing each
one's enclosing method. Three refinements the card did not carry, all of them now in the
amendment:
runPublishSideEffects, notpublishMetaItem. The projectingcall sits at that helper's own body level (unconditional), and the helper has two
callers:
publishMetaItemandpublishPackageDrafts. So the package-draft publish pathhas been projecting undocumented. D2 named only the first caller.
projectionAppliedis not surfaced at every site. It is surfaced at four of seven.rollbackMetaItemand bothrevertCommitlimbs call and await the projector whiledeclaring no such key on their response types — fix(metadata-protocol): recovery doors run the ADR-0094 mutation projector #14982 deliberately added none. D2's
failure-surfacing sentence therefore holds at four sites, not seven.
ADR contains no sentence with the word "only" attached to boot reconciliation; the
actual claim is D2's best-effort clause ending "boot reconciliation heals on next
start", which reads as though a failed projection is stranded until the next boot. It
never quite was — any later projecting door re-derives, because the projector re-reads
the fresh layered effective body. Before fix(metadata-protocol): recovery doors run the ADR-0094 mutation projector #14982 that set was an unrelated save, publish
or delete; since fix(metadata-protocol): recovery doors run the ADR-0094 mutation projector #14982 it also includes rollback and both revert limbs. The amendment
says that, and keeps D4's boot pass as the guaranteed floor.
The card's warning about the six pre-existing
rollback|revertmentions checked out:git grep -ci "rollback\|revert"on the ADR returns 6, and none of them is about therollbackMetaItem/revertCommitprotocol methods — all six are the ADR-0005 rollback(#6483 / PR #6608) and one unrelated "reverting an admin's reclassification". No stale
sentence there, so none was touched.
Forward pointer added per the card: ADR-0131 (merged 2026-09-04 as PR #14976) retires this
machinery — its D2 names "the
sys_permission_setprojector and reconciler of ADR-0094D2/D4", its D3 retires the object itself (with D13) on the ground that ADR-0094 D1 was
right and is being generalized, D14 puts it on the v18 line, and the executing card is
C3, tracked on #15204.
Governed surface
docs/adr/**is a governed surface. This PR is opened as a draft; auto-merge is notarmed and will not be; no AI seat merges it. The maintainer hand-merges.
Changeset
None, deliberately — docs-only, publishes nothing from any released package. The
skip-changesetlabel is applied. An empty-frontmatter changeset is rejected by the gate(#4898), so none was written.
Gates
Derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack,letting it compute its own change set (1 path, vs merge base
1bc3c092a). It named 16runnable families; all 16 were run at the final commit
f2acdc322, exit code capturedbefore any pipe:
node scripts/check-adr-links.mjs(+--self-test)node scripts/check-adr-symbol-anchors.mjs(+--self-test)pnpm check:adr-anchorspnpm check:doc-authoringpnpm check:nul-bytespnpm check:pm-governed-mergespnpm check:cross-package-test-inputsnode scripts/check-ci-filter-parity.mjsnode scripts/check-closing-keyword-parity.mjs(+--self-test)node scripts/check-comment-mask-corpus.mjspnpm check:refd-timer-probepnpm check:watch-hint-literalpnpm --filter @objectstack/lint run check:doc-formula-expressionsexit 3runsThe three ADR gates the card called out —
check-adr-links,check-adr-anchorsandcheck-adr-symbol-anchors— are green. Nofile.ts:NNNanchor was written: theamendment cites bare file paths and names symbols in prose, and line numbers appear
nowhere in it.
One NOT MEASURED, then resolved.
check:doc-formula-expressionsfirst answeredexit 3—PREREQUISITE NOT MET, the gate's own distinct code for "nothing wasmeasured", not a finding. It wanted
@objectstack/formulabuilt, then@objectstack/lintbuilt. Both were built and the gate re-run to a real
exit 0. Recorded here because anexit 3is neither a pass nor a fail and should not be reported as either.pnpm lint— a declared narrowing, not a skip. The repo-wide eslint scan is CI's run.Three pieces of evidence that this diff cannot move any eslint verdict: (1) the population
is read from eslint's own config, not guessed — every
files:glob ineslint.config.mjsnames only
{ts,tsx,mts,cts,js,jsx,mjs,cjs}, with no Markdown block anywhere; (2) thecount comes from
--format json— running eslint on the changed file returns one entry,0 errors, message
File ignored because no matching configuration was supplied, i.e. zerofiles linted; (3) invariance for untouched files — the diff is one
.mdfile and touchesno eslint config, no TS source and no tsconfig, and no type-aware path reaches a Markdown
file, so no untouched file's verdict can move.
Verify-lock declaration. The two builds went through
scripts/pm/os-verify-lock.sh, which reported:UNLOCKED (declared) · no usable flock on this host, so the shared verify lock was NEVER taken and NOTHING was serialized. Declaredhere as that entry point asks. Both builds still exited 0.
Out of scope, filed separately
While verifying the call sites I found that two comments introduced by #14982 cite
"ADR-0094 D3" for boot reconciliation, which is D4 in the record (D3 is the data-door
write-through). Every other citation in the tree spells it D4. That is a code-comment
defect, out of scope for a docs-only PR on a governed surface, and is filed separately as
#15247 — not addressed here.
Generated by Claude Code