Skip to content

fix(driver-turso)!: match the url scheme in any case, and refuse a url the local engine cannot open instead of running it on :memory: - #19996

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-19976-turso-unrecognised-scheme-local
Sep 24, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-19976-turso-unrecognised-scheme-local

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #19976

Clause-②: no (narrowing)

TursoDriver no longer runs a url it cannot open on a private :memory: engine. The classifier now reads the url scheme in any letter case, the way @libsql/client routes it, so an uppercase LIBSQL:// is remote. Whatever is still unrecognised (a bare path, an unsupported scheme) is refused at construction in a local or replica mode, as VALIDATION_ERROR / 400, naming the file: spelling. Both options in triage's execution note were weighed. This PR case-folds the scheme like the client and refuses what is left. Neither half routes any configuration to a :memory: engine it did not name.

Why fold the case instead of refusing uppercase too

  • @libsql/core@0.17.4 lib-esm/config.js line 26: const originalUriScheme = uri.scheme.toLowerCase();. Executed: expandConfig({ url: 'LIBSQL://r.turso.io' }, true).scheme === 'https', and createClient opens it (protocol=http). FILE:./x.db expands to file and opens.
  • The host url sniffers already read it case-insensitively and hand this driver the url as written: /^libsql:\/\//i in inferDriverTypeFromUrl (packages/cli/src/utils/storage-driver.ts) and in detectDriverFromUrl (packages/runtime/src/standalone-stack.ts). So an uppercase OS_DATABASE_URL selected this driver and then ran on :memory:.
  • The driver's own ridesWebSocketTransport already folded case, with an in-code note that folding detectMode "must be argued on its own". That argument is above: after this PR, every reader of one url (host sniffer, client, both driver predicates) reads the scheme one way. The note is replaced with a single helper, startsWithScheme, that every predicate in the file compares through.
  • Folding widens nothing. An uppercase remote url constructed before, but it ran on the wrong engine. Refusing the uppercase spelling would have refused a url the client, the CLI and the runtime all accept.

Why refuse a bare path instead of treating it as file:

H3, measured against the installed @libsql/client@0.17.4: createClient refuses ./data/app.db, data/app.db and /abs/app.db as URL_INVALID ("The URL '…' is not in a valid format"), and C:\data\app.db, sqlite:./x.db and memory://x as URL_SCHEME_NOT_SUPPORTED. Also URL_INVALID: :MEMORY:, '', ' file:./y.db' (leading space) and libsql:host (no //). Control: file:./x.db opens with protocol=file. Reading a bare path as file: would invent a spelling the client refuses, so the same string would open a local file and fail as a replica.

