Skip to content

docs(cli): say what a read-only boot's first connection writes to a SQLite file - #21779

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-21745-writes-nothing-wal
Oct 4, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-21745-writes-nothing-wal

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #21745

Clause-②: no. Documentation is made true about existing behaviour; nothing is published.

Docs only: two files under content/docs/, which no package ships. No code change, no behaviour change, no changeset.

What changed

Triage graded #21745 option B: make the reference true, and leave the one-rule-for-every-connect ruling closed. content/docs/deployment/cli.mdx now says it once, in the read-only-boot paragraph under Data migrations:

  • the commands write no row and no schema;
  • on a SQLite file no ObjectStack process has opened before, the first connection converts its journal to WAL, as every ObjectStack connection does. That changes the file's header and no row or table;
  • after that, a read-only run leaves the file byte-identical.

Each other "writes nothing" sentence about a command that opens the database now says only what is true on its own ("writes no row", "no row and no schema"). The prose sites (secret orphans, plan/apply, duplicates) link to that paragraph. content/docs/deployment/seed-tenancy-repair.mdx made the same claim about os migrate duplicates, and it gets the same qualification. The heading "Nothing is written before you confirm" is now "Nothing is applied before you confirm". No page under content/docs links to the old anchor.

Measured

Base: origin/main d7fff21736. Every run used the built CLI binary packages/cli/bin/run.js, launched as a child process.

The fixtures.

  • A database that os serve left behind for a one-object app with an inline seed: 35 tables, WAL, auto_vacuum INCREMENTAL. Called "configured" below.
  • Legacy copies of it, made with raw better-sqlite3 (VACUUM INTO, then PRAGMA journal_mode = DELETE), so no ObjectStack process ever opened them:
    • auto_vacuum NONE, the shape another tool leaves;
    • auto_vacuum INCREMENTAL, the shape of a pre-WAL ObjectStack file;
    • auto_vacuum FULL, as a control.
  • Header bytes were read with fs only, never through a SQLite connection.

A1. os migrate duplicates --database-url file:X on the NONE copy:

journal [18] [19] [27] [95] md5
before delete 1 1 1 1 71cdd04b096d
after run 1 wal 2 2 2 2 02bf2c53ba03
after run 2 wal 2 2 2 2 02bf2c53ba03

Run 1 changed 4 bytes in the file, all in the header (offsets 18, 19, 27 and 95). Run 2 changed none. No -wal, -shm or -journal file was left after either run.

A2. The no-write mode of every bootSchemaStack caller ran twice, each time on a fresh copy of each fixture. The callers are the family that schema-migrate.one-shot-family.integration.test.ts reads off the source.

  • The modes: migrate duplicates, plan, multi-value-columns, unmapped-columns, files-to-references, value-shapes, summary-nulls, meta --stored, recorded-by, resume, audit-metadata-bodies and account-issuer; secret orphans; secret rewrap; storage orphans; meta resync.
fixture run 1 run 2
legacy NONE (16 modes) 16 of 16 converted: header offsets 18, 19, 27 and 95 only, 4 bytes each 16 of 16 unchanged: 0 bytes, same md5
legacy INCREMENTAL (16 modes) 16 of 16 converted, the same 4 header offsets 16 of 16 unchanged: 0 bytes
legacy FULL (duplicates, plan) converted, and auto_vacuum went FULL to INCREMENTAL (offset 67): 5 header bytes 0 bytes
configured (16 modes) 0 bytes 0 bytes

Every run exited 0 and printed parseable JSON.

Other modes.

  • os migrate apply answered n at a real [y/N] prompt (a pty via script), against a release one field and one object ahead: "Aborted — no changes made.", and the legacy file converted all the same (offsets 18, 19, 27 and 95). The configured file did not change.
  • Re-runs of write modes:
    • secret rewrap --apply and summary-nulls --apply changed 0 bytes on their second run (legacy and configured alike).
    • value-shapes --apply rewrites only its flag row, sys_migration adr-0104-value-shapes. A row diff of both runs shows nothing else.

