Repository navigation
fix(cli): three more read-only arms claim no row or table, and name the journal-mode switch on a SQLite file - #23895
Conversation
…he journal-mode switch on a SQLite file `os storage orphans`, the `os secret rewrap` dry-run note and the `os migrate summary-nulls` dry run said "nothing was written" after a boot whose connect can switch a SQLite file's journal mode (header only). Each now claims no row or table; the storage and summary-nulls commands append JOURNAL_MODE_SWITCH_NOTE to the formatter's line on a SQLite target. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 10 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 5 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 44 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 95d4661b09abcb44bfd364241aec505063ed7fc1 && git checkout 95d4661b09abcb44bfd364241aec505063ed7fc1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 497518c3f67d8cc022fc6663d2e08f97117298a4 7bdbb42afbd17588ee419cec69de9bf4af8a5583 && git checkout -B drift-repro 497518c3f67d8cc022fc6663d2e08f97117298a4 && git merge --no-ff 7bdbb42afbd17588ee419cec69de9bf4af8a5583
node scripts/docs-audit/affected-docs.mjs --json 497518c3f67d8cc022fc6663d2e08f97117298a4
|
席位核对ACCEPT · #23726 ·
Seat ruling on Landing: once every check on the head is green or an expected skip, this seat flips the PR ready and arms auto-merge through the relay, then follows it to Generated by Claude Code |
Fixes #23726
Clause-②: no
The family close-out for the "read-only arm says it wrote nothing" class, with the pattern PR #23608 set and PR #23725 applied (the earlier card in this family, #23622, landed with PR #23725; it is named here only as context). Three runs boot through the SQL driver's connect before they print. On a SQLite file still on a rollback journal, that connect switches the journal mode, which rewrites the file header and no row. Each run then said "nothing was written". Each now claims no row or table, and on a SQLite target its line is followed by the exported
JOURNAL_MODE_SWITCH_NOTE: "Opening a SQLite file can still switch its journal mode (to WAL by default), which changes its header only."The basis for
Clause-②: nois the claim's: operator text only. No flag, exit code, prompt, refusal, accepted input or export signature moves, no--jsondocument gains a field, and there is nopackages/specedit.The premise, measured at the door, before and after
The real CLI through
packages/cli/bin/run-dev.js(tsx), with@objectstack/service-storageand@objectstack/objectqlrebuilt at each sha. Before:6422da58e(base). After:7bdbb42af(this head). Every run got its own copy of the fixture and a freshOS_HOME, with the database env vars unset. Header bytes 18 and 19 are the journal mode (1,1 rollback journal; 2,2 WAL); the counter is bytes 24 to 27. "Content" is an md5 over the schema plus every row of every table, read on a connection of my own.journal_mode = delete, run from an empty directory.examples/app-crmcompiled withos build, its tables created by one plain boot, onecrm_opportunityrow inserted withline_totalNULL, then the file put onjournal_mode = delete; run from the example's directory.os storage orphansos secret rewrap --no-declared-datasources(dry run, text)--jsonreport.notes[2]= the old sentencereport.notes[2]= the new sentenceos migrate summary-nulls(dry run, 1 NULL roll-up)Row 3's own line was measured at the door for the first time here (the earlier reading was of its boot only).
How the note reaches rows 1 and 3, and which doors it reaches
@objectstack/service-storage(formatStrandedOrphanInventory) and@objectstack/objectql(formatSummaryBackfillReport). Neither importspackages/cli, so neither can reachJOURNAL_MODE_SWITCH_NOTEor the SQLite test. Each formatter's own line changes its claim; the CLI command that prints it appends the note to that line whendescribeDriverSqliteFile(stack.driver?.config) !== undefined, the test PR fix(cli): six read-only arms claim no row or table, and name the journal-mode switch on a SQLite file #23725 uses (utils/connection-display.ts, read and not edited).Clause-②: nostays true. The claim allowed a parameter; the CLI-side append made it unnecessary.!report.applied && report.nullRows > 0, the conditionsummary-nulls.tsrepeats. Each formatter's docblock or comment now says so. The CLI pins run the real formatters, so a reorder there turns a pin red.git grep: each formatter has exactly one caller, its command's text output. The--jsondocuments ofos storage orphansandos migrate summary-nullscarry the report object and no sentence, so they are unchanged.REWRAP_NOTES.dryRunis printed under "Read before acting" in the text output and is an element of--jsonreport.notes; both now carry the corrected claim. The note is not appended to this sentence: in the text output the dry run's closing line already carries it on a SQLite file (PR fix(cli): six read-only arms claim no row or table, and name the journal-mode switch on a SQLite file #23725), and a second copy two lines apart would add nothing.--jsongets no note, following PR fix(cli): six read-only arms claim no row or table, and name the journal-mode switch on a SQLite file #23725, which moved no--jsondocument; no field or array element was added.The texts
service-storage/src/stranded-orphan-inventory.ts:421(os storage orphans)cli/src/commands/storage/orphans.tscli/src/utils/sys-secret-rewrap.ts:507(REWRAP_NOTES.dryRun)objectql/src/summary-backfill.ts:539(os migrate summary-nulls)cli/src/commands/migrate/summary-nulls.tsDeveloper comments the card names, one phrase each:
migrate/recorded-by.ts:63("writes nothing" becomes "writes no row or table"),secret/rewrap.ts:46(the same), andsecret/orphans.ts:221("this report promises to write nothing" becomes "this report promises to write no row or table, and writes no key file either": the comment is about the key file the settings service's default provider would mint).Pins (one per printed text)
cli/src/commands/storage/orphans.test.tsgains two report cases with the boot replaced; the inventory and its formatter run for real. A SQLite driver config gets exactly "READ-ONLY line + note"; a PostgreSQL config gets exactly the line and no note anywhere.cli/src/commands/secret/rewrap.guards.test.tsgains one case: the dry run's note claims no row or table in the text output and in--jsonreport.notes, and no line says "nothing was written".cli/src/commands/migrate/summary-nulls.test.tsgains two cases (SQLite: line + note; PostgreSQL: line, no note). Its@objectstack/objectqlmock now keeps the realformatSummaryBackfillReport(importOriginal); the walk stays replaced.objectql/src/summary-backfill.test.ts:355, a byte-for-byte literal of the formatter's lines, carries the new line.storage/orphans.test.tsnow value-importsbootSchemaStackto mock it, sopackages/cli/vitest-tiers.ts's KERNEL signal moves it fromunittointegration, besidesummary-nulls.test.tsandrewrap.guards.test.ts, which mock it the same way. The partition is derived, so nothing else moves.Ablation (one: the pins check the note by a condition)
At
7bdbb42af, throughscripts/ablation-replace.mjsin WRAP mode with an EXIT/INT/TERM trap that restores fromHEAD. No rebuild was needed: the subject is the CLI's ownsrc, reached by relative import.migrate/summary-nulls.ts,&& describeDriverSqliteFile(stack.driver?.config) !== undefinedbecame&& false. Anchor 1 to 0, blobc51055eef276todd0a41900e2c.Tests 1 failed | 7 passed (8). The SQLite case went red: Expected "Dry run — no row or table was written. Re-run with --apply to backfill. Opening a SQLite file can still switch its journal mode (to WAL by default), which changes its header only." Received "Dry run — no row or table was written. Re-run with --apply to backfill." The PostgreSQL case stayed green.c51055eef276),git diff HEADis empty, the marker count is 0, andgit status --porcelainis empty. The direction was red, as expected.Verification (head
7bdbb42af)turbo run build --filter='@objectstack/cli...' --concurrency=2: 61 tasks, VERDICT command-exit 0. The new texts are in both dists (service-storage/dist/index.{js,cjs},objectql/dist/index.{js,mjs}: 1 hit each; the old ones: 0).@objectstack/service-storage:testTest Files 59 passed (59), Tests 1021 passed (1021);typecheckexit 0 (check:test-typecheckOK, 0 files of debt).@objectstack/objectql:vitest run --project local --maxWorkers=2Test Files 434 passed (434), Tests 8213 passed (8213), VERDICT 0;typecheckexit 0 (check:test-typecheckheld at 39 files, 233 errors, 64 signatures).@objectstack/cli: the four touched test files by path (storage/orphans,migrate/summary-nulls,secret/rewrap.guards,utils/sys-secret-rewrap): Test Files 4 passed (4), Tests 37 passed (37), VERDICT 0. These are integration-tier files, run by path; the rest of that tier is declared to CI. The unit tier (vitest run --project unit --maxWorkers=2): Test Files 300 passed (300), Tests 4480 passed (4480), VERDICT 0.typecheckexit 0 (check:test-typecheckheld at 3 files, 28 errors, 6 signatures);tsc --noEmit --listFiles -p tsconfig.jsonputs the three touched CLI test files in its 1587-file program.pnpm exec eslint --no-inline-configover the 12 changed.tsfiles exited 0. The repo-widepnpm lintis CI'sLint & Repo Gates.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 72 commands from merge base6422da58e(13 paths, 248 changed lines). All 72 ran, each exit captured before any pipe. 68 exited 0 on the first run; four refused on a prerequisite and exited 0 when re-run after it was met:check-engine-split-ratio.mjs --days 90(exit 2: shallow clone inside its window; 0 aftergit fetch --shallow-since=2026-07-11),check:dual-build-cjs-loadsandcheck:i18n-coverage(exit 3: missingdist/; 0 afterturbo run build),check:type-check-debt(exit 3: its own closure build did not get the lock; 0 run under the lock).--ranprinted "72 derived, 72 run, 0 NOT-MEASURED, 0 UNRUN".The census: what is left of this family
Re-grepped every source file that boots a database (
bootSchemaStack,createStandaloneStack, the SQL driver, the occupancy probe) inpackages/cli,packages/services,packages/objectql,packages/runtimeandpackages/pluginsfor "nothing was written / changed", "writes nothing", "no changes made" and the like. The census is not complete: four more sentences on the same mechanism remain, outside this claim's file surface and its hold-off ("no other read-only arm"), so they are left alone and named for the seat.os migrate plan,cli/src/utils/unmanaged-tables.ts:508— "They are reported for information only — nothing here drops them, and a plan writes nothing." Measured at the door at7bdbb42af(the line is untouched here): the CRM fixture plus onesys_legacy_widgettable, on a rollback journal: header 1,1 · 5 to 2,2 · 6, content unchanged, and the line printed.os migrate plan/applydeclaration boot,cli/src/utils/schema-migration-plugins.ts:1148— "Refused N write(s) during the declaration boot — a plan writes nothing" (printed only when a plugin's write was refused; read, not measured), and:1163, "If those commands only read, nothing was written".os migrate summary-nulls --recompute-undefined-on-emptywith a name it cannot find,objectql/src/summary-backfill.ts:295— "Refused before any row was read; nothing was written." It is thrown after the boot in either mode (read, not measured). The claim limited this file to the dry-run line.os secret rewrap,REWRAP_NOTES.resumable,cli/src/utils/sys-secret-rewrap.ts:502— "… and a finished run writes nothing.", printed by every run (both modes, text and--json), and mirrored incontent/docs/deployment/cli.mdx:632. Measured at7bdbb42af: a finished re-run (--apply --yes, 0 rows re-wrapped) on a provisioned file: header 1,1 · 5 to 2,2 · 6, content unchanged. The first--applyon an unprovisioned file also created 10 tables and 2 rows.migrate/apply.ts:551("Aborted — no changes made.", declined after adeferSchemaDdlboot) andutils/organization-ownership-apply.ts:1542,:2483,:2494,:2503and:2540("Nothing was written." refusals after the apply boot). Not measured.activity-changed-fields,audit-metadata-bodies,comment-reactions,files-to-references,multi-value-columns,summary-nullsandmetaprint before their boot;generate.ts's "Nothing was written." lines open no database.Acceptance notes
origin/mainbefore opening. 13 commits landed since the base. Four touch the boot path's neighbours (runtime/src/standalone-stack.ts,cli/src/utils/storage-driver.ts,cli/src/commands/dev.ts: URL-scheme and file-URL parsing, the dev watcher), none of this diff's files, the driver's connect or the two formatters.git merge-tree --write-tree HEAD origin/mainis clean (exit 0). This is a declared narrowing; CI's merge-ref run covers the joint tree.dispatch-gatesflagged one changed derivation input upstream (.github/workflows/spec-shape-gate.yml), which no path here reaches.--jsondoor of row 2, and row 3's own line at the door (above).Built by
session_018t6a5rvqPRvEDMCVVW9kYT, the dev fordomain:cliseat 2's claim6213581109.Generated by Claude Code