fix(driver-turso): a remote upsert that lands a NULL record number now says so (#7099) - #8412
Merged
os-zhuang merged 2 commits intoAug 13, 2026
Conversation
… verifications Adds the changeset and the test-file header section: why the detection lives on TursoDriver rather than being plumbed into RemoteTransport, and the two predicted-then-measured reverse verifications (fix reverted; predicate dropped).
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 6 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
August 13, 2026 12:23
os-zhuang
deleted the
claude/issue-7099-turso-upsert-null-autonumber-warning
branch
August 13, 2026 12:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7099
What changes — and what deliberately does not
#6944 made the Turso REMOTE face refuse an
auto_numberwrite it cannot fulfil instead of silently writing NULL. One leg was left uncovered and declared as known residue: anupsertcarrying anidor explicitconflictKeysthat matches nothing still inserts, and the record-number slot still lands NULL.That outcome is unchanged here. What changes is that it is no longer silent.
Out of scope, unchanged, per the triage promotion:
The premise, re-measured
The residue was recorded under the claim that classifying this leg "needs the round trip this refusal exists to avoid". That round trip is already paid, unconditionally:
RemoteTransport.upsertfollows itsINSERT … ON CONFLICTwith aSELECTby id and returns the mapped row — the residue pin's ownexpect(inserted.case_number).toBeNull()was reading that very result. The NULL was in hand all along, one field read away. No probe query was added, and none is needed.Where the detection lives — the one place this PR departs from the suggested shape
The promoted scope suggested handing the autonumber rule down to
RemoteTransport, following thesetFilterColumnSql/setDiagnosticSinkplumbing precedent. Measured, the driver is the better layer and needs no plumbing at all:TursoDriver.upsertalready receives the transport's returned row, and already holdsautoNumberFields[object]— both halves of the detection are in hand at the same frame.logger.warnis literally the sinksetDiagnosticSink((message) => this.logger.warn(message))forwards to, so routing through the transport would be a longer path to the same log line, with no added reach.turso-remote-autonumber-refusal.test.tsasserts no member ofRemoteTransport.prototypematches/autonumber|sequence/i, encoding TursoDriver remote 面根本不生成自增号:RemoteTransport.create 自建 INSERT,auto_number 只是个 TEXT 列 #6944's layering decision ("RemoteTransport cannot see a field type, so it cannot be the one to refuse"). Teaching the transport this rule would have gone red there — correctly.So: detection and reporting both on
TursoDriver, zero new mechanisms, and the schema-blind transport stays schema-blind.False positives were designed against, not hoped away
The check reads the result row, not the request — a merging upsert leaves the slot empty in the request too, so a request-shaped rule would warn on the one leg #6944 measured as correct. Three silence controls pin that: the MERGE leg, an object with no
auto_numberfield, and a caller-supplied number on this same leg (the seed-replay / import path). The empty-slot predicate is now stated once and read by both the pre-write refusal and the post-write report, so "empty" cannot acquire two spellings.Reported at
warn, noterror: everything the caller submitted persisted, and the returned row carries thenullin plain sight; what is missing is a derived value this face declares it does not issue. Per AGENTS.md §Degradation log levels that is a functional degradation, not a durability one. Not throttled, unlike the tenant-audit warning it sits beside — here the rowidis the payload.Reverse verification — both directions predicted before running
1 failed / 19 passed—expected [] to have a length of 12 failed / 18 passed— bothexpected [ Array(1) ] to deeply equal []② is the half ① cannot prove: the pin alone cannot tell a correct warning from a wolf-crying one, and the controls are what do. Also green in ②: the no-
auto_number-field control, because the registry lookup returns before the predicate is reached.Verification
pnpm --filter @objectstack/driver-turso test— 35 files, 969 passed.typecheckclean,eslintclean on both changed files. Gates derived from the actual changed paths viascripts/pm/dispatch-gates.mjsand run green:check:driver-conformance,check:nul-bytes,check:changeset-gate-self-tests,check:objectui-changeset,check:test-source-alias,check:type-source-resolution,check:query-options-erasure,check:type-check-coverage,check:durability-log-level,check-changeset-no-major. Two reds not caused by this diff:check:objectui-pin-fresh(known-ambient, fires for any PR carrying a changeset) andcheck-dev-prereqs(my worktree only built the driver's dependency closure, not all 67 packages).Changeset:
patch— no public export moves, the new member isprivate, and no caller-visible behaviour changes; the only new output is an operator log line where there was silence.Generated by Claude Code