The refused set, exactly (for #19977 to mirror at authoring time)

Mode is the forced mode, or else auto-detected from url (and syncUrl). Schemes compare case-insensitively. "A remote url" means one starting with libsql://, https://, http://, wss:// or ws://.

  1. unrecognised-url (new): mode is 'local' or 'replica' (forced, or auto-detected with or without syncUrl), and url is none of: exactly :memory:; a url starting file:; a remote url.
  2. remote-url (unchanged, now case-insensitive): mode is 'local' or 'replica' and url is a remote url. That means a forced local or replica mode, or no mode with syncUrl set.
  3. in-memory-replica (renamed from replica-without-file, now covering only in-memory urls; see "The one widened cell" below): mode is 'replica' and url is exactly :memory:, or a file: url whose remainder is :memory: or starts with :memory:?.

Checked in the order 2, 1, 3. The two remote-mode timeout refusals are unchanged: timeout over 0 with a wss:///ws:// url, and timeout over 0 with a supplied client. Case-folding now brings an uppercase WebSocket url with no mode into the first. Not refused: a forced mode: 'remote' with any url. It runs no local engine, and @libsql/client refuses a bare path there itself at connect() (URL_INVALID, pinned).

TursoDriver.detectMode now also answers 'replica' for an unrecognised url beside syncUrl (it answered 'local'), like its other two arms. The refusal stays in the constructor. toKnexConfig's last arm, which handed such a url :memory:, now calls the same refusal. It cannot be reached from the constructor, and it can no longer produce an in-memory engine nobody named.

CONTROL 1 of turso-driver-uppercase-ws-scheme-timeout-refusal.test.ts: flipped, deliberately

It pinned that WSS:// / Ws:// / HTTPS:// / LIBSQL:// with no mode construct as 'local', and that WSS:// + timeout with no mode stays local and unrefused. That was the fall-through this card removes: a local engine on a private :memory: database. The control now pins the opposite: those urls are 'remote', and WSS:// + timeout with no mode meets the WebSocket refusal with the same message as wss://, except for the echoed scheme. The file's docblock records the flip and its reason. Its reverse-verification paragraph was re-measured (below) and rewritten to match.

Measurements

Before/after probe: initObjects, create, find, then a fresh driver on the same config. BASE = a7581b326's turso-driver.ts, loaded next to HEAD's in one vitest run over the same dependency closure. TMP and DIR stand for temp and relative directories.

configuration BASE a7581b326 HEAD
LIBSQL://…, no mode local, 1 row, 0 after restart remote (constructed)
./DIR/app.db, no mode local, 1 row, 0 after restart, file never created refused VALIDATION_ERROR / 400
TMP/bare.db (absolute), no mode local, 1 row, 0 after restart, file never created refused VALIDATION_ERROR / 400
./DIR/app.db + mode: 'local' local, 1 row, 0 after restart, file never created refused VALIDATION_ERROR / 400
FILE:TMP/upper.db, no mode local, 1 row, 0 after restart, file never created local, 1 row, 1 after restart, file created
CONTROL file:TMP/ctl.db local, 1 row, 1 after restart local, 1 row, 1 after restart

H1 confirmed by reading a7581b326 (the // Fallback: treat as local in detectMode, the :memory: last arm of toKnexConfig) and by the rows above. H2 and H3 are covered above. H4: the guard extended is localEngineDefect / refuseNonDurableLocalEngine. The "or drop mode: 'replica' … for a plain local database" sentence is now emitted only for an in-memory url: a bare path takes the new refusal. It reads "for a plain in-memory local database, which is what the url names: ephemeral by declaration", which is true because dropping the replica on :memory: / file::memory: gives exactly that. The new refusal's own ways out were checked per arm: a file: url for a local file or replica, and "drop" every key (mode, syncUrl) that would still keep a remote url local.

H5 reach, on this tree (a7581b326 + this diff), git grep:

  • uppercase or mixed-case libsql:// spellings, excluding this package's tests: 3 hits, all prose (the driver-turso: a remote url plus syncUrl is classified replica and handed a :memory: Knex connection, so every write lands in process memory and never reaches the remote #19893 changeset, CHANGELOG.md, a driver comment). Positive control, lowercase libsql://: 487 hits.
  • uppercase HTTP(S)/WS(S)/FILE scheme literals, excluding this package's tests and changelogs: 5 hits, none a turso config (a CLI File: label, driver comments, a spec redirect-url test). Positive control, the same pattern lowercase: 3,652 hits.
  • url literals in every file that mentions turso or libsql, excluding this package's tests: 269 listed. None is a turso config with a bare path or unknown scheme. The leftovers are other drivers, route paths and a ' ' empty-url refusal fixture. Positive control: 46 file: values in the same listing.
  • OS_DATABASE_DRIVER=turso / driver: 'turso': no bare path anywhere. Neither host sniffer selects this driver for a bare path. The runtime maps it to sqlite, and both select turso only for libsql:// or an http(s):// url with a .turso. host.

Out-of-repo deployments: NOT MEASURED, not claimed zero.

Tests

HEAD e5e29cb11:

  • pnpm --filter @objectstack/driver-turso test: Test Files 62 passed (62), Tests 1404 passed (1404).
  • pnpm --filter @objectstack/driver-turso typecheck (tsc --noEmit): clean. --listFiles includes both touched test files.
  • New file turso-driver-unrecognised-url-refusal.test.ts (40 cases) pins:
    • refusals as the envelope (code + status + the identifying first sentence), before and after, for the uppercase url and the bare path, with and without syncUrl, under a forced mode: 'local' and mode: 'replica', and through createTursoDriver;
    • that the url is never echoed;
    • that the client stub is untouched;
    • uppercase FILE: durability across a restart, local and replica;
    • the forced-remote scope, where the client answers URL_INVALID at connect;
    • preservation: file: local and replica, :memory: local, lowercase remote, mode: 'remote'.

Reverse verification. The direction was predicted in the new file's header before the run. It ran at 457d65f23, where src/ is byte-identical to e5e29cb11 except comments; git diff 457d65f23 e5e29cb11 -- packages/drivers/driver-turso/src is comments only. turso-driver.ts was restored to the a7581b326 blob (07363cdb…, on-disk hash verified, startsWithScheme count 8 then 0) under an absolute-path trap, over the three suites: Tests 35 failed | 52 passed (87).

  • New file: 30 RED (every refusal and uppercase case) and 10 GREEN (scope and 9 preservation).
  • ws file: 5 RED (the flipped CONTROL 1) and 15 GREEN.
  • turso-driver-remote-url-replica-refusal.test.ts: all GREEN.
  • Restore: blob 02da2f8f equals HEAD's, and git diff HEAD is empty.

Ablation. Using node scripts/ablation-replace.mjs, ridesWebSocketTransport was reverted to url.startsWith('wss://') || url.startsWith('ws://'). The anchor fell from 1 hit to 0 and the blob went 02da2f8f to 5d086ff9. Result: Tests 7 failed | 13 passed (20). RED: the 6 refusal cases and CONTROL 1's timeout case. GREEN: CONTROL 1's classification cases and controls 2 and 3, which is what the rewritten docblock says. Restored, with the blob equal to HEAD's and git diff HEAD empty.

Gates (HEAD e5e29cb11)

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack returns the same 61 commands at bc2e4f5f8 and e5e29cb11. All 61 were run at e5e29cb11. --ran: ✓ dispatch-gates --ran: 61 derived famil(ies) accounted for — 59 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3).
  • NOT MEASURED: check:dual-build-cjs-loads and check:type-check-debt. Reason: PREREQUISITE NOT MET, exit 3. Each needs a whole-workspace dist/ build (turbo run build --filter='./packages/*' …, as in lint.yml), which was not run locally. Targeted substitute: the built dist/index.js (CJS) and dist/index.mjs (ESM) of this package load, and each refuses a bare path as VALIDATION_ERROR 400. driver-turso has no DEBT / TEST_DEBT entry, and its tsc --noEmit is clean.
  • check:lean-entry-closure first answered exit 3 (objectql not built). It is green after turbo run build --filter=@objectstack/objectql.
  • node scripts/check-changeset-no-major.mjs --base origin/main: ✓ This diff introduces no \major` bump.The clause-② axis reads this PR's body in CI:LEVEL AXIS: NOT APPLICABLElocally, because a local run has nopull_request` payload.
  • node scripts/check-adr-0087-registration.mjs --base origin/main: ✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. Classified as [BREAKING+bang+clause-②-narrowing] not-required (no-migration-prescription), the same shape as the precedent changeset from PR fix(driver-turso)!: refuse a remote url beside syncUrl, and any replica not on a local file, instead of running on :memory: #19971.
  • pnpm check:driver-conformance: OK — 50 covered cell(s), 0 in the DEBT ledger, 0 exempt. Same reading on base a7581b326 before the change and on e5e29cb11 after.
  • node scripts/check-issue-citations.mjs --base origin/main: ✅ check-issue-citations: every citation this change adds resolves (or is a declared cross-repo reference).
  • Lint, a declared narrowing to the three changed .ts files: eslint --no-inline-config --format json counts 3 files, 0 errors and 0 warnings at e5e29cb11. Each file is in eslint's own config (--print-config exit 0; none reported as ignored). Invariance: eslint.config.mjs enables no type-aware linting (no parserOptions.project, no typed rules; its own note at line 327), so this diff cannot move the verdict on an untouched file. pnpm lint over the repo is CI's.
  • Stale-tree caveat: dispatch-gates reports this tree 10 commits behind origin/main (67ebc84a7). The 17 family-definition files changed there are sdui/objectui manifest scripts, lint.yml, cut-rc.yml, package.json and similar. None of them touches packages/drivers/driver-turso, and no merge was taken. CI runs the current definitions on the merge ref.

Acceptance notes

The one widened cell (seat correction of this body, after contract review 5818338847)

The earlier text of this body called arm 3 "same coverage". That was wrong. Under a forced mode: 'replica', a url that is none of file:, :memory: or a remote url now meets arm 1 (unrecognised-url) instead. An uppercase or mixed-case FILE: url naming a file, under a forced mode: 'replica', with or without syncUrl, is no longer refused: it is a file: url, the replica runs on that file, and its rows survive a restart. The #19893 change refused it, because it read the scheme case-sensitively. It is the only configuration refused at base a7581b326 and accepted at this head. It is stated in .changeset/19976-turso-unrecognised-url-refusal.md ("Newly accepted") and pinned in the PRESERVATION table, 4 cases that go red on the base turso-driver.ts. Measured at 4a7cb4de: with turso-driver.ts restored to the base blob 07363cdb, -t PRESERVATION gives Tests 4 failed | 9 passed | 31 skipped (44). The 4 red cases are exactly the widened ones, each refused VALIDATION_ERROR / 400 by the base replica-without-file message. At head they are 4 of 4 green.

Deliberate correction of a pending changeset

This PR changes .changeset/19893-turso-remote-url-replica-refusal.md. That note is pending: it landed with PR #19971 and no release has consumed it, and it compiles into the same version's CHANGELOG as this PR's note (lockstep fixed group). This change made three of its sentences false for the shipped code, so they are rewritten here, from the code at this head. No other sentence of that note moves (git diff -U0: lines 20, 25, 31 only). Check Changeset is therefore red by design (the DELIBERATE CORRECTION class of scripts/check-empty-changeset.mjs). skip-changeset is not applied, and the note is not restored. The confirmation is the same-head at-tier contract review record that names this note and judges each rewritten sentence (ruling 1A, #19940, 5814546887).


Generated by Claude Code

…recognised url in a local or replica mode

WIP: source change only; tests follow.

Claude-Session: https://claude.ai/code/session_01Bvd69VPa6puiNzzPUroDBx
Co-authored-by: Claude <noreply@anthropic.com>
…ed-url refusal; flip CONTROL 1

Claude-Session: https://claude.ai/code/session_01Bvd69VPa6puiNzzPUroDBx
Co-authored-by: Claude <noreply@anthropic.com>
…nd the unrecognised-url refusal

Claude-Session: https://claude.ai/code/session_01Bvd69VPa6puiNzzPUroDBx
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/driver-turso, touching 12 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/drivers/driver-turso/README.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/data-modeling/drivers.mdx (via TursoDriver (symbol, a top-level class))
  • content/docs/plugins/packages.mdx (via TursoDriver (symbol, a top-level class))

⛔ 1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-4.mdx (via TursoDriverConfig (symbol, a top-level interface))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/drivers/driver-turso/README.md) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 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; 100 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 — 6 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 e8f163fc3a62cc6c65f91d2197f7b516e3a2c90b → packageMentionDocs.

