Skip to content

fix(driver-sql,cli): a unique index its rows violate is listed as not appliable, never offered --allow-destructive - #25217

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-lpzi51
Oct 22, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-lpzi51

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

Fixes #25031

Clause-②: yes (widening)
Metadata-protocol: none

Card thread of domain:engine seat 2 (seat post #20966), session session_01YRXQdFH1TrCyDQ5L1xDQRg, claim 6340150456. Cross-lane declaration for the one CLI line: comment 6340209215 on #22648.

What changes

driver-sql's ADR-0120 D4 duplicate pre-flight now declares a blocked unique index's apply limit on the entry: a new optional ManagedDriftEntry.applyLimit, set in the same branch that marks the entry BLOCKED, with the conflicting groups as the reason. The CLI's apply-limit seam (SchemaStack.driftApplyLimit, schema-migrate.ts) reads the entry's own limit first, then the owning driver's per-op one. One line of CLI code, as routing correction 6307307994 invited.

Nothing in plan.ts / apply.ts changes. The rendering PR #24954 and PR #25029 built for declared limits takes over: the entry is listed with its reason, the footer and the skip lines stop offering --allow-destructive for it, and it is counted among the changes that remain drift.

Measured (SQLite file, rows duplicating a plain and an organization-scoped unique index)

Door main @ e5f194b This branch
os migrate plan footer Apply with: os migrate apply (add --allow-destructive for drops / tightenings), exit 0 each entry ends "os migrate apply" cannot apply it on this database: existing rows already violate the unique key: (code="A") × 2 rows.; footer Apply with: os migrate apply and 2 change(s) above cannot be applied on this database …, exit 0
os migrate apply --yes 2 destructive change(s) will be SKIPPED (re-run with --allow-destructive to include them). and Skipped 2 destructive change(s) (re-run …), exit 0 2 destructive change(s) remain drift that a re-run with --allow-destructive cannot apply on this database either and Skipped 2 … that a re-run could not apply on this database either, exit 0
os migrate apply --allow-destructive --yes both named NOT applied, PARTIAL apply, exit 1 listing carries the limit; both named NOT applied, PARTIAL apply, exit 1 (unchanged, see below)
re-run of plan as the first row as the first row of this column

driver-turso, measured at the driver: on the local and the remote face alike the blocked create_index was destructive with BLOCKED in the message, no structured field, and driftApplyLimit('create_index') answered null. Both faces inherit the pre-flight (detectTableDrift → applyNullSafeUniquePreflight; the remote face overrides only the probe's statement), so both now carry applyLimit. TursoDriver is untouched and still answers by op type.

Why a structured field rather than a widened driftApplyLimit(entry)

  • One judgement. The pre-flight already decides the block on that entry. The field records that decision where it is made. A per-entry driftApplyLimit would still need the driver to remember it per entry (a field or a side table) and add a second door to the same fact.
  • Smaller published surface, no narrowing. One optional key on an exported interface (yes (widening), measured: applyLimit appears once in packages/drivers/driver-sql/dist/index.d.ts). Widening the parameter of TursoDriver.driftApplyLimit(opType), which is published, would narrow it for every existing caller, and an older CLI calling a newer driver with an op-type string would read .op of a string.
  • Version skew is benign both ways. An older CLI ignores the field and prints as before. A newer CLI over an older driver finds no field and asks by op type.
  • --json documents now carry applyLimit on a blocked entry. That is an additive key, and no other entry gains it.

Not changed, deliberately: the PARTIAL verdict under --allow-destructive

The seat's mechanism note expected apply --allow-destructive to stop ending PARTIAL on the blocked entry's account. It still does, exit 1. apply.ts documents that notApplied, PARTIAL and the exit code never read declared limits: an entry the operator confirmed and the driver did not apply makes the run PARTIAL, because the database still differs from its metadata. It is the same verdict the Turso REMOTE face gives a tightening (apply.remote-face.integration.test.ts). Excluding declared limits from PARTIAL would change the CLI's exit-code contract for both cases, which is the domain:cli lane's call and more than the one line this card carries. The new integration pin records today's verdict, so a later ruling flips one assertion. Raised in the report as an open question.

Tests

  • packages/drivers/driver-sql/src/sql-driver-25031-blocked-unique-apply-limit.test.ts (new): plain and organization-scoped blocked entries carry applyLimit naming the conflicting group, and the apply skips them under allowDestructive. Controls: a duplicate-free unique index has no applyLimit and a plain apply creates it, and deduplicating withdraws the limit.
  • packages/drivers/driver-turso/src/turso-remote-drift-apply-limit.test.ts (+1 describe): local and remote faces each declare the limit on the entry, while driftApplyLimit('create_index') stays null.
  • packages/cli/src/commands/migrate/apply.blocked-unique-index.integration.test.ts (new, integration tier): real boot, real plan / apply over a SQLite file. Each blocked entry lists its conflicting group as the limit. No line offers --allow-destructive in plan, apply --yes or apply --allow-destructive --yes, and the last still reports PARTIAL with exit 1. Control: duplicate-free unique indexes keep the unchanged footer, and a plain apply creates them.

Runs at fa53728a8: driver-sql vitest 232 files / 3752 passed, 12 files skipped; driver-turso 94 / 2485 passed; cli --project unit 324 / 4777 passed; the new integration file 4/4; typecheck green for all three packages.

Ablation (scripts/ablation-replace.mjs deleting the d.applyLimit = … line, driver-sql rebuilt, ablation-dist-preflight --absent passing): the driver-sql pins went red (3 of 4: both pins plus the dedup control, which first asserts the limit), with the duplicate-free control green. The Turso pins went red (2 of 6), with the 4 pre-existing cases green. The CLI pins went red (3 of 4), with the control green. Restored: blob equals HEAD, git diff HEAD empty, driver-sql rebuilt, and the preflight marker is present again in dist/.

Gates

dispatch-gates --commands derived 73 families at fa53728a8. 71 exited 0. check-plugin-teardown-shape --self-test exited 3 on the shallow clone, then 0 after fetching its fixture commit. NOT MEASURED locally: check:dual-build-cjs-loads and check:i18n-coverage (exit 3, PREREQUISITE NOT MET: they need the whole workspace built). CI runs both. --ran reconciliation: 73 derived, 71 run, 2 NOT-MEASURED. check:driver-conformance: OK, 55 covered cells, 0 DEBT, 0 exempt. No ledger file is touched.

Acceptance notes

  • The listing still places a blocked entry under Destructive (requires --allow-destructive), because renderPlan groups by category. The entry's own line and the footer now say it cannot be applied. The heading is CLI rendering and was out of scope. The Turso REMOTE tightening reads the same way today.
  • category: 'destructive' itself is unchanged. It is what keeps a plain apply, dev autoMigrate: 'safe' and the artifact boot gate from attempting the CREATE. Reclassifying it is a separate question, and this PR does not answer it.
  • The tightening arm of the pre-flight (recreate_index with tightenNullSafeOnly) sets the same field in the same branch. Since ADR-0131 D1 made the organization key part plain, no fixture here reaches that arm, so it is covered by construction, not by a pin.
  • Unrelated to this diff: on a database with a blocked unique index, the deferred boot's flush still tries the CREATE and logs [sql-driver] cannot create unique index … at every os migrate apply, as on main.

Generated by Claude Code

…appliable, per entry

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YRXQdFH1TrCyDQ5L1xDQRg
…-entry apply limit; changeset

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YRXQdFH1TrCyDQ5L1xDQRg
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

4 anchor(s) derived from 2 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 37 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 15764cd723e750c01416cfe91b44bbd0a4c28f37 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 1acb4f970752aa2ed4cd2c96d6bbacfb7b4776ae — the merge of head fa53728a843bc719b9781082da7f958a89c51d75 into base 15764cd723e750c01416cfe91b44bbd0a4c28f37, 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 1acb4f970752aa2ed4cd2c96d6bbacfb7b4776ae && git checkout 1acb4f970752aa2ed4cd2c96d6bbacfb7b4776ae
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 15764cd723e750c01416cfe91b44bbd0a4c28f37 fa53728a843bc719b9781082da7f958a89c51d75 && git checkout -B drift-repro 15764cd723e750c01416cfe91b44bbd0a4c28f37 && git merge --no-ff fa53728a843bc719b9781082da7f958a89c51d75

node scripts/docs-audit/affected-docs.mjs --json 15764cd723e750c01416cfe91b44bbd0a4c28f37

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: fa53728a843bc719b9781082da7f958a89c51d75
Local-runs: none
Scope: hits: none by path (reviewFace over the file list finds no published-schema and no governed path; the Clause-②: yes (widening) line is what makes the review owed, and the dispatch asked for this tier on a published-type widening) + the whole diff outside the prose face: packages/drivers/driver-sql/src/schema-drift.ts, packages/drivers/driver-sql/src/sql-driver.ts, packages/cli/src/utils/schema-migrate.ts, packages/drivers/driver-sql/src/sql-driver-25031-blocked-unique-apply-limit.test.ts, packages/drivers/driver-turso/src/turso-remote-drift-apply-limit.test.ts, packages/cli/src/commands/migrate/apply.blocked-unique-index.integration.test.ts; .changeset/25031-blocked-unique-index-apply-limit.md sentence by sentence; cited code read at the head: sql-driver.ts (applyNullSafeUniquePreflight, probeNullSafeUniqueDuplicates, applyMigrationEntries, applyIndexDriftOp, formatDuplicateGroups), schema-drift.ts (driftFindingWithoutRemedy), driver-sql/src/index.ts export block and driver-sql/package.json exports, turso-driver.ts (driftApplyLimit, applyMigrationEntries, applyRemoteIndexDriftOp, REMOTE_DRIFT_OP_ANSWERS, the probeNullSafeUniqueDuplicates override), driver-sqlite-wasm/src/sqlite-wasm-driver.ts (overrides no drift seam), cli/src/utils/schema-migrate.ts (SqlDriverLike, SchemaStack, collectDriftApplyLimits, describeWithApplyLimits, CATEGORY_META), cli/src/utils/apply-drop-gates.ts (describeDriftEntries), cli/src/commands/migrate/plan.ts (header, --json branch, footer), cli/src/commands/migrate/apply.ts (header contract, the limits read, notApplied / PARTIAL / exit), cli/src/utils/artifact-boot-migration.ts, cli/src/commands/migrate/apply.remote-face.integration.test.ts, cli/vitest-tiers.ts, scripts/check-changeset-no-major.mjs and the "WHICH LEVEL" prose in .github/workflows/pr-automation.yml, docs/adr/0120-… D4; card #25031 body and comments 6307307994, 6307517392, 6340150456, 6341277431; declaration 6340209215 on #22648; the PR body and its Acceptance notes.

① Derived judgments

  1. Published surface: ManagedDriftEntry.applyLimit?: string (@objectstack/driver-sql). ManagedDriftEntry sits in src/index.ts's export type { … } block and reaches consumers through exports["."].types (dist/index.d.ts), so the member is a published widening: one optional key on an exported interface, no existing member changed, no implementer forced to produce it, no reader forced to consume it. Clause-②: yes (widening) names the only published change there is. No other exported declaration moves: SqlDriver.applyNullSafeUniquePreflight stays protected with the same signature; SqlDriverLike.driftApplyLimit(opType), SchemaStack.driftApplyLimit(entry) and TursoDriver.driftApplyLimit(opType) are byte-identical to main. The member's TSDoc states the producer, the absence rule and the "never re-derived from message" rule, and each is true of the code. RIGHT.
  2. Producer. The field is written in exactly one place, SqlDriver.applyNullSafeUniquePreflight, inside the duplicates branch (duplicates.length above zero) and above the nullSafeKey ? … : … message ternary, so both arms (the NULL-safe recreate_index tightening and the plain or NULL-safe create_index) carry it, a clean probe never does, and nothing else in the repo writes it (git grep applyLimit at the head outside tests: one declaration, one assignment, one read). TursoDriver inherits it on both faces (its REMOTE face overrides only the probe's statement; detectTableDrift is the base's), and SqliteWasmDriver extends SqlDriver overriding none of the drift seams. RIGHT; Turso pinned on both faces, sqlite-wasm by construction.
  3. (a) Nothing becomes looser, on any driver. No guard reads the field. SqlDriver.applyMigrationEntries and TursoDriver.applyMigrationEntries split on category alone, and category: 'destructive' on a blocked entry is unchanged by the diff, so a plain apply, dev autoMigrate: 'safe' and the artifact boot gate (artifact-boot-migration.ts reads category only) still never attempt the CREATE. Under allowDestructive the apply refuses it as before: the NULL-safe tightening re-probes in applyIndexDriftOp's recreate_index arm before any drop; the plain unique CREATE fails on the duplicates and the create_index arm reports presence only; on the Turso REMOTE face the failed write batch lands in skipped through the catch. In the CLI the limit is read by collectDriftApplyLimits for the human face only: apply.ts builds intended / deferred from category and the flag, and notApplied, PARTIAL and the exit code never consult limits (its comment at the read, verified against the code at :696–:752). The field can only mark an entry as not re-runnable; it admits nothing and skips no guard. RIGHT.
  4. (b) Precedence entry.applyLimit ?? owner.driftApplyLimit(entry.op.type). Absent (an older or third-party driver, any SqlDriverLike without the field, every unblocked entry): ?? falls through and the seam answers exactly as on main. Present: the entry is limited; collectDriftApplyLimits keeps any non-null string, so even an empty string would tighten, never loosen, and the field's type excludes null, so no value exists that silences a transport's per-op limit. Where both would exist they agree ("cannot apply") and only the printed reason differs, the entry's data-dependent one first, which is the more specific of the two; on the one transport with per-op limits (Turso REMOTE) every index op is 'applied' in REMOTE_DRIFT_OP_ANSWERS, so no entry today carries both. The owner lookup and its refusal of an undetected entry still run ahead of the read. RIGHT in both directions.
  5. (c) Other surfaces. The --json documents of plan (changes: drift) and apply (applied, skipped, notApplied) emit entries whole, so a blocked entry now carries applyLimit there: an additive key, which the changeset states. RIGHT. Rendering, in unchanged code: a limited create_index line is driftFindingWithoutRemedy(d) followed by "os migrate apply" cannot apply it on this database: …; an index op has no remedy suffix to strip, so the entry's full BLOCKED message (duplicate report included) precedes the limit and the duplicate report appears twice on one line, under a group heading that still reads Destructive (requires --allow-destructive) (CATEGORY_META). Correct behaviour, redundant text: an observation, not a defect of this diff.
  6. (d) The CLI line. The production diff in packages/cli is the one line declaration 6340209215 names (schema-migrate.ts :1569) plus the two comment lines above it; no signature moves. RIGHT. The diff also adds packages/cli/src/commands/migrate/apply.blocked-unique-index.integration.test.ts (278 lines) in the cli lane's package, which the declaration does not name: carried to ③.3.
  7. Tests. driver-sql pin: plain and organization-scoped blocked entries carry the limit naming the conflicting group, the apply under allowDestructive skips them, and two controls (a duplicate-free index has no applyLimit, is safe, and a plain apply creates it; deduplicating withdraws the limit). Turso: both faces, driftApplyLimit('create_index') still null, the apply skips. CLI integration: real boot over a SQLite file through plan, apply --yes and apply --allow-destructive --yes (listing with the limits, zero re-run offers, PARTIAL with exit 1 pinned as today's verdict), plus a control pinning the byte-exact unchanged footer and a successful create. The file carries no nightly-tier name, so it is in the queue population (vitest-tiers.ts: nightly selects by name only), and better-sqlite3, @objectstack/runtime, @objectstack/objectql are cli devDependencies. The ablation (3 of 4, 2 of 6 and 3 of 4 red with the assignment deleted, every control green) is the dev's reading; not re-run here. RIGHT.

② Semver level

  • .changeset/25031-blocked-unique-index-apply-limit.md: @objectstack/driver-sql: patch, @objectstack/cli: patch. Both packages publish (no private field; driver-sql carries an exports map with types). @objectstack/driver-turso moves only a test file, so nothing publishes from it and no entry is owed. RIGHT.
  • Clause-②: yes (widening) and Metadata-protocol: none stand line-initial in both the PR body and the changeset. none is right: no authorable key, value or type moves (a driver type and CLI output do). Under maintainer ruling A on changeset levels: minor only when the metadata protocol changes (added / narrowed / semantic), patch otherwise; release notes grouped by what the reader must do (maintainer ruling A, 2026-10-21) #25105 as check-changeset-no-major.mjs and the "WHICH LEVEL" prose encode it, a declared none grades every introduced entry patch; Check Changeset on this head is success. The level is the one the rule prescribes. RIGHT.
  • The changeset prose, sentence by sentence against the diff: the main behaviour it describes (a block carried in the message only; a seam asking by op type) is main's code at schema-migrate.ts :1568; "absent on every other entry": the one assignment sits in the duplicates branch; "On --json, a blocked entry now carries this key": entries are emitted whole; "reads the entry's own limit first, then the driver's per-op one": :1569; "listed with the conflicting groups as the reason … the footer and the skip lines stop offering --allow-destructive for it … counted among the changes that remain drift": plan.ts's footer branch and apply.ts's notReRunnable lines; "A database with no blocked index prints as before": limits.size === 0 keeps the unchanged branches; "Unchanged: … reports the run PARTIAL with exit 1": apply.ts :696–:752. No sentence is false.

③ Boundary flags

  1. Open question 1: should apply --allow-destructive stop ending PARTIAL on a declared-limit entry? Answered: keep it (the dev's option A), and keeping it is in scope to accept. The card's acceptance text ("Wanted") asks for three things: a BLOCKED entry is not offered --allow-destructive by plan's footer or apply's skip lines and is listed with its reason and what clears it, in the migrate plan on a Turso remote primary: a tighten_not_null (and every column entry the remote face cannot apply) is printed with the remedy os migrate apply --allow-destructive, which then names it as not applied and exits 1 #23925 / os migrate apply and os migrate plan still name --allow-destructive as the remedy for drops that apply itself refuses (the pre-prompt listing and skip lines before the ownership ceremony, and sys_comment.reactions in the plan) #24958 shape; read from the one place that already says BLOCKED, no second judgement; a database with no blocked entry prints as today. The diff delivers all three; the PARTIAL row in the card's "Measured" table is the consequence of the offer, not a Wanted item. apply.ts's documented contract (os migrate apply cannot run on a Turso remote database (it arms deferred DDL), so destructive drift on a remote primary has no CLI remedy #23848 guarantee 4, and the comment at :688) is that a destructive entry the operator confirmed and the driver did not apply makes the run PARTIAL and exit 1 "in both faces", and that notApplied, PARTIAL and the exit code never read declared limits (:345); the Turso REMOTE tightening pins the same verdict in apply.remote-face.integration.test.ts. With the offer gone, an operator reaches that exit only by forcing --allow-destructive against a listing that said the entry cannot be applied, and exit 1 then reports a database that still differs from its metadata, which is true. Option B changes the CLI's exit-code contract for Turso as well and is a domain:cli card if anyone wants it; nothing in it blocks this PR.
  2. Open question 2: Clause-② and the tier. The correction from no to yes (widening) is right (①.1) and patch is right (②). On the tier: reviewFace over the file list finds no published-schema or governed path, so by the rule the Clause-②: yes line alone makes a declaration-face review owed (TIER_DEFAULT); the dispatch asked for CONTRACT_REVIEW_TIER on a published-type widening and this record serves that tier, above the floor and not below it.
  3. Undeclared cross-lane file. Declaration 6340209215 on [PM seat] domain:cli · seat 2 — ⏳ vacant #22648 names one line of schema-migrate.ts; the PR also lands a 278-line integration test under packages/cli/src/commands/migrate/. It pins exactly the card's Wanted behaviour and changes no cli production code, so it is no contract matter, but it is a cross-lane write the declaration does not cover. Escalated to the seat: supplement the declaration on [PM seat] domain:cli · seat 2 — ⏳ vacant #22648 with one line naming the file before the queue holds the PR (the runbook's "cross-package is cross-lane; the PM supplements the seat declaration after the fact").
  4. Acceptance note 1 (the group heading). True: renderPlan groups by category and the destructive label reads Destructive (requires --allow-destructive); the entry's own line and the footer say otherwise, and the Turso REMOTE tightening reads the same way today. CLI rendering, outside this card; an observation for the cli lane together with ①.5's doubled duplicate report on the same line.
  5. Acceptance note 2 (category unchanged). True and necessary: it is what keeps every automatic door shut (①.3). Accepted.
  6. Acceptance note 3 (the tightening arm, covered by construction). True: the assignment is unconditional inside the duplicates branch both arms share, above the arm-specific message. Accepted without a pin.
  7. Acceptance note 4 and out-of-scope finding 2 (the deferred boot flush retries the CREATE and logs [sql-driver] cannot create unique index … on every os migrate apply). Pre-existing: syncDeclaredIndexes degrades the failure to a log, which is also why the apply's create_index arm reports presence only; untouched by this diff and the same on main. Not filed by the dev; the seat cards it or dismisses it.
  8. Gates. Locally NOT MEASURED by the dev: check:dual-build-cjs-loads and check:i18n-coverage (prerequisite: a whole-workspace build), and check-plugin-teardown-shape --self-test exited 3 on a shallow clone, then 0 after its fixture commit was fetched. Read on the head at 2026-10-22T13:07:00Z: 25 check-runs success (among them Lint & Repo Gates and every Lint · job, Check Changeset, Governed Surface Queue Guard, Build Core, Dogfood Regression Gate, Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, and the claim, branch and single-writer guards), 7 skipped (the opt-in and main-only batteries), and 7 still in_progress: Test Core (1/6) through (6/6) and Type Check · workspace. An in-progress gate is not a pass: this record is the contract verdict alone, and the queue waits for those seven.
  9. dogfood_battery: n/a is correct (no ! in the title, no (narrowing) arm). premise_still_valid: true: the measured main rows match the card's. Serial note 6307517392's wake condition held: PR feat(driver-sql,driver-turso,driver-memory,objectql,cli,metadata-protocol): plain organization key part in new unique indexes; the differ reads COALESCE(tenant) as tenant on a certified database (18.x step 7b-1 + 7b-2) #24893's d5d814601 and seat 1's PR fix(driver-sql): the boot drift line for an organization_id drop names the ownership ceremony on an uncertified database #25188 5d66021a7 are both ancestors of the branch point e5f194b, and main has not moved on the three touched source files since.

Implemented-by: claude/project-thread-lpzi51
Reviewed-by: session_01FaVyM6YvQLg9CsjPVCYc5K

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:engine seat 2 (seat post #20966) · os-bill · session_01FaVyM6YvQLg9CsjPVCYc5K · 2026-10-22T13:10Z · head fa53728a843bc719b9781082da7f958a89c51d75 · card #25031.

  1. Form: draft, Fixes #25031, assignee os-bill, 7 files (+494 / −2). check-governed-merges --pr 25217: 0 of 7 paths governed, 496 changed lines ≤ 3000 ⇒ ordinary queue landing, no human-merge tier.
  2. Premise measured: the dev report 6341277431 measured the blocked unique index on driver-sql and both Turso faces, with an ablation that turns every pin red; the contract review read the single producer (applyNullSafeUniquePreflight) and the single reader (SchemaStack.driftApplyLimit) in code.
  3. Gates: contract review at CONTRACT_REVIEW_TIER PASS on this head, comment 6341526231 (Clause-②: yes (widening) face: ManagedDriftEntry.applyLimit?: string is the only published change; no guard reads it, so nothing gets looser on any driver). Check-runs on this head are still running (Test Core shards, Type Check · workspace). Landing waits for every check green on this head; it is re-read at the flip.
  4. Accepted deviations:
  5. Out of scope, disposed:
    • CLI rendering of a limited create_index line repeats the duplicate report (once from the entry's message, once from the limit); the group heading still reads Destructive (requires --allow-destructive); plan.ts's header prose about the --json document is now stale. Cosmetic, cli lane, code reading; noted here, not filed.
    • The deferred boot flush retries the blocked CREATE and logs cannot create unique index … on each os migrate apply: pre-existing, identical on main; noted, not filed.
  6. Landing: once every check on this head is green, the seat flips ready and arms auto-merge through the relay; the card closes on merge by Fixes #25031, and the landing comment records the queue branch and origin/main readings.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 22, 2026 13:27
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 22, 2026 13:27
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit 8c73398 Oct 22, 2026
42 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-lpzi51 branch October 22, 2026 14:02
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 tests tooling

Projects

None yet

2 participants