Skip to content

fix(driver-sql): a SQLite file that is not a database fails connect() at once, naming it, and a dev boot no longer renames and replaces it - #25006

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-24960-sqlite-notadb-connect
Oct 21, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-24960-sqlite-notadb-connect

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24960

Clause-②: no (narrowing)

Narrowing face: a SqlDriver.connect() that resolved now refuses. On a SQLite file whose bytes are not a SQLite database, connect() used to log and swallow SQLITE_NOTADB and resolve; it now rejects with SqliteDatabaseFileError (code: 'SQLITE_NOTADB', filename, a message naming the file), in development and production alike. On a development boot, resolveSqliteDriver re-throws that refusal instead of stepping down to wasm SQLite, so the operator's file is no longer renamed to FILE.corrupt-TIMESTAMP and replaced with an empty database. Nothing exported changes shape.

Route A, the card's seat ruling, as dispatched: the producer refuses the file, and the step-down classifies the code. B (consumer-only sniffing) and C (keep the wasm quarantine as dev recovery) are not taken.

What changed

Premise, measured (better-sqlite3 13.0.3, Node v22.22.0)

  • H1 holds. On a plain-text file, better-sqlite3's new Database(file) succeeds, and the first statement, PRAGMA auto_vacuum, throws code: 'SQLITE_NOTADB' ("file is not a database"). So do PRAGMA journal_mode and SELECT 1. A file whose 100-byte header was overwritten past the magic string answers the same code. On base, applyAutoVacuumIncremental matched only SQLITE_CANTOPEN, logged "Failed to set PRAGMA auto_vacuum=INCREMENTAL" and returned false, so connect() resolved.
  • H2 holds. With the code in isSqliteFileOpenFailure, the development probe re-throws at the native rung, and wasm's quarantineCorruptFile is never reached for a declared datasource whose addon loads. The pins assert that the file's bytes are unchanged and that the directory holds only that file: no .corrupt-* sibling, no journal, no WAL.
  • H3 holds. The card's "native better-sqlite3 unavailable (ABI mismatch or not built)" line is the step-down's own NATIVE_SQLITE_WASM_FALLBACK_WARNING ("dev using wasm SQLite"). The driver's similar warning fires only for a native load failure, which SQLITE_NOTADB is not. With this change the step-down never runs on this path, so the warning no longer prints: the pin asserts warn was never called, and the public-door run below prints neither line.
  • Does any caller rely on connect() resolving over a file that is not a database? I found none. I grepped the tree for NOTADB / "not a database" / SQLITE_CORRUPT / "disk image is malformed", for tests that write arbitrary bytes into a .db file, and read every resolveSqliteDriver caller.

Evidence

Pins (all at head 6d3dd6247, under os-verify-lock):

Ablation (scripts/ablation-replace.mjs WRAP with a trap restore, at c5b499cf0):

Public door, the card's own: bin/run-dev.js migrate apply --yes --database-url file:main.db over a dist/objectstack.json that declares archive (driver: sqlite, autoConnect: true) on a plain-text file, at head 6d3dd6247 after a turbo run build --filter=@objectstack/cli^... (60/60).

  • Object bound to archive: the run prints datasource 'archive': connect failed — SQLite database file '…/data/archive.db' is not a SQLite database, and was left as it is: PRAGMA auto_vacuum - file is not a database. (1 object(s) bind to it explicitly … fail-fast per ADR-0062 D5) and exits 1. The md5 is unchanged and data/ holds archive.db main.db.
  • autoConnect only: the run prints WARN datasource 'archive': connect failed — … is not a SQLite database … — degrading (datasource left unconnected), then Applied 0 change(s), and exits 0. The md5 is unchanged, there is no .corrupt-* sibling, and no step-down warning prints.
  • The card measured the base on the same door: the file was renamed, an empty 16384-byte database was written, and the run read connected.

Driver ledger (pnpm check:driver-conformance):

  • Before, at 4b8f597ba: "55 covered cell(s), 0 in the DEBT ledger, 0 exempt"; dialect axis 9 suites, 8 matrix and 1 named, 0 in the DIALECT ledger.
  • After, at 6d3dd6247: the same counts. The ledger did not move.

