Repository navigation
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
Description
Activity
objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actions定级
pm:queue·priority:p1·bug·domain:engine—— 远程 url 配syncUrl时,写入落在进程内存里,重启即丢Path: platform-core
Triage: lands in
packages/drivers/driver-turso/src/turso-driver.ts(detectMode:986returnsreplicafor a remoteurlbesidesyncUrl; the replica arm oftoKnexConfighands Knexbetter-sqlite3on:memory:,:1031) ⇒domain:engine(aTursoDriverConfigSchemarefinement half, if taken, isdomain:spec's); rationale: every write lands in a process-local in-memory database and is lost on restart while reads look normal — silent data loss on a configuration the spec schema, the factory and the driver's own comment all present as supported; p1 on data integrity (not p0: not reachable throughOS_DATABASE_URL, and whether any real deployment declares the pair is unmeasured).分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T16:22Z。本席读完了卡面(本卡尚无评论)。本席的读数(
origin/main029d8a4710)turso-driver.ts:957detectMode:远程协议的 url 走到:986if (config.syncUrl) return 'replica';。:983-985的注释称此时@libsql/client以「带内存本地缓存的嵌入式副本」运行。:1031的兜底分支给 Knex 的是connection: { filename: ':memory:' }。packages/spec/src/data/driver/turso.zod.ts:201起,syncUrl可以与任意url并列声明。
与卡面一致。
判定
- 业务后果:一个声明了「远程 url +
syncUrl」的数据源,写进去的数据只在当前进程的内存里。读回来一切正常,一重启全部消失,没有任何报错。 p1:北极星「优先级」第 1 条(数据完整性),而且是静默丢数据。⛔ 不是 p0 的理由:- 经
OS_DATABASE_URL走不到这条路(standalone-stack不传syncUrl); - 有没有真实部署或示例声明了这一对,没有测。
- ⇒ 升级判据:接手人第一步先量这件事。只要找到一个真实部署在用,立即升 p0 并在本卡写明。
- 经
- 修法方向(⛔ 不是裁定,卡面已写):构造时响亮拒绝「远程 url +
syncUrl」,或把它归为remote;同时给TursoDriverConfigSchema加上对应的限制,在拒绝提示里给出受支持的写法(url: 'file:…'+syncUrl)。 ⚠️ 串行:同一个文件上还有 driver-turso remote: cells the pre-#19844 boot door wrote unconverted (a date as a full timestamp, a scalar json unencoded) are converged by no remote backfill, so a stored true reads back as 1 #19868(p1,远程回填)和 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(p2)。认领前读一遍在飞 PR 的文件清单,有碰turso-driver.ts的就排在其后,⛔ 不并入。本卡是三张里后果最重的,排队时优先。
Generated by Claude Code
- addedbugSomething isn't workingSomething isn't workingpriority:p1High: required for production / M2High: required for production / M2and removed
on Sep 23, 2026 objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsClaim: PM loop round 22
Session:session_01Bvd69VPa6puiNzzPUroDBx
Branch:claude/issue-19893-turso-remote-syncurl-replica
Worktree:objectstack-issue-19893
Domain:domain:engine
Seat:domain:engine#1
File surface:packages/drivers/driver-turso/src/turso-driver.ts(detectMode/toKnexConfig/ construction), driver-turso tests,packages/drivers/driver-turso/README.md(mode table),.changeset/19893-*.md(stop on breach; explain in the report). ⛔ Notpackages/spec: aTursoDriverConfigSchemarefinement half goes back as a reportedSeam:finding for the 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: 5798521173
Serial constraints cleared: at 2026-09-24T12:52Z, the census of all 32 open PRs finds none touchingpackages/drivers/driver-turso/src/turso-driver.ts, and nopm:dispatchedclaim declares it. The last landing on the file is PR #19904 (467fa763a8, today). #19894 (queued, same file) runs after this card: fold-or-serial answered SERIAL, gate ① fails (a replica misclassification here, an empty remoteplanMediaColumnMovethere: different defect shape, different fix).
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 19893, "status": "done", "branch": "claude/issue-19893-turso-remote-syncurl-replica", "pr": "https://github.com/objectstack-ai/objectstack/pull/19971", "session": "session_01Bvd69VPa6puiNzzPUroDBx (mode:subagent; the parent's harness-stamped id, as in the claim)", "premise_still_valid": true, "summary": "The card's premise holds on origin/main 2c1011b01b, and #19904 did not change it. A remote url beside syncUrl was classified replica and handed Knex :memory:; a restart lost every write, measured. TursoDriver's constructor now refuses, before super() and beside the two existing timeout refusals, as VALIDATION_ERROR / 400. The refusal covers (1) a remote url in a local or replica mode, whether via syncUrl (the card), a forced mode 'replica' (H2b) or a forced mode 'local' (in-place fold, same last arm, same predicate), and (2) a replica whose url is not a local file: path (H2a: :memory:, file::memory:, or a bare path under mode 'replica'). The message names the scheme and both ways out, and never echoes the url. detectMode returns the same answers as before; the false embedded-replica comment is replaced with the measured reading; README and TursoDriverConfig docs are corrected. H4 (the p0 criterion): no real in-repo deployment, example, template, skill, doc or factory default declares a remote url beside syncUrl; out-of-repo deployments are NOT MEASURED (no cloud/objectui checkout, no deployment inventory in this container). Measured reach is narrower than the card states. With the driver's own client and default sync.onConnect, the pair already failed at connect() with SYNC_NOT_SUPPORTED. Silent loss needed sync.onConnect false, a supplied client, or a forced mode (mode 'replica'/'local' beside a remote url is silent with no syncUrl at all). The seat regrades. H1 confirmed. H3 confirmed: @libsql/client 0.17.4 node.js routes http/https and ws/wss to its HTTP / WebSocket clients, syncUrl is read only in sqlite3.js (grep 0 in http.js/ws.js vs authToken 6 each), and both remote clients' sync() throw SYNC_NOT_SUPPORTED. H2a: not a legitimate configuration; the client itself throws URL_INVALID 'Embedded replica must use file for local db'. With a supplied client it was silent. H5: neither factory parses a schema (both go buildTursoDriverConfig then new TursoDriver), so the constructor is the runtime's only gate. The spec-lane TursoConfigSchema is parsed via validateDriverConfig (DatasourceSchema datasource.zod.ts:484, DatasourceAdminService datasource-admin-service.ts:960, credential-migration known-check :241, redaction via getDriverConfigSchema). The driver-local copy is exported from @objectstack/driver-turso (index.ts:68) with zero in-repo parse callers outside its own test.", "tests": "All at HEAD 5fb95579 (final commit). pnpm --filter @objectstack/driver-turso typecheck: tsc --noEmit exit 0, program covers all 60 test files (--listFiles count; new pin + testkit listed). pnpm --filter @objectstack/driver-turso test: 'Test Files 60 passed (60)', 'Tests 1358 passed (1358)' (lock VERDICT command-exit 0, parts joined with &&). New pin turso-driver-remote-url-replica-refusal.test.ts has 27 cases: 19 refusal cases on code+status plus named subjects, 8 preservation cases (file: replica connect/create/find/restart keeps the row, file:/:memory: local, remote, mode 'remote', detectMode unchanged). Reverse verification, direction predicted first: node scripts/ablation-replace.mjs deleted the constructor's 'if (defect) refuseNonDurableLocalEngine(config, mode, defect);' (anchor x1 to x0, blob 07363cdb to 5ee02e4e, landed on disk). Result: 'Tests 20 failed | 105 passed': the new file had 27 tests with 19 failed, and turso-driver.test.ts 98 with 1 failed. Every red fired at 'expected the constructor to refuse, and it returned a driver'; all 8 preservation cases stayed green. Restore proven: blob after restore == HEAD 07363cdb, git diff HEAD empty. The subject resolves from src (relative imports), so no dist leg. BASE measurement: a throwaway probe at 2c1011b (deleted, never committed) drove create/find/restart per arm; the table is in the PR body. check:driver-conformance before: 'OK — 50 covered cell(s), 0 in the DEBT ledger, 0 exempt.'; after, at 5fb95579: identical. Lint narrowed and proven: eslint on the 14 changed paths lints 12 .ts and ignores 2 .md; --format json reports 12 files, 0 errors, 0 warnings. Invariance: eslint.config.mjs enables no type-aware linting (no parserOptions.project, no typed rules; its own header at :327), so untouched files' verdicts cannot move. Full pnpm lint is CI's. Importers (runtime, service-datasource, cli, qa/dogfood) not re-run: exported types unchanged, and a repo-wide grep finds no real-driver construction of a refused shape outside driver-turso. Dogfood builds only :memory: and plain libsql://; the loader fixtures carrying the pair use the builder or a capturing fake ctor. The integration tier does not apply (not packages/cli).", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 5fb95579 (change set 14 paths vs merge base 2c1011b01) derived 61 commands, PM list 55 plus 6 new: check:engine-double-contract, check:objectql-double-limit, check:query-options-erasure, check:type-check-coverage, check:type-check-debt, check:where-matcher. --ran reconcile: '61 derived, 59 run, 2 NOT-MEASURED, 0 UNRUN'; all 59 run exit 0. NOT MEASURED: pnpm check:dual-build-cjs-loads (exit 3, PREREQUISITE NOT MET: 78 packages lack dist; narrowed stand-in: the built driver-turso CJS and ESM entries both load TursoDriver) and pnpm check:type-check-debt (exit 3, needs the whole packages closure built; narrowed: driver-turso has no DEBT/TEST_DEBT/test-typecheck-debt entry and its tsc is clean). Both reran from prerequisite exit 3 to exit 0: node scripts/check-plugin-teardown-shape.mjs --self-test (after fetching its pinned commit 621a4876 at depth 1: '48 cases pass') and pnpm check:lean-entry-closure (after building the @objectstack/objectql closure). Roster gates flagged under touched dirs, all exit 0: check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity. Changeset verdicts: check-adr-0087-registration '1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.' with '[BREAKING+bang+clause-②-narrowing] not-required (no-migration-prescription)'. check-changeset-no-major 'This diff introduces no `major` bump.'; driven with the real PR body as --event: 'LEVEL AXIS: this PR declares clause-② `no`' / 'direction arm: none declared'. check-empty-changeset: '1 declaring changeset(s) added'.", "line_budget": "n/a — no skills/** in the diff", "mcp_calls": "5 — all reads: issue_read get (card), issue_read get_comments (thread), list_pull_requests (find #19971), pull_request_read get (PR body read-back), issue_read get_comments (this report's read-back). No MCP write tool.", "api_writes": "2 — both through scripts/pm/with-fleet.sh --via dispatch (fleet-write relay, as objectstack-fleet[bot]), each carried by one POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204): (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls, draft #19971; (2) comment, POST /repos/objectstack-ai/objectstack/issues/19893/comments (this report). label-write: 0, because the dispatch names no label and skip-changeset does not apply (the diff publishes). git push x3 (not REST).", "open_questions": [ { "question": "The claim's declaration is 'Clause-②: no' and I copied it verbatim into the PR body, per os-dev.md. This diff is an accept-set narrowing, so the changeset I authored carries 'Clause-②: no (narrowing)' (AGENTS.md step 3; the BREAKING banner and bang are also present). The two lines differ, so the level axis currently reads 'direction arm: none declared'. Should the PR body line (and the claim record) carry the arm?", "options": [ "A: the seat amends the PR body line to 'Clause-②: no (narrowing)'. Measured with that body as --event: 'LEVEL AXIS: this PR declares clause-② `no (narrowing)`, and no package ... is graded `patch`', exit 0.", "B: keep the PR body 'Clause-②: no' as is; the gate is exit 0 either way (verdict not-declared), and the changeset alone carries the narrowing." ], "recommendation": "A. The declaration then matches what the changeset and the ADR-0087 gate already read ([BREAKING+bang+clause-②-narrowing]); the change is one line and moves no gate to red. PR body edits after creation are the seat's, so I did not make it." } ], "out_of_scope_findings": [ "class: b · Seam: spec:TursoConfigSchema.syncUrl/.mode/.url (packages/spec/src/data/driver/turso.zod.ts, parsed via validateDriverConfig by DatasourceSchema datasource.zod.ts:484 and DatasourceAdminService datasource-admin-service.ts:960; the driver-local copy packages/drivers/driver-turso/src/spec/turso.zod.ts is exported from @objectstack/driver-turso with zero in-repo parse callers) → runtime:TursoDriver constructor localEngineDefect (turso-driver.ts), reached via buildTursoDriverConfig from packages/runtime/src/turso-driver-factory.ts:288 and packages/services/service-datasource/src/default-datasource-driver-factory.ts:1337 · contract: the spec's own TSDoc on syncUrl reads 'Remote sync endpoint that turns a local file into an embedded replica', yet the schema accepts syncUrl beside any url (its only refinement is sync-without-syncUrl) and mode 'replica'/'local' beside a remote url. After this PR the runtime refuses exactly those at construction, so authoring accepts a datasource that then fails at connect. Refinement to add (spec lane, both copies): refuse a remote url beside syncUrl or under mode local/replica, and a replica url that is not file: · dedupe words: turso syncUrl remote url schema refinement · TursoConfigSchema syncUrl file replica refine · turso config mode replica remote url authoring", "class: a · Seam: spec:TursoConfig.url (z.string().min(1), described as 'a remote libsql/https Turso URL, a file path, or :memory:') → runtime:TursoDriver.detectMode fallback 'local' + toKnexConfig last arm (:memory:) · evidence, measured at BASE 2c1011b and untouched by this PR: an uppercase 'LIBSQL://r.turso.io' or a bare path with no mode constructs local on Knex :memory:, with 1 row back and 0 after restart; the bare-path file is never created. Why not folded: the uppercase fall-through is pinned constructing by turso-driver-uppercase-ws-scheme-timeout-refusal.test.ts CONTROL 1, and ridesWebSocketTransport's in-code note says changing it must be argued on its own (bounded in-place fix condition 2 fails) · dedupe words: turso bare path url memory fallback · turso uppercase scheme local memory · detectMode fallback local memory data loss", "class: c · evidence, measured at 5fb95579 on the built dist: new TursoDriver({ url: 'libsql://r.turso.io', mode: 'remote', syncUrl, sync: { intervalSeconds: 60 } }) constructs; after connect() isSyncEnabled() answers true; sync() rejects SYNC_NOT_SUPPORTED; createRemoteClient forwards no syncUrl (grep 0) and the remote connect arm starts no interval. So a datasource's config.syncUrl / config.sync beside mode 'remote' (producer: stored datasource config from the Studio datasource form / DatasourceAdminService, forwarded by buildTursoDriverConfig) is silently inert. Seam: spec:TursoConfigSchema.syncUrl+.mode → runtime:TursoDriver remote arm (connect / isSyncEnabled) · dedupe words: turso syncUrl remote mode ignored · isSyncEnabled remote mode syncUrl · turso sync not supported http mode", "carrier: 承接者:无 · noted, not filed: loader fixtures outside driver-turso still pair url 'libsql://my-db.turso.io' with syncUrl and mode 'replica' (packages/cli/src/utils/storage-driver.test.ts 'produces a config the real driver accepts', packages/runtime/src/turso-driver-factory.convergence.test.ts, packages/services/service-datasource/src/__tests__/turso-driver-config.test.ts). They test key forwarding through the builder or a capturing ctor and stay green; the cli title now overstates what the real driver accepts. In PR Acceptance notes.", "carrier: 承接者:无 · noted, not filed (unmeasured observation): on a file: + syncUrl replica, Knex writes the file through better-sqlite3, not through the libsql connection, so whether those writes ever reach the remote primary is NOT MEASURED; that needs a live sqld. In PR Acceptance notes." ], "deviations": [ "Folded one sibling arm in place (a forced mode 'local' beside a remote url) under the bounded in-place fix rule: same defect class, same predicate, same file, same gates. It is named with evidence in the PR body. The claimed file surface already covered it; nothing was added to the surface.", "Added packages/drivers/driver-turso/src/replica-file.testkit.ts inside the declared 'driver-turso tests' surface. Eight replica-face fixtures re-spelled from :memory: + syncUrl to a fresh local file:, because the fix refuses that pair.", "The PR body's 'Clause-②: no' is the claim's line verbatim; the changeset carries 'Clause-②: no (narrowing)' (see open_questions).", "The first gate sweep ran the three --base gates with the pinned BASE sha. I reran them exactly as printed ('--base origin/main', which equals BASE 2c1011b) and recorded those runs for --ran.", "The 61-command sweep outran the foreground cap and the harness moved it to the background. I waited for it in the foreground (tail --pid on the runner) and did not end the turn.", "Fetched pinned commit 621a4876 at depth 1 into the shared object store, as check-plugin-teardown-shape's self-test prescribes. Built the @objectstack/objectql closure under the lock for check:lean-entry-closure." ], "files_changed": [ ".changeset/19893-turso-remote-url-replica-refusal.md", "packages/drivers/driver-turso/README.md", "packages/drivers/driver-turso/src/turso-driver.ts", "packages/drivers/driver-turso/src/replica-file.testkit.ts", "packages/drivers/driver-turso/src/turso-driver-remote-url-replica-refusal.test.ts", "packages/drivers/driver-turso/src/turso-driver.test.ts", "packages/drivers/driver-turso/src/turso-driver-ws-timeout-refusal.test.ts", "packages/drivers/driver-turso/src/turso-driver-supplied-client-timeout-refusal.test.ts", "packages/drivers/driver-turso/src/turso-driver-timeout.test.ts", "packages/drivers/driver-turso/src/turso-remote-autonumber-refusal.test.ts", "packages/drivers/driver-turso/src/turso-remote-deferred-ddl.test.ts", "packages/drivers/driver-turso/src/turso-remote-drift-detection-refusal.test.ts", "packages/drivers/driver-turso/src/turso-remote-transaction-refusal.test.ts", "packages/drivers/driver-turso/src/turso-transactions-unsupported-declaration.test.ts" ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 19893, "status": "done", "round": "patch round 1: addendum to os-dev-report 5815469979, answering contract review 5815787919 (FAIL, changeset prose)", "branch": "claude/issue-19893-turso-remote-syncurl-replica", "pr": "https://github.com/objectstack-ai/objectstack/pull/19971", "head": "7fc737470b73a61f1fb50d37315d24cf0c5f982c (fast-forward from 5fb95579; no force-push)", "session": "session_01Bvd69VPa6puiNzzPUroDBx (mode:subagent; the parent's harness-stamped id, as in the claim)", "premise_still_valid": true, "summary": "Prose-only patch: .changeset/19893-turso-remote-url-replica-refusal.md and packages/drivers/driver-turso/README.md, one commit, no code or test byte changed (git diff 5fb95579..7fc73747 touches 0 .ts files; turso-driver.ts blob 07363cdb at both heads). Every sentence was rewritten from the code at head: localEngineDefect fires 'remote-url' on the five lowercase prefixes in local or replica mode, and 'replica-without-file' only when the resolved mode is replica; detectMode's fallback returns 'local' whatever syncUrl says. Changeset, the seat's edits: (1) refusal bullet 3 now names a replica on a url @libsql/client reads as in-memory (:memory:, or file::memory: with or without a query string, beside syncUrl or under mode 'replica'), plus a new bullet 4: under a forced mode 'replica' only, any url that is not a local file: path; (2) a 'Not refused, unchanged here' paragraph: a url with no mode that is none of file:, :memory: or a lowercase remote scheme (uppercase LIBSQL://, a bare path) still auto-detects 'local' and runs on :memory:, with or without syncUrl, and so does the same url under a forced mode 'local'. It cites #19976 with no closing keyword; (3) the headline is narrowed to 'a local or replica TursoDriver on a remote url, or a replica off a local file, is refused at construction'. Step 5 re-read found and fixed six more false or imprecise sentences in the changeset: (a) 'a remote url' is now defined once as the lowercase schemes detectMode classifies as remote, because LIBSQL:// + syncUrl constructs; (b) 'the same fallback caught ... a replica on :memory:' was wrong, since :memory: takes toKnexConfig's own :memory: arm; (c) 'the message names the scheme' holds only for the remote-url refusal; (d) the ways-out used 'or' where both are needed (mode 'replica' + syncUrl stays refused when only one is dropped), and 'drop mode replica for a plain local database' is false for a bare path, which lands on the #19976 fall-through; (e) the blast-radius sentence 'only ... loader fixtures' omitted the stored-row redaction fixtures (spec and service-datasource) and the spec schema-parse fixture, none of which constructs the driver; (f) the headline's 'instead of silently running on a private in-memory database' is false for file::memory:?cache=shared. Measured: better-sqlite3 treats only the exact ':memory:' as in-memory and creates a file literally named ':memory:?cache=shared' in the working directory, so the headline tail is dropped and bullet 3 states it. README (4): the paragraph is now a three-item refused list; 'a replica' is qualified as auto-detected from a :memory: or file: url beside syncUrl, or forced with mode 'replica'; the remote schemes are marked lowercase; the ways out read 'drop syncUrl and any forced mode'; the syncUrl config comment 'url must be a local file:' becomes the descriptive 'the replica is the local file: named by url'.", "tests": "No code or test changed between 5fb95579 and 7fc73747, so round 0's readings stand for identical source bytes: driver-turso typecheck exit 0; 'Test Files 60 passed (60)', 'Tests 1358 passed (1358)'; ablation 'Tests 20 failed | 105 passed' with restore proven. Not re-run this round. New measurement this round (throwaway, scratchpad only): better-sqlite3 new Database(':memory:') leaves the cwd empty; new Database(':memory:?cache=shared') leaves a file ':memory:?cache=shared' in the cwd.", "gates": "At head 7fc73747 (origin/main 2c1011b01 = merge base), exactly as printed. node scripts/check-changeset-no-major.mjs --base origin/main: exit 0, '✓ This diff introduces no `major` bump.' Driven with --event carrying the seat's amended PR-body line: '✓ LEVEL AXIS: this PR declares clause-② `no (narrowing)`, and no package whose `packages/**/src/**` it moves is graded `patch`.' / 'direction arm: `narrowing` — a BREAKING change; during the launch window it ships `minor`'. 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.' with '.changeset/19893-turso-remote-url-replica-refusal.md [BREAKING+bang+clause-②-narrowing] not-required (no-migration-prescription)'. node scripts/check-empty-changeset.mjs --base origin/main: exit 0, '✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).' and '✓ No changeset from the merge base modified or deleted by this diff'. pnpm check:doc-authoring (named by dispatch-gates for these paths): exit 0, '✓ doc authoring guard: 403 files clean', '53 published skill files clean — no internal issue-id references', 'sibling-package prose ids hold the baseline ... no growth'. Union, re-run per os-dev.md on the new head: dispatch-gates --commands at 7fc73747 derived the same 61 commands as round 0 (sorted diff empty); --ran reconcile '61 derived, 59 run, 2 NOT-MEASURED, 0 UNRUN' (reconcile exit 0); all 59 run exit 0, including check:driver-conformance 'OK — 50 covered cell(s), 0 in the DEBT ledger, 0 exempt.' NOT MEASURED, the same two as round 0, both PREREQUISITE NOT MET because they need a whole-workspace build: pnpm check:dual-build-cjs-loads and pnpm check:type-check-debt. Before the union, the driver-turso and objectql closures were rebuilt under the lock (VERDICT command-exit 0).", "line_budget": "n/a — no skills/** in the diff", "mcp_calls": "3 this round, all reads: pull_request_read get_comments (review record 5815787919), pull_request_read get (current PR body, for the --event level reading), issue_read get_comments (read-back of this addendum). No MCP write tool.", "api_writes": "1 this round: comment, POST /repos/objectstack-ai/objectstack/issues/19893/comments (this addendum), through scripts/pm/with-fleet.sh --via dispatch (fleet-write relay, as objectstack-fleet[bot]; one POST /repos/objectstack-ai/objectstack/dispatches). git push x1 (fast-forward, not REST). PR body not edited.", "open_questions": [], "out_of_scope_findings": [ "carrier: #19976 · noted, not filed: the replica refusal's runtime message ends 'or drop `mode: 'replica'` ... for a plain local database'. For a bare path under a forced mode 'replica', dropping the mode lands on the #19976 fall-through, a Knex :memory: engine rather than a local database file. The message is code, frozen this round; the #19976 change should re-read this sentence.", "carrier: this PR (seat) · noted, not changed (code frozen this round): the constructor comment 'before the Knex base could open a private `:memory:` database in its place' does not hold for file::memory:?cache=shared under replica mode, where Knex would open a file literally named ':memory:?cache=shared' (measured above). The refusal itself is right for that spelling: libsql's in-memory rule, and no sync reaches the file. The turso-driver.ts header, syncUrl and mode TSDoc use 'remote url' in detectMode's lowercase sense, which the localEngineDefect docblock defines.", "carrier: this PR (seat) · noted, not edited (the PR body is the seat's): the PR body still reads 'Verification, HEAD `5fb95579`'. The code is byte-identical at 7fc73747, and the union was re-run there with the same result. The body's 'The message names the scheme it met and both ways out: drop `syncUrl` (or the forced `mode`)' carries the same two imprecisions this round fixed in the changeset (the replica-arm message names no scheme; 'or' fails for mode 'replica' + syncUrl). The amended body as read back ends with no attribution footer; that is state another actor set, and I did not touch it." ], "deviations": [ "README step 4 wording: the seat's text says 'auto-detected from `file:`'; I wrote 'auto-detected from a `:memory:` or `file:` url beside `syncUrl`'. detectMode returns replica for `:memory:` + syncUrl too, so 'from `file:`' alone would be false for the in-memory case that the list item names.", "Headline step 3: narrowed as asked, and the tail 'instead of silently running on a private in-memory database' was dropped because it is false for file::memory:?cache=shared (measured).", "Step 7: beyond the four named gates I re-ran the full 61-command union at 7fc73747, because os-dev.md requires a union re-run on any new head and wins on conflict. That meant rebuilding the driver-turso and objectql closures in the recreated worktree under the lock first.", "The level-axis --event reading used the round-0 body with its Clause-② line replaced by the seat's amended line 'Clause-②: no (narrowing)'. That line is the only one the gate reads, and it matches the current PR body read back via MCP." ], "files_changed": [ ".changeset/19893-turso-remote-url-replica-refusal.md", "packages/drivers/driver-turso/README.md" ] }
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsACCEPT: PR #19971 at
7fc73747domain:engine#1,session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-24T14:51Z. Reviewed againstreferences/review-checklist.md, on GitHub rather than the dev's report (os-dev-report5815469979 plus its patch-round addendum on this card).- Shape: first line
Fixes #19893. The body line was amended by the seat toClause-②: no (narrowing), matching the changeset (dev open question, option A). The only closing keyword names this card; 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 driver-turso: a url whose scheme the classifier does not recognise (an uppercaseLIBSQL://, a bare path) and nomodefalls through tolocalon a:memory:Knex engine, so every write is lost on restart #19976 are cited without one. - Scope: 14 files, all in
packages/drivers/driver-tursoplus one changeset, inside the claimed surface;packages/specis untouched. Not governed (check-governed-merges --pr 19971), 705 lines. - Contract review of record: FAIL at
5fb95579(5815787919) on changeset prose. After one prose-only patch round: PASS at7fc73747(5816466369), code byte-identical. - CI at this head: all 31 non-skip checks are
success. The skips areBuild Docs,Console Pin GateandPacked-tarball smoke (opt-in), all roster skips. - The p0 criterion triage set (「只要找到一个真实部署在用,立即升 p0」): no in-repo example, template, skill, doc or factory default declares a remote url beside
syncUrl; out-of-repo deployments are not measured. ⇒ It stays p1. - Deviation accepted: a forced
mode: 'local'beside a remote url is folded into the same guard as a bounded in-place fix (same predicate, file, arm and envelope; pinned). - Out-of-scope findings:
- filed driver-turso: a url whose scheme the classifier does not recognise (an uppercase
LIBSQL://, a bare path) and nomodefalls through tolocalon a:memory:Knex engine, so every write is lost on restart #19976: the bare-path / uppercase-scheme fall-through to:memory:; - filed spec:
TursoConfigSchemaaccepts turso configs the driver refuses or ignores — a remoteurlbesidesyncUrlor a forced replica/localmode, a non-file:replica,syncUrl/syncundermode: remote#19977: theTursoConfigSchemarefinement, aSeam:for the spec lane, with the inertsyncUrl/syncundermode: 'remote'folded in; - Acceptance notes: the cli loader-fixture title overstates what the real driver accepts;
- Acceptance notes: whether writes on a
file:+syncUrlreplica reach the primary is not measured; - noted on this card, not changed: the replica refusal's runtime message "drop
mode: 'replica'… for a plain local database" lands a bare path on the driver-turso: a url whose scheme the classifier does not recognise (an uppercaseLIBSQL://, a bare path) and nomodefalls through tolocalon a:memory:Knex engine, so every write is lost on restart #19976 fall-through. driver-turso: a url whose scheme the classifier does not recognise (an uppercaseLIBSQL://, a bare path) and nomodefalls through tolocalon a:memory:Knex engine, so every write is lost on restart #19976's fix re-reads that sentence.
- filed driver-turso: a url whose scheme the classifier does not recognise (an uppercase
Landing:
ready+ auto-merge through the queue. #19894 (serial, same file) unlocks when this lands.
Generated by Claude Code
- Shape: first line
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsLanded: PR #19971, verified on
maindomain:engine#1,session_01Bvd69VPa6puiNzzPUroDBx, written 2026-09-24T15:23Z.- Merged through the merge queue as squash
0142415760: one parent, an ancestor oforigin/mainafter a fresh fetch.refuseNonDurableLocalEnginehas 2 hits inturso-driver.tsat the squash and 0 at its parent. - This card closed
completedviaFixes #19893.pm:dispatchedwas removed in the same act. - No other lane card closed with this landing.
- Serial queue:
packages/drivers/driver-turso/src/turso-driver.tsis released. 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 behind this card, deferral 5814602854) is next in that region. - Follow-ups filed from this round: driver-turso: a url whose scheme the classifier does not recognise (an uppercase
LIBSQL://, a bare path) and nomodefalls through tolocalon a:memory:Knex engine, so every write is lost on restart #19976 (the bare-path / uppercase-scheme fall-through, same file) and spec:TursoConfigSchemaaccepts turso configs the driver refuses or ignores — a remoteurlbesidesyncUrlor a forced replica/localmode, a non-file:replica,syncUrl/syncundermode: remote#19977 (theTursoConfigSchemarefinement, aSeam:for the spec lane).
Generated by Claude Code
- Merged through the merge queue as squash
- added 3 commits that reference this issue
on Sep 28, 2026
Filing gate: ① a defect with a named landing site,
TursoDriver.detectMode/toKnexConfiginpackages/drivers/driver-turso/src/turso-driver.ts. Finding class (a). Derived from #19845's round (PR #19891); ⛔ that PR does not touch it.Filed by the
domain:engineexecution seat 1 (session_01TEhopqrWQYBycZzyJHpAZr) from the out-of-scope findings of its #19845 dev (report on #19845). The seat's isolated contract reviewer read the code independently on PR #19891 (record 5797857806, flag ③-2: 「REAL … the most serious of the four; FILE SEPARATELY with priority」). ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.What happens
detectMode: alibsql://,https://,http://,wss://orws://urlWITH asyncUrlreturnsreplica. A code comment asserts@libsql/client"operates in embedded replica mode with an in-memory local cache".toKnexConfig's last arm hands that modebetter-sqlite3onconnection: { filename: ':memory:' }.connect()callssuper.connect()(Knex) and builds a libsql client that onlysync()uses. Every CRUD and schema call takes the inherited Knex path.⇒ Writes land in a process-local in-memory SQLite database and vanish when the process exits.
sync()moves nothing out of Knex's:memory:, so nothing reaches the remote through the driver's data path, whether or not the client accepts the pair.Reproduction (the #19845 dev, on the libsql SQLite double)
url: 'libsql://r.turso.io',syncUrlset, client stub,sync.onConnect: false:initObjectspluscreate, thenfindreturned the row, while the libsql client's database held no tables. The contract reviewer confirmed the path by code reading; it did not re-measure.Reach
It looks like a supported configuration:
TursoDriverConfigSchema(packages/spec,turso.zod.ts) acceptssyncUrlbeside anyurl; its only refinement issyncwithoutsyncUrl.turso-driver-factory.tsforwardssyncUrlfrom a declared datasource.Not reachable through
OS_DATABASE_URL:standalone-stackpasses nosyncUrl. The README's mode table lists onlyfile:plussyncUrl. A deployment that declares this datasource gets silent data loss that looks normal from outside: reads return what was written, until a restart.Suggested shape (⛔ not a ruling)
Refuse a remote
urlbesidesyncUrlat construction (or classify itremote), with the matching refinement onTursoDriverConfigSchema, loudly, naming the supported replica spelling (url: 'file:…'plussyncUrl). Measure first whether any real deployment or example declares the pair.Filing-gate answers
packages/drivers/driver-tursoafter triage routes it (the domain table:domain:engine); a spec refinement half, if taken, isdomain:spec's.closedincluded:turso replica remote url syncUrl memory knex writes lost embedded replica mode→ 7 hits: driver-turso remotedetectManagedDrift()diffs against the dummy:memory:Knex connection remote mode is given — drift is always empty, so the artifact boot migration gate reads a remote Turso database as never drifted #19845, driver-turso remotesyncSchemasBatch— the engine boot door — skips read-coercion registration, managed-object recording and the canonical backfill: a remote boolean can read back as 1, JSON as a string #19844, driver-turso remote mode ignores deferred DDL —os migrate planagainst a remote Turso datasource performs the DDL and the canonical backfill it was meant to preview, and prints no pending work #19823 (the remote-face siblings of this round), drivers(turso): the remote face dropsuniqueentirely — a declared-unique column takes duplicates, and an upsert onconflictKeysdies with a raw SqliteError #8413, Turso remote:带 id/conflictKeys 但没匹配上的 upsert 仍会静默写入 NULL 自增号(#6944 拒绝闸门覆盖不到的那条腿) #7099, drivers(turso): remote 模式没有继承 #5146 的$notNULL-safe —— 同一个 TursoDriver,local 返回 NULL 行、remote 不返回(实测,origin/main) #5903, drivers(turso): remote 模式补 canonical backfill 通道(@libsql/client,分批、可恢复、完成标记)—— cloud#1005 移交,维护者已裁方案 1 #5770 (closed, other remote-face gaps). None is this defect.Dedupe words:
turso replica remote url syncUrl memory·embedded replica knex memory writes lost·turso replica mode libsql url syncUrlGenerated by Claude Code