Uh oh!
There was an error while loading. Please reload this page.
docs(adr): ADR-0120 D4 amendment — the duplicate pre-flight is per index class - #11387
docs(adr): ADR-0120 D4 amendment — the duplicate pre-flight is per index class#11387os-zhuang wants to merge 1 commit into
Conversation
…dex class D4 decides that `os migrate plan` gains a duplicate pre-flight probe per affected index. That was written for the declared class — the `recreate_index` drift ops the reconciler can see — and it is true there. It cannot reach the three `kernel:ready` runtime index tightenings in `packages/metadata-protocol`, which are invisible to the drift differ by construction. The maintainer's 2026-08-22 ruling on #8725 routes that class to `os migrate duplicates` instead, explicitly leaving `os migrate plan`'s drift contract untouched. PR #11031 shipped that behaviour; this records the split in D4's text, where the three migration modules' doc comments already point. Documentation only: no behaviour, gate or test changes. Governed surface (docs/adr/**) — draft PR, maintainer merges by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR
os-zhuang
commented
Aug 23, 2026
PM: review routing corrected to the ruled fallback. Content of the PR is accepted — this is about who gets notified. The body says "Review requested from
The maintainer's stated intent when that rule was set (2026-08-19) was the phone notification: 「发给 os-zhuang 审核。我的手机 github 应该会收到推送消息吧」. Applied: the PR is now assigned to Per the same rule, the assignee fallback is named explicitly in this round's report rather than left as a silent substitution. Nothing else changes: still draft, ⛔ not enqueued, ⛔ not armed, One note for whoever merges: the PR body's "Review requested from Generated by Claude Code |
os-zhuang
commented
Aug 23, 2026
PM correction to my own comment above — the
The dev's report shows it did the right thing in the right order:
I verified point 2 rather than repeating it: So for ⭐ Platform fact worth carrying forward, surfaced by this dev and not previously recorded in this lane: a DRAFT PR does not trigger CODEOWNERS auto-request. Governed-surface PRs are draft by rule, so on exactly the PRs where CODEOWNERS matters most it does not fire on its own — the explicit request is required, and omitting it would leave the PR silently unrouted while looking correctly governed. Generated by Claude Code |
os-zhuang
commented
Aug 23, 2026
PM: CI is clean at Verified job-by-job rather than from the aggregate. Everything is
Both are superseded duplicate runs, each replaced by a later run of the same-named check that passed. I am naming them explicitly because The five path-derived gate families the dev reported all pass on their own verdict lines: What you are merging: one file, +41 / −1 — the D4 summary row plus an amendment blockquote, following the ADR-0112 convention that leaves the original decision text standing. No source file was touched, and the PR body carries the per-site verification (12 reference points across five modules) showing why none needed to be: amending D4 makes the three migration modules' doc comments correct where they already stand. Routing: Generated by Claude Code |
Fixes#11032
⛔ Governed surface —
docs/adr/**. This PR stays draft. Review requested fromos-zhuang; the human merge IS the review and the audit record. Never flipped ready, never enqueued, no auto-merge armed.What is wrong
ADR-0120 D4 decides:
That was written for the declared index class — the
recreate_indexdrift ops the reconciler can see — and it is true there. It cannot reach a second class. Threekernel:readymigrations inpackages/metadata-protocoltighten an index at runtime (ensureMetadataOverlayIndexesonsys_metadata,ensureViewDefinitionActiveIndexonsys_view_definition,ensureSysSettingIdentityIndexonsys_setting), and each is invisible to the drift differ by construction: after the tighteningisRuntimeManagedIndexexcludes the index (isSyncReproducibleIndexis false for a partial index and for aCOALESCEkey part over a non-tenant column — correctly, or a boot would propose rebuilding away the guarantee it just created), and before it each migration deliberately reuses the DECLARED index's name, so the name-matched slot reads as filled either way.PR #11031 shipped the behaviour for that class. D4's text did not move with it, so the three modules' doc comments cite D4 while naming
os migrate duplicates— accurate about behaviour, inaccurate about D4's text, and the next author reads that as an error in the code rather than a split in the decision.The ruling this records — quoted verbatim
From the maintainer ruling recorded on #8725 (2026-08-22, decision-inbox digest; the maintainer accepted the batch, verbatim: 「接受所有」). Quoted rather than paraphrased, and the Chinese is kept untranslated:
The change
One file, 41 insertions / 1 deletion. The D4 summary row (line 38) and an amendment blockquote at the end of the D4 section — the ADR-0112 amendment convention: the original decision text is left standing, the amendment records what changed and why.
Summary row, after:
The amendment's operative wording:
The amendment also carries the by-construction invisibility argument and the matched-control measurement (one database carrying the same duplicate damage under both classes;
plannamed the declared index in full and said nothing about the runtime-managed one) — both already recorded inruntime-index-preflight.tsandview-definition-active-index.ts, now stated where the decision lives.Route 2 retires the split
The amendment says so in its own last paragraph, and it is worth stating here too: the split exists only because these three platform indexes are tightened at runtime rather than declared. ADR-0120 Route 2 — NULL-safe uniqueness declared in the spec, so a declaration states its own row identity and no runtime migration is needed — was parked to the ADR-0120 / v18 train by #8629's ruling. If Route 2 lands, all three runtime migrations retire, the class collapses back into the declared one, and this per-class split goes with them. This amendment is therefore a record of a temporary shape, with its expiry named.
No source file is touched — verified, not assumed
The claim under test: amending D4 makes the three migration modules' doc comments correct where they already stand. Every
ADR-0120 D4andos migratereference inpackages/metadata-protocol/src/migrations/was read against the amended wording (read-only; nothing edited):overlay-index.tsL59-64os migrate duplicates, never block the boot"os migrate duplicates; the rest of the disposition is unchanged and explicitly held for both classes.overlay-index.tsL66-73os migrate planuntil #8725, and it was FALSE for this class … ruling 2026-08-22 routes the class toos migrate duplicates"overlay-index.tsL98, L150overlay-index.tsL194-201, L317, L377os migrate duplicatesview-definition-active-index.tsL114-117os migrate duplicates, and the boot continues"view-definition-active-index.tsL119-128os migrate planand was FALSE; matched-control measurement; ruling routes the classview-definition-active-index.tsL235-241, L341-344, L393-396, L405os migrate duplicatessys-setting-identity-index.tsL125-128errorline … without reaching foros migrate duplicatesfirst"sys-setting-identity-index.tsL130-138os migrate planuntil #8725 and was FALSE; ruling routes the class, leavingplan's drift contract untouchedsys-setting-identity-index.tsL305-312, L487-490, L556-557os migrate duplicatesand cites D4partial-index-probe.tsL34runtime-index-preflight.tsL18-56os migrate plancannot carry it … by construction, twice over"; ruling routes toos migrate duplicatesResult: zero comments remain inaccurate, so no source file needs an edit and none was made. ⛔
packages/metadata-protocol/**was not touched —protocol.tsis held by in-flight #11095 under a hard file serial.The two helpers the amendment names were verified in
packages/drivers/driver-sql/src/schema-drift.ts(isSyncReproducibleIndexL895 returnsfalseforindex.partial === trueand for aCOALESCEpart whose column is not the tenant field;isRuntimeManagedIndexL916 is true in that case, so the differ skips), andos migrate duplicatesexists atpackages/cli/src/commands/migrate/duplicates.ts, whose own header records the same 2026-08-22 ruling for itsruntimeIndexPreflightsection.Changeset: none,
skip-changesetappliesRepo convention for an ADR-only change, checked rather than assumed: comparable merged ADR-only PRs — #10718, #10154, #9233 — each carry
documentation+skip-changesetand add no.changeset/*.md.pr-automation.yml'schangeset-checkhas no path-based exemption, so the label is the mechanism. This PR publishes nothing, so it takes the same treatment: label applied additively, then read back.Verification — all five derived gate families green at
484ae001Gate set derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no paths passed; it took the changeset from the merge base itself: 1 path,docs/adr/0120-…md). Verdict lines are the gates' own:check:adr-anchors—check-adr-anchors: OK (52 anchored file(s), every governing ADR still referenced; 123 decision number(s) …; 27580 citation(s) across 3415 file(s) resolve).Self-test:✓ … 74 assertions.check:doc-authoring—✓ doc authoring guard: 389 files clean — no bare metadata literals.Self-test green.check:doc-formula-expressions—✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 418 files / 1447 TS blocks judged clean by @objectstack/formula.Self-test:✓ … 30 cases passed. (Neededpnpm --workspace-concurrency=2 --filter '@objectstack/lint^...' buildfirst —@objectstack/formula'sdist/is its input.)check:pm-governed-merges—✓ check-governed-merges --self-test: 129 assertions ….check-adr-links.mjs—✅ check-adr-links: 551 relative link destination(s) under docs/adr/ resolve.Exit codes captured before any pipe (
cmd > file 2>&1; EXIT=$?), never throughtail.Repo-level
pnpm lint: a proven narrowing to zero, not a skip. The three pieces of evidence, all read from eslint itself rather than from the config globs:docs/adr/0120-…md,ESLint.isPathIgnored()returnstrueandcalculateConfigForFile()returnsundefined— the file matches no configuration block at all.lintFiles()on the changed file yields 1 result with 0 errors and 1 warning, that warning beingFile ignored because no matching configuration was supplied.— i.e. 0 linted files in this diff's reachable population.parserOptions.projectandparserOptions.projectServiceboth resolve tonull— so no markdown edit can move any untouched file's verdict, and this diff touches no eslint config, baseline or ratchet file.Control-byte self-scan on the changed file:
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'→ no match.⛔ No behaviour, gate or test was changed. ⛔
content/docs/releases/**untouched. ⛔ No other ADR amended, and no neighbouring text in ADR-0120 tidied.Generated by Claude Code
Generated by Claude Code