Skip to content

Commit cc57860

Browse files
committed
fix(cli): the multi-value-columns help, the occupancy changeset and the 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>
1 parent 86ce110 commit cc57860

9 files changed

Lines changed: 14 additions & 13 deletions

‎.changeset/23400-ownership-occupancy-gate.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Clause-②: no (narrowing)
1212

1313
**FROM.** `os migrate organization-ownership --apply` opened a SQLite database that another process held open (a running `os dev` or `os serve`, or a runtime's pooled connection) without checking for it, and ran the ceremony beside that process. `os migrate apply` and the other data migrations probe the database first; this command did not. One measured run of the plan and then `--apply` beside a runtime holding a connection left that runtime writing to a deleted `-wal` file, and a row it wrote afterwards never reached the database.
1414

15-
**TO.** Before it boots the stack, the command probes the database the way its siblings do. Beside a holder, `--apply` is refused with exit 1: the ceremony boots nothing and writes nothing (the probe itself opens the file only briefly, to check). The check runs first, so a busy `--apply` that also lacks its plan file or its backup acknowledgement answers reason `database-busy` rather than `plan-file-required` or `backup-unacknowledged`; it is refused either way. The text output names the file and, when the probe can tell, the process holding it, then the remedy: stop the process using it and re-run. With `--json` the refusal uses this command's own envelope: `{ "error": "apply_refused", "reason": "database-busy", "database", "signal", "detail", "hint" }`. There is no `--force`: the ceremony has no flag that skips its reviewed plan or its backup either, and its refusal never names one. The plan (no `--apply`) writes nothing, so it still runs. Its text output warns that `--apply` will refuse until the database is free, and its `--json` document is unchanged.
15+
**TO.** Before it boots the stack, the command probes the database the way its siblings do. Beside a holder, `--apply` is refused with exit 1: the ceremony boots nothing and writes nothing (the probe itself opens the file only briefly, to check). The check runs first, so a busy `--apply` that also lacks its plan file or its backup acknowledgement answers reason `database-busy` rather than `plan-file-required` or `backup-unacknowledged`; it is refused either way. The text output names the file and, when the probe can tell, the process holding it, then the remedy: stop the process using it and re-run. With `--json` the refusal uses this command's own envelope: `{ "error": "apply_refused", "reason": "database-busy", "database", "signal", "detail", "hint" }`. There is no `--force`: the ceremony has no flag that skips its reviewed plan or its backup either, and its refusal never names one. The plan (no `--apply`) writes no row or table, so it still runs. Its text output warns that `--apply` will refuse until the database is free, and its `--json` document is unchanged.
1616

1717
**Unchanged.** A PostgreSQL, MySQL or remote libSQL target is not probed. A probe that cannot tell (for example, the database file cannot be read) warns in the text output and proceeds; under `--json` it is silent. The probe reads the project's `.env` files, as the run itself does.
1818

‎.changeset/23562-read-only-arms-journal-mode-note.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ fix(cli): the read-only `os migrate` arms say they write no row or table, and th
66

77
**What was wrong.** These arms boot read-only: no DDL, no seed, SELECT statements only. But the boot still opens the database through the SQL driver, and the driver's connect switches a SQLite file to the journal mode it asks for: WAL, unless `OS_DATABASE_SQLITE_JOURNAL_MODE=delete` is set in the command's own environment or the project's `.env` (then a WAL file is switched back to a rollback journal). On a file still on a rollback journal, a plan or a dry run therefore rewrote the file's header (the journal-mode bytes and the change counter) while saying "writes nothing" or "nothing in the database was changed". No row or table changed, and nothing was lost.
88

9-
**What changed.** Each sentence below now claims no row or table, and is followed by one shared note: "Opening a SQLite file can still switch its journal mode (to WAL by default), which changes its header only."
9+
**What changed.** Each sentence below now claims no row or table, and each warning is followed by one shared note: "Opening a SQLite file can still switch its journal mode (to WAL by default), which changes its header only."
1010

1111
- `os migrate organization-ownership` (the plan), beside a process holding the file. FROM: "The plan below writes nothing, but --apply will refuse until the database is free." This sentence has not been published: it arrives in this same release, with the occupancy gate the ceremony's `--apply` now takes. TO: "The plan below writes no row or table, but --apply will refuse until the database is free." and the note.
1212
- Its closing line. FROM: "Plan written to FILE — nothing in the database was changed." TO: "Plan written to FILE — no row or table in the database was changed." On a SQLite target the note follows; on PostgreSQL or MySQL the line ends there.
1313
- `os migrate plan`, beside a holder. FROM: "The plan below is still accurate — nothing is written — but "os migrate apply" will refuse until it is free (or you pass --force)." TO: "The plan below is still accurate — it writes no row or table — but …", the rest unchanged, and the note.
1414
- The dry runs of `os migrate audit-metadata-bodies`, `comment-reactions`, `files-to-references` and `summary-nulls`, beside a holder. FROM: "The dry run below writes nothing, but its counts may shift while that process is running." TO: "The dry run below writes no row or table, but its counts may shift while that process is running." and the note.
1515
- The dry run of `os migrate multi-value-columns`, beside a holder. FROM: "The dry run below writes nothing." TO: "The dry run below writes no row or table." and the note.
16+
- The help description of `os migrate multi-value-columns` (`os migrate multi-value-columns --help`). FROM: "… Dry-run by default: prints the exact statements and the database they would run against, and writes nothing." TO: "… and writes no row or table." The help text carries no note.
1617
- The preview of `os migrate meta --stored`, beside a holder. FROM: "The preview below writes nothing, but a live process may be saving metadata while it runs." TO: "The preview below writes no row or table, but a live process may be saving metadata while it runs." and the note.
1718

1819
**Unchanged.** No exit code, `--json` document or refusal changes; the warnings are still printed in the text output only. What the driver does on connect is unchanged: once a file is in the mode the driver asks for, a read-only run leaves it byte-identical.

‎packages/cli/src/commands/migrate/audit-metadata-bodies.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ async function confirm(question: string): Promise<boolean> {
5454
* withholds the hashes in those notes, in the same pass. Operators run it once
5555
* after upgrading, dry run first.
5656
*
57-
* Dry run by default (writes nothing), `--apply` to rewrite. Idempotent: a
57+
* Dry run by default (writes no row), `--apply` to rewrite. Idempotent: a
5858
* second run finds nothing — a redacted copy has no credential and no hash
5959
* left — so re-running and reading a clean report is the verification. No
6060
* `sys_migration` flag is recorded: nothing gates irreversible behaviour on this

‎packages/cli/src/commands/migrate/files-to-references.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ async function confirm(question: string): Promise<boolean> {
9595
* platform version) is what later opens released-file collection (#3459) and
9696
* strict media value-shape enforcement (#3438) on this deployment.
9797
*
98-
* Dry run by default, and a dry run writes NOTHING — not conversions, not the
98+
* Dry run by default, and a dry run writes NO ROW — not conversions, not the
9999
* flag. Not run / not passed → files keep being retained forever: storage
100100
* cost, zero data loss.
101101
*
@@ -188,7 +188,7 @@ export default class MigrateFilesToReferences extends Command {
188188
return;
189189
}
190190
if (occupancy.status === 'busy' && !flags.json) {
191-
// A dry run writes nothing, so it only ever warns — but it warns, because
191+
// A dry run writes no row, so it only ever warns — but it warns, because
192192
// the numbers it reports are a moving target while another process writes.
193193
printWarning(apply
194194
? `--force: ${describeOccupancy(occupancy)} Converting anyway — the live process may write records mid-scan.`

‎packages/cli/src/commands/migrate/meta.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1486,7 +1486,7 @@ export default class MigrateMeta extends Command {
14861486
// takes. A plain boot runs schema sync and the artifact's inline seed
14871487
// loader, and the seed upserts every seeded row of the app's tables
14881488
// (`updated_at` bumped, `organization_id` stamped, relative-date values
1489-
// re-evaluated) before the report says "writes nothing". `deferSchemaDdl`
1489+
// re-evaluated) before the report says "writes no row". `deferSchemaDdl`
14901490
// holds the DDL back and suppresses the seed; `readOnlyProbe` keeps a
14911491
// missing sqlite file from being created. `--apply` keeps the plain boot:
14921492
// it is the writing mode, and its behaviour is unchanged.

‎packages/cli/src/commands/migrate/multi-value-columns.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ export default class MigrateMultiValueColumns extends Command {
331331
static override description =
332332
'Migrate a stale varchar/text column to json where the field declares multiple: true; boot and ' +
333333
'os migrate plan only report such a column and never alter it unattended. ' +
334-
'Dry-run by default: prints the exact statements and the database they would run against, and writes nothing.';
334+
'Dry-run by default: prints the exact statements and the database they would run against, and writes no row or table.';
335335

336336
static override examples = [
337337
'$ os migrate multi-value-columns',

‎packages/cli/src/commands/migrate/organization-ownership.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@ async function confirm(question: string): Promise<boolean> {
7272
* The boot is the family's read-only boot (`deferSchemaDdl` + `readOnlyProbe`:
7373
* no DDL, no seed, no database file brought into existence), and the plan
7474
* itself issues SELECT statements only (`../../utils/organization-ownership-plan.ts`).
75-
* The one thing it writes is the plan FILE, on the operator's disk, which the
76-
* ADR asks for: "the plan is written to a file the operator keeps". It never
77-
* overwrites one — a kept plan is evidence.
75+
* It writes no row or table; the one thing it writes is the plan FILE, on the
76+
* operator's disk, which the ADR asks for: "the plan is written to a file the
77+
* operator keeps". It never overwrites one — a kept plan is evidence.
7878
*
7979
* ## Refusal
8080
*

‎packages/cli/src/commands/migrate/plan.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import {
5252
* boot-time create-table / add-column sync that used to run before this command
5353
* printed a single line is now REPORTED as pending work instead of performed.
5454
* A database another process is using is reported too — as a warning, not a
55-
* refusal, since a plan writes nothing either way.
55+
* refusal, since a plan writes no row either way.
5656
*
5757
* Since #6743 the dry run also stops short of CREATING the database. The
5858
* deferral #3917 introduced covered the DDL and the seed but not the open
@@ -140,7 +140,7 @@ export default class MigratePlan extends Command {
140140
jsonOutput: flags.json,
141141
databaseUrl: flags['database-url'],
142142
deferSchemaDdl: true,
143-
// A plan writes nothing — so it must not bring a database file into
143+
// A plan writes no row — so it must not bring a database file into
144144
// existence either (#6743). On a never-started project the target is
145145
// opened as an empty in-memory database: the plan below is unchanged
146146
// (an empty database is an empty database), and no file, `-wal` or

‎packages/cli/src/commands/migrate/summary-nulls.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ async function confirm(question: string): Promise<boolean> {
5151
* A freshly seeded database has no such rows; this command is for the other
5252
* kind, and is needed exactly once per deployment.
5353
*
54-
* Dry run by default (writes nothing at all), `--apply` to backfill. Each
54+
* Dry run by default (writes no row at all), `--apply` to backfill. Each
5555
* `NULL` row is RECOMPUTED — a pre-upgrade parent that has children is `NULL`
5656
* too and its correct value is the real aggregate, so writing 0 everywhere
5757
* would swap a missing value for a wrong one. Idempotent: a second run finds

0 commit comments

Comments
 (0)