Gates. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) at 6d3dd6247 derived 72 commands: the seat's 55 plus 17 for the changeset and the new test files. I ran each one once, sequentially, with its exit captured before any pipe, and all 72 exit 0. Two answered exit 3 (PREREQUISITE NOT MET) on the first pass and were re-run after their prerequisite:

  • check-plugin-teardown-shape.mjs --self-test needed its pinned fixture commit, fetched at depth 1;
  • check:dual-build-cjs-loads needed every package's dist/, from a turbo run build that was 75 of 76 cache hits.

--ran reconciliation: "72 derived, 72 run, 0 NOT-MEASURED, 0 UNRUN", a derived zero because all 72 recorded an exit code. CI owns the full farm and test:repo.

Acceptance notes


Generated by Claude Code

…ct, naming it

SqlDriver.connect() swallowed SQLITE_NOTADB from its first PRAGMA and
resolved over a file it could not read. It now refuses it as a
SqliteDatabaseFileError, as SQLITE_CANTOPEN already was, and the step-down
in service-datasource classifies the code as a file failure, so a
development boot no longer hands the file to wasm SQLite, whose open moved
it aside and wrote an empty database in its place.

Claude-Session: https://claude.ai/code/session_01QK6pdbxKZiHdFApL8RYVh9
Co-authored-by: Claude <noreply@anthropic.com>
…tabase as refused and untouched

A development boot of a declared datasource on a plain-text file is refused
with the driver's SQLITE_NOTADB refusal, steps down to nothing, warns
nothing, and leaves the file's bytes and directory exactly as they were;
production refuses at connect too. Adds the patch changeset for both
packages.

Claude-Session: https://claude.ai/code/session_01QK6pdbxKZiHdFApL8RYVh9
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 20, 2026
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/driver-sql, @objectstack/service-datasource, touching 6 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/drivers/driver-sql/src/sqlite-database-file.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/data-modeling/drivers.mdx (via SqlDriver (symbol, a top-level class))
  • content/docs/data-modeling/index.mdx (via SqlDriver (symbol, a top-level class))
  • content/docs/data-modeling/queries.mdx (via SqlDriver (symbol, a top-level class))
  • content/docs/permissions/tenant-audit-census.mdx (via SqlDriver (symbol, a top-level class))
  • content/docs/plugins/packages.mdx (via SqlDriver (symbol, a top-level class))
  • content/docs/protocol/kernel/index.mdx (via SqlDriver (symbol, a top-level class))
  • content/docs/protocol/kernel/lifecycle.mdx (via SqlDriver (symbol, a top-level class))
  • content/docs/protocol/objectql/query-syntax.mdx (via SqlDriver (symbol, a top-level class))
  • content/docs/protocol/objectql/types.mdx (via SqlDriver (symbol, a top-level class))

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

  • content/docs/releases/v17/17-0.mdx (via SqlDriver (symbol, a top-level class))
  • content/docs/releases/v17/17-5.mdx (via SqlDriver (symbol, a top-level class))
  • content/docs/releases/v17/17-6.mdx (via SqlDriver (symbol, a top-level class))

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-sql/src/sqlite-database-file.ts) — pages documenting those are invisible to this run
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 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; 97 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 — 13 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 62bc1293080968f0ec8c3d31dc1f9ed8da4077df → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 62bc1293080968f0ec8c3d31dc1f9ed8da4077df

⚠️ 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 62bc1293080968f0ec8c3d31dc1f9ed8da4077df → 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

Seat review · domain:engine seat 2 · session_01QK6pdbxKZiHdFApL8RYVh9 · 2026-10-21T00:53Z · head 6d3dd6247 · verdict: REWORK, patch round 1 (one item).


Generated by Claude Code

…ed narrowing ships in the launch window

The PR declares Clause-② as no (narrowing), which the Check Changeset level axis reads as a breaking change. A breaking change takes minor during the launch window, so both packages move from patch to minor and the banner sentence states that level and where the breaking-ness is carried.

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

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 69f54e9130bebfc69d274ae74b10fb27e50a250f
Local-runs: none
Scope: packages/drivers/driver-sql/src/sql-driver.ts, packages/drivers/driver-sql/src/sqlite-database-file.ts, packages/drivers/driver-sql/src/sql-driver-24960-sqlite-not-a-database.test.ts, packages/services/service-datasource/src/sqlite-driver-fallback.ts, packages/services/service-datasource/src/tests/sqlite-driver-fallback-not-a-database.test.ts + packages/drivers/driver-sqlite-wasm/src/wasm-connection.ts (the quarantine), packages/services/service-datasource/src/default-datasource-driver-factory.ts (the sqlite arm), packages/services/service-datasource/src/datasource-connection-service.ts (handleFailure), packages/cli/src/commands/db/clean.ts, packages/cli/src/utils/telemetry-datasource.ts, examples/app-showcase/src/system/datasources/external-fixture.ts, packages/drivers/driver-turso/src/turso-driver.ts (connect and the local-engine note), packages/drivers/driver-sql/src/index.ts (the export surface), packages/drivers/driver-sql/src/sql-driver-23696-sqlite-uncreatable-file.test.ts, packages/services/service-datasource/src/tests/sqlite-driver-fallback-file-open-failure.test.ts, packages/services/service-datasource/src/sqlite-driver-fallback.test.ts, scripts/check-changeset-no-major.mjs, scripts/check-adr-0087-registration.mjs, .changeset/24960-sqlite-notadb-connect.md (the prose face, read for ②)

Inputs: card #24960 (body, Claim 6303624171, os-dev-reports 6304560281 and 6305467682), PR #25006 (body, 6-file list, net diff base...head = +302/-22, identical to the file list), and the check-runs on the head, read ONCE at 2026-10-21T01:20Z and not polled. Read-only throughout: no worktree, build, test, gate or ablation was run; the branch head and the PR base were fetched into review refs of this reader's own and diffed there.

Check-runs on the head, exactly as read (38 runs): 15 success (Governed Surface Queue Guard, Check Changeset, Check PR Size, Check Documentation Links, Flag docs affected by code changes, Auto Label, Dogfood Regression Gate, filter, Lint · PM dispatch-gates, Lint · repo & tooling gates, Lint · scoped · tooling self-tests, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch); 7 skipped (the opt-in and full-battery lanes: 18.x upgrade harness, Packed-tarball smoke, Build Docs, Console Pin Gate, Dogfood sharded, Dogfood Verify CLI, Temporal Conformance); 16 in_progress with no conclusion yet (Build Core, Test Core 1/6 to 6/6, Type Check · workspace / consumer gates / debt ledger / source gates, Lint · ESLint, Lint · CI release & engine gates, Lint · docs & code guards, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus). None failed. The in-progress ones are the required gate carriers (Build Core, Test Core, the type-check and lint lanes); this record does not stand in for them, and the queue is armed only once they conclude success. The PR is a draft on an internal branch (head repo = base repo), 324 changed lines, and its file list touches no governed surface.

① Derived judgments

