Repository navigation
fix(cli): the read-only migrate arms say they write no row or table, and that opening a SQLite file can still switch its journal mode - #23608
Conversation
…ing a SQLite file can still switch its journal mode The ownership ceremony's plan arm, os migrate plan's occupancy warning and six sibling dry-run/preview warnings said "writes nothing" / "nothing in the database was changed". Their read-only boot still reaches the SQL driver's connect, which switches a SQLite file still on a rollback journal to WAL (a header change). Each now says it writes no row or table, plus one shared note, JOURNAL_MODE_SWITCH_NOTE, beside describeOccupancy. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
…al SQLite file A real-boot case switches the fixture's header to WAL and leaves its rows, and the success line names the switch instead of "nothing in the database was changed"; the occupancy case pins the busy warning's new sentence. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 27 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 28 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 2488267269eff541057b3473029333ec07abf2ad && git checkout 2488267269eff541057b3473029333ec07abf2ad
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin fc1a55a212581118304d47289c03ad486ad03480 cc578600b0af249f67b2b3b6c334cd723cd13bfa && git checkout -B drift-repro fc1a55a212581118304d47289c03ad486ad03480 && git merge --no-ff cc578600b0af249f67b2b3b6c334cd723cd13bfa
node scripts/docs-audit/affected-docs.mjs --json fc1a55a212581118304d47289c03ad486ad03480
|
席位复核: patch round 1 (the open questions answered, plus two same-class sentences) · 2026-10-15T04:13Z
What holds (read on GitHub, not from the report):
The open questions, this seat's answers:
Asked of this round:
Generated by Claude Code |
…he read-only arms' comments say no row instead of nothing Patch round 1: the help description of os migrate multi-value-columns and the unreleased occupancy-gate changeset's plan sentence say the run writes no row or table; developer comments in the touched arms that said a dry run or plan writes nothing say it writes no row. Claude-Session: https://claude.ai/code/session_01Q9PZ5Pzqcus4woVvTZwWNU Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: What this review read:
This round ran at the default tier, and the ① Derived judgmentsThe rewritten sentence in
Accept set: no change. Right. No flag, argument, exit code, refusal reason, or Public surface:
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
席位核对✅ ACCEPT: PR #23608 at
Checklist, read against GitHub and
The DELIBERATE CORRECTION, confirmed (
Why it lands with this red (SKILL.md, the third case; all three conditions hold):
Accepted deviations:
Out-of-scope, filed: #23622, the remainder. It covers:
Not built (Q3, ruled A): a journal-mode-preserving read-only boot. Landing: Generated by Claude Code |
Fixes #23562
Clause-②: no
What changed
The ownership ceremony's plan arm (
os migrate organization-ownershipwithout--apply) printed "The plan below writes nothing, but --apply will refuse until the database is free." beside a holder, and closed with "Plan written to FILE — nothing in the database was changed." Its boot is read-only, but it still opens the file through the SQL driver, and the driver's connect switches a SQLite file still on a rollback journal to WAL. Measured below: header bytes 18 and 19 go from 1,1 to 2,2 and the change counter moves; no row or table does.Both sentences now claim no row or table, followed by one shared note,
JOURNAL_MODE_SWITCH_NOTE, exported besidedescribeOccupancyinpackages/cli/src/utils/sqlite-occupancy.ts:The closing line carries the note only on a SQLite target: the occupancy probe answers
not_applicablefor PostgreSQL, MySQL and:memory:. The same sentence is corrected inos migrate plan(triage's fold-in) and in six sibling read-only arms (the four-condition fold-ins below). The driver's first-connect rule does not change: nopackages/drivers/driver-sqlpath is touched. No exit code,--jsondocument or refusal changes.Measurements (real CLI through
bin/run-dev.js, at9474bc6ea9)Each run used a fresh SQLite file on a rollback journal, holding one
sys_organizationrow. An idle better-sqlite3 connection in ANOTHER process held it open, which is the case the fd probe sees. Each command ran once.os migrate organization-ownership(plan)os migrate planos migrate summary-nulls(dry run)Measured with better-sqlite3 alone:
A switch to WAL and back moves only header bytes 18, 19, 24-27 and 92-95. After a clean close, no sidecar remains.
The
auto_vacuumsetter the driver sends to a legacy NONE file that holds tables moves no byte (md5 unchanged, under both WAL and a rollback journal). So the journal-mode switch is the only header change.(a) The call path: plan arm, then
bootSchemaStack(utils/schema-migrate.ts),createStandaloneStack(@objectstack/runtime),DefaultDatasourcePlugin,SqlDriver.connect(),applyAutoVacuumIncremental(),applySqliteJournalMode().(b)
readOnlyProbeacts only on an ABSENT file. It setssqliteAbsentFile: 'empty-in-memory'behind anexistsSyncguard instandalone-stack.ts, andSqlDriver.knexConfigForredirects only a missing file. An existing file opens as it is, andconnect()runs both pragmas. Soplan.ts:134's "nothing is written" was the same false sentence. It is folded in, and the table above measures it.(e) Where the driver reads
OS_DATABASE_SQLITE_JOURNAL_MODE: fromprocess.envat connect.bootSchemaStackfirst loads the project's.env*intoprocess.envthrough dotenv-flow, which never overrides an exported variable. So the arm reads its own shell and the project.env, and never the serving process's shell.=deleteexported in the arm's shell, a WAL file is switched to a rollback journal (2,2 to 1,1).=deleteonly in the project.env, a rollback file is left alone (1,1 to 1,1).Fold-ins: the four conditions, one row each
Every row is the warning a read-only arm prints beside a holder. Each arm boots
bootSchemaStackwithreadOnlyProbeon that arm, which reaches the sameconnect()as (a).plan.ts:13419c554ddc1)packages/cli, one derived listaudit-metadata-bodies.ts:118comment-reactions.ts:139files-to-references.ts:195meta.ts:1442multi-value-columns.ts:397summary-nulls.ts:159security-catalog-overlays.ts:198:290Tests, round 0 (all on
86ce1100c3, the head this PR opened with)pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 --reporter=verboseover three files:organization-ownership.journal-mode.integration.test.ts,organization-ownership.occupancy.integration.test.tsandutils/sqlite-occupancy.test.ts. Result:Test Files 3 passed (3),Tests 21 passed (21),VERDICT command-exit 0. The new real-boot case took 5442 ms (shared box).pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2:Test Files 290 passed (290),Tests 4324 passed (4324),VERDICT command-exit 0. The integration layer is declared to CI. The two integration files this PR touches ran by path, above.pnpm --filter @objectstack/cli build && pnpm --filter @objectstack/cli typecheck: exit 0.check:test-typecheckheld its debt at 3 files, 28 errors, 6 signatures.tsc --noEmit --listFiles -p tsconfig.jsonputs both touched test files andsqlite-occupancy.tsin its 1564-file program.organization-ownership.journal-mode.integration.test.ts: the real boot runs on a rollback-journal file. It asserts:JOURNAL_MODE_SWITCH_NOTE;If a read-only boot ever stops switching the file, the header assertion goes red, and the note and this pin can both go.
organization-ownership.occupancy.integration.test.ts(plan case): the busy warning says "The plan below writes no row or table" and carries the note, and no line says "writes nothing".plan.ts. No existing test asserted their old text.Ablation (one-shot, run through
scripts/ablation-replace.mjswith HEAD restores, at6a65958a88)The fix was committed first. Each leg puts one old sentence back into
organization-ownership.tsand runs both pin files:fa72f5c40052tob853ff316a93. The new pin went RED:Received: " ✓ Plan written to …/plan.json — nothing in the database was changed.". The header assertion before it passed, so the file was switched under the old text too.1 failed | 5 passed.fa72f5c40052to9996f6490f4f. The occupancy plan case went RED:expected ' ⚠ /tmp/os-ownership-occupancy-…' to contain 'The plan below writes no row or table'.1 failed | 5 passed.fa72f5c40052), andgit diff HEADis empty. The wrapping script's own trap proved it again.Gates, round 0 (on
86ce1100c3)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 64 families from the merge base19c554ddc, over 12 paths and 235 changed lines. All 64 ran, each exit code captured before any pipe.check:dual-build-cjs-loadsandcheck:i18n-coveragefirst exited 3 (PREREQUISITE NOT MET: missingdist/). Afterturbo run build --filter=@objectstack/connector-slack, both re-ran and exited 0.check:i18n-coveragereportedOK (14 config(s), 621 baselined untranslated string(s), none new).--ranthen printed64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN.pnpm exec eslint --no-inline-configover the 11 changed.tsfiles exited 0, with no "ignored" notice.--print-configshows 5 and 6 active rules on two of them. The repo-widepnpm lintis CI's.Patch round 1 (head
cc578600b0; the seat's rulings are PR comment 6184246424)The seat answered the three open questions: Q1 A (
security-catalog-overlays.tsstays out; the seat files the remainder as its own card), Q2 A (fixed in this round), Q3 A (keep the text). This round makes four changes, and nothing else changes..changeset/23400-ownership-occupancy-gate.md:15(unreleased): "The plan (no--apply) writes nothing, so it still runs." now reads "The plan (no--apply) writes no row or table, so it still runs." The rest of that file is byte-identical: the committed file equals the HEAD copy with that one replacement, 3143 to 3151 bytes.multi-value-columns.ts:334, the help description: "… and the database they would run against, and writes nothing." now reads "… and writes no row or table." This PR's changeset gains a FROM and TO line for it. The help text carries no note, so the changeset's note sentence now says "each warning". No test asserted the old description (git grepover the repo: only the source line).organization-ownership.tsheader (:75): "The one thing it writes is the plan FILE" now reads "It writes no row or table; the one thing it writes is the plan FILE" (re-wrapped over the same three lines).files-to-references.ts:98: "writes NOTHING" now reads "writes NO ROW".:191: "writes nothing" now reads "writes no row".plan.ts:55and:143: "writes nothing" now reads "writes no row".audit-metadata-bodies.ts:57: "(writes nothing)" now reads "(writes no row)".summary-nulls.ts:54: "(writes nothing at all)" now reads "(writes no row at all)".meta.ts:1489: the quoted report text "writes nothing" now reads "writes no row".plan.ts:65("nothing is written to disk", a never-started project opened in memory), and theorganization-ownership.tsheader's "no database file brought into existence".organization-ownership.ts:25-29and:579(PR feat(metadata-core,metadata-protocol)!: the sys_metadata family declares no organization column; indexes re-keyed (ADR-0131 D6/D7, C7b S2) #23469's hunks) are untouched.recorded-by.ts:158andresume.ts:172. Both print "Nothing was changed." after the same read-only boot through the same connect, so on a SQLite file still on a rollback journal both sentences are false the same way. The answer is in the Acceptance notes.Tests on
cc578600b0:pnpm --filter @objectstack/cli build && pnpm --filter @objectstack/cli typecheck && vitest runby path over the three pin files:Test Files 3 passed (3),Tests 21 passed (21),VERDICT command-exit 0.check:test-typecheckheld its debt at 3 files, 28 errors, 6 signatures. The real-boot case took 5421 ms.vitest run --project unit --maxWorkers=2:Test Files 290 passed (290),Tests 4324 passed (4324),VERDICT command-exit 0. Narrowedeslint --no-inline-configover the 11 changed.tsfiles exited 0.Gates on
cc578600b0:dispatch-gates --commands --repo objectstack-ai/objectstackderived the same 64 families as round 0, from 13 paths and 260 changed lines. All 64 ran, each exit captured before any pipe. 61 exited 0.check:dual-build-cjs-loadsandcheck:i18n-coveragefirst exited 3 (missingdist/in the fresh worktree); both re-ran after the later gates had built it and exited 0.--ranprinted64 derived, 64 run, 0 NOT-MEASURED, 0 UNRUN.node scripts/check-empty-changeset.mjs --base origin/mainexits 1. It reports: "This PR changes a changeset it did not add: .changeset/23400-ownership-occupancy-gate.md". This is the gate's DELIBERATE CORRECTION class, not a filename collision. The PR's own changeset keeps its issue-scoped name, and the 23400 note is corrected because its plan sentence was false. The gate's own remedy for this class is to say so on the PR and get it confirmed. The confirmation is the seat's ruling, PR comment 6184246424 (Q2: A). The gate stays red until a person takes that decision, and noskip-changesetlabel is applied.scripts/measure-test-shard-timings.mjschanged onorigin/main(fc1a55a212) after the base.git merge-tree --write-tree HEAD origin/mainis clean (exit 0, tree84fc586f90), soorigin/mainwas not merged in, by the order. Since the base, main has touched nothing inpackages/cli,packages/runtime,packages/driversor the 23400 changeset.Acceptance notes
os migrate recorded-byandos migrate resume(read, not measured, not edited). Both boot the same read-only stack and print "Nothing was changed.", so on a SQLite file still on a rollback journal both sentences are false in the same way:recorded-by.ts:105-110: the dry run callsbootSchemaStack({ …, deferSchemaDdl: true, readOnlyProbe: true })(:109, when no--applyis given).resume.ts:114-119: the list mode calls the same (:118, when no--runis given).createStandaloneStack,SqlDriver.connect(),applySqliteJournalMode().printInfo("Nothing was changed. …"):recorded-by.ts:158(pending sentinel rows exist) andresume.ts:172(interrupted runs exist).security-catalog-overlays.ts:198remainder.security-catalog-overlays.ts:198("The preview below writes nothing.") stays out by the seat's Q1 ruling: PR feat(metadata-core,metadata-protocol)!: the sys_metadata family declares no organization column; indexes re-keyed (ADR-0131 D6/D7, C7b S2) #23469 holds the file. The seat files it as its own card, ordered after PR feat(metadata-core,metadata-protocol)!: the sys_metadata family declares no organization column; indexes re-keyed (ADR-0131 D6/D7, C7b S2) #23469, using the exportedJOURNAL_MODE_SWITCH_NOTE.sqliteJournalModefrom the header it finds.organization-ownership.tsimports the constant on its own line (24), beside thedescribeOccupancyimport, because the claim holds line 23 off-limits.This body was updated for patch round 1 by session
session_01Q9PZ5Pzqcus4woVvTZwWNUthrough the fleet relay; the original was written by the same session at PR creation.