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
Description
Activity
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsBlocked-by: #19971
分诊首次定级:
priority:p1·bug·domain:engine·pm:blocked—— turso 驱动把识别不了的 url(大写LIBSQL://、不带file:的路径)当成本地内存库,重启后所有写入丢失Path:
packages/drivers/driver-turso/src/turso-driver.ts(detectMode的兜底:1004,toKnexConfig的:memory:分支:1021/:1029)Triage: lands in the turso driver's mode classifier ⇒
domain:engine,bug,priority:p1,pm:blockedBlocked-by #19971 (open draft, same file and same constructor guard); rationale: a plausible spelling of a local database — a bare path, which theTursoConfig.urldescribe itself names as "a file path" — constructs a working driver whose every write lives in process memory and is gone on restart, with nothing logged: silent loss of all data, the durability class AGENTS.md treats as most severe.分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ,座位贴 #6015),2026-09-24T14:21Z。⛔ 不认领、不派发。本席读完了卡面(本卡尚无评论),在main(2c1011b01b)上核对了代码,并读了 PR #19971 的正文。本席核对
turso-driver.ts:1004仍是// Fallback: treat as local,toKnexConfig的最后分支(:1021/:1029)给本地引擎filename: ':memory:',与卡面一致。- PR fix(driver-turso)!: refuse a remote url beside syncUrl, and any replica not on a local file, instead of running on :memory: #19971 的正文把这一分支列为"刻意不改":
LIBSQL://和裸路径仍会落到:memory:,并由turso-driver-uppercase-ws-scheme-timeout-refusal.test.ts(CONTROL 1)钉着"能构造"。PR 的实测表里,这两行都是"写入 1 行、重启后 0 行"。
定级说明
- p1:不是少见的边角输入。裸路径是描述文本里直接写的一种合法写法,写进去后读写都正常,只在重启时全部丢失,事前没有任何信号。
@libsql/core会先把 scheme 转成小写,所以同一个 url,客户端当远端,驱动当本地,两边判断不一致。
为什么挂
pm:blocked同一文件、同一个构造函数校验,卡面也写明"在 PR #19971 之后落"。⇒ Blocked-by #19971,合并后立即可派。#19894(已排队)也在同一文件,注意先后。
执行要点
- 二选一:构造时拒绝(报错写出
file:的正确写法),或者按客户端同样的小写 scheme 分类。⛔ 任何配置都不能落到它没要求的:memory:上。 - 有意翻转或重新论证 CONTROL 1 那条钉子,在 PR 里写明理由。
- 如果选择拒绝,这是收窄已接受的写法 ⇒ 按规则判断 Clause-② 并登记 ADR-0087。
Generated by Claude Code
- addedbugSomething isn't workingSomething isn't workingpriority:p1High: required for production / M2High: required for production / M2and removed
on Sep 24, 2026 objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsUnlocked:
Blocked-by: #19971is satisfieddomain:engine#1,session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-24T15:27Z. Thread read through 5815954616 (triage).- PR fix(driver-turso)!: refuse a remote url beside syncUrl, and any replica not on a local file, instead of running on :memory: #19971 (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) merged through the queue as squash
0142415760, verified onorigin/main(landing record 5817017589 on 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). - The blocker line is re-derived, not waved through. This card's landing site is unchanged by that landing:
detectMode's finalreturn 'local'andtoKnexConfig's:memory:last arm are still onmainata7581b326. There is no new blocker. - driver-turso remote planMediaColumnMove answers empty, so os migrate files-to-references reports nothing to move on a remote Turso database whose media columns are present #19894 (queued, same file) is serial, not a blocker. This card (p1) goes first in the
turso-driver.tsregion.
Claimed and dispatched in the same act (claim below).
Generated by Claude Code
- PR fix(driver-turso)!: refuse a remote url beside syncUrl, and any replica not on a local file, instead of running on :memory: #19971 (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) merged through the queue as squash
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsClaim: PM loop round 22
Session:session_01Bvd69VPa6puiNzzPUroDBx
Branch:claude/issue-19976-turso-unrecognised-scheme-local
Worktree:objectstack-issue-19976
Domain:domain:engine
Seat:domain:engine#1
File surface:packages/drivers/driver-turso/src/turso-driver.ts(detectMode's fallback, the construction guardlocalEngineDefect/refuseNonDurableLocalEngine,toKnexConfig's last arm), driver-turso tests (including a deliberate flip or re-argument of CONTROL 1 inturso-driver-uppercase-ws-scheme-timeout-refusal.test.ts),packages/drivers/driver-turso/README.md,.changeset/19976-*.md(stop on breach; explain in the report). ⛔ Notpackages/specand not eitherturso.zod.ts: the authoring half is #19977 (spec lane).
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable)
Clause-②: no
Thread-read: 5817085958
Serial constraints cleared: at 2026-09-24T15:28Z, the census of all 32 open PRs finds none touchingpackages/drivers/driver-turso/src/, and nopm:dispatchedclaim declares it. The last landing on the file is PR #19971 (0142415760, today), which added the construction guard this card extends. #19894 (queued, same file) runs after this card: fold-or-serial answered SERIAL, because gate ① fails (a classifier fall-through here, an empty remoteplanMediaColumnMovethere). #19977 (spec lane, blocked on the same landing) mirrors whatever refused set this card ships.
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 19976, "status": "done", "branch": "claude/issue-19976-turso-unrecognised-scheme-local", "pr": "https://github.com/objectstack-ai/objectstack/pull/19996", "session": "session_01Bvd69VPa6puiNzzPUroDBx (mode:subagent; the parent's harness-stamped id, as in claim 5817098533)", "premise_still_valid": true, "summary": "Premise holds on origin/main a7581b326. Measured with BASE's turso-driver.ts: LIBSQL:// (no mode), FILE:TMP/x.db, a relative or absolute bare path, and a bare path under mode 'local' each ran as local, 1 row back, 0 after restart, file never created. The file: control kept its row. Of triage's two routes this takes both halves: every url predicate in turso-driver.ts now compares the scheme case-insensitively through one helper, startsWithScheme, because @libsql/core 0.17.4 routes on uri.scheme.toLowerCase() and both host sniffers already select turso on /^libsql:\\/\\//i. So LIBSQL:// is remote and FILE: is a durable file. Whatever is still unrecognised is refused at construction (VALIDATION_ERROR/400, url never echoed, file: spelling named) in any local or replica mode. That is the new localEngineDefect arm 'unrecognised-url'. toKnexConfig's :memory: last arm now calls the same refusal. A bare path is refused, not read as file:, because the client itself refuses it as URL_INVALID (H3). CONTROL 1 was flipped deliberately, and the PR body names the exact refused set for #19977. Assignee: os-sales, set by the PM dispatch, untouched.", "tests": "At HEAD e5e29cb11: `pnpm --filter @objectstack/driver-turso test` gives Test Files 62 passed (62), Tests 1404 passed (1404). `pnpm --filter @objectstack/driver-turso typecheck` (tsc --noEmit) is clean, and --listFiles counts both touched test files. The new turso-driver-unrecognised-url-refusal.test.ts has 40 cases: refusal pins assert code+status+first sentence for the uppercase url and the bare path, with and without syncUrl, under forced mode 'local' and 'replica', and via createTursoDriver; plus no url echo, an untouched client stub, uppercase FILE: durability local and replica, the forced-remote scope (URL_INVALID at connect), and preservation of file: local/replica, :memory:, lowercase remote and mode 'remote'. REVERSE VERIFICATION at 457d65f23 (src differs from e5e29cb11 in comments only): turso-driver.ts was restored to the a7581b326 blob 07363cdb (hash verified on disk; startsWithScheme count 8 then 0) under an absolute-path trap. Result over 3 suites: Tests 35 failed | 52 passed (87). New file: 30 RED (every refusal and uppercase case), 10 GREEN (scope + 9 preservation). ws file: 5 RED (flipped CONTROL 1), 15 GREEN. #19971's refusal file: all GREEN, which is the predicted direction. Restore: blob 02da2f8f == HEAD, git diff HEAD empty, status clean. ABLATION via node scripts/ablation-replace.mjs: ridesWebSocketTransport reverted to the literal-prefix form (anchor x1 to x0, blob 02da2f8f to 5d086ff9) gives Tests 7 failed | 13 passed (20). RED: 6 uppercase refusals + CONTROL 1's WSS+timeout case. GREEN: CONTROL 1 classification + controls 2/3, as the rewritten docblock predicts. Restored: blob == HEAD, git diff HEAD empty. Relative imports resolve src, so no dist preflight applies. Built dist smoke: dist/index.js (CJS) and dist/index.mjs (ESM) both load and refuse './data/app.db' as VALIDATION_ERROR 400.", "gates": { "head": "e5e29cb11", "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack gave 61 commands, the same list at bc2e4f5f8 and e5e29cb11. STALE TREE warning: 10 commits behind origin/main 67ebc84a7. The 17 changed family-definition files (sdui/objectui scripts, lint.yml, cut-rc.yml, package.json, engine-double-contract.pinned.json, ...) touch nothing under packages/drivers/driver-turso. No merge taken.", "ran_reconciliation": "✓ dispatch-gates --ran: 61 derived famil(ies) accounted for — 59 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3).", "not_measured": [ "NOT MEASURED: pnpm check:dual-build-cjs-loads, reason: PREREQUISITE NOT MET (exit 3). Needs every package dist (78 absent); whole-workspace build not run locally. Targeted substitute: driver-turso CJS and ESM dist load.", "NOT MEASURED: pnpm check:type-check-debt, reason: PREREQUISITE NOT MET (exit 3). Needs the packages closure build. driver-turso has no DEBT/TEST_DEBT entry and its tsc --noEmit is clean." ], "explicit": [ "node scripts/check-issue-citations.mjs --base origin/main :: exit 0 — ✅ check-issue-citations: every citation this change adds resolves (or is a declared cross-repo reference).", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 — ✓ This diff introduces no `major` bump. (LEVEL AXIS: NOT APPLICABLE locally, no pull_request payload; CI reads the PR body line Clause-②: no (narrowing))", "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 — ✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. [BREAKING+bang+clause-②-narrowing] not-required (no-migration-prescription)", "pnpm check:driver-conformance, before (base a7581b326) and after (e5e29cb11) :: exit 0 both — OK — 50 covered cell(s), 0 in the DEBT ledger, 0 exempt.", "pnpm check:lean-entry-closure :: first exit 3 (objectql not built), exit 0 after turbo run build --filter=@objectstack/objectql — ✓ check-lean-entry-closure: 2 published condition(s) measured from a real load.", "eslint, declared narrowing: eslint --no-inline-config --format json on the 3 changed .ts files gives 3 files, 0 errors, 0 warnings at e5e29cb11. Population: each file is under eslint's own config (--print-config exit 0, none ignored). Invariance: eslint.config.mjs enables no type-aware linting (its note at line 327), so untouched files' verdicts cannot move. pnpm lint is CI's." ], "union_at_head": [ "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0", "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0", "node scripts/check-changeset-no-major.mjs --self-test :: exit 0", "node scripts/check-ci-filter-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0", "node scripts/check-comment-mask-adoption.mjs :: exit 0", "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0", "node scripts/check-comment-mask-corpus.mjs :: exit 0", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0", "node scripts/check-empty-changeset.mjs --self-test :: exit 0", "node scripts/check-keyed-text-bounds.mjs :: exit 0", "node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0", "node scripts/check-platform-object-tenancy-census.mjs :: exit 0", "node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0", "node scripts/check-plugin-teardown-shape.mjs :: exit 0", "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0", "node scripts/check-registry-log-declared.mjs :: exit 0", "node scripts/check-registry-log-declared.mjs --self-test :: exit 0", "node scripts/check-rest-log-spy-declared.mjs :: exit 0", "node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0", "node scripts/check-system-context-census.mjs :: exit 0", "node scripts/check-system-context-census.mjs --self-test :: exit 0", "node scripts/check-undeclared-dep-imports.mjs :: exit 0", "node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0", "node scripts/docs-audit/check-affected-docs.mjs :: exit 0", "node scripts/docs-audit/check-drift-comment.mjs :: exit 0", "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0", "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0", "pnpm check:changeset-gate-self-tests :: exit 0", "pnpm check:cross-package-test-inputs :: exit 0", "pnpm check:doc-authoring :: exit 0", "pnpm check:driver-conformance :: exit 0", "pnpm check:driver-memory-census :: exit 0", "pnpm check:dts-closure :: exit 0", "pnpm check:dual-build-cjs-loads :: exit 3", "pnpm check:engine-double-contract :: exit 0", "pnpm check:gitlink-declared :: exit 0", "pnpm check:issue-citations :: exit 0", "pnpm check:lean-entry-closure :: exit 0", "pnpm check:logger-receiver-detach :: exit 0", "pnpm check:nul-bytes :: exit 0", "pnpm check:object-def-param-keys :: exit 0", "pnpm check:objectql-double-limit :: exit 0", "pnpm check:objectui-changeset :: exit 0", "pnpm check:org-identifier :: exit 0", "pnpm check:page-declaration-shape :: exit 0", "pnpm check:pm-changeset-deadline-census :: exit 0", "pnpm check:published-files :: exit 0", "pnpm check:refd-timer-probe :: exit 0", "pnpm check:sourcemap-no-sources-content :: exit 0", "pnpm check:tenant-chokepoint :: exit 0", "pnpm check:test-source-alias :: exit 0", "pnpm check:tier-file-adoption :: exit 0", "pnpm check:type-check-coverage :: exit 0", "pnpm check:type-check-debt :: exit 3", "pnpm check:watch-hint-literal :: exit 0", "pnpm check:where-matcher :: exit 0", "pnpm check:query-options-erasure :: exit 0", "pnpm check:slot-lookup :: exit 0" ], "ci": "PR #19996 at e5e29cb11 on first read: 30 check runs, 9 success, 3 skipped, 18 in_progress, 0 failed. in_progress; not waited on.", "labels": "Zero label writes. The dispatch named no label, and skip-changeset is not due because @objectstack/driver-turso publishes and this PR carries a changeset." }, "line_budget": "n/a — no skills/** in the diff", "mcp_calls": "0", "api_writes": "2 — both through the fleet-write relay (the session sends POST /repos/objectstack-ai/objectstack/dispatches; the runner executes as objectstack-fleet[bot]): (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls with draft forced, which opened PR #19996 (run 36027536193, conclusion success; body read back byte-identical, 14059 bytes); (2) comment, i.e. POST /repos/objectstack-ai/objectstack/issues/19976/comments, this os-dev-report. label-write: 0. git push (5 pushes of the branch) is not a REST write. Reads: unauthenticated REST GETs of the card, its comments, comment 5815469979, PR #19996 and its check-runs.", "open_questions": [], "out_of_scope_findings": [ "class: c · evidence: packages/spec/src/data/driver/turso.zod.ts, TursoConfigSchema.url .describe('libSQL endpoint or local file: a remote libsql/https Turso URL, a file path, or :memory:'). \"a file path\" invites an author (or an AI) to write a bare path, and after this PR the runtime refuses that at construction (VALIDATION_ERROR/400, pinned in turso-driver-unrecognised-url-refusal.test.ts). Producer: a datasource config authored through TursoConfigSchema and stored in sys_metadata. Seam: spec:TursoConfigSchema.url (.describe 'a file path') → runtime:TursoDriver constructor localEngineDefect('unrecognised-url'). Routing: inside the scope of queued #19977 (the spec lane mirrors this refused set at authoring time), so name it there, not a new card · dedupe words: turso url describe file path · TursoConfigSchema url bare path · turso spec url refused set", "carrier: the release-notes compile (maintainer) · noted, not filed: the unreleased .changeset/19893-turso-remote-url-replica-refusal.md calls the uppercase / bare-path fall-through 'Not refused, unchanged here' and defines a remote url as 'one of the lowercase schemes'. Both are scoped to that change and this PR's changeset states the fall-through is gone; outside this card's claimed file surface, so not edited.", "承接者:无 · noted, not filed (Acceptance notes only): a file: url with a query string (file:./x.db?mode=ro) is still handed to better-sqlite3 as a literal filename. Pre-existing and durable, NOT MEASURED, no finding claimed." ], "deviations": [ "origin/main was not merged: the gate derivation reads a tree 10 commits behind (declared above). CI runs current definitions on the merge ref.", "Two derived families are NOT MEASURED locally (exit 3, whole-workspace build prerequisite). Declared, with targeted substitutes.", "Lint is a declared narrowing to the 3 changed .ts files, with the three proofs.", "The BASE measurement ran a temporary copy of a7581b326's turso-driver.ts inside src/ (zz-base-probe*.tmp.ts). Deleted before any commit, never staged; tree verified clean.", "Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By; the PR footer uses AGENTS.md's session-URL form, not the harness's default text. AGENTS.md takes precedence, and the pre-push hook refuses a model identifier.", "Reverse verification and ablation ran at 457d65f23. HEAD e5e29cb11 differs from it under src/ only in comments (checked: no non-comment diff lines)." ], "files_changed": [ ".changeset/19976-turso-unrecognised-url-refusal.md", "packages/drivers/driver-turso/README.md", "packages/drivers/driver-turso/src/turso-driver.ts", "packages/drivers/driver-turso/src/turso-driver-unrecognised-url-refusal.test.ts", "packages/drivers/driver-turso/src/turso-driver-uppercase-ws-scheme-timeout-refusal.test.ts" ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 19976, "status": "done", "addendum": "Patch round 1 on PR #19996: answers contract review 5818338847 (FAIL at e5e29cb11). Supersedes the head, tests and gates fields of report 5818087714; every other field of that report stands.", "branch": "claude/issue-19976-turso-unrecognised-scheme-local", "pr": "https://github.com/objectstack-ai/objectstack/pull/19996", "head": "4a7cb4de9440da179117f7c2eef2002c9ed13ccc", "session": "session_01Bvd69VPa6puiNzzPUroDBx (mode:subagent; the parent's harness-stamped id, as in claim 5817098533)", "premise_still_valid": true, "summary": "One commit on top of e5e29cb11, 4a7cb4de9, no force-push. It changes no logic: turso-driver.ts is byte-identical to e5e29cb11. (1) The pending .changeset/19893-turso-remote-url-replica-refusal.md is corrected as a DELIBERATE CORRECTION: only S3, S2 and S1 are rewritten, from the code at head, and no other sentence changed (the exact old/new text is in note_19893_rewrites). (2) .changeset/19976-turso-unrecognised-url-refusal.md gains a 'Newly accepted' paragraph for FILE: + forced mode 'replica', with or without syncUrl, and F2 is rewritten so it stays true after the correction. (3) The PRESERVATION table of turso-driver-unrecognised-url-refusal.test.ts pins the widened cell: 2 construct rows plus a 2-case durability round trip, with and without syncUrl. The file header states the cell and its reverse-verification direction. The widening is confirmed against a7581b326: the base constructor refuses FILE: + mode 'replica' with its replica-without-file message ('`TursoDriverConfig.url` is not a `file:` url, so it cannot hold an embedded replica'). The PR body was not edited; the seat owns it.", "note_19893_rewrites": { "S3_line_20_sentence": { "old": "A remote url here means one of the lowercase schemes `TursoDriver.detectMode` classifies as remote: `libsql://`, `https://`, `http://`, `wss://`, `ws://`.", "new": "A remote url here means one of the schemes `TursoDriver.detectMode` classifies as remote: `libsql://`, `https://`, `http://`, `wss://`, `ws://`. The #19976 entry in this same version matches them in any letter case, so an uppercase `LIBSQL://` is a remote url too." }, "S2_line_25_bullet": { "old": "- under a forced `mode: 'replica'` only, any `url` that is not a local `file:` path, such as a bare path or an uppercase scheme.", "new": "- under a forced `mode: 'replica'`, a `url` that is none of `:memory:`, a `file:` url or a remote url, such as a bare path or an unsupported scheme. The #19976 entry in this same version refuses such a url in every local or replica mode, and matches the `file:` scheme in any letter case: an uppercase `FILE:` url naming a file is a `file:` url and is not refused, and the replica runs on that file (`FILE::memory:` is refused as in-memory, like `file::memory:`).", "why_only_dropped": "'only' was dropped. With the #19976 entry in the same version, a bare path or unsupported scheme is refused in every local or replica mode, so 'under a forced replica only' would be false for the shipped code. An uppercase FILE::memory: is still refused (in-memory-replica), so the not-refused clause is scoped to a FILE: url naming a file." }, "S1_line_31_paragraph": { "old": "**Not refused, unchanged here:** a url with no `mode` that is none of `file:`, `:memory:` or a lowercase remote scheme, such as an uppercase `LIBSQL://` or a bare path like `./data/app.db`, still auto-detects `'local'` and still runs on `:memory:`, with or without `syncUrl`. So does the same url under a forced `mode: 'local'`. That fall-through is tracked as #19976.", "new": "**Not refused by this change:** a url with no `mode` that is none of a lowercase `file:` url, `:memory:` or a lowercase remote scheme, such as an uppercase `LIBSQL://`, an uppercase `FILE:` url or a bare path like `./data/app.db`, auto-detected `'local'` with or without `syncUrl`, and the local engine was handed `:memory:`. Under a forced `mode: 'local'` the same url got the same `:memory:` engine. The #19976 entry in this same version removes that fall-through: it matches every scheme in any letter case, so an uppercase remote url is a remote url and an uppercase `FILE:` url is a `file:` url, and it refuses every other url in a local or replica mode. No configuration runs on an in-memory database it did not name." }, "untouched": "No other sentence of the 19893 note changed: git diff -U0 shows exactly 3 changed lines (20, 25, 31), and within line 20 only the sentence above." }, "changeset_19976_edits": { "newly_accepted_added": "**Newly accepted:** an uppercase or mixed-case `FILE:` url naming a file, under a forced `mode: 'replica'`, with or without `syncUrl`. The #19893 change refused it, because under a forced `mode: 'replica'` it refused every url that did not start with a lowercase `file:`. With this change it is a `file:` url: the replica runs on that file, and its rows survive a restart (pinned). It is the one configuration the #19893 change refused that this change accepts.", "F2_old": "This closes the fall-through that the changelog entry for #19893 (the constructor refusal of a remote url in a local or replica mode) lists as \"not refused, unchanged here\".", "F2_new": "The #19893 entry in this same version (the constructor refusal of a remote url in a local or replica mode) describes this fall-through as not refused by that change, and names this entry as the one that removes it." }, "pr_body_same_coverage_suggestion": "In place of 'in-memory-replica (renamed from `replica-without-file`, same coverage)', suggested: 'in-memory-replica (renamed from `replica-without-file`, now covering only in-memory urls). A forced-replica url that is none of `file:`, `:memory:` or remote now meets unrecognised-url instead, and an uppercase `FILE:` url naming a file under a forced replica is no longer refused: that is the one widened cell.'", "tests": "At head 4a7cb4de9: `pnpm --filter @objectstack/driver-turso test` gives Test Files 62 passed (62), Tests 1408 passed (1408): 1404 plus the 4 widened-cell cases. `pnpm --filter @objectstack/driver-turso typecheck` (tsc --noEmit) is clean. REVERSE VERIFICATION of the new pins: turso-driver.ts restored to the a7581b326 blob 07363cdb (hash verified; startsWithScheme count 8 then 0) under the absolute-path trap, running the new file with -t PRESERVATION, gives Tests 4 failed | 9 passed | 31 skipped (44). RED: exactly the 4 WIDENED cases, all refused as VALIDATION_ERROR/400 with the base replica-without-file message. GREEN: the 9 other preservation rows. That is the direction the file header predicts. Restore: blob 91426eb5 == HEAD, git diff HEAD empty, status clean. Targeted eslint on the changed test file: 1 file, 0 errors, 0 warnings. Control-byte grep over the 3 changed files: none.", "gates": { "head": "4a7cb4de9", "requested": [ "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 — Diffing HEAD from a7581b326 (merge base with origin/main). ✓ This diff introduces no `major` bump. (LEVEL AXIS: NOT APPLICABLE locally, no pull_request payload)", "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 — ✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. .changeset/19976-turso-unrecognised-url-refusal.md [BREAKING+bang+clause-②-narrowing] not-required (no-migration-prescription)", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 1 — RED BY DESIGN, DELIBERATE CORRECTION class. Quoted: 'This PR changes a changeset it did not add:' / '.changeset/19893-turso-remote-url-replica-refusal.md' / 'present on the merge base and CHANGED by this PR -- this is somebody else's release note' / 'DELIBERATE CORRECTION -- your change may have made this PENDING release note false, and you rewrote it in the same stroke. Remedy: do NOT restore it -- say so on the PR and get it confirmed; restoring it from the base would put the false sentence back.' Also: '✓ No empty-frontmatter changeset introduced by this diff (2 declaring changeset(s) added).' Not restored; skip-changeset not applied.", "node scripts/check-issue-citations.mjs --base origin/main :: exit 2, NOT a finding of this diff. The gate takes a declared --base VERBATIM as the fork point (resolveDiffBase: 'A declared base is taken VERBATIM'). origin/main had moved to e8f163fc3, 20+ commits past the fork a7581b326, so it diffed that tip against this tree. All 3 findings are #16608 in packages/plugins/plugin-security/src/security-plugin.ts (:207, :1815, :3054), a file this branch never touches (git diff a7581b326 HEAD on it is empty) and which PR #19988 changed on main. Re-run against the true fork: node scripts/check-issue-citations.mjs --base a7581b326 (the merge-base) :: exit 0 — ✅ check-issue-citations: no issue citations added against a7581b326 (1 file(s) read).", "pnpm check:doc-authoring :: exit 0 — ✓ doc authoring guard: 403 files clean — no bare metadata literals. (and the published-skill, spec-string and sibling-prose-id lines, all ✓)", "pnpm check:driver-conformance :: exit 0 — check-driver-conformance: OK — 50 covered cell(s), 0 in the DEBT ledger, 0 exempt." ], "union_rerun": "dispatch-gates --commands at 4a7cb4de9 gives the same 61 commands as at e5e29cb11. All 61 re-run at 4a7cb4de9. --ran: '✓ dispatch-gates --ran: 61 derived famil(ies) accounted for — 59 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3).' Non-zero: check-empty-changeset exit 1 (by design, above); check:dual-build-cjs-loads and check:type-check-debt exit 3 (NOT MEASURED, whole-workspace build prerequisite, unchanged from the first report). check:lean-entry-closure read exit 0 after an objectql rebuild (turbo cache, 14/14 cached) in the recreated worktree.", "union_at_head": [ "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0", "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0", "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0", "node scripts/check-changeset-no-major.mjs --self-test :: exit 0", "node scripts/check-ci-filter-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs :: exit 0", "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0", "node scripts/check-comment-mask-adoption.mjs :: exit 0", "node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0", "node scripts/check-comment-mask-corpus.mjs :: exit 0", "node scripts/check-empty-changeset.mjs --base origin/main :: exit 1", "node scripts/check-empty-changeset.mjs --self-test :: exit 0", "node scripts/check-keyed-text-bounds.mjs :: exit 0", "node scripts/check-keyed-text-bounds.mjs --self-test :: exit 0", "node scripts/check-platform-object-tenancy-census.mjs :: exit 0", "node scripts/check-platform-object-tenancy-census.mjs --self-test :: exit 0", "node scripts/check-plugin-teardown-shape.mjs :: exit 0", "node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0", "node scripts/check-registry-log-declared.mjs :: exit 0", "node scripts/check-registry-log-declared.mjs --self-test :: exit 0", "node scripts/check-rest-log-spy-declared.mjs :: exit 0", "node scripts/check-rest-log-spy-declared.mjs --self-test :: exit 0", "node scripts/check-system-context-census.mjs :: exit 0", "node scripts/check-system-context-census.mjs --self-test :: exit 0", "node scripts/check-undeclared-dep-imports.mjs :: exit 0", "node scripts/check-undeclared-dep-imports.mjs --self-test :: exit 0", "node scripts/docs-audit/check-affected-docs.mjs :: exit 0", "node scripts/docs-audit/check-drift-comment.mjs :: exit 0", "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0", "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0", "pnpm check:changeset-gate-self-tests :: exit 0", "pnpm check:cross-package-test-inputs :: exit 0", "pnpm check:doc-authoring :: exit 0", "pnpm check:driver-conformance :: exit 0", "pnpm check:driver-memory-census :: exit 0", "pnpm check:dts-closure :: exit 0", "pnpm check:dual-build-cjs-loads :: exit 3", "pnpm check:engine-double-contract :: exit 0", "pnpm check:gitlink-declared :: exit 0", "pnpm check:issue-citations :: exit 0", "pnpm check:lean-entry-closure :: exit 0", "pnpm check:logger-receiver-detach :: exit 0", "pnpm check:nul-bytes :: exit 0", "pnpm check:object-def-param-keys :: exit 0", "pnpm check:objectql-double-limit :: exit 0", "pnpm check:objectui-changeset :: exit 0", "pnpm check:org-identifier :: exit 0", "pnpm check:page-declaration-shape :: exit 0", "pnpm check:pm-changeset-deadline-census :: exit 0", "pnpm check:published-files :: exit 0", "pnpm check:refd-timer-probe :: exit 0", "pnpm check:sourcemap-no-sources-content :: exit 0", "pnpm check:tenant-chokepoint :: exit 0", "pnpm check:test-source-alias :: exit 0", "pnpm check:tier-file-adoption :: exit 0", "pnpm check:type-check-coverage :: exit 0", "pnpm check:type-check-debt :: exit 3", "pnpm check:watch-hint-literal :: exit 0", "pnpm check:where-matcher :: exit 0", "pnpm check:query-options-erasure :: exit 0", "pnpm check:slot-lookup :: exit 0" ], "ci": "At 4a7cb4de9 on one read: 34 check runs, 29 success, 3 skipped, 1 in_progress, 1 failure = Check Changeset (the DELIBERATE CORRECTION red, by design). Not waited on.", "labels": "Zero label writes. skip-changeset is deliberately NOT applied." }, "line_budget": "n/a — no skills/** in the diff", "mcp_calls": "0", "api_writes": "1 this round — this os-dev-report addendum: comment, i.e. POST /repos/objectstack-ai/objectstack/issues/19976/comments through the fleet-write relay (session POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]). PR body untouched (seat-owned). git push: 1 (fast-forward e5e29cb11..4a7cb4de9, not a REST write). Reads: unauthenticated REST GETs of review 5818338847 and the head check-runs.", "open_questions": [], "out_of_scope_findings": [ "Finding 2 of report 5818087714 is withdrawn, as the review ruled. The 19893 note is corrected in this PR instead.", "Finding 1 (class c, spec TursoConfigSchema.url describe 'a file path') stands. The seat routed it to #19977 (5818125643).", "Finding 3 (承接者:无, a file: url with a query string) stands unchanged: Acceptance notes only." ], "deviations": [ "check-issue-citations --base origin/main as ordered gives a false red: the gate takes the declared base verbatim and origin/main is past the fork. The merge-base run is quoted beside it. origin/main was not merged (optional per the order; no touched path moved).", "The S2 rewrite drops 'only' from the original bullet, which the order's S2 wording kept, because 'only' is false for the shipped code (see note_19893_rewrites.S2_line_25_bullet.why_only_dropped).", "The worktree was recreated on the existing branch (git worktree add ../objectstack-issue-19976 claude/issue-19976-turso-unrecognised-scheme-local at e5e29cb11, matching origin). It is removed again after this report." ], "files_changed_this_round": [ ".changeset/19893-turso-remote-url-replica-refusal.md", ".changeset/19976-turso-unrecognised-url-refusal.md", "packages/drivers/driver-turso/src/turso-driver-unrecognised-url-refusal.test.ts" ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsACCEPT: PR #19996 at
4a7cb4dedomain:engine#1,session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-24T17:21Z. Reviewed againstreferences/review-checklist.mdon GitHub, not from the dev's report (os-dev-report5818087714 plus its patch-round addendum 5818627230 on this card).- Shape: the first line is
Fixes #19976, and the body declaresClause-②: no (narrowing). That matches the 19976 changeset: BREAKING,minor, one ADR-0087 marker.Fixes #19976is the only closing keyword. 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, driver-turso remote planMediaColumnMove answers empty, so os migrate files-to-references reports nothing to move on a remote Turso database whose media columns are present #19894 and spec:TursoConfigSchemaaccepts turso configs the driver refuses or ignores — a remoteurlbesidesyncUrlor a forced replica/localmode, a non-file:replica,syncUrl/syncundermode: remote#19977 are cited without one. - Scope: 6 files:
packages/drivers/driver-turso(source, tests, README), the 19976 changeset, and the corrected 19893 changeset. That is inside the claimed surface, andpackages/specis untouched.check-governed-merges --pr 19996says not governed; 690 changed lines. - Contract review of record:
- FAIL at
e5e29cb1(5818338847) on three things: the undeclared widened cell, the false pending 19893 note, and the PR body. - After one prose-only patch round, PASS at
4a7cb4de(5818788549).turso-driver.tsis byte-identical between those two heads.
- FAIL at
- Widened cell accepted:
FILE:<path>under a forcedmode: 'replica'was refused at base and is accepted at head. It is declared under "Newly accepted" in the 19976 note and pinned by 4 cases that fail on the base blob. It is the only class that moves from refused to accepted. - DELIBERATE CORRECTION under ruling 1A (Why three engine-lane landings needed the maintainer this round (a pending release-note correction, a first-time queue-flake signature, a subagent's denied label write): can each become seat-decidable? #19940, 5814546887):
.changeset/19893-turso-remote-url-replica-refusal.mdlines 20, 25 and 31 are rewritten. The PASS record names that note and judges sentences (a)–(l) TRUE against this head. The three red-by-design conditions are on the PR (5818797266). - CI at this head: every required context is
success.- The only failures are the three
Check Changesetruns, annotated on the 19893 note in the DELIBERATE CORRECTION class. - The skips are
Build Docs,Console Pin GateandPacked-tarball smoke (opt-in), plus the duplicate event-runAuto Label/Check PR Size, each with a success run. All are on the roster.
- The only failures are the three
- Out-of-scope findings:
- class c (
TursoConfigSchema.urldescribe says "a file path"): routed to the spec lane's spec:TursoConfigSchemaaccepts turso configs the driver refuses or ignores — a remoteurlbesidesyncUrlor a forced replica/localmode, a non-file:replica,syncUrl/syncundermode: remote#19977 (5818125643), with no new card; - the dev's finding 2 is withdrawn, because the 19893 note is corrected in this PR;
- Acceptance notes only: a
file:url with a query string (file:./x.db?mode=ro) still reaches better-sqlite3 as a literal filename. This predates the PR, was not measured, and no finding is claimed.
- class c (
Landing:
ready+ auto-merge through the queue. #19894 (serial, same file) unlocks when this lands.- Shape: the first line is
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsLanded: PR #19996, verified on
maindomain:engine#1,session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-24T17:54Z.- Verified on main:
- PR 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 through the merge queue as
61609edf81, which has one parent and is an ancestor oforigin/mainafter a fresh fetch. - The new
unrecognised-urlrefusal has 5 hits inturso-driver.tsat that commit and 0 at its parent. .changeset/19976-turso-unrecognised-url-refusal.mdis onmain, and so is the corrected.changeset/19893-turso-remote-url-replica-refusal.md(the DELIBERATE CORRECTION under ruling 1A).
- PR 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 through the merge queue as
- This card: closed
completedviaFixes #19976.pm:dispatchedwas removed in the same act. - No lane card closed by mistake: 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 was already closed by PR fix(driver-turso)!: refuse a remote url beside syncUrl, and any replica not on a local file, instead of running on :memory: #19971. driver-turso remote planMediaColumnMove answers empty, so os migrate files-to-references reports nothing to move on a remote Turso database whose media columns are present #19894 and spec:
TursoConfigSchemaaccepts turso configs the driver refuses or ignores — a remoteurlbesidesyncUrlor a forced replica/localmode, a non-file:replica,syncUrl/syncundermode: remote#19977 are cited without a closing keyword, and both are still open. - Serial queue:
packages/drivers/driver-turso/src/turso-driver.tsis released, and driver-turso remote planMediaColumnMove answers empty, so os migrate files-to-references reports nothing to move on a remote Turso database whose media columns are present #19894 is next in that region. - Spec lane: spec:
TursoConfigSchemaaccepts turso configs the driver refuses or ignores — a remoteurlbesidesyncUrlor a forced replica/localmode, a non-file:replica,syncUrl/syncundermode: remote#19977 mirrors the refused set this PR shipped. The PR body names that set exactly: every url in a local or replica mode that is none of:memory:, afile:url or a remote url, with schemes matched in any letter case, andFILE::memory:refused as in-memory. The spec:TursoConfigSchemaaccepts turso configs the driver refuses or ignores — a remoteurlbesidesyncUrlor a forced replica/localmode, a non-file:replica,syncUrl/syncundermode: remote#19977 authoring half is the spec lane's.
Generated by Claude Code
- Verified on main:
- added a commit that references this issue
on Sep 28, 2026
Filing gate: ① a defect with a named landing site:
TursoDriver.detectMode's finalreturn 'local'fallback, together withtoKnexConfig's last arm (connection: { filename: ':memory:' }), inpackages/drivers/driver-turso/src/turso-driver.ts. Finding class (a).Filed by the
domain:engineexecution seat 1 (session_01Bvd69VPa6puiNzzPUroDBx) from the out-of-scope findings of its #19893 dev (report comment 5815469979 on #19893; PR #19971 body, "Measurements, BASE2c1011b01b"). ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.What happens
detectModerecognises only lowercaselibsql://,https://,http://,wss://andws://as remote, and onlyfile:or:memory:as local. Anything else with nomodehits the final// Fallback: treat as localand returns'local'.:memory:norfile:,toKnexConfig's last arm hands the local engine better-sqlite3 on:memory:.url: 'LIBSQL://r.turso.io'(uppercase) orurl: './data/app.db'(a bare path, nofile:) constructs a working driver whose every write lives in process memory.Measured (the #19893 dev, BASE
2c1011b01b; the seat did not re-run it)initObjects,create,find, then a fresh driver on the same config (a restart):LIBSQL://, no mode:memory::memory:file:localPR #19971 (#19893) deliberately does not change this arm. The in-code note beside
ridesWebSocketTransportsays folding case intodetectMode"would delete its uppercase →'local'fall-through … it must be argued on its own".turso-driver-uppercase-ws-scheme-timeout-refusal.test.ts(CONTROL 1) pins the uppercase url as constructing.Reach
@libsql/core@0.17.4lowercases the scheme before routing (lib-esm/config.js:uri.scheme.toLowerCase()), so the client would treatLIBSQL://as remote while the driver treats it as local.TursoConfig.urldescribe names "a file path".Suggested shape (⛔ not a ruling)
Refuse at construction, or classify by the same case-folded scheme the client uses, so no configuration reaches a
:memory:engine it did not ask for. Flip or re-argue the uppercase CONTROL pin deliberately. Land after PR #19971 (same file, same constructor guard).Filing-gate answers
packages/drivers/driver-tursoafter triage routes it. Same file as 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 (in flight) and driver-turso remote planMediaColumnMove answers empty, so os migrate files-to-references reports nothing to move on a remote Turso database whose media columns are present #19894 (queued): serial.closedincluded:turso uppercase scheme or bare path url falls back to local memory data loss detectMode→ 2 hits: 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 (this finding's parent, a different arm), turso: the bound secret is never read, so post-#8078 a new turso datasource cannot be authenticated by any supported route #8152 (closed, unrelated).turso uppercase LIBSQL scheme ws scheme case-insensitive transport→ 3 hits: service-analytics: all three SQL compilers emit a plain LIKE for the case-sensitive $contains family, which folds ASCII case on SQLite — the read scope and the native where admit rows the #4706 contract excludes #15684, turso 迁回本仓后,CLI 的 URL→driver 解析仍对libsql://抛 UnsupportedDriverError —— runtime 的 provisioning 却把 turso 排在偏好第一位,两处口径相反 #5602, drivers(turso): RemoteTransport 条件层的$-算子键被当列名编译成静默空集 —— SqlDriver 已按 #5348 拒收,remote 是唯一剩余面(cloud#1077 移交) #5769 (all closed, unrelated). None is this defect.Dedupe words:
turso bare path url memory fallback·turso uppercase scheme local memory·detectMode fallback local memory data lossGenerated by Claude Code