Skip to content

refactor(driver-sql): move the NULL-safe unique pre-flight and the declared-index sync out of sql-driver.ts into sql-driver-schema-sync.ts (#25521 cut 9) - #26245

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-k34erv
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-k34erv

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Part of #25521
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6270097840
Ruling-ref: 6348891160

Cut 9 of the sql-driver.ts split (#24586 ruling A, recorded in 6270097840). The claim is 6420465502. Cut 8 measured no next slice, so this cut opens with a fresh seam table (below) and takes the area it prices best that breaks no series limit. The bodies of three protected SqlDriver methods move unedited into the existing seam module packages/drivers/driver-sql/src/sql-driver-schema-sync.ts, through its existing host SchemaSyncHost:

  • applyNullSafeUniquePreflight, the plan-side duplicate pre-flight that clears or blocks a unique index op;
  • syncDeclaredIndexes, which materializes a table's declared indexes;
  • createNullSafeUniqueIndex, the raw DDL for an organization-scoped unique's expression key.

The module-level helper formatDuplicateGroups moves with them, byte-identical with its docblock. Its only two callers are the pre-flight and the sync, so it stays module-local and unexported, as its docblock requires.

These bodies are the rest of the ADR-0120 tightening machinery. Cut 8 already moved its apply side and put the ADR-0120 D4 anchor paragraph on this module's entry. That paragraph describes the duplicate pre-flight, which now lives in the module the paragraph anchors. No anchor entry changes, there is no docs/adr/** edit and no packages/spec/** edit, and there is no new gate.

The change is move-only, and the card stays open for later cuts. Branch claude/project-thread-k34erv (the platform assigned it to this card thread, as the claim records). One commit, b2b00ab42, on origin/main e6dfeb08f.

Net line count (the seat's rule). sql-driver.ts goes from 21,334 lines to 20,990: -344.

  • Out: the three bodies, 354 lines (64 + 258 + 32); formatDuplicateGroups with its docblock and the blank line before it, 20 lines; and the describeMissingIndexColumns import name, 1 line, because its last reader was the moved sync body.
  • In: the three stubs, 29 lines (7 + 13 + 9), and 2 import-comment lines.
  • The module grows from 372 to 777 lines.

Serial with the file's in-flight PRs. I measured this twice: at start, and again before pr_create (41 open PRs). Each time I read every open PR's file list in full. Two touch packages/drivers/driver-sql/**, and neither touches src/: #26203 changes only tsconfig.json and #26198 only package.json. No PR touches sql-driver.ts, sql-driver-schema-sync.ts, turso-driver.ts or the driver-sql anchor entries.

Seam table, fresh, on e6dfeb08f

origin/main is at e6dfeb08f, after cut 8 (51cc7f9603).

  • Size and churn. sql-driver.ts is 21,334 lines. 30-day lines are git blame --line-porcelain lines whose committer time is on or after 2026-09-26T15:16:49Z, 30 days before the base commit. I deepened history to 2026-09-18 first, and scripts/pm/git-history.mjs count proves 68 commits on the file in that window (26 in the last 7 days). In all, 3,206 lines (15.0%) are from the last 30 days.
  • Class shape. SqlDriver spans lines 5532-21259 with 348 members: 252 methods (49 of them public instance methods), 13 getters and 32 statics, all 32 of them private or protected.
  • Inferred return types. One public member has one: supports, in the class head.
  • How it was measured. A TypeScript compiler AST script, which strips casts, parentheses and ! before it reads this.
  • Sections. These are the class's own banner comments (// === blocks and // ── sub-banners), cut by contiguous line range.
Lines Section First … last member Lines 30-day Methods (public) Inferred public getBuilder sites Statics (private/protected)
1-5531 Module preamble - 5,531 754 - - - -
5532-7123 Class head: fields, statics, constructor, capability getters name … assertDialectEmits 1,592 290 14 (1) 1 0 18 (18)
7124-7555 Lifecycle connect … disconnect 432 155 11 (3) 0 0 6 (6)
7556-10260 CRUD — IDataDriver core find … delete 2,705 739 43 (7) 0 12 0 (0)
10261-10872 Bulk & Batch Operations bulkCreate … count 612 119 9 (6) 0 7 0 (0)
10873-10969 Raw Execution rawStatementFault … noteRuntimeIndexDdl 97 28 3 (1) 0 0 0 (0)
10970-11062 Transactions beginTransaction … rollbackTransaction 93 46 7 (5) 0 0 0 (0)
11063-11594 Aggregation aggregate … unresolvableAggregateColumnRefusal 532 60 5 (1) 0 1 0 (0)
11595-11882 Distinct distinct … unresolvableDistinctColumnRefusal 288 17 3 (1) 0 1 0 (0)
11883-11998 Window Functions findWithWindowFunctions … findWithWindowFunctions 116 4 1 (1) 0 1 0 (0)
11999-12097 Query Plan Analysis explain … analyzeQuery 99 1 2 (2) 0 1 0 (0)
12098-12156 Schema Sync (syncSchema / init) syncSchema … reclaimSpace 59 34 3 (3) 0 0 0 (0)
12157-13898 Schema Sync › Data-lifecycle rotation (ADR-0057 P2) rotationStateByTable … migrateMysqlTimeColumns 1,742 72 26 (4) 0 0 0 (0)
13899-14155 Schema Sync › Deferred schema DDL setDeferredDdl … flushDeferredSchemaDdl 257 25 6 (4) 0 0 0 (0)
14156-15533 Schema Sync › Managed-schema drift & reconcile dialectName … createNullSafeUniqueIndex 1,378 197 23 (3) 0 0 0 (0)
15534-15585 Schema Introspection listPhysicalColumns … introspectSchema 52 46 2 (2) 0 0 0 (0)
15586-15856 Internal helpers (head: tenancy, misc) getKnex … auditMissingTenant 271 73 8 (1) 0 1 0 (0)
15857-18109 Internal helpers › Filter helpers tableNameForBuilder … applyFalseConstant 2,253 250 42 (2) 0 0 0 (0)
18110-18285 Internal helpers › Field mapping mapSortField … mapAggregateFunc 176 2 6 (0) 0 0 0 (0)
18286-18333 Internal helpers › Window function builder buildWindowFunction … buildWindowFunction 48 26 1 (0) 0 0 0 (0)
18334-20280 Internal helpers › Column creation helper nowColumnDefault … applyDeclaredColumnDefault 1,947 92 21 (0) 0 0 8 (8)
20281-20578 Internal helpers › Database helpers sqliteFilename … isJsonField 298 76 9 (2) 0 0 0 (0)
20579-21055 Internal helpers › SQLite serialisation formatInput … formatOutput 477 27 2 (0) 0 0 0 (0)
21056-21259 Internal helpers › Introspection internals introspectColumnOrder … introspectUniqueConstraints 204 73 5 (0) 0 0 0 (0)
21260-21334 After the class - 75 0 - - - -

Pricing the eligible methods

A method is eligible to move when it meets every condition below. 191 of the 241 instance methods do.

  • Its return type is annotated, or it is not public.
  • It has no getBuilder site.
  • It has no this.FIELD.delete/insert/update( site.
  • It reads no private or protected static.
  • It makes no super read and no bare this pass.
  • It makes no write to this.
  • It is not insertOnlyUpsertColumns.

The net-line rule. Each candidate is priced as its stub minus its body. A stub costs 4 lines, plus 2 for each field or getter read and 1 for each method read.

The highest-priced candidates, and why each one stays or moves:

Method (section) Net Disposition
createColumn (Column creation helper) -463 Stays. ADR-0113 cites sql-driver.ts#createColumn for the NOT NULL mechanism in this body, so moving it would need a docs/adr/** edit. Five packages/cli generate-*.pin.test.ts files also slice protected createColumn( … switch (type) out of this file's text.
applyFilterCondition (Filter helpers) -357 Stays. ADR-0139 cites sql-driver.ts#applyFilterCondition, and a governed .claude/** reference names it too. sql-driver-compile-refusal-seam.test.ts enumerates INVALID_FILTER builders in this file only.
syncDeclaredIndexes (Managed-schema drift & reconcile) -245 Moves (this cut).
formatOutput (SQLite serialisation) -236 Stays. The sql-driver.ts anchor entry's ADR-0053 paragraph describes this body, ADR-0104 cites sql-driver.ts#formatOutput, and the body reads 11 fields.
initObjects (Rotation) -195 Stays. packages/spec/liveness/object.json cites sql-driver.ts#initObjects, which would need a spec edit.
formatInput (SQLite serialisation) -183 Stays. ADR-0053 and ADR-0104 cite sql-driver.ts#formatInput.
applyCrossFieldComparison (Filter helpers) -179 Not taken here: it needs a new module, and I did not screen it against the two readers named above. Proposed below.
applyNullSafeUniquePreflight (Managed-schema drift & reconcile) -57 Moves (this cut).
createNullSafeUniqueIndex (Managed-schema drift & reconcile) -23 Moves (this cut).

Why this area

  • It pays the most of any eligible area. The cut is -344, and the best eligible single method is -245. No candidate priced above it is free of a pointer that a spec or ADR edit would have to move.
  • It belongs in an existing module and host. sql-driver-schema-sync.ts already holds the apply side of the same mechanism (cut 8's applyIndexDriftOp, which calls syncDeclaredIndexes and probeNullSafeUniqueDuplicates). SchemaSyncHost already listed 7 of the 10 members these bodies read.
  • No series limit is touched:
    • none of the three bodies builds through getBuilder;
    • none reads a static;
    • none has a this.FIELD.delete/insert/update( site;
    • none uses super, a bare this or a cast-wrapped this;
    • all three are protected, with annotated return types.
  • Subclasses. TursoDriver overrides probeNullSafeUniqueDuplicates and calls super. The bodies reach it through a late-bound closure, so the override is still the one that runs. No subclass overrides any of the three moved methods.
  • Path readers, screened. No script, test, doc or ADR names any of the three by sql-driver.ts#… path, and none slices their text out of sql-driver.ts.
    • ADR-0005, ADR-0120, packages/cli/src/commands/generate.ts and driver-turso/src/remote-transport.ts name SqlDriver.syncDeclaredIndexes in prose. That method is still on the class, with the same docblock, so the prose still holds.
    • The only reader of formatDuplicateGroups outside its callers is one {@link formatDuplicateGroups} in the docblock of reclaimBetterSqlite3 in sql-driver.ts. See Acceptance notes.
  • What stays on purpose. probeNullSafeUniqueDuplicates stays in the class. Its body reads the module-level uniqueProbeNotNullClause, so moving it would drag a second helper, and Turso's override already reaches it through the host.

The shape (#23050 shape B, as cuts 1-8 apply it)

  • Each class method keeps its docblock and signature, byte-identical less async. Its body is now one host literal plus NAME.call(host, ...), marked [#25521 cut 9], and it returns the moved function's own promise.
  • knex, logger and isMysql go as GETTERS that read the driver at call time. The methods go as closures that call the method on the driver at call time. So a subclass override, an instance spy, or a knex or logger replaced after construction is still the one the body reaches (see the probe below).
  • Each moved function types its this as the Pick of SchemaSyncHost over exactly the members it reads. Each stub annotates its literal as the ThisParameterType of the moved function. There is no cast, and no member becomes public.
  • SchemaSyncHost gains createHashShadowUniqueIndex, createNullSafeUniqueIndex and explainUnkeyableTextColumn, each typed as the class member's indexed access, in the interface's alphabetical order. The other members these bodies read were already there: knex, logger, isMysql, getExistingIndexNames, logDurabilityFailure, probeNullSafeUniqueDuplicates and resolveTenantField.
  • Module-level declarations.
    • The module imports normalizeDeclaredIndex, describeMissingIndexColumns, GLOBAL_TENANT and the type DeclaredIndexInput from schema-drift.ts, and isUniqueViolationError from @objectstack/types, the modules that declare them. No export was added anywhere.
    • sql-driver.ts drops only describeMissingIndexColumns from its import, because the moved sync body was its last reader. Everything else it imported is still read there.
    • index.ts re-exports nothing from the module, so the published surface does not move.
  • Comments. The module header gains a [#25521 cut 9] paragraph that cites no decision letter. The sql-driver.ts import comment names cut 9 in 2 lines.
  • How the move was made. A script produced it from the source text; nothing was retyped. Each body line is re-indented two columns. No line in the moved bodies starts inside template-literal text, so no line keeps its base indentation.

ADR anchors

check:adr-anchors counts decision-letter citations on the anchored surface: each anchor's invariant, plus the full text of each anchored file.

  • Both files are anchored. sql-driver.ts has its ADR-0053 entry, and sql-driver-schema-sync.ts has the ADR-0120 entry cut 8 created.
  • The citations move between anchored files. The moved bodies carry their ADR-0120 D3 and ADR-0120 D4 citations from one anchored file to the other, and the new header paragraph cites no letter.
  • The count. It is 1883 decision-letter citations at base e6dfeb08f and at head b2b00ab42, over 90 anchored files at both ends. Both runs exit 0.
  • No entry needs a new home. The only paragraph that describes a moved body is the ADR-0120 D4 sentence on the duplicate pre-flight, and it already lives on sql-driver-schema-sync.ts's entry. The ADR-0053 paragraph on sql-driver.ts describes formatOutput and presentReadValue, and neither moved.

The published .d.ts does not change

This is measured under the series bar (ruling 6348891160, item 1).

  • How each end was built. I built each end with pnpm turbo run build --filter=@objectstack/driver-sql --force, against the same dependency tree. The base leg restores the base text of the two source files in this worktree after the commit, under an EXIT/INT/TERM trap that restores HEAD. The restore is proved by the HEAD blob hashes (dbe720a2, 86929118), an empty git diff HEAD and 0 status entries.
  • The hashes. The raw sha256 of dist/index.d.ts and dist/index.d.mts is the same at base and at head: 5028e040e1501b0bd72b91fafcf4e1cb372393c59baa5bab8b936b240dd2d3f5 (cut 8's hash). cmp finds them identical.
  • Positive control. I dropped protected from syncDeclaredIndexes in the head index.d.ts. The result hashes to f6a003a07258bfdcd858e4e68504f0960aa01bd85e6e9d4ab387fb26dfa81a2c, with a 4-line diff and cmp exit 1.
  • Export lists. CJS require and ESM import of the built dist give 56 / 56 names at both ends, identical.
  • The head dist carries the move. .call(host sites in index.mjs go from 21 at base to 24 at head.

Move-only proof

The proof compares TypeScript compiler AST leaf tokens (JSDoc nodes skipped), base e6dfeb08f against head b2b00ab42. It is a scratch script and is not committed.

Moved body Body tokens Comments Parameters less this Return type Body lines
applyNullSafeUniquePreflight 316 14 5 4 66
syncDeclaredIndexes 806 118 29 4 260
createNullSafeUniqueIndex 212 8 17 4 34
  • Each moved body. In every row, 0 tokens differ and every comment is identical. async is on both ends, and there are no type parameters. Each body line is the base line less two leading spaces.
  • The three stubs. Leading trivia (the docblock) and the signature are identical to base, less async.
  • formatDuplicateGroups. Its 19 lines with the docblock are byte-identical, it is gone from sql-driver.ts, and it is still unexported.
  • The class. It has 348 members at both ends, in the same order, and the 345 unmoved members are byte-identical, trivia included.
  • sql-driver.ts top level. It goes from 207 to 206 statements. The only base statements that differ are the two imports, the moved helper and the class.
  • sql-driver-schema-sync.ts. It goes from 7 to 12 statements. The 2 base statements that differ are the first import (which carries the module header) and SchemaSyncHost. The 5 new statements are the @objectstack/types import, the helper and the three functions.
  • Result. PROOF OK, exit 0.
  • Negative control. I changed was NOT created: to was NOT creat3d: in a module copy. The proof then reports 1 token and 1 line differing, prints PROOF FAILED, and exits 1.

Tests

Each suite ran under its own os-verify-lock hold. I compared the vitest JSON per test name and status, base e6dfeb08f against head b2b00ab42:

Suite Base Head Lost / changed / added
@objectstack/driver-sql 4010 (3796 passed, 214 skipped; 248 files) 4010 (3796 passed, 214 skipped; 248 files) 0 / 0 / 0
@objectstack/driver-turso, against that end's built driver-sql dist 2526 (2492 passed, 34 skipped; 95 files) same 0 / 0 / 0
@objectstack/driver-sqlite-wasm 704 (704 passed; 39 files) same 0 / 0 / 0
  • Type check and lint. pnpm exec tsc --noEmit in driver-sql exits 0. pnpm exec eslint --no-inline-config on the two touched files exits 0, with 0 problems.
  • Late-binding probe. This is a scratch script, not committed. It runs node against each end's built dist on in-memory SQLite. Its 20 readings print identical output at base and head (diff is empty). They cover:
    1. syncDeclaredIndexes through a knex replaced on the instance after construction;
    2. a missing-column skip reaching an instance spy on logDurabilityFailure, and an instance spy on getExistingIndexNames;
    3. the NULL-safe route reaching a subclass override of createNullSafeUniqueIndex, with the emitted COALESCE(organization_id, '__global__') DDL;
    4. a plain unique over seven duplicate groups reaching an instance spy on the probe, with the durability line formatDuplicateGroups renders, five groups then "…and 2 more";
    5. a NULL-safe unique over duplicates logged while the sync goes on to the next index;
    6. createNullSafeUniqueIndex through a subclass isMysql getter, on its functional-key-part fallback to the bare composite;
    7. applyNullSafeUniquePreflight on a dirty create (blocked, with applyLimit and message), a clean tightening (safe), and a probe failure warned through a logger replaced after construction;
    8. the same pre-flight through an instance spy on the probe;
    9. a bad argument to each moved method answering a TypeError rejection, never a synchronous throw.
  • Not run. No ablation, because no tested behaviour changed. test:repo was not run locally: the diff touches no repo-project test, and CI's Test Core runs it.

Gates

All local runs are on head b2b00ab42.

  • Derived gates. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 55 commands from the change set against merge base e6dfeb08f (2 paths). I captured each exit code before any pipe.
    • 52 exit 0 on the first run.
    • check-plugin-teardown-shape.mjs --self-test exited 3 on the shallow clone, then 0 after git fetch origin 621a4876 (its positive control).
    • check:lean-entry-closure exited 3 with no objectql dist, then 0 after pnpm turbo run build --filter=@objectstack/objectql under the lock.
    • check:dual-build-cjs-loads exited 3, PREREQUISITE NOT MET: it needs every package's dist. It is NOT MEASURED locally, and CI runs it.
  • Reconciliation. --ran reports "55 derived famil(ies) accounted for — 54 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)", exit 0.
  • Card-named gates. I read each at base in a clean detached worktree at e6dfeb08f and at head b2b00ab42. All exit 0 at both ends.
Gate Base Head Note
check:adr-anchors 90 anchored files, 1883 citations same
check:adr-symbol-anchors 2664 anchors resolve same
check:tenant-chokepoint 24 getBuilder() bindings across 3 files; rule 2 clean same See the first acceptance note.
check:logger-receiver-detach 0 detaches, 3736 non-test TS files 0 detaches, 3737 files The head has one more walked file; the base worktree lacks the gitignored generated packages/spec/src/migrations/registry.ts.
check:driver-conformance 55 covered, 0 DEBT, 0 exempt same
check:keyed-text-bounds 112 object files, 238 index entries same
check-tenant-audit-census 243 write call sites certified same
  • Not owed. check-platform-checklist --changed-from is not owed: no exported symbol was renamed or deleted, because formatDuplicateGroups was never exported. Pre-push check:commit-card-trailers and check:nul-bytes pass.
  • origin/main moved while this ran. It moved 10 commits, to df8aac41f. None of them touches packages/drivers/**, scripts/adr-anchors/**, check-adr-anchors.mjs or check-tenant-chokepoint.mjs. Two touch packages/types/src, which driver-sql imports isUniqueViolationError from, so CI's merge-ref run covers that. git merge-tree against it is clean. I did not merge, because CI tests the merge ref.

Acceptance notes

  • check:tenant-chokepoint rule 2 visits a little less. Rule 2 (the D8 null-arm rule) scans class members of the three driver files, so it no longer visits what moved:

    • one knex.raw( site, createNullSafeUniqueIndex's CREATE UNIQUE INDEX with its COALESCE(... '__global__') key part (sql-driver.ts goes from 57 to 56 .raw( sites);
    • syncDeclaredIndexes' tenantField parameter and its resolveTenantField binding.

    None of these says IS NULL against a tenant column, and none is a where arm. The verdict is unchanged, and the gate is not empty (24 bindings, 3 files). This is the same disclosure cuts 5-7 made, and the card already carries the SCAN_FILES precondition.

  • A docblock link no longer resolves in its file. The docblock of reclaimBetterSqlite3 in sql-driver.ts says "Module-local, like {@link formatDuplicateGroups}", and that helper now lives in sql-driver-schema-sync.ts. The sentence is still true, because the helper is still module-local. I left the docblock unedited, since the cut is move-only.

  • Proposed next slice: applyCrossFieldComparison (Filter helpers, about -179, reads 6 methods and no field). It would need a new seam module. Before pricing it at dispatch, screen it against sql-driver-compile-refusal-seam.test.ts (whether it builds INVALID_FILTER) and against check:tenant-chokepoint rule 2.

skip-changeset: the change is move-only, so nothing a published package ships changes. The .d.ts is byte-identical, and the emitted JS runs the same bodies through the host.


Generated by Claude Code

…clared-index sync into sql-driver-schema-sync.ts (cut 9)

The bodies of SqlDriver#applyNullSafeUniquePreflight, #syncDeclaredIndexes
and #createNullSafeUniqueIndex move unedited into sql-driver-schema-sync.ts
through the existing SchemaSyncHost, widened by the three members they read
that it did not list (createHashShadowUniqueIndex, createNullSafeUniqueIndex,
explainUnkeyableTextColumn). The module-level formatDuplicateGroups moves with
its only two callers and stays module-local. Each class method keeps its
docblock and signature less async.

Claude-Session: https://claude.ai/code/session_01EcqJz48p6ineaiedw6SCaX
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/driver-sql, touching 16 documentable anchor(s).

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

  • content/docs/data-modeling/indexing.mdx (via recreate_index (literal, a string literal in applyNullSafeUniquePreflight))
  • content/docs/deployment/cli.mdx (via create_index (literal, a string literal in applyNullSafeUniquePreflight), recreate_index (literal, a string literal in applyNullSafeUniquePreflight))
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 — 13 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 df8aac41f2d9d14db04afdfe48511216419dafc0 → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 df8aac41f2d9d14db04afdfe48511216419dafc0 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:server seat 2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-26T17:10Z · head b2b00ab42839c48bc6a1c51007fba8bad8751696 · #25521 cut 9

The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.

  1. Shape: a draft against main. The body opens with Part of #25521 (⛔ not Fixes; the card stays open for later cuts). Clause-②: no and Metadata-protocol: none are line-initial. skip-changeset applies: the change is move-only. check-governed-merges --pr 26245: NOT governed, 821 changed lines.
  2. Premise measured: the claim 6420465502 asked for a fresh seam table after cut 8; it is in the PR body with every eligible method priced, and the higher-priced bodies are each held in the class by an ADR or spec-liveness pointer. The pick is the ADR-0120 NULL-safe unique machinery: applyNullSafeUniquePreflight, syncDeclaredIndexes and createNullSafeUniqueIndex move into the existing sql-driver-schema-sync.ts through SchemaSyncHost (three members added), and the module-level formatDuplicateGroups moves with its only two callers, so nothing new is exported. The seat compared every line removed from sql-driver.ts with the module after stripping indentation: each one is present there, except the import line, the three signatures (which drop async only) and describeMissingIndexColumns, which leaves sql-driver.ts's import because its last reader moved (the module imports it from schema-drift.js). Each stub delegates every method through this, so an override or a spy is still the one the moved body reaches. No moved body builds through getBuilder. sql-driver.ts goes 21,334 → 20,990. The dev reports .d.ts byte-identical with a positive control, the AST proof at 0 differing tokens with a negative control, the three driver suites at 0 lost, changed or added, a late-binding probe identical at both ends, and check:adr-anchors 1,883 = 1,883.
  3. Gates: at this stamp the head's 52 check-runs read 36 success and 16 skipped (UI, docs-build, dogfood matrix placeholders and opt-in harnesses with no path in this diff, plus the duplicate-event twins of Auto Label, Check Changeset and Check PR Size), none failing or in progress; Test Core 1–8, Build Core, every Type Check · and Lint · lane, and the Governed Surface Queue Guard are green.
  4. Accepted deviations: none beyond the series shape.
  5. Out of scope: none filed.
  6. Landing: this seat runs pr_ready and then automerge_enable through the relay, confirms MERGED on both readings, and records the landing on [maintainer] refactor(driver-sql): split src/sql-driver.ts (22,096 lines, +2,533 in 30 days) along its SqlDriver seams, one move-only slice per PR #25521 with a Release: back to pm:queue for cut 10.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 26, 2026 17:11
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 17:11
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit ab112ea Oct 26, 2026
53 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-k34erv branch October 26, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/l 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.

2 participants