Repository navigation
[finding] TursoDriver remote mode silently discards options.transaction — a write between beginTransaction() and rollback() is already durable and the rollback does not undo it #18616
Description
Activity
huangyiirene commented
on Sep 17, 2026 CollaboratorAuthorMore actionsClaim: PM loop round 3
Session:session_01CqmCgU5RGDoJYhHUMVp2af
Branch:claude/issue-18616-turso-remote-transaction-refusal
Worktree:objectstack-issue-18616
Domain:domain:engine
Seat:domain:engine#1
File surface:packages/drivers/driver-turso/src/(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: opus (default judgement tier)— quoting this dispatch's owndispatch-gates.mjs --tier packages/drivers/driver-turso/src/remote-transport.ts: 「Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s) … The tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable).」 ⇒ default judgement tier: the deliverable is a refusal boundary on a data-integrity path, ⛔ not a mechanical edit.
Clause-②: yes
Thread-read: 5716552116
Serial constraints cleared:No in-flight card in this lane touches packages/drivers/driver-turso. The only pm:dispatched card at claim time is #18554 (file surface packages/formula/src/), disjoint. ⭐ #18408's PR #18668 landed at 14:59:50Z as ef67b47afb and DID touch driver-turso/src/remote-transport.ts (mapFieldTypeToSQL) — it is merged, not in flight, so this is a predecessor to branch from, ⛔ not a conflict; branch from a main that contains it. ⚠️ #18063 sits pm:awaiting-maintainer and this card's own body raised it as a possible parent; triage answered that question in 5716552116 and graded this free-standing and dispatchable with no new ruling needed, so the seat does ⛔ not re-open it.The deliverable, and the line triage drew
⛔ The deliverable is「stop the silence」, NOT「implement remote transactions」. Triage wrote that boundary into the card and this dispatch adopts it verbatim:
止损(本卡):remote 模式遇到
options.transaction或beginTransaction()时大声拒绝 / 声明不支持,让调用者立刻知道自己没有事务语义。
实现远程事务:那是 #18116 已完成的那次「measure, do not implement」量出来的半径,是另一件事、另一个量级。⛔ 不要把它折进本卡。⇒ If your measurement says the honest refusal is cheap and remote transactions are one file further, that is ⛔ not an invitation to do both. Report the radius; do not spend it.
What this seat owes you, measured before dispatch
The card's readings were re-taken by the filing seat on
origin/mainbefore filing, and the zero carries its own firing control — 0 data methods acceptingoptionsor a transaction, against 9 data methods present in the file. ⛔ Re-derive both anyway on your head:packages/drivers/driver-turso/src/remote-transport.tswas edited by PR #18668 since the card was written, so the line numbers in the card body may have moved. ⭐ Locate by content, ⛔ not by line.⚠️ The part that needs your judgement, ⛔ not this seat'sWhere does the refusal live, and what shape is it? Three candidates the seat can see, and it is ⛔ not ruling between them:
beginTransaction()on the remote arm throws instead of returning a decorative handle.- Every remote data method that receives
options.transactionthrows. - Both.
⭐ (1) alone may be the whole fix or may be a false floor — a caller that never calls
beginTransaction()but passes a handle it got elsewhere would still be silently ignored. Measure which callers can reach a remote data method with a transaction in hand, then choose, and say why the one you did not choose is not needed. ⛔ A choice with no stated alternative is not a measurement.⚠️ The existing local-mode pin is your model:turso-driver.test.ts:211-214already asserts this shape for LOCAL mode. ⇒ The tree knows what correct looks like here; the remote face is what is unpinned.Clause-② is
yes, and here is the seat's reasoning — argue it down if you measure otherwiseThis card changes what the driver accepts: a call that today succeeds silently will refuse. That is contract accept/reject behaviour moving, so the conservative grade applies and
needs:contract-reviewrides with it.⚠️ If your fix needs a NEW error code, that lands inpackages/spec/src/api/error-code-ledger.zod.ts, which is ⛔ outside your file surface and belongs to thedomain:specseat. Report it as a blocker with the code you would want and why an existing one does not fit — ⛔ do not reach intopackages/spec.⛔
skip-changesetis almost certainly wrong here:@objectstack/driver-tursopublishes and this moves its behaviour. Measure it the way your role file prescribes rather than taking this line as the finding.Report
Per
.claude/agents/os-dev.md— that file governs your report's field set, your write budget and your label discipline, ⛔ not this comment. Where this dispatch and the role file differ, the role file wins and you say so in the report.
Generated by Claude Code
huangyiirene commented
on Sep 17, 2026 CollaboratorAuthorMore actionsos-dev-report
{ "issue": 18616, "status": "done", "branch": "claude/issue-18616-turso-remote-transaction-refusal", "pr": "https://github.com/objectstack-ai/objectstack/pull/18717", "session": "session_01CqmCgU5RGDoJYhHUMVp2af", "premise_still_valid": true, "summary": "The Turso REMOTE face now refuses transactions with NOT_IMPLEMENTED/501 instead of accepting them and dropping them: the trio (beginTransaction/commit/rollback) on the remote arm, plus every remote-arm override that arrives carrying options.transaction (17 doors: find, findOne, count, aggregate, create, update, upsert, delete, the three bulk methods, updateMany, deleteMany, execute, syncSchema, syncSchemasBatch, dropTable). BOTH doors, and that was the judgement the dispatch left open: measured, there are two independent producers of a handle, because objectql's buildDriverOptions reads execCtx.transaction FIRST and transactionCoversDriverFor declines to judge a handle that is not the ambient store's, so an ExecutionContext-threaded handle never passes through beginTransaction() — ablation 2 below measures that false floor rather than arguing it. The refusal lives on TursoDriver, not RemoteTransport, because the transport's data methods have no options parameter at all. NO new error code and packages/spec untouched: NOT_IMPLEMENTED is a StandardErrorCode member and the ledger registers extension codes only, so the blocker the dispatch anticipated did not materialise. Local and embedded-replica behaviour unchanged (the guard returns unless isRemote). Card readings re-derived by content on base f8eaf670454a (contains ef67b47afb/#18668): transaction-naming members 3, data methods taking options 0, this.isRemote guards 26 — all match; the firing control re-derives to 13 data methods, not the card's 9, which strengthens the zero rather than weakening it and is recorded in the PR body. Radius NOT spent, as instructed: implementing remote transactions means an options parameter on 13 transport data methods, threading a libsql Transaction through each statement builder, and deciding what the knex-free remote connect() does about SqlDriver's openTransactions accounting. The card arrived assigned (huangyiirene) with pm:dispatched/priority:p1/domain:engine; assignee untouched. No dispatch-vs-role-file conflict arose in substance: the role file governs the field set and the write budget and it did; on labels the role file's 'label the PR as soon as it opens' resolves to ZERO label writes here, because the only dev-owned label is skip-changeset (measured inapplicable) and needs:contract-review is the seat's by the dispatch and by check-clause2-carriers' own report-only stance.", "tests": "ALL GREEN. (1) pnpm --filter @objectstack/driver-turso test :: exit 0 — 'Test Files 54 passed (54) / Tests 1278 passed (1278)', which contains the pre-existing local-mode transaction pins. (2) pnpm --filter @objectstack/driver-turso typecheck :: exit 0. (3) pnpm --filter '@objectstack/driver-turso^...' build :: VERDICT command-exit 0 (dependency closure, via scripts/pm/os-verify-lock.sh, OS_VERIFY_LOCK_SLOT=issue-18616 on every locked call). (4) Full package build 'pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/*' :: VERDICT command-exit 0, 'Tasks: 72 successful, 72 total'. (5) GATE FAMILIES derived mechanically with 'node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack' (never from a hand-written diff; derivation run twice and byte-identical both times; its only STALE-TREE complaint names scripts/pm/post-stamped.mjs, a PM tool in the always-runs tail, not a family judging this diff): 60 derived, 60 run, 0 NOT MEASURED, 0 UNRUN, reconciled back through --ran with 'command :: exit N' per family, verdict line 'dispatch-gates --ran: 60 derived famil(ies) accounted for — 60 run, 0 NOT-MEASURED (a DERIVED zero)'. Every exit code captured BEFORE any pipe (redirect-then-capture). One family was a REAL RED and is fixed in this PR: check:doc-authoring refused the tracker id inside the refusal's runtime string; the id moved to the docblock and the re-run is exit 0 ('sibling-package prose ids hold the baseline — 821 pinned site(s) ... no growth'). Three families answered PREREQUISITE NOT MET (exit 3 = NOT MEASURED, not a failure) until the full build above, then exit 0: check:dual-build-cjs-loads, check:lean-entry-closure, check:type-check-debt. check:error-code-casing run additionally :: exit 0. (6) pnpm lint :: exit 0 — the repo-wide 'eslint . --no-inline-config' UNION, not a narrowed run, at final commit 97009bc42e, so no narrowing evidence is owed. ABLATION — two legs, both run from the COMMITTED fix, direction predicted before either ran, each mutating turso-driver.ts on disk under a trap whose restore is 'git checkout HEAD -- ABSOLUTE_PATH' verified by blob equality AND empty 'git diff HEAD'. Subject HEAD blob 4b2b21a0da4e1b65f67d0f8f803a2283bff64c6c. ON-DISK MUTATION PROOF per leg, by grep count and by a changed blob, with a hard abort wired for a no-op: leg 1 'guard-body lines 1 -> 0, injected ABLATED-OPTIONS-DOOR marker 1, injected ABLATED-TRIO markers 3, refuseRemoteTransaction call sites 4 -> 1, blob 0dffa9731274'; leg 2 'guard-body lines 1 -> 0, marker 1, ABLATED-TRIO 0, call sites 4 -> 4, blob 4ca762d9e647'. LEG 1 (both doors removed, trio restored to its pre-fix delegation) predicted the two REMOTE blocks red and the silence controls green; measured '21 failed | 5 passed (26)' — 4 trio + 17 options red, 5 controls green, and the failures REPRINT the defect ('expected the remote face to refuse, but it resolved with ...'). LEG 2 (only the options door removed, i.e. exactly a fix that had stopped at beginTransaction()) predicted 17 red / 9 green; measured '17 failed | 9 passed (26)' — the false floor, measured rather than argued. Both legs closed with 'RESTORE OK — on-disk blob 4b2b21a0da4e... == HEAD blob 4b2b21a0da4e..., and git diff HEAD is empty'. REBUILD NOTE: the ablation needs no dist round trip and none is claimed — the pin suite imports the package's own source relatively ('./index.js' -> src/index.ts), so the resolution path is src/, not dist/; the full-package build above is separate and was run for the three prerequisite gates. CHANGESET MEASURED, not assumed: skip-changeset is WRONG here. driver-turso publishes with files[] = dist, README.md, CHANGELOG.md; after building, a symbol unique to this change hits dist/index.js:1 and dist/index.mjs:1, the positive control (a pre-existing shipped refusal sentence) hits dist/index.js:2, dist/index.mjs:2, CHANGELOG.md:2, and the negative control (a test-only helper name) hits 0 anywhere in files[] — so it publishes and .changeset/18616-turso-remote-transaction-refusal.md grades @objectstack/driver-turso minor (clause-② level axis satisfied; launch-window major ban respected). CONTRACT-REVIEW READINGS, as the dispatch asked: needs:contract-review is NOT on PR 18717 and not on card 18616 (PR labels read back: ['size/l'], added by the repo labeler, not by me); 'node scripts/pm/check-clause2-carriers.mjs --pair 18717' :: exit 4, row C3 — 'card #18616 declares Clause-②: yes while NEITHER it nor its delivering open PR #18717 (draft) carries needs:contract-review, and the event stream shows the gate was NEVER HUNG on either carrier'. Reported, not acted on: that label is the seat's, and the checker itself states it never writes one. PR body read back after creation: byte-identical to what was sent apart from a trailing newline, one footer in the session-URL form, 'Fixes #18616' on line 1, 'Clause-②: yes' at column 0 — no sanitizer mutation. CI not waited on, per the role file; nothing on the PR has been polled.", "mcp_calls": "0 — no MCP GitHub tool was called at all, read or write. Every GitHub read and write went through the REST proxy with curl and GITHUB_TOKEN.", "api_writes": "2 REST writes, plus 5 git pushes. REST: POST /repos/objectstack-ai/objectstack/pulls (draft, HTTP 201, PR 18717); POST /repos/objectstack-ai/objectstack/issues/18616/comments (this report). ZERO label writes — POST /issues/18616/labels and the PR-side equivalent were deliberately not issued: skip-changeset is measured inapplicable (see tests) and needs:contract-review belongs to the dispatching seat. git push x5: the empty-branch write-route probe before any edit, then one per commit (implementation, tests, changeset, doc-authoring fix). No PATCH of any body, no POST /issues.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: engineCanRollBack (packages/core/src/utils/migration-journal.ts) gates atomicity on METHOD PRESENCE — typeof defaultDriver.beginTransaction === 'function' — which is true for a remote TursoDriver because the method is inherited from SqlDriver at class-definition time while the transport mode is runtime config. There is no way for a driver to declare 'present, but not on this connection mode'. This is the spec's declared design (the retired transactions capability bit says transaction use is gated on method presence), and after this PR its consequence is loud rather than silent — batchData's atomic gate now receives a refusal instead of a half-applied batch — so nothing is left silently wrong. Not class (a)/(b)/(c): no repro of a wrong outcome remains, no contract text is violated, no authorable metadata is involved. 承接者: the #18063 / #18116 semantics decision, which is exactly what this shape is about.", "noted, not filed: RemoteTransport.beginTransaction / commit / rollback are now unreachable through TursoDriver. They stay reachable via getRemoteTransport() and are pinned by turso-driver-doors-declared-types.test.ts, so this is not dead code and not a finding; whether they are removed belongs to the same implement-or-retire ruling. 承接者: the same ruling." ] }
Generated by Claude Code
huangyiirene commented
on Sep 17, 2026 CollaboratorAuthorMore actionsContract review
Served-tier:
CONTRACT_REVIEW_TIER
Head-sha:97009bc42ec8e532a22fa878b748e91dcc00e2addomain:engineexecution seat,session_01CqmCgU5RGDoJYhHUMVp2af, in-seat review of the clause-② it dispatched. Written 2026-09-17T16:36Z. PR #18717. ⛔ Every reading is command output on this head or onorigin/main, ⛔ not the PR body's account of it.Fuse. Served tier read from
get_session→external_metadata.last_served_model, ⛔ notsession_context.model.dispatch-gates.mjs --tier packages/drivers/driver-turso/src/turso-driver.ts: no path-derived mandate ⇒ the bar is the default judgement tier and the reading meets it.⛔ First, this seat's own miss, because the dev reported it and it was mine.
--pair 18717exited 4 on row C3: the card declaredClause-②: yesandneeds:contract-reviewhad never been hung on either carrier. The claim stroke that setpm:dispatchedon #18616 should have carried the gate in the same write and did not — the seat did it correctly on #18554 in R2 and skipped it here. ⇒ Hung on both carriers before this record;--pair 18717now exits 0. ⭐ The dev was right to report it as a reading and right ⛔ not to hang it itself.① Derived judgments
-
The accept set NARROWS on the remote arm only —
beginTransaction/commit/rollbackplus 17 doors that receiveoptions.transactionnow throw where they previously resolved. Right, and it is the entire point of the card: what they previously did was resolve while dropping the handle. -
No new published symbol and ⛔ no new error code — the blocker the dispatch anticipated genuinely did not materialise, and the seat verified the claim rather than accepting it:
NOT_IMPLEMENTEDis a member ofStandardErrorCode(packages/spec/src/api/errors.zod.ts:114), mapped to 501 at:182; the error-code ledger's own docblock (error-code-ledger.zod.ts:8-11) says it registers extension codes only, the standard catalog living inerrors.zod.ts. ⇒packages/specis untouched and correctly so. Right. -
Local and embedded-replica are untouched — the guard returns unless
isRemote, so the two transports that actually inheritSqlDriver's knex transactions keep every verdict they had. Right, and it is what keeps this a narrowing of one face rather than of the driver. -
⭐⭐ Both doors, and door (1) alone would have been a FALSE FLOOR — independently confirmed. The dispatch left this judgement open and warned that closing
beginTransaction()alone might look sufficient. Verified at the source, ⛔ not from the report:packages/objectql/src/engine.ts:4276-4278—buildDriverOptionsresolvesexecCtx?.transaction !== undefined ? execCtx.transaction : this.txStore.getStore()?.transaction, under its own comment 「Explicit wins; ambient is the safety net」. ⇒ An explicitly threaded handle never passes throughbeginTransaction().transactionCoversDriverForopens withif (!scope) return true;— with no ambient scope the same-origin gate declines to judge and lets it through.
⇒ Two independent producers, so both doors are owed. Right, and ⭐ the dev measured it (ablation ② keeps only the trio refusal: 17 failed / 9 passed) rather than arguing it — which is the difference between a claim and a reading.
-
The refusal lives on
TursoDriver, ⛔ notRemoteTransport— correct by construction: the transport's data methods take nooptionsat all (0 of 13, the control the card and this PR both re-derive), so by that layer the handle is already gone. This class is the last one still holding it. Right, and it follows the siblingauto_numberrefusal precedent in the same file. -
Error shape —
err.code = StandardErrorCode.enum.NOT_IMPLEMENTED,err.status = 501, with a message that names the concrete failure (already durable; a later rollback resolves having undone nothing) and points at the two supported routes. Right.
⭐ The structural point: every refusal is behind
isRemote, and nothing in the diff removes a name from any accepted set on the local or embedded arm. The narrowing is confined to the face that never delivered the semantics it reported.② Semver level
minoron@objectstack/driver-turso, declared in.changeset/18616-turso-remote-transaction-refusal.md; the PR body carriesClause-②: yesat column 0;--pair 18717exits 0.⚠️ The honest tension, stated rather than skipped: a call that used to succeed now throws, which reads like a breaking change. The seat's ruling isminor, and the reason is ⛔ not the launch-window ban:What changes is a path that never delivered the semantics it reported. Depending on it means depending on a
rollback()that resolves while keeping the data — i.e. depending on silent data loss. Withdrawing a capability nobody could correctly rely on is not the removal amajorexists to announce.⚠️ The repo'scheck-changeset-no-major.mjsban does apply andmajoris unavailable anyway — ⛔ but that is a constraint, not a justification, and a record that leaned on it would be a bad one. The reasoning above stands without it.patchwould be wrong: behaviour moves, and a new refusal is exactly what the level axis exists to announce.⛔
skip-changesetwould be wrong, and the dev measured it instead of taking the dispatch's word:files[] = dist, README.md, CHANGELOG.md; a symbol unique to the change hitsdist/index.jsanddist/index.mjs; positive control (a pre-existing shipped refusal sentence) hits three published paths; negative control (a test-only helper) hits 0. ⇒ It publishes.③ Boundary flags
open_questionswas empty; two acceptance notes, both correctly classed and neither filed.F1 —
engineCanRollBackgates on METHOD PRESENCE (packages/core/src/utils/migration-journal.ts), which istruefor a remoteTursoDriverbecause the method is inherited at class-definition time while the transport mode is runtime config. ⇒ Agreed, and ⛔ not discharged here. The spec's own retirement note for thetransactionscapability bit declares that gating, so this is the design, ⛔ not a defect in that helper — and after this PR its consequence is loud (batchData's atomic gate receives a refusal) rather than a half-applied batch. ⭐ The underlying shape — 「presence cannot express a per-mode capability」 — is the semantics half of #18063, which is in the decision box. Carrier accepted as named; ⛔ this review does not widen it into a new card.F2 —
RemoteTransport's own trio is now unreachable throughTursoDriver. Agreed it is ⛔ not dead code: it stays reachable viagetRemoteTransport()and is pinned by an existing declared-types suite. Removing it belongs to the same implement-or-retire ruling. Carrier accepted.⛔ The radius NOT spent, and the seat endorses not spending it. Triage drew the line and the dev reported the measured cost of the other half — an
optionsparameter on 13 transport data methods, threading a libsqlTransactionthrough each statement builder, and deciding what the knex-free remoteconnect()does aboutSqlDriver'sopenTransactionsaccounting. ⭐ Reporting a radius instead of spending it is the behaviour the dispatch asked for, and it is what keeps this PR reviewable.Implemented-by:
claude/issue-18616-turso-remote-transaction-refusal
Reviewed-by:session_01CqmCgU5RGDoJYhHUMVp2af⇒
mode:subagent, so the pair compares a branch against a session and is ⛔ not SELF-REVIEW by the charter's reading: in-seat review of a subagent's diff is the design.VERDICT: PASS
⏹️ ⛔ Landing is NOT cleared by this record alone. ① is now in case; ② will be re-read at the clearing stroke; ③ is not met — 16 checks are still running on this head. The carriers stay hung until all three are in case.
Generated by Claude Code
-
huangyiirene commented
on Sep 17, 2026 CollaboratorAuthorMore actionsLanding reconciliation — PR #18717 is MERGED; this card is closed out. Written 2026-09-17T17:35Z.
domain:engineexecution seat,session_01CqmCgU5RGDoJYhHUMVp2af, R3 close. ⛔ Every criterion is command output, ⛔ not the merge event's echo.criterion reading squash commit 62bce5c297d3907b29515ff6943b26b01342608d, merged 2026-09-17T17:32:55Zparent count 1 ( 5762eaf7e9) ⇒ genuinely a squash, ⛔ not read off the auto-merge echocontent on origin/main(⛔ not the merge event)refuseRemoteTransaction6 (its definition, its four call sites and the docblock that points at it) ·StandardErrorCode.enum.NOT_IMPLEMENTED2 ·status = 5012 · the new pin fileturso-remote-transaction-refusal.test.tspresent · the changeset present⭐ why those two-counts are right, not a surprise the second of each belongs to the sibling auto_numberrefusal this PR followed as precedent ⇒ the greps fire on a known second subject, which is its own controlclosed_by_pull_requests(enriched reader)total_count1, only #18717 ⇒ ⛔ no card closed by a stray body keywordlane reconciliation open domain:engine55 → 54; the only disappearance is this card ⇒ the expected onelabel residue pm:dispatchedstripped, read back. Bothneeds:contract-reviewcarriers were cleared before landingWhat landed, and the one thing worth carrying forward
The Turso remote face now answers
NOT_IMPLEMENTED/ 501 onbeginTransaction/commit/rollbackand on every remote-arm door that receivesoptions.transaction. Local and embedded-replica behaviour is unchanged — the guard returns unlessisRemote.⭐ The reusable lesson is the FALSE FLOOR, and it was measured rather than argued. The dispatch left open which door to close and warned that
beginTransaction()alone might look sufficient. It is not:buildDriverOptionsreadsexecCtx.transactionfirst (「Explicit wins; ambient is the safety net」) andtransactionCoversDriverForopensif (!scope) return true, so an explicitly threaded handle never passes throughbeginTransaction()and the same-origin gate declines to stop it. ⇒ Two independent producers. The dev proved it with an ablation that keeps only the trio refusal — 17 failed / 9 passed — i.e. it showed what the smaller fix would have shipped instead of asserting that it was insufficient.⏹️ Residue: two
noted, not filed, both carried by the #18063 ruling (theengineCanRollBackmethod-presence gate, whose consequence this PR makes loud rather than silent; andRemoteTransport's now-unreachable trio, which stays pinned and is ⛔ not dead code). ⇒ ⛔ No new card is owed by this landing.⚠️ For the record: this card's own body asked whether it belonged as a sub-issue of #18063. Triage answered that it stands free and needs no new ruling, and the landing bears that out — the stop-the-silence half shipped today without touching the semantics question, which is still #18063's.
Generated by Claude Code
- added 2 commits that reference this issue
on Sep 28, 2026
Filed by the
domain:engineexecution seat,session_01CqmCgU5RGDoJYhHUMVp2af, R1, out of the #18116 measurement tranche (dev report5711799466, which flagged it 「to file」). ⛔ Nodomain:*and nopriority:*asserted — both have exactly one producer, the triage seat.⭐ Every reading below was re-taken by the filing seat on
origin/mainbefore filing, ⛔ not carried over from the report.The defect
packages/spec/src/data/driver.zod.ts:636states the contract verbatim:In
TursoDriverremote mode nothing can receive that handle. A write issued betweenbeginTransaction()androllback()is already durable, and the rollback does not undo it.Readings, with a firing control
RemoteTransportnaming a transactionpackages/drivers/driver-turso/src/remote-transport.tsbeginTransaction(),commit(transaction),rollback(transaction)optionsor a transactionthis.isRemoteguards inturso-driver.tsgit grep -cThe report adds, and the seat did not independently re-derive: the guards forward without
options(e.g.find:970,create:1106), andconnect():860skips knex initialisation on the remote arm — which is why noSqlDriverbody that would honour a handle ever runs.Why this is class (b), and arguably (a)
(b) — violates a declared contract, with the contract text quoted above. The declaration is not ambiguous: it names
options.transactionas the delivery mechanism, and the remote transport has no parameter to deliver it to.(a) is also available: the failure is reproducible and user-reachable — begin, write, roll back, observe the row.⚠️ The filing seat has not run that reproduction (no Turso remote endpoint in this container), so (a) is offered as a claim to be measured, ⛔ not as a measured fact. The (b) limb stands on the quoted text alone.
⭐ The asymmetry that makes this worth a card rather than a note:
turso-driver.test.ts:211-214asserts exactly this shape for LOCAL mode. Nothing asserts it for remote. So the tree already knows what correct looks like here.Carried verbatim from the measuring dev, and the filing seat agrees it is a real question it should ⛔ not decide:
#18063 was returned to the decision box in the same round (
5711873237) precisely because the measurement showed the declaration half buys a correct type over a transaction that collects no statements — i.e. this defect is what makes that one hard to price. If the maintainer's answer to #18063 is 「let a transport declare it has no transactions」, this card may be discharged by that ruling rather than fixed separately.Dedupe words
Turso remote transaction ignored·options.transaction dropped remote·RemoteTransport no transaction handle·remote rollback does not undo·decorative transaction libsqlRe-check
Generated by Claude Code