Which tree this was computed on

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

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

⚠️ 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 e8f163fc3a62cc6c65f91d2197f7b516e3a2c90b → 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

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e5e29cb11970a0ea11d3795ae74ec7293d48b0f9

① Derived judgments

The reviewer's own probe built 63 configurations against the head and base (a7581b326) turso-driver.ts side by side, ran a durability round trip on each, and asked @libsql/client@0.17.4 how it reads each url.

  1. detectMode now routes an uppercase or mixed-case remote scheme remote. At base it routed local on :memory:. RIGHT: hasRemotePrefix goes through startsWithScheme, and @libsql/core lowercases the scheme the same way.
  2. FILE: / File: is now a local file, and a replica beside syncUrl, on the named file. At base it ran on :memory:, with the row lost on restart. RIGHT.
  3. An unrecognised url beside syncUrl now answers replica and is refused. RIGHT.
  4. startsWithScheme folds only the compared prefix, and the url reaches the client as authored. RIGHT.
  5. New localEngineDefect arm 'unrecognised-url'. RIGHT. 18 cells narrowed from :memory: at base to VALIDATION_ERROR / 400 at head. They include bare paths, C:\…, sqlite:, memory://, :MEMORY:, libsql:host, the empty or blank url, and bare path + mode: 'local' / syncUrl. The client itself refuses every url in the changeset's list (URL_INVALID / URL_SCHEME_NOT_SUPPORTED).
  6. No legitimate configuration is wrongly refused: file: in any case, :memory:, lowercase or uppercase remote alone or with mode: 'remote', a file: replica. RIGHT.
  7. No configuration reaches a Knex :memory: engine it did not name: 0 of 63 cells. RIGHT.
  8. LIBSQL:// routes remote everywhere the lowercase spelling does: ridesWebSocketTransport, createRemoteClient, both host sniffers and the factories. RIGHT.
  9. toKnexConfig's last arm refuses with the same envelope. RIGHT.
  10. The envelope is VALIDATION_ERROR / 400, and the url, host and token are never echoed. RIGHT.
  11. The published surface is unchanged. RIGHT.
  12. PR body: "in-memory-replica (renamed from replica-without-file, same coverage)". WRONG. At base, localEngineDefect refused every forced-replica url not starting with a lowercase file:. At head, FILE:PATH under mode: 'replica' constructs, with or without syncUrl, and is durable. These are two WIDENED cells, the only configurations refused at base and accepted at head. Neither the body, the changeset nor the README names them. The seat verified this against both localEngineDefect bodies.
  13. The flipped CONTROL 1 is argued in the PR body and asserts the new behaviour (code + status + message). RIGHT.

