Skip to content

docs(metadata-protocol): boot reconciliation is ADR-0094 D4, not D3, at two comment sites - #16153

Queued
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-15247-adr-0094-d4-citation
Queued

docs(metadata-protocol): boot reconciliation is ADR-0094 D4, not D3, at two comment sites#16153
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-15247-adr-0094-d4-citation

Conversation

@claude

@claude claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Fixes #15247

Two comments in packages/metadata-protocol cite ADR-0094 D3 for boot reconciliation. Read from the record itself, that mechanism is D4:

  • ### D3 — Data-door writes are redirected into metadata (write-through) (ADR-0094:133) — an engine middleware registered by plugin-security that translates every non-system data-door write into saveMetaItem / deleteMetaItem.
  • ### D4 — Boot reconciliation and the migration/backfill path (ADR-0094:185) — "At kernel:ready, after the ADR-0086 D5 package seeding, plugin-security runs a convergence pass": overlays to records, the one-time backfill, drift healing.

Prime Directive #13's mechanism is that a comment naming an ADR section sends the next author to the decision they are standing on. These two sent them to the write-through redirect table when they were looking for the kernel:ready convergence pass — a plausible-looking wrong answer, which is worse than no citation.

The diff

Two characters, comment text only. No behaviour, no assertion, no test semantics.

packages/metadata-protocol/src/protocol.ts:20024                          (rollbackMetaItem projector comment)
  - // write or boot reconciliation (ADR-0094 D3) eventually heals it.
  + // write or boot reconciliation (ADR-0094 D4) eventually heals it.

packages/metadata-protocol/src/protocol.recovery-doors-mutation-projector.test.ts:20   (file header)
  -  * name, or a boot reconciliation (ADR-0094 D3), re-derived it.
  +  * name, or a boot reconciliation (ADR-0094 D4), re-derived it.

Triage measured the second site at protocol.ts:20062 on 1bc3c092a; on this PR's base 932acc3df the same sentence sits at :20024. Re-located by text, not by line number.

The census — the real job of this PR

git grep -n "ADR-0094 D3\|ADR-0094 D4" -- packages/20 hits, every one classified, so that "I changed exactly the two that were wrong" is checkable rather than asserted.

Genuine D3 — data-door write-through. 4 hits. CORRECT, left alone.

Site Why it is genuinely D3
plugin-security/src/permission-set-projection.ts:861 Section banner // Data-door write-through (ADR-0094 D3)
plugin-security/src/permission-set-projection.ts:959 Docblock of the redirect middleware: "redirect every non-system data-door write on sys_permission_set into the metadata store"
plugin-security/src/permission-set-projection.ts:1140 Log string inside createPermissionSetWriteThrough (declared at :975) — the enclosing function IS the D3 middleware
plugin-security/src/permission-set-projection.test.ts:566 Section banner over the write-through tests

Genuine D4 — boot reconciliation / backfill / drift healing. 14 hits. CORRECT, left alone.

plugin-security/src/permission-set-projection.ts :300 (ADR-0094 D4 boot backfill), :1299 (banner Boot reconciliation + one-time backfill), :1438 (backfill into metadata FAILED (ADR-0094 D4)), :1465 (drift re-projection), :1481 (failed-backfill summary), :1497 (projection reconciled (ADR-0094 D4)) · permission-set-drift.ts:22 (D4 step 1) · permission-set-overlay-discard.ts:18 (step 2/3) · security-plugin.ts:3562 ([ADR-0094 D4] Converge record ↔ metadata) · permission-set-projection.test.ts :253, :913, :1111 · plugin-security/CHANGELOG.md :5864, :9491 (historic, generated).

The two outliers — boot reconciliation spelled D3. 2 hits. THE ONLY THING THIS PR CHANGES.

metadata-protocol/src/protocol.ts:20024 · metadata-protocol/src/protocol.recovery-doors-mutation-projector.test.ts:20

4 + 14 + 2 = 20. Post-edit the same census returns 4 D3 + 16 D4 = 20 — same total, so no citation was added or dropped, exactly two were re-pointed.

The census population, proved with a firing control

The boilerplate's pathspec trap is real and it fires on this very query:

Pathspec Hits
packages/*/src/**/*.ts 0git grep applies no :(glob) magic, so this silently excludes everything
packages/**/*.ts 18 — misses the two CHANGELOG.md rows
-- packages/ (used here) 20

Positive control inside the chosen set, at both directory depths the population spans: 2 files matched at packages/<pkg>/src/ and 6 at packages/plugins/<pkg>/src/.

Repo-wide, outside packages/, the same pattern hits only docs/adr/0086-…:379, docs/adr/0131-…:260 and docs/adr/0131-…:301 — all three cite D3 for the write-through redirect and are correct. They are a governed surface and are not touched.

Clause ②: no

Measured by ablation on @objectstack/metadata-protocol, not assumed.

files: ["dist", …], so the published surface is all 24 files under dist/. Built at head, swapped protocol.ts back to origin/main, rebuilt, diffed all 24, restored.

  • Mutation proved on disk before measuring: boot reconciliation (ADR-0094 D4) count 1 to 0, D3 count 0 to 1, blob 426728d2a to 89436302d — byte-identical to origin/main's blob.
  • Mutation proved to reach dist/: ablation-dist-preflight --absent 'ADR-0094 D4' exit 0, "marker absent from all 24 built files".
  • Result: 22 of 24 byte-identical. The only two that differ are dist/index.js.map and dist/index.cjs.map. index.d.ts, index.d.cts, index.js and index.cjs are all byte-identical.
  • Untouched-text control, so the sourcemap delta is shown to be normal rather than something this diff introduced: a pre-existing sentence from the same docblock that this PR does not touch — registry to the target version — occurs in exactly index.js.map and index.cjs.map and in none of the other 22 files, the same distribution as the changed text. Sourcemaps embed sourcesContent verbatim; they are not a contract face.
  • Restore proved after measuring: whole-tree git status --porcelain empty, blob back to 426728d2a, git diff HEAD empty, and the rebuilt dist/ byte-identical to the pre-ablation snapshot across all 24 files.
  • Independent corroboration from the repo's own instrument: on the restore leg ablation-dist-preflight with the marker expected present exits 1 and says the marker is found "ONLY in 2 sourcemap files and in no executable output". That is the tool stating, without being asked, that this text never reaches running code.

Line-number neutrality, proved rather than assumed

A comment-only diff is not a no-op for any gate that cites a line number, so:

  • Byte length unchanged: protocol.ts 1235907 bytes before and after; the test file 24258 before and after.
  • Line count unchanged: protocol.ts 21625 to 21625; the test file 511 to 511.
  • Numbered-line diff: comparing cat -n of base against head, exactly one line differs per file, at the same line number (20024 and 20). Every other line is byte-identical at its own number, so no anchor can have moved.
  • 14 line-numbered anchors point into protocol.ts from elsewhere in the tree (:106, :1580, :2938, :6764, :8082, and the nine rows in scripts/check-durability-degradation-log-level.mjs). None names :20024, and none names :20 in the test file.

Gates run locally, at this PR's final commit 2c904e9c4

Derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (102 families; CI runs the farm). The anchor/census family was run in full and not narrowed on the grounds that the diff is only comments:

check-system-context-census.mjs + --self-test · check:doc-anchors · check:adr-anchors · check:adr-symbol-anchors · check:scripts-symbol-anchors · check-keyed-text-bounds.mjs + --self-test · check:nul-bytes · check:doc-authoring · check:cross-package-test-inputs · check:durability-log-level · check:single-claim-paths · check-comment-mask-adoption.mjs · check-comment-mask-corpus.mjs15 runs, all REAL_EXIT=0 (redirect-then-read, never through a pipe).

Population proof rather than a bare exit code: check:cross-package-test-inputs names metadata-protocol in its output; check-system-context-census sweeps metadata-protocol (it anchors metadata-protocol/src/seed-loader.ts:2032) and carries 0 anchors into protocol.ts.

