You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit fcc1387
Browse filesBrowse the repository at this point in the historyBrowse files
docs(cli): say what a read-only boot's first connection writes to a SQLite 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
that does not exist is not created. Its first connection can still change a SQLite
549
+
file's header; [Data migrations](#data-migrations) says when. Pointed at a database
550
+
that lacks `sys_secret` (one that was never booted, or the wrong `--database-url`), it
551
+
does not read the table, since a table that does not exist holds no row: it reports
552
+
nothing to act on, names the tables it did not read, and exits 0. Any other read it
553
+
cannot make still refuses and exits 1 (under `--json`: `"error": "scan_failed"`).
553
554
554
555
```bash
555
-
os secret orphans # report (writes nothing)
556
+
os secret orphans # report (writes no rows)
556
557
os secret orphans --json # the same report, machine-readable
557
558
os secret orphans --no-declared-datasources # state that this host declares none
558
559
os secret orphans --delete --export ./secrets-backup.json --no-declared-datasources
@@ -599,11 +600,11 @@ Re-wraps the `sys_secret` ciphertext sealed before secrets were bound to the pro
599
600
that wrote them, so it carries the current binding (ADR-0128). Each row is re-sealed
600
601
under the scope of the producer whose holder references it: a setting, an object's
601
602
`secret` field, or a datasource credential. **A dry run by default: without `--apply`
602
-
it writes nothing.** It is never run for you; nothing on any boot or upgrade path
603
+
it writes no row.** It is never run for you; nothing on any boot or upgrade path
603
604
invokes it.
604
605
605
606
```bash
606
-
os secret rewrap --no-declared-datasources # dry run (writes nothing)
607
+
os secret rewrap --no-declared-datasources # dry run (writes no rows)
607
608
os secret rewrap --json --no-declared-datasources # the same, machine-readable
608
609
os secret rewrap --declared-datasources ./datasources.json
609
610
os secret rewrap --apply --no-declared-datasources # write the re-wrapped rows
@@ -865,10 +866,10 @@ os migrate plan --json # Machine-readable output
865
866
os migrate unmapped-columns --object contact --json # A retired field's stored values, keyed by record id (read-only)
866
867
```
867
868
868
-
#### Nothing is written before you confirm
869
+
#### Nothing is applied before you confirm
869
870
870
-
Both commands boot your app to read its metadata. That boot no longer touches
871
-
the target database: the additive schema sync (create missing tables, add
871
+
Both commands boot your app to read its metadata. That boot writes no row and no
872
+
schema to the target database: the additive schema sync (create missing tables, add
872
873
missing columns) and the artifact's inline seed data are **deferred**, not
873
874
performed. So `plan` really is a dry run, and everything `apply` is about to do
874
875
— additive work included — is on screen before the `[y/N]` prompt:
@@ -885,7 +886,9 @@ performed. So `plan` really is a dry run, and everything `apply` is about to do
885
886
✓ crm_contact.email [relax_not_null]
886
887
```
887
888
888
-
Answering `n` leaves the database exactly as it was.
889
+
Answering `n` leaves every table and row exactly as it was. The boot's first
890
+
connection can still change a SQLite file's header; [Data migrations](#data-migrations)
891
+
says when.
889
892
890
893
The two upper sections differ in a way worth reading carefully. **New** is
891
894
purely additive — it creates tables and columns and never touches a row. **In
@@ -930,7 +933,7 @@ written.
930
933
931
934
| Command | If the database is in use |
932
935
|---------|---------------------------|
933
-
| `os migrate plan` | Warns and continues — a plan writes nothing either way |
936
+
| `os migrate plan` | Warns and continues — a plan writes no row and no schema either way |
934
937
| `os migrate apply` | **Refuses** (exit 1, `error: database_busy` under `--json`). Stop the other process, or pass `--force` |
935
938
| `os migrate files-to-references --apply` | **Refuses** likewise — it rewrites rows, so a concurrent writer is at least as dangerous |
936
939
| `os migrate meta --stored --apply` | **Refuses** likewise — it rewrites `sys_metadata` rows, and a live process saving metadata is exactly the collision |
@@ -1002,8 +1005,8 @@ os migrate multi-value-columns --table crm_case # Restrict to one physical
1002
1005
os migrate multi-value-columns --database-url postgres://…
1003
1006
```
1004
1007
1005
-
**Take a backup first.** The dry run is the default and writes nothing at all —
1006
-
not a probe, not a temporary table — so run it, read the statements it prints,
1008
+
**Take a backup first.** The dry run is the default and writes no row and no schema
1009
+
at all — not a probe, not a temporary table — so run it, read the statements it prints,
1007
1010
and only then re-run with `--apply`.
1008
1011
1009
1012
The statement is the one the drift finding itself prints, per dialect, and the
@@ -1077,8 +1080,8 @@ them into the declared fields with your own script, then run
1077
1080
own `id`, `created_at` and `updated_at`.
1078
1081
- **Operator-only and read-only.** It runs under the database credentials you
1079
1082
pass, and covers every organization's rows. There is no REST route or API
1080
-
flag behind it. It boots the way `plan` does, so it writes nothing, and it
1081
-
drops nothing.
1083
+
flag behind it. It boots the way `plan` does, so it writes no row and no schema,
1084
+
and it drops nothing.
1082
1085
- **Values as stored.** An unmapped column has no declared type, so each value is
1083
1086
emitted as the database client returns it, with no field-type decoding: a
1084
1087
retired `json` field on SQLite reads as its stored text, a retired `boolean`
@@ -1111,9 +1114,9 @@ where the data lives.
1111
1114
| `os migrate value-shapes` | Scan stored reference and structured-JSON field values against the platform's value contract, and record the deployment's migration flag when clean |
1112
1115
| `os migrate summary-nulls` | Backfill roll-up `count` / `sum` columns still stored as `NULL` on parent rows created before the insert-time seed. Repairs values; no flag, nothing depends on it having run |
1113
1116
| `os migrate meta --stored` | Replay the metadata conversion chain over this deployment's `sys_metadata` rows and rewrite the ones still carrying a pre-protocol shape. Hygiene, not a gate — nothing depends on it having run |
1114
-
| `os migrate duplicates` | Report business identifiers already minted twice across the organization partitions, and the rows blocking the boot-time NULL-safe index tightenings — a read-only inventory as JSON on stdout. Renumbers nothing and writes nothing at all; run it before the boot-time tenancy repair, which overwrites part of the evidence |
1117
+
| `os migrate duplicates` | Report business identifiers already minted twice across the organization partitions, and the rows blocking the boot-time NULL-safe index tightenings — a read-only inventory as JSON on stdout. Renumbers nothing and writes no row and no schema; run it before the boot-time tenancy repair, which overwrites part of the evidence |
1115
1118
1116
-
**The boot itself writes nothing you did not ask for.** Each of these commands boots
1119
+
**The boot itself writes no row and no schema you did not ask for.** Each of these commands boots
1117
1120
your app to read its metadata. Without `--apply`, that boot is read-only, the same boot
1118
1121
`os migrate plan` takes: the schema sync is held back, the app's inline seed data is not
1119
1122
loaded, and a SQLite file that does not exist is not created. With `--apply`, the boot
@@ -1132,6 +1135,16 @@ report no secret and no file. A read the command cannot avoid and that fails for
1132
1135
other reason still refuses and exits 1. Point `--database-url` at the deployment's
1133
1136
database, or boot the deployment once first, to see what it holds.
1134
1137
1138
+
**The first connection to a SQLite file can change its header.** SQLite keeps a file's
1139
+
journal mode in the file itself, and every ObjectStack connection switches a file still
0 commit comments