② Semver level

Consistent: Clause-②: no (narrowing), BREAKING, minor, and ADR-0087 not-required (no-migration-prescription). Uppercase remote urls routing remote corrects an accepted-but-misrouted input; it is not a widening. The one real widening, FILE: + forced replica, is covered by minor. The closed arm pair cannot declare both directions, so the level and the arm stand; the prose must name it (③). At the reviewer's read, CI had 27 success, 3 skipped and 2 in_progress, with 0 failed.

③ Boundary flags

Implemented-by: claude/issue-19976-turso-unrecognised-scheme-local
Reviewed-by: session_01Bvd69VPa6puiNzzPUroDBx

VERDICT: FAIL

Edits that make it PASS:

  1. Correct the pending 19893 note in this PR (S1, S2, S3 above), as a DELIBERATE CORRECTION: Check Changeset goes red by design. The re-review record then names the corrected note and judges each rewritten sentence (ruling 1A).
  2. In the new 19976 changeset, add a "Newly accepted" statement for FILE: + forced mode: 'replica', and rewrite F2.
  3. In the PR body (seat-owned), correct "same coverage" and add a "Deliberate correction of a pending changeset" section naming the note.
  4. Add the widened cell to the preservation pins.

Isolated reviewer: a separate contract-review-tier subagent, fed the card, the PR, the sibling PR #19971 and AGENTS.md only; the seat verified item ① 12 against both localEngineDefect bodies and the three note lines before adopting.


Generated by Claude Code

…e the FILE: forced-replica widening, pin it

The pending remote-url refusal changeset described a case-sensitive
classifier and a fall-through that this branch removes; its three affected
sentences are rewritten against the shipped code (a deliberate correction
of a pending release note). The new changeset names the one configuration
this branch accepts that the earlier change refused, an uppercase FILE: url
under a forced replica mode, and the preservation table pins it with a
durability round trip.

Claude-Session: https://claude.ai/code/session_01Bvd69VPa6puiNzzPUroDBx
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 4a7cb4de9440da179117f7c2eef2002c9ed13ccc

① Derived judgments

  1. turso-driver.ts is byte-identical to the previously reviewed head e5e29cb1 (blob 91426eb5). Every code measurement of record 5818338847 stands for this head: the 63-cell base-versus-head matrix, the Knex filename scan, the envelope, the no-echo check, durability, and the client readings. RIGHT.
  2. The widened cell, FILE:PATH under a forced mode: 'replica', with or without syncUrl, is now declared in the 19976 changeset ("Newly accepted"). It is pinned by 4 cases that go red on the base blob 07363cdb (Tests 4 failed | 9 passed | 31 skipped) and green at head, and it is the only class refused at base and accepted at head. RIGHT.
  3. The PR body's "The one widened cell", "Deliberate correction of a pending changeset" and first "Acceptance notes" bullet are true against the head. No other sentence contradicts the corrected state. RIGHT.
  4. Check-runs at this head, all completed: every required context is success. The skips are Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in), and duplicate event-run Auto Label / Check PR Size, each with a success run. The only failures are Check Changeset, annotated on .changeset/19893-turso-remote-url-replica-refusal.md in the DELIBERATE CORRECTION class. RIGHT.

