Skip to content

docs(adr): ADR-0094 D2 enumerates three projecting doors; the shipped code has seven (amendment) - #15249

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-15244-adr-0094-recovery-doors-amendment
Sep 4, 2026
Merged

docs(adr): ADR-0094 D2 enumerates three projecting doors; the shipped code has seven (amendment)#15249
os-zhuang merged 1 commit into
mainfrom
claude/issue-15244-adr-0094-recovery-doors-amendment

Conversation

@hotlong

@hotlong hotlong commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fixes #15244

What this changes

docs/adr/0094-sys-permission-set-pure-projection.md only. No code, no changeset, no
release notes.

PR #14982 (merged 2026-09-03 as 95464ed65) put the ADR-0094 mutation projector behind
the 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): title shape the record already carries for 2026-08-14), plus two
inline 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.ts

Expected output on this branch:

7

The count is structurally pinned, not incidental —
packages/metadata-protocol/src/protocol.recovery-doors-mutation-projector.test.ts asserts
"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.ts

which 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:

  • The publish door is runPublishSideEffects, not publishMetaItem. The projecting
    call sits at that helper's own body level (unconditional), and the helper has two
    callers: publishMetaItem and publishPackageDrafts. So the package-draft publish path
    has been projecting undocumented. D2 named only the first caller.
  • projectionApplied is not surfaced at every site. It is surfaced at four of seven.
    rollbackMetaItem and both revertCommit limbs call and await the projector while
    declaring 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.
  • The "only healing path" defect is narrower than "the recovery doors broke it". The
    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|revert mentions checked out:
git grep -ci "rollback\|revert" on the ADR returns 6, and none of them is about the
rollbackMetaItem / revertCommit protocol 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_set projector and reconciler of ADR-0094
D2/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 not
armed 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-changeset label 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 16
runnable families
; all 16 were run at the final commit f2acdc322, exit code captured
before any pipe:

Family Verdict
node scripts/check-adr-links.mjs (+ --self-test) 0 — green
node scripts/check-adr-symbol-anchors.mjs (+ --self-test) 0 — green
pnpm check:adr-anchors 0 — green
pnpm check:doc-authoring 0 — green
pnpm check:nul-bytes 0 — green
pnpm check:pm-governed-merges 0 — green
pnpm check:cross-package-test-inputs 0 — green
node scripts/check-ci-filter-parity.mjs 0 — green
node scripts/check-closing-keyword-parity.mjs (+ --self-test) 0 — green
node scripts/check-comment-mask-corpus.mjs 0 — green
pnpm check:refd-timer-probe 0 — green
pnpm check:watch-hint-literal 0 — green
pnpm --filter @objectstack/lint run check:doc-formula-expressions 0 — green, after two exit 3 runs

The three ADR gates the card called out — check-adr-links, check-adr-anchors and
check-adr-symbol-anchors — are green. No file.ts:NNN anchor was written: the
amendment cites bare file paths and names symbols in prose, and line numbers appear
nowhere in it.

One NOT MEASURED, then resolved. check:doc-formula-expressions first answered
exit 3PREREQUISITE NOT MET, the gate's own distinct code for "nothing was
measured", not a finding. It wanted @objectstack/formula built, then @objectstack/lint
built. Both were built and the gate re-run to a real exit 0. Recorded here because an
exit 3 is 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 in eslint.config.mjs
names only {ts,tsx,mts,cts,js,jsx,mjs,cjs}, with no Markdown block anywhere; (2) the
count 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. zero
files linted; (3) invariance for untouched files — the diff is one .md file and touches
no 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. Declared
here 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

… 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>
@hotlong hotlong added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 4, 2026
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Director review — APPROVE for hand-merge. Every factual claim in the amendment re-derived on origin/main; nothing false, nothing missing against the card's scope. Governed surface: ⛔ this seat does not flip ready, arm auto-merge or merge — the maintainer does.

Director seat (项目总监席), session session_01LsEjuNMPitCHwEfYftZ1um, GitHub os-warren, at the maintainer's request (live chat, 2026-09-04 13:0xZ). Reviewed at head f2acdc322, one file, +105/−2, diff read locally; claims checked against origin/main 6e67b86+ (the ADR is byte-identical between the merge base 1bc3c092a and today's main; trial merge: zero conflict hunks; mergeable_state: clean).

What was verified, not inherited

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-anchors is 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
os-zhuang marked this pull request as ready for review September 4, 2026 14:02
@os-zhuang
os-zhuang enabled auto-merge September 4, 2026 14:03
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit bcb6a17 Sep 4, 2026
36 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-15244-adr-0094-recovery-doors-amendment branch September 4, 2026 14:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(adr): ADR-0094 D2 under-describes its own shipped code — the recovery doors project too, and D3 is no longer the only healing path

3 participants