Each accept-set or public-surface change the diff implies, named right or wrong:

  1. SqlDriver.connect()'s accept set — narrowed by exactly one code, right. On base, applyAutoVacuumIncremental refused only SQLITE_CANTOPEN; every other first-PRAGMA failure logged and returned false, so connect() resolved. At the head the lookup is a two-row private table, SQLITE_FILE_REFUSALS (SQLITE_CANTOPEN with the runtime: a declared sqlite datasource whose file cannot be created never settles its connect, so the boot is refused only by a plugin start timeout (30 s, 120 s after PR #23694) that names neither the datasource, the path nor the open error #23696 words verbatim, SQLITE_NOTADB new), and a hit throws SqliteDatabaseFileError(filename, words, cause). Every other code keeps the base path unchanged: native-load failures still warn the step-down one-liner and return false; anything else keeps the full warning and false. The refusal is raised before anything is written: the PRAGMA auto_vacuum read is the connection's first statement and applySqliteJournalMode (the WAL switch) runs only on true, so no journal, WAL or .corrupt-* sibling can appear; the driver pin asserts the bytes and the directory listing. :memory: and the openedEmptyInMemory redirect never answer SQLITE_NOTADB. Exactly route A as ruled; B and C are not taken.

  2. SqliteDatabaseFileError's code set — grows by one, right; it is not a public export. code is the cause's own: an operating-system code from ensureDatabaseExists (ENOENT, EACCES, ENOTDIR, EEXIST, …), SQLITE_CANTOPEN, and now SQLITE_NOTADB. Its shape (name, code, filename, cause, message SQLite database file 'PATH' WORDS: REASON) is unchanged. packages/drivers/driver-sql/src/index.ts does not export the class, so the consumer contract is duck-typed name + code (the service-datasource pin reads it exactly so). No api-surface artifact moves; the changeset's sentence about the class keeping its shape is accurate, and stronger than it needs to be.

  3. resolveSqliteDriver's step-down — narrowed in development by the same one code, right. isSqliteFileOpenFailure is /^E[A-Z0-9]+$/ || SQLITE_CANTOPEN || SQLITE_NOTADB, judged on code alone, no message text. In development the native rung's connect() now throws before the probe's SELECT 1, the predicate matches, and the refusal is re-thrown: no wasm rung, no in-memory rung, no NATIVE_SQLITE_WASM_FALLBACK_WARNING (the pin asserts warn never called, which also retires the card's wrong-cause line). Production is untouched: native returned unprobed, refusal at the service's connect. The native-load step-down (ERR_DLOPEN_FAILED, the bindings no-code case) is unchanged, and the fix(cli,service-datasource): under bin/run-dev.js (NODE_ENV=development) an unopenable SQLite datasource steps down to an in-memory engine, so a dev-mode os migrate audit-mail-bodies reports an unreachable archive as examined and clean #24950 pins and the mocked sqlite-driver-fallback.test.ts cases carry no old-behaviour pin for a non-database file. The module note's claim "every rung opens the same file, so no rung can cure this" is true of the wasm rung in the cited code: WasmSqliteConnection.open reads the bytes, new SQL.Database(bytes) or PRAGMA quick_check throws, and quarantineCorruptFile renames to FILE.corrupt-TIMESTAMP and starts an empty database. Only this PR's classification keeps a declared datasource off that path; the quarantine itself is unchanged, by the claim's scope.

  4. Callers whose answer flips — all flip to the ruled answer; one is unlisted by the dev.

    • DatasourceConnectionService.connectDeclared through the factory's sqlite arm: the throw reaches handleFailure; an object bound to the datasource (or a mapping, or boot-critical) is fail-fast per ADR-0062 D5 (exit 1, the message carries the file), autoConnect-only is warn … degrading (datasource left unconnected), state failed, no driver registered. Pre-existing mapping, now reached; pinned in both modes. Right.
    • os db clean (dev: true): before, a garbage primary was quarantined and the empty replacement VACUUMed and reported; now VACUUM failed for FILE: …, exit 1, file untouched. Right.
    • provisionTelemetryDatasource (the dev telemetry sibling): any throw is caught to undefined, so a garbage telemetry file now leaves lifecycle-classed objects on the primary with the file untouched, instead of a quarantine plus a wasm warning. Right, and pre-existing best-effort shape (the fix(cli,service-datasource): under bin/run-dev.js (NODE_ENV=development) an unopenable SQLite datasource steps down to an in-memory engine, so a dev-mode os migrate audit-mail-bodies reports an unreachable archive as examined and clean #24950 codes land there too); see ③ for the silence it leaves.
    • examples/app-showcase setupShowcaseExternalDatasource — not in the dev's caller list. It calls resolveSqliteDriver with dev defaulted from NODE_ENV on EXTERNAL_DB_FILE; a non-database fixture file used to be quarantined and re-provisioned empty, and now the stack's onEnable throws the refusal naming the file. That is the card's ⛔ applied to a declared external fixture, and right; it is noted here only because the implementation claim omitted it.
    • TursoDriver.connect() local and replica: both call super.connect() first, and the driver's own localEngineDefect note records that both non-remote arms route every read and write through the inherited Knex + better-sqlite3 engine (the libsql client is used for sync() alone). So a file better-sqlite3 answers SQLITE_NOTADB for (a libSQL-encrypted file is the measured case) was never a working datasource: every read failed with that code; it is now refused at connect, naming the file. Remote mode never reaches super.connect(). No working caller flips. Right.
    • driver: sqlite-wasm declared directly: unchanged, the quarantine stays on that path. Right by the claim's scope.
    • No test in the tree writes non-database bytes into a .db and then connects through SqlDriver or resolveSqliteDriver expecting success (dev-organization-ownership-upgrade.test.ts writes text into a telemetry file only to list a backup directory; dev-default-db.test.ts writes an empty file, which is a valid empty database and is the driver pin's control).
  5. Two hand-mirrored code lists, accepted. The driver's SQLITE_FILE_REFUSALS is private and isSqliteFileOpenFailure re-lists the SQLite codes by hand (plus the OS-code regex). That is the same shape runtime: a declared sqlite datasource whose file cannot be created never settles its connect, so the boot is refused only by a plugin start timeout (30 s, 120 s after PR #23694) that names neither the datasource, the path nor the open error #23696/fix(cli,service-datasource): under bin/run-dev.js (NODE_ENV=development) an unopenable SQLite datasource steps down to an in-memory engine, so a dev-mode os migrate audit-mail-bodies reports an unreachable archive as examined and clean #24950 shipped, and the service pins hold the mirror today; a third row (see ③, SQLITE_CORRUPT) would be added to both. Not a contract defect; the family card may consider exporting the code set so the mirror has one source.

② Semver level

  • Declaration. Clause-②: no (narrowing) on the PR body and in the changeset, and nothing authorable is added, so no is right and (narrowing) is the arm the diff publishes: a connect() that resolved now rejects, and a development resolveSqliteDriver that resolved to wasm now rejects.
  • Level. @objectstack/driver-sql: minor, @objectstack/service-datasource: minor. Right: a declared narrowing is BREAKING, and the level axis of check-changeset-no-major.mjs requires a moved package graded minor or above during the launch window (the patch × 2 of head 6d3dd62 was exactly its red; the patch round moved both). Check Changeset is success on this head. Only the two packages whose published source moves are graded; the CLI's behaviour changes through the service and takes no changeset of its own.
  • BREAKING banner and migration. Present, with Before / Now / Fix. The Fix is an operator's remedy (point at a real database, or move the file aside yourself; an absent file is created empty), which is what a consumer needs here: no exported declaration, spec key, config field or stored shape is removed or renamed, so there is no FROM → TO code rewrite to state.
  • ADR-0087 disposition. not-required (no-migration-prescription), with the other categories closed on facts in the marker. Right: no metadata moves, objectstack migrate meta has nothing to rewrite, and the changeset's body carries no consumer-code prescription for the gate's contradiction check to refuse. The registration gate's CI home is Lint · docs & code guards, in_progress at the read; the dev's offline run of check-adr-0087-registration.mjs --base origin/main is recorded on the card as exit 0 and is not re-run here.
  • Two prose residues, neither a diff change. (a) The PR body's "What changed" bullet still says patch for both packages; the committed changeset says minor. The seat corrects that one word in the body before landing (a body edit keeps this head). (b) The changeset heading is fix(driver-sql): without the conventional-commit bang the other BREAKING changesets on main carry; no gate reads the bang, the banner and the Clause-② line carry the breaking-ness, so it is optional.

Clause-②: no (narrowing)

③ Boundary flags

Every dev flag and open question on #24960, answered or escalated:

Round 1 deviations (6304560281).

  1. Ran the whole 72-command derived union instead of the 17-delta: more measurement than asked, nothing withheld. Accepted.
  2. Prerequisites: a git fetch --depth=1 of fixture commit 621a487 into the shared object store (additive, a shallow root, no ref moved) and a root turbo run build (75/76 cache hits). Accepted; neither touches the diff.
  3. Built the CLI closure to re-measure the card's public door: beyond the local minimum, and the reason the record above can cite the door's new text. Accepted.
  4. main moved (fix(plugin-auth): the register form's Application Type reaches the OAuth provider as application_type #24992, then fix(cli)!: os migrate apply exits 1 when the confirmation it needs is not given #25004) and was not merged: nothing in those commits touches these packages, the PR reads mergeable, and the net diff against the merge base equals the PR's file list. Accepted; the queue rebuilds on current main.
  5. Model-free commit trailers per AGENTS.md: correct, not a deviation.
  6. A stray /full-tests.pid at the dev container's filesystem root: outside every repo and outside the diff; nothing to land. Accepted.
  7. Probe-body comments in sqlite-driver-fallback.ts still say "cannot open or create" (the driver-sql: the read-only probe's absent-file test (existsSync) reads an unopenable SQLite path as absent, so os migrate plan opens an empty in-memory database, reports the datasource connected, and plans DDL into a file that cannot exist #24961 region): comment-only residue, read as such; whichever of driver-sql: the read-only probe's absent-file test (existsSync) reads an unopenable SQLite path as absent, so os migrate plan opens an empty in-memory database, reports the datasource connected, and plans DDL into a file that cannot exist #24961 and this PR lands second rewords it in its merge. Accepted.

Round 2 deviations (6305467682). The 21-command path-scoped re-run with 51 not re-run (code and tests byte-identical to the head that measured them): accepted, and the Check Changeset run on this head is the gate's own verdict. The unreadable job log: the red was reproduced offline from the PR's live payload and is green on this head. The banner wording follows the two landed launch-window changesets: accepted. The stale patch in the PR body and the missing bang: ② above.

Open question (SQLITE_CORRUPT, same defect one code over). Measured by the dev on this head through the card's own door: a header-truncated database answers the first PRAGMA SQLITE_CORRUPT, connect() still swallows it, and a development boot still renames the file and reads connected. ESCALATED, not a FAIL item: the seat ruling and the Claim name SQLITE_NOTADB and scope the file surface to that code, so the PR delivers what was ruled; and sqlite-driver-fallback.ts is co-held by #24961, so the in-place exemption the dev needed does not hold. Of the dev's options, C (leave it) is refused by the card's own ⛔ line (no rename of an operator's file on a development boot of a declared datasource), which that case still violates. The seat chooses between A (amend the Claim's file surface and take a patch round on #25006: one more row in each list, with the two pin files run over a header-truncated file; this record would be re-rendered on the new head) and B (a family-residual card, route A, dispatched after #24961 lands). Either keeps the ⛔ line from riding silently; this record does not pick, because A changes the Claim and B costs a cycle, and both are the seat's.

Out-of-scope findings.

Implemented-by: claude/issue-24960-sqlite-notadb-connect
Reviewed-by: session_01QK6pdbxKZiHdFApL8RYVh9

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

Seat review · domain:engine seat 2 · session_01QK6pdbxKZiHdFApL8RYVh9 · 2026-10-21T01:38Z · head 69f54e913 · verdict: ACCEPT


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Queue kick · domain:engine seat 2 · session_01QK6pdbxKZiHdFApL8RYVh9 · 2026-10-21T02:40Z · head 69f54e913 (unchanged)

Signature: merge-group CI run 39528984460 (base 4d991588f, which already carries PR #25007). Test Core (6/6) was cancelled at its timeout-minutes: 50 ("The operation was canceled", 02:34:22Z, after 50 m 15 s). The aggregate Test Core then failed its "Verify test shard results" step. No assertion failed, and no test file reported a failure.

Initial reading: a slow runner, not this PR. All of this comes from a read-only log diagnosis of the reachable window (the last 5,000 of 44,158 lines) plus the Jobs API step timings:

  • The job's own checkout and install steps were 2–3.5x slower than on this PR's head, before any test ran.
  • Every @objectstack/runtime file in the window ran about 2.7x slower: median ratio 2.71 for files that never open SQLite, 2.57 for the SQLite/datasource ones. A cost that the two PRs' combined SQLite paths added would have hit only the latter.
  • There is no stall in the window (largest gap between log lines 10.3 s), and the stall guard did not fire. runtime was at about 97% of its files when the wall came.
  • The same shard took 21.4 min on this PR's head and 14.1 min in PR fix(driver-sql): a read-only boot refuses a SQLite datasource whose file can be neither opened nor created, instead of reporting it connected #25007's queue group.
  • One limit stays open: the log before 02:25:08Z was not reachable.

The three facts for one re-queue:

  1. The file running at the cancellation (runtime's sandbox/quickjs tests) does not import the two SQLite regions this PR changes.
  2. The same shard was green on the queue base's own group (PR fix(driver-sql): a read-only boot refuses a SQLite datasource whose file can be neither opened nor created, instead of reporting it connected #25007's, 14.1 min).
  3. The first error is a timeout, not an assertion.

So this seat re-queues once. If the same shard fails again, it is read as real and diagnosed in full, not re-queued.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 7f99616 Oct 21, 2026
42 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24960-sqlite-notadb-connect branch October 21, 2026 03:25
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/m tests tooling

Projects

None yet

2 participants