Repository navigation
refactor(driver-sql): move the autonumber sequence path out of sql-driver.ts into sql-driver-autonumber.ts (#25521 cut 11) - #26322
Conversation
…iver.ts into sql-driver-autonumber.ts The bodies of SqlDriver#getNextSequenceValue, #fillAutoNumberFields, #issueAutonumbers, #maxAutonumberCounter, #collidingAutoNumberReservations and #resyncSequenceToDataMax move unedited into a new seam module through AutonumberHost. Each method keeps its docblock and signature and hands its body the members it reads (getters for fields, late-bound closures for methods), so TursoDriver's overrides and instance spies are still the ones reached. SEQUENCES_TABLE gains `export` in place and is imported back; five import names whose last reader moved are dropped. Move-only. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012UMsT3A7YJv6eQtR5HED2B
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 1 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 13 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 701f5588291548bc5881fc0080a2432c4c28b8e3 && git checkout 701f5588291548bc5881fc0080a2432c4c28b8e3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 77dc7062e5a791e02aa2ba9173e067295ebdebca f19c814197b58fed33badb2946158962608d3d40 && git checkout -B drift-repro 77dc7062e5a791e02aa2ba9173e067295ebdebca && git merge --no-ff f19c814197b58fed33badb2946158962608d3d40
node scripts/docs-audit/affected-docs.mjs --json 77dc7062e5a791e02aa2ba9173e067295ebdebca
|
席位核对ACCEPT · The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.
Generated by Claude Code |
Requested by Leehom · project thread
Part of #25521
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6270097840
Ruling-ref: 6348891160
Cut 11 of the
sql-driver.tssplit (#24586 ruling A, recorded in6270097840). The claim is6424911800. The cut opens with a fresh seam table (below), taken onorigin/mainafter cut 10 merged, and takes the area that pays best without breaking a series limit. The bodies of sixSqlDrivermethods move unedited into a new seam module,packages/drivers/driver-sql/src/sql-driver-autonumber.ts, through a new host,AutonumberHost. Together they are the autonumber sequence path:getNextSequenceValue, which reads and advances one counter row under its lock (and bootstraps a cold counter from the data);fillAutoNumberFields, which fills a row'sautonumberfields from those counters;issueAutonumbers(public), the issue-ahead door, with its collision follow-up;maxAutonumberCounter, the bootstrap reading of the highest counter in a set of values;collidingAutoNumberReservationsandresyncSequenceToDataMax, the Autonumber counter neither syncs to MAX(existing) per tenant nor re-checks on collision — warm-DB creates 409 in bursts, each failure burning a number (25 retries observed) #5495 collision re-seed.The change is move-only, and the card stays open for later cuts. There is no
docs/adr/**edit, nopackages/spec/**edit, no anchor entry change and no new gate. Branchclaude/project-thread-r8r6to(the platform assigned it to this card thread, as the claim records). One commit,69181bf14, onorigin/main817cb8983(cut 10's merge).Net line count (the seat's rule).
sql-driver.tsgoes from 21,018 lines to 20,827: -191.isUniqueViolationErroranduniqueViolationColumn, whose last reader moved. Three more such names (renderAutonumber,readAutonumberCounter,missingFieldValues) leave a line that stays.maxAutonumberCounterreads no member, so its stub is a comment and a direct call), and 5 lines for the new import and its comment.Serial with the file's in-flight PRs. I read the full file list of every open PR at start (36 PRs, on cut 10's head) and again after cut 10 merged, before the first edit (28 PRs). No open PR touches
sql-driver.ts,turso-driver.ts, the CRUD section,check-tenant-chokepoint.mjsor the driver-sql anchor entries.{ $now: true, addDays }shifted by days held on the record #26261 ($nowcomparand) was open at start, editing the preamble and Filter helpers. It merged before cut 10 did, so it is in this base.driver-sql/src/builtin-column-collision.tsonly, and test(vitest): one shared root preset for the 18 identical package configs (#26092 part 2) #26267 thevitest.config.tsof driver-turso and driver-sqlite-wasm only.origin/mainhas moved 8 commits past the base since; none touchespackages/drivers/**.Seam table, fresh, on
817cb8983origin/mainis at817cb8983, after cut 10 (#26287) and #26261.sql-driver.tsis 21,018 lines. 30-day lines aregit blamelines from commits on or after 2026-09-26T19:27:54Z, 30 days before the base commit. History reaches 2026-09-15, andscripts/pm/git-history.mjs countproves 71 commits on the file in that window (29 in the last 7 days). In all, 3,523 lines (16.8%) are from the last 30 days.SqlDriverspans lines 5591-20943 with 350 members: 243 instance methods (49 public), 13 accessors and 32 statics, all 32 of them private or protected. 30 of the instance methods are already stubs from cuts 1-10.supportsgetter, in the class head.!andsatisfiesbefore it readsthis. Sections are the class's own banner comments (// ===blocks and// ──sub-banners), as cuts 9 and 10 cut them, with the docblock heading "The sequence semantics a second face shares" folded into CRUD.name…assertDialectEmitsconnect…disconnectfind…deletebulkCreate…countrawStatementFault…noteRuntimeIndexDdlbeginTransaction…rollbackTransactionaggregate…unresolvableAggregateColumnRefusaldistinct…unresolvableDistinctColumnRefusalfindWithWindowFunctions…findWithWindowFunctionsexplain…analyzeQuerysyncSchema…reclaimSpacerotationStateByTable…migrateMysqlTimeColumnssetDeferredDdl…flushDeferredSchemaDdldialectName…createNullSafeUniqueIndexlistPhysicalColumns…introspectSchemagetKnex…auditMissingTenanttableNameForBuilder…applyFalseConstantmapSortField…mapAggregateFuncbuildWindowFunction…buildWindowFunctionnowColumnDefault…applyDeclaredColumnDefaultsqliteFilename…isJsonFieldformatInput…formatOutputintrospectColumnOrder…introspectUniqueConstraintsPricing the eligible methods
A method is eligible to move when its return type is annotated or it is not public; it has no
getBuildersite, nothis.FIELD.delete/insert/update(site, no private or protected static read, nosuperread, no barethispass and no write tothis; and it is notinsertOnlyUpsertColumns. 164 members are eligible on this base. Each is priced as its stub minus its body (a stub costs 4 lines, plus 2 per field or getter read and 1 per method read).Priced by area (every eligible body with a negative price and no
sql-driver.ts#…pointer, summed per section): Filter helpers -550 (22 bodies), Rotation -388 (15), CRUD -351 (18), and every other section -93 or less.The highest-priced candidates, and why each one stays or moves:
createColumn(Column creation helper)sql-driver.ts#createColumn, and five cligenerate-*.pin.test.tsfiles read this file's text.applyFilterCondition(Filter helpers)sql-driver.ts#applyFilterCondition.formatOutput,formatInput(SQLite serialisation)sql-driver.tsanchor entry's ADR-0053 paragraph describesformatOutput.initObjects(Rotation)packages/spec/liveness/object.jsoncites it.applyCrossFieldComparison(Filter helpers)uncompilableFieldReferenceErroralone has 16 readers).applyNowAnchorComparison(Filter helpers)crossFieldComparisonClass,fieldReferenceOffsetOf) shared with the cross-field code. #26261 added it at 19:11Z today.assertConflictTargetHonoured(CRUD)backendStatementFault(Bulk & Batch),compileFilters(Filter helpers)PRE_DDL_QUESTION_SCOPE;refusalSubtree,assertCompilableComparandand three more).getNextSequenceValue(CRUD)SEQUENCES_TABLE(see below).fillAutoNumberFields(CRUD)issueAutonumbers(CRUD, public)maxAutonumberCounter(CRUD)resyncSequenceToDataMax(CRUD)SEQUENCES_TABLE.collidingAutoNumberReservations(CRUD)Why this area
SEQUENCES_TABLEis read by the two moved bodies and by three members that stay:ensureSequencesTable,ensureSequencesKeyHashShapeand thesequencesTableNamegetter. It stays insql-driver.ts, gainsexport, and the module imports it back, as cut 2 did with seven declarations. The value import is read only inside function bodies, so the module cycle is evaluation-safe.index.tsdoes not re-export it, and the published.d.tsdoes not move (proof below).ensureSequencesTableandensureSequencesKeyHashShapewritethis.autoNumberValueExistsandscanMaxNumericTaileach hold one of the four tenant-column predicates thatcheck:tenant-chokepoint's ADR-0131 D8 rule recognises. That gate scans the three driver files, not seam modules, and does not print the count on success, so moving them would narrow it silently (its floor is 3). I measured the count with the gate's ownnullArmsOf: 4 at base and 4 at head, all four insql-driver.ts.TursoDriver's remote face calls (resolveSequenceTenantId,sequenceKeyHash,defineSequencesTable,createSequencesTableand two getters) and the genericattemptWithoutPoisoningprice at -3 or better, so they stay.getBuilder, reads a static, has athis.FIELD.delete/insert/update(site, usessuperor a barethis, or writes tothis.issueAutonumbers, the one public method, has an annotated return type (a promise ofstring[][]).TursoDriveroverridesgetNextSequenceValue,autoNumberValueExistsandresyncSequenceToDataMax, and each override callssuperon its local path. The stubs stay on the class with the same signatures, sosuperstill reaches them. The moved bodies reach every method through a late-bound closure on the instance, sofillAutoNumberFieldsstill reaches Turso's counter, and the collision path still reaches itsautoNumberValueExistsandresyncSequenceToDataMax.sql-driver.ts#…path or asql-driver.ts::…key. No test slices their text out ofsql-driver.ts. See Acceptance notes for prose that names them.The shape (#23050 shape B, as cuts 1-10 apply it)
async. Its body is now one host literal plusNAME.call(host, ...), marked[#25521 cut 11]. The fiveasyncmethods return the moved function's own promise.maxAutonumberCounterreads no member, so its stub calls the function directly, as cut 3/16 of [maintainer] refactor(metadata-protocol): splitsrc/protocol.ts(33,065 lines, +10,169 in 30 days) along itsObjectStackProtocolImplementationseams, one move-only slice per PR #25467 did.autoNumberFields,issuedAutoNumbers,knex,physicalTableByObjectandsequencesHasKeyHash. Methods go as closures that call the method on the driver at call time. So a subclass override, an instance spy, or a field replaced after construction is still the one the body reaches (see the probe below).thisas the Pick ofAutonumberHostover 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.AutonumberHostlists 17 members, each typed as the class member's indexed access.DriverOptions(type),missingFieldValues,readAutonumberCounterandrenderAutonumberfrom@objectstack/spec/data,StorageNameMappingfrom@objectstack/spec/system,isUniqueViolationErroranduniqueViolationColumnfrom@objectstack/types, the typeKnex,GLOBAL_TENANTfromschema-drift.ts, the typesAutoNumberReservationandSqlDriver, andSEQUENCES_TABLEfromsql-driver.ts.sql-driver.tsdrops the five of those names it no longer reads.sql-driver.tsimport comment names cut 11 in 4 lines.ADR anchors
check:adr-anchorscounts decision-letter citations on the anchored surface: each anchor'sinvariant, plus the full text of each anchored file.817cb8983and 1879 at head69181bf14, over 90 anchored files at both ends. Both runs exit 0.getNextSequenceValue's body carries one comment that citesADR-0131 D9(the organization-less counter refusal happens first). That comment moves from the anchoredsql-driver.tsto the new module, which has no anchor entry. The refusal itself (resolveSequenceTenantId) stays insql-driver.ts.sql-driver.tsentry is ADR-0053's, whose paragraph describesformatOutputandpresentReadValue; neither moved. No new entry was added, and noscripts/adr-anchors/**file changed.The published
.d.tsdoes not changeThis is measured under the series bar (ruling
6348891160, item 1).pnpm turbo run build --filter=@objectstack/driver-sql --forceat each end, against the same dependency tree. The base leg writes basesql-driver.tsinto this worktree and removes the new module after the commit, under an EXIT/INT/TERM trap that restores HEAD. The restore is proved by the HEAD blob hashes (13a5215f,026dfdda), an emptygit diff HEADand 0 status entries.dist/index.d.tsanddist/index.d.mtsis the same at base and at head:0f33bf3febaac3c7aa96f68a4226d752766b8203ab7acade523b69f9aaa44d74.cmpfinds them identical.protectedfromgetNextSequenceValuein the headindex.d.ts. The result hashes tobbd6f18ba89c1fdbfa84c1bb16628d3e6956f8c4b2c8296d7629f75e63de20cb, with a 1-line diff andcmpexit 1.requireand ESMimportof the built dist give 59 / 59 names at both ends, identical (SEQUENCES_TABLEis not among them)..call(hostsites inindex.mjsgo from 30 at base to 35 at head (five;maxAutonumberCounteris a direct call).Move-only proof
The proof compares TypeScript compiler AST leaf tokens (JSDoc nodes skipped), base
817cb8983against head69181bf14. It is a scratch script and is not committed.thisgetNextSequenceValuefillAutoNumberFieldsissueAutonumbersmaxAutonumberCountercollidingAutoNumberReservationsresyncSequenceToDataMaxasyncmatches on both ends, and there are no type parameters. Each body line is the base line less two leading spaces.async.sql-driver.tstop level. 210 statements at base, 206 of them byte-identical in head. The four that differ: the@objectstack/spec/dataand@objectstack/typesimports (names dropped),SEQUENCES_TABLE(a leadingexport, nothing else) and the class. The one new statement is the import of the new module.PROOF OK, exit 0.legacy shape.tolegacy shap3.in a module copy. The proof then reportsgetNextSequenceValuewith its body tokens and lines differing, printsPROOF FAILED, and exits 1.Tests
Each end ran under one
os-verify-lockhold, after building that end's driver-sql dist. I compared the vitest JSON per test name and status, base817cb8983against head69181bf14. Every part's exit code was captured on its own (build, probe, three suites: all 0 at both ends).@objectstack/driver-sql@objectstack/driver-turso, against that end's built driver-sql dist@objectstack/driver-sqlite-wasmtsc --noEmit -p .in driver-sql exits 0.pnpm exec eslint --no-inline-config packages/drivers/driver-sqlexits 0, with 0 problems.diffis empty). They cover:createreaching a SUBCLASS override ofgetNextSequenceValuethroughfillAutoNumberFields, with the arguments it passes;getNextSequenceValuereaching instance spies onensureSequencesTable,resolveSequenceTenantId,sequenceKeyHash,scanMaxNumericTailandattemptWithoutPoisoning, cold and warm;sequencesHasKeyHashflipped on the instance: a scoped format answers the legacy-shape refusal;fillAutoNumberFieldsreadingphysicalTableByObjectandautoNumberFieldsreplaced after construction (the scan then names the replaced table; an emptied registry fills nothing), through an instance spy ongetNextSequenceValue;issueAutonumbersahead of a write and after a collision (a column-named one and a column-blind one), through instance spies oninjectTenantOnInsert,fillAutoNumberFields,collidingAutoNumberReservations,autoNumberValueExists,autoNumberCounterKeyandresyncSequenceToDataMax; the transaction early-out; andissuedAutoNumbersreplaced after construction;create, reaching instance overrides ofautoNumberValueExistsandresyncSequenceToDataMax;maxAutonumberCounteron three value sets;maxAutonumberCounterthrows synchronously, at both ends (it was neverasync).test:repowas not run locally: the diff touches no repo-project test, and CI's Test Core runs it.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 53 commands for the 2 changed paths (merge base817cb8983). On head69181bf14, 50 exit 0 on the first run. Two exit 3 on a missing prerequisite and pass after it is supplied:check-plugin-teardown-shape.mjs --self-test(shallow clone; exit 0 aftergit fetch origin 621a487607) andcheck:lean-entry-closure(exit 0 after building objectql under the lock).check:dual-build-cjs-loadsexits 3,PREREQUISITE NOT MET(no full build here): NOT MEASURED locally, CI runs it.dispatch-gates --ran: 53 derived families accounted for, 52 run, 1 NOT-MEASURED (derived from the recorded exit 3), exit 0.817cb8983) and at head, all exit 0:check:adr-anchorscheck:adr-symbol-anchorscheck:tenant-chokepointcheck:driver-conformancecheck:keyed-text-boundscheck:object-def-param-keyscheck:system-context-censuscheck-tenant-audit-censuscheck:logger-receiver-detachcheck:commit-card-trailersandcheck:nul-bytespass.Acceptance notes
getNextSequenceValue"usesrunner = parentTrx ?? this.knex… (sql-driver.ts)". The method still lives onSqlDriverin that file; its body now runs fromsql-driver-autonumber.ts. Left as is: nodocs/adr/**edit in this series.isUnbackedConflictTargetErrorinsql-driver.ts(line 2196) warns "Deliberately NOTisUniqueViolationError, which is imported one line away".sql-driver.tsno longer imports that predicate (its last reader moved). The warning stays true; "imported one line away" no longer does. Left unedited because the cut is move-only.TursoDriver's docblocks nameSqlDriver.getNextSequenceValue,SqlDriver.maxAutonumberCounterand the others as the members its remote face shares; each is still a member ofSqlDriver.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_012UMsT3A7YJv6eQtR5HED2B