Package verification: pnpm --filter @objectstack/metadata-protocol typecheck exit 0, and tsc --noEmit --listFiles confirms both edited files are in the swept set (this package's tsconfig includes src/**/* with no test exclusion, so the green is about them). vitest run src/protocol.recovery-doors-mutation-projector.test.ts — 10 passed, exit 0.

Changeset: skip-changeset, no changeset file

AGENTS.md: that label "is for a diff that publishes nothing from any released package." The ablation above measured this case — no declaration, no runtime output, no behaviour changes; the only published bytes that move are sourcemap sourcesContent. Merged precedent of the same shape: #16103 (comment-only in driver-memory/src/memory-matcher.ts) and #15985 (comment-only in a service-cluster-redis test header), both merged carrying skip-changeset and no changeset.

In-flight check on protocol.ts

Run before claiming, against current origin/main = 932acc3df. Two open PRs also touch this file: #16117 (+34 -4) and #16123 (+8 -3), both with hunks in the @@ -7290 region — about 13,000 lines above :20024, no overlap, and this diff is length-neutral so it moves nothing for either. #16125 is an open issue against a different sentence at :14967; it is deliberately not addressed here and remains open.


Generated by Claude Code

…at two comment sites

Two comments in `packages/metadata-protocol` cite `ADR-0094 D3` for boot
reconciliation. In ADR-0094 that is D4 (`### D4 — Boot reconciliation and the
migration/backfill path`); D3 is a different decision — `### D3 — Data-door
writes are redirected into metadata (write-through)`.

Prime Directive #13's mechanism is that a code comment naming an ADR section
sends the next author to the decision they are standing on. A wrong section
sends them to the write-through redirect table when they were looking for the
`kernel:ready` convergence pass — a plausible-looking wrong answer, which is
worse than no citation at all.

Comment text only; no behaviour, no assertion, no test semantics. The edit is
byte-length neutral, so no line number moves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/metadata-protocol, touching 4 documentable anchor(s).

2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via meta.rollbackItem (sdk, the route ledger binds it to POST /api/v1/meta/:type/:name/rollback, selected by route anchor /:type/:name/rollback), rollbackItem (sdk, the bare tail of client method meta.rollbackItem, bound to POST /api/v1/meta/:type/:name/rollback))
  • content/docs/kernel/contracts/metadata-service.mdx (via /:type/:name/rollback (route, bridged from symbol rollbackMetaItem — its route source's handler names it))
What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 9 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json f7ffbd65eaac1919e85dcb597c996d1177b54dbbpackageMentionDocs.

Which tree this was computed on

This run read content/docs from 2d88dea85c16e4398dcdc6cc30c911cde434238e — the merge of head 2c904e9c4c169dde68e9c2a00da66538569713b1 into base f7ffbd65eaac1919e85dcb597c996d1177b54dbb, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 2d88dea85c16e4398dcdc6cc30c911cde434238e && git checkout 2d88dea85c16e4398dcdc6cc30c911cde434238e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f7ffbd65eaac1919e85dcb597c996d1177b54dbb 2c904e9c4c169dde68e9c2a00da66538569713b1 && git checkout -B drift-repro f7ffbd65eaac1919e85dcb597c996d1177b54dbb && git merge --no-ff 2c904e9c4c169dde68e9c2a00da66538569713b1

node scripts/docs-audit/affected-docs.mjs --json f7ffbd65eaac1919e85dcb597c996d1177b54dbb

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs f7ffbd65eaac1919e85dcb597c996d1177b54dbb → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@claude

claude Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Docs Drift Check — the two named pages, and the blind spot that was actually worth searching

The two hand-written rows: no action, and why that is a measurement rather than a shrug

Both rows exist because this diff sits inside the rollbackMetaItem projector comment, so the anchor extractor attributes it to that symbol and to its bound route POST /api/v1/meta/:type/:name/rollback. What the diff actually moves is two characters inside a // comment. Measured, not assumed:

  • No symbol, signature, route or SDK method moved. The ablation in the PR body diffed all 24 files this package publishes: index.d.ts, index.d.cts, index.js and index.cjs are byte-identical; only the two .map files differ, and an untouched-text control from the same docblock shows that is ordinary sourcesContent embedding.
  • ⇒ Neither content/docs/api/client-sdk.mdx nor content/docs/kernel/contracts/metadata-service.mdx can have been falsified by this change. An unchanged surface cannot make a page stale, so neither page was opened as a suspect and neither was edited.

The input-side blind spot the check names itself — searched by hand

The row that would be invisible to this check on every run is prose that restates the boot-reconciliation rule while citing the wrong ADR section, because it shares no identifier with the emitter. That is precisely this card's shape, so the census was extended past packages/.

Every ADR-0094 D<n> citation in the tree, on origin/main f7ffbd65e, any section, any spelling (ADR-0094 D3, ADR-0094's D3, §D3):

Section Hits Verdict
D5-R 35 correct (overlay retirement)
D4 14 correct (boot reconciliation / backfill / drift)
D5 11 correct (the retired 2026-07-14 direction, cited as retired)
D3 9 all correct — 6 in packages/ (4 genuine + this PR's 2 outliers), 3 in docs/adr/
D2 3 correct (record written only by the projector)
R1 / R2 2 the object-posture-gate arms, a different thread

The three D3 citations outside packages/ are docs/adr/0086-…:379, docs/adr/0131-…:260 and docs/adr/0131-…:301. All three cite D3 for the data-door write-through redirect, which is what D3 is. Correct, governed surface, read-only, untouched.

Coverage the other way — every boot reconcil / reconciliation pass / kernel:ready convergence mention in the tree was read for a section citation. Outside the two sites this PR fixes, not one of them cites ADR-0094 D3. The content/docs/** hits are: permissions/system-context.mdx:8 (says "a plugin's boot reconciler", cites no section), permissions/authorization.mdx (cites the ADR-0094 write-through with no section number at :239, and D5 / D5-R correctly at :244 / :246), and releases/v17.mdx:2614 (ADR-0119's migration-journal boot reconciliation — a different mechanism, no ADR-0094 citation).

Nothing found outside the two authorised sites. Nothing to file, and nothing in content/docs/releases/** was touched — the only release-notes hits are v15.mdx / v17.mdx mentions of ADR-0094 that carry no wrong section.

Firing positive controls, because a zero result without one is not a reading:

  • The boot reconcil pattern fires on this PR's own two sites (protocol.ts:20024, protocol.recovery-doors-mutation-projector.test.ts:20, both now reading ADR-0094 D4) plus 11 further hits in metadata-protocol alone — the pattern reaches into the intended set.
  • .changeset/** returns zero ADR-0094 mentions; the control proving that population is real and searchable is 190 files there, 189 of them matching a control term.
  • Pathspec control, since git grep applies no :(glob) magic: packages/*/src/**/*.ts returns 0 for this query, packages/**/*.ts returns 18, and the -- packages/ form actually used returns 20.

Tree note

The bot read content/docs from 2d88dea85c… (a merge of head 2c904e9c4c… into base f7ffbd65ea…) and reported its own checkout carried uncommitted changes, so that sha does not fully identify what it read. The census above was re-run against origin/main = f7ffbd65ea, which is that same base.

Anchor family, re-run on the merged tree

origin/main moved 4 commits past this branch's base while it was open — including #16087, which hit the line-rot mechanism in packages/objectql/src/engine.ts and repaired the content/docs/permissions/system-context.mdx anchors with the gate's own --fix. This diff is length-neutral, so it cannot disturb that repair; proved rather than assumed, in a scratch worktree cut at f7ffbd65e with this patch applied on top:

  • The patch applies cleanly to current main — no conflict with any of the 4 new commits.
  • protocol.ts 21625 lines either side; the test file 511 either side. Comparing cat -n output, exactly one line differs per file, at the same line number (:20024 and :20).
  • On that merged tree: check-system-context-census + --self-test, check:doc-anchors, check:adr-anchors, check:adr-symbol-anchors, check:scripts-symbol-anchors, check-keyed-text-bounds, check:nul-bytes8 runs, all REAL_EXIT=0, redirect-then-read. The census reports the same 105 read sites / 140 anchors as on the base.

Generated by Claude Code

@zhuangjianguo
zhuangjianguo marked this pull request as ready for review September 6, 2026 04:00
@zhuangjianguo
zhuangjianguo added this pull request to the merge queue Sep 6, 2026
Any commits made after this event will not be merged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(metadata-protocol): two comments cite ADR-0094 D3 for boot reconciliation, which is that record's D4

1 participant