② Semver level

Consistent: Clause-②: no (narrowing), BREAKING, minor, and one ADR-0087 marker not-required (no-migration-prescription) in the 19976 note. The corrected 19893 note keeps its own frontmatter, level, banner and marker (line 42) untouched. minor also covers the one declared widening.

③ Boundary flags

The corrected note: .changeset/19893-turso-remote-url-replica-refusal.md (pending, same lockstep version). Only lines 20, 25 and 31 moved; every other line is byte-identical to origin/main. Each rewritten sentence was judged against the code at this head:

Line 20

  • (a) "BREAKING accept-set narrowing on a published driver option, shipped as minor …": unchanged wording. TRUE.
  • (b) "The constructor now refuses configurations it accepted before, at new TursoDriver(), ahead of the Knex base and of any client, with … VALIDATION_ERROR … 400": TRUE. Every refusal precedes super() and any client, and code and status were measured on every arm.
  • (c) "A remote url here means one of the schemes TursoDriver.detectMode classifies as remote: libsql://, https://, http://, wss://, ws://.": TRUE. REMOTE_URL_PREFIXES is exactly those five, read through hasRemotePrefix.
  • (d) "The driver-turso: a url whose scheme the classifier does not recognise (an uppercase LIBSQL://, a bare path) and no mode falls through to local on a :memory: Knex engine, so every write is lost on restart #19976 entry in this same version matches them in any letter case, so an uppercase LIBSQL:// is a remote url too.": TRUE. Comparison goes through startsWithScheme; the probe gives LIBSQL:// → mode=remote; both notes are in one lockstep fixed group.

Line 25

Line 31

The unchanged sentences of the note were re-judged against head and are TRUE: title, refused bullets, message, what stays accepted, ways out, and marker. No sentence of the corrected note is false or unmeasured against the shipped code. Per ruling 1A (#19940, 5814546887), this record names the corrected note and judges each rewritten sentence. With the PASS below, it is the confirmation of the DELIBERATE CORRECTION red.

.changeset/19976-turso-unrecognised-url-refusal.md and the README hunk: every sentence is TRUE, including "Newly accepted" and the rewritten cross-reference to the 19893 entry.

Optional, not a FAIL item: the PR-body sentence "Folding widens nothing." is true within its heading's scope (the remote schemes). Folding file: is what widened the forced-replica cell, and "The one widened cell" states that.

Implemented-by: claude/issue-19976-turso-unrecognised-scheme-local
Reviewed-by: session_01Bvd69VPa6puiNzzPUroDBx

VERDICT: PASS

Isolated reviewer: the same contract-review-tier subagent, third read on this head, fed only the card, the PR, the sibling PR #19971 and AGENTS.md; adopted by the seat.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Pending release-note correction on this PR: Check Changeset is red by design and confirmed

domain:engine#1, session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-24T17:18Z.


Generated by Claude Code

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/l tests tooling

Projects

None yet

1 participant