The sites, one row each (line numbers are d7fff21736's)

site command converts? evidence edit
cli.mdx skip-seed-data gloss (old :308) a bootSchemaStack boot (skipSeedData: true on every one) yes A2 "writes nothing" to "runs no seed"
:544, :547, :555 secret orphans report yes A2 "writes no row"; pointer to Data migrations; code comment "writes no rows"
:601, :606 secret rewrap dry run yes A2 "writes no row"; code comment
:631 "a finished run writes nothing" secret rewrap --apply re-run no: the file was already opened by the first run 0 bytes on the second --apply run kept
:853, :859 "(no changes applied)" plan yes, but these sentences are about drift changes A2 kept
:868 heading, :870, :888 plan / apply boot, answered n yes A2 plus the pty n run heading reworded; "writes no row and no schema"; "every table and row exactly as it was" plus pointer
:933 occupancy table plan yes A2 "writes no row and no schema either way"
:1005 multi-value-columns dry run yes A2 "writes no row and no schema at all"
:1078–:1081 unmapped-columns yes A2 "writes no row and no schema"
:1114 duplicates (table row) yes A1 "writes no row and no schema"
:1116–:1135 the shared read-only-boot paragraph yes A2 lead sentence "no row and no schema"; the one qualification paragraph added after it
:1150, :1203, :1255, :1349 dry run / scan / preview code comments yes A2 "writes no rows"
:1185, :1239 files-to-references / value-shapes dry runs yes A2 "writes no row"
:1215 "--apply's only write is the flag row" value-shapes --apply yes, plus the flag row the write-mode legs above "the only row --apply writes is the flag itself"
:1290 "a second run … writes nothing" summary-nulls --apply re-run no: the file was already opened by the first run 0 bytes on the second run kept
:1438 SDK migrateStored() runs inside the serving process, on its existing connection no new connection route doc: it uses the server's live engine kept
:1451, :1473–:1476 duplicates yes A1 "writes no row and no schema under any flag"; "changes no data in production" plus the WAL sentence and pointer
:1539 "boots read-only and repairs nothing" duplicates the term the qualified paragraph now defines — kept
:739 os validate, :1595/:1616/:1711/:1662/:1729 os generate, :1806/:1820 os lint --fix open no SQLite file no git grep of each command for bootSchemaStack, SqlDriver and better-sqlite3: 0 imports kept
seed-tenancy-repair.mdx:311 duplicates yes A1 "writes no row"; "its own queries are SELECTs only"; WAL sentence plus link

A4, other pages. I ran git grep over content/docs (excluding releases/ and references/) for "writes nothing", "read-only boot", "byte-identical", "changes nothing", and for every family command name. Hits with no edit:

  • seed-tenancy-repair.mdx:123 is about the log, and :251 is about a later serving boot's repair rows.
  • ui/translations.mdx:335: os i18n extract --check. commands/i18n/*.ts has 0 hits for SQLite or boot imports.
  • automation/flows.mdx:1445: "writes nothing for it" refers to the decision nodes' rows.
  • protocol/kernel/http-protocol.mdx:1241, ui/actions.mdx:261 and concepts/metadata-lifecycle.mdx:112 are unrelated surfaces.

Acceptance notes

  • Kept out on purpose: the journal opt-out. OS_DATABASE_SQLITE_JOURNAL_MODE=delete makes the first run on a rollback-journal file change 0 bytes (measured on the NONE and INCREMENTAL copies). The reference does not offer it for read-only runs, because on a file already in WAL the same setting converts the file back to delete. That is a write, to a served deployment's file.
  • Same phenomenon in runtime text. The CLI's decline line "Aborted — no changes made." prints after the conversion on a never-opened file. That text speaks of the migration's changes. This card rules out any code change. Noted, not filed; no follow-up PR is planned.
  • "After that" holds beyond the legacy files the card named. On an auto_vacuum FULL legacy file, the first connection also flips auto_vacuum to INCREMENTAL. That is a header change too, and the second run changes 0 bytes. The new paragraph says "the file's header changes" so that this case stays true.

Verification (head fcc13878fa)

  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 41 families at fcc13878fa. All 41 ran: 41 exited 0.
    • The first check:skill-examples run exited 3 (PREREQUISITE NOT MET: client-react was not built). It exited 0 after pnpm --filter @objectstack/client build && pnpm --filter @objectstack/client-react build.
    • --ran reconciliation: "41 derived famil(ies) accounted for — 41 run, 0 NOT-MEASURED".
  • check:doc-anchors: "428 internal #fragment link(s) across 414 source file(s) all resolve". Two one-time ablations through scripts/ablation-replace.mjs:
    • the new cross-page link /docs/deployment/cli#data-migrations mutated: red, naming seed-tenancy-repair.mdx:316;
    • a new same-page #data-migrations link mutated: red, naming cli.mdx:549.
    • Both were restored, with the blob equal to HEAD (24ba2ab3876e, 385558ab3097) and git diff HEAD empty.
  • pnpm lint: eslint's population is **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} (eslint.config.mjs:971). eslint --format json on the 2 changed files reports 2 files, each "File ignored because no matching configuration was supplied". So no file in the lint population changed, and an .mdx edit cannot move a linted file's verdict.
  • No package changed, so no package build or test is owed.
  • NOT MEASURED: the docs site build and the lychee link check (Check Documentation Links). Reason: CI runs them over the whole site, and lychee is not installed in this container.

Generated by Claude Code

…QLite file

The CLI reference said `os migrate duplicates` "writes nothing at all", and the
same wording stood for every read-only boot: the data-migration dry runs,
`os migrate plan`, `os secret orphans` / `rewrap`, and the shared read-only-boot
paragraph. Measured through the CLI binary on a rollback-journal SQLite file no
ObjectStack process had opened, each of those no-write modes moves header
offsets 18, 19, 27 and 95 on its first run (the journal goes to WAL, as every
ObjectStack connection does) and nothing on a second run; on a file ObjectStack
already switched to WAL, both runs move nothing.

The shared read-only-boot paragraph under Data migrations now states that once:
the first connection converts the journal (a header change, no row or table),
and after that a read-only run leaves the file byte-identical. Each per-command
"writes nothing" now says what is true on its own (no row, no schema) and the
prose sites point to that paragraph. seed-tenancy-repair.mdx carried the same
claim about `os migrate duplicates` and gets the same qualification.

Documentation only: no code or behaviour changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016GiHYRmLSNWTfbX9gVQkpz
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Oct 4, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 4, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 4, 2026 22:23
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 4, 2026 22:23
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 4, 2026
Merged via the queue into main with commit ebfe658 Oct 4, 2026
38 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21745-writes-nothing-wal branch October 4, 2026 22:56
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/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants