docs(db2-7): story closeout — acceptance met per task, progress table, WO_DATA is always a path

- frontmatter `status: done`, `readiness: ready`, `review_pending` (the
  nonexistent-path rule settled under autonomy 2026-09-10)
- every criterion met with its evidence: task 1 smokes A–D (`b31bd40`),
  residency section 8 checks i–vii (`aaea6b2`), db-bench `--wo-data-file`
  181 checks / 5 failures — the same 5 as the directory form
  (`residency.keys.fit` rc 74, databasev2 13's sibling, not this defect),
  temps-beside-the-log test plus its mutation control (`ccee2d0`)
- the fork, settled: existing dir or trailing `/` → <dir>/shard-0.wal,
  byte-identical; otherwise the path IS the log, created behind an existing
  parent; a missing parent or a non-regular non-directory path refuses,
  exit 2, naming path and parent — never a silent mkdir -p
- `WO_DATA` stays a path: ephemerality is WO_EPHEMERAL=1 (databasev2 2 task
  6a), so the file-vs-directory parse carries no `:memory:` sentinel
- progress table with the four hashes; history

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 38f4f1e5832e79b9834a93fd6c3273fefe5eeadc)
This commit is contained in:
shoney.arickathil 2026-09-15 01:04:20 +02:00
parent 79352bd95c
commit 9b5498fafe

View file

@ -2,8 +2,9 @@
track: databasev2 track: databasev2
iteration: "7" iteration: "7"
was_language_iteration: "33" was_language_iteration: "33"
status: pending status: done
readiness: refine readiness: ready
review_pending: "fork settled 2026-09-10 for autonomous execution (nonexistent-path rule below) — developer second review before close"
--- ---
# databasev2 7 — `WO_DATA=<path>.db`: the persistent store as one file # databasev2 7 — `WO_DATA=<path>.db`: the persistent store as one file
@ -34,21 +35,69 @@ readiness: refine
the WAL owner-shard-only (shard 0 is the sole writer), so nothing the WAL owner-shard-only (shard 0 is the sole writer), so nothing
multi-shard ever adds a second file. multi-shard ever adds a second file.
- **The contract says it out loud**: `04-db-binding.md` documents the - **The contract says it out loud**: `04-db-binding.md` documents the
file form, and documents honestly that the file is an append-only log file form, including that compaction (databasev2 3, landed 2026-08-29) and
that grows until iteration 32 lands. schema migration (databasev2 12) both rewrite the log through a temp file +
`rename` beside it and fsync the parent directory — so the file form must
derive that directory from the file path for both.
- **`WO_DATA` is always a path.** Ephemerality is a separate variable —
`WO_EPHEMERAL=1`, decided with [databasev2 2](02-table-storage-modes.md)'s
task 6a on 2026-09-09 — so the file-vs-directory parse here never
special-cases a sentinel such as `:memory:`. Both variables set together is
already a startup refusal there; the file form inherits it unchanged.
## Acceptance Criteria ## Acceptance Criteria
Met:
- **Given** `WO_DATA=/tmp/app.db` (no such directory), **when** a - **Given** `WO_DATA=/tmp/app.db` (no such directory), **when** a
program seeds, restarts, and verifies, **then** replay is byte-true program seeds, restarts, and verifies, **then** replay is byte-true
and `/tmp/app.db` is the only artifact on disk. and `/tmp/app.db` is the only artifact on disk. ✅ task 1 smoke A
(`b31bd40`): seed creates the file, restart+`order` prints the same
lines as the directory form, `ls` shows `app.db` as the only artifact.
Confirmed at gate level by `residency-accept.sh` section 8 check (i)
(`aaea6b2`): `mix.wob` seed → restart prints `kept=1 scratch=0`;
`find f8/a -mindepth 1 -printf %P` == `app.db`.
- **Given** `WO_DATA=<dir>` (existing directory), **when** the same - **Given** `WO_DATA=<dir>` (existing directory), **when** the same
program runs, **then** behavior is byte-identical to today — program runs, **then** behavior is byte-identical to today —
`<dir>/shard-0.wal`, every standing gate unchanged. `<dir>/shard-0.wal`, every standing gate unchanged. ✅ task 1 smoke D
(`$W/d1` and `$W/d2/` both produce `shard-0.wal` inside the dir,
unchanged form); section 8 check (iv) pins the same as a regression.
- **Given** the db-bench durability legs pointed at the file form, - **Given** the db-bench durability legs pointed at the file form,
**when** the restart proof and kill -9 battery run, **then** every **when** the restart proof and kill -9 battery run, **then** every
guarantee holds identically (the file IS the same WAL, only named by guarantee holds identically (the file IS the same WAL, only named by
the operator). the operator). ✅ `./scripts/db-bench.py --quick --wo-data-file`:
**`db-bench: 181 checks, 5 failures`** — the same 5 as the directory
form (`residency.keys.fit` rc 74, not this defect; see databasev2 13);
`restart.s1.file`, `crash.s1.0.file`, `restart.sN.file`,
`crash.sN.0.file` all ok. `residency-accept.sh` section 8 check (vi):
inline `onefile.wo` kill -9 battery, N=1e6, killed at 0.20–0.49s,
asserts rc 137 (the kill landed) before verifying — landed after
45781 acks on tmpfs, all replayed.
- **Given** `WO_DATA=/tmp/app.db` where `/tmp/app.db` does not exist and
`/tmp` does, **when** the program starts, **then** the file is created
and used as the log; **given** `WO_DATA=/tmp/app.db/` (trailing slash)
or an existing directory, **then** the directory form applies; **given**
a path whose parent directory does not exist, **then** startup refuses
with exit 2 naming the path and the parent — never a silent `mkdir -p`.
✅ task 1 smoke B (missing parent: rc 2, `its parent <path>/nope is not
an existing directory; create it first`) and C (fifo: rc 2, `neither a
regular file nor a directory`); section 8 checks (ii) missing parent
(rc 2, message matches `its parent .*/nope is not an existing
directory` + `nope/app.db`, asserts `! -e nope`) and (iii) fifo (rc 2
under `timeout 10`, so a wrong resolution blocking on the fifo also
fails); section 8 check (iv) `nodir/` (trailing slash, missing dir) →
rc 2 `cannot open .*/nodir//shard-0.wal`, byte-identical to pre-7.
- **Given** the file form, **when** compaction (databasev2 3) or a schema
migration (databasev2 12) rewrites the log, **then** the temp file is
`<file><suffix>` beside it and the fsync'd parent is the file's own
directory — pinned by a test, since both paths today derive from the
log path rather than from `WO_DATA`. ✅ `test_file_form_temps_beside_log`
(`ccee2d0`, `test_wal: 6629 pass, 0 fail`, +57 assertions), teeth proven
by a mutation control (compaction temp redirected into a decoy dir):
14 assertions turn red. Gate-level: section 8 check (vii)
(`WO_CHECKPOINT_BYTES=1 WO_WAL_STATS=1 wal 300`): `compactions=` >= 1
asserted (5 measured), `app.db` still the only artifact, `verify 300`
rc 0.
## Out Of Scope ## Out Of Scope
@ -58,7 +107,9 @@ readiness: refine
rename-swap (write snapshot+tail to a NEW file, fsync, `rename()` rename-swap (write snapshot+tail to a NEW file, fsync, `rename()`
over the old) is exactly what keeps the single-file promise crash-safe over the old) is exactly what keeps the single-file promise crash-safe
when it lands. 33 before or after 32 works; landing 33 first means when it lands. 33 before or after 32 works; landing 33 first means
32's spec inherits the file form as a stated constraint. 32's spec inherits the file form as a stated constraint. *(2026-09-10:
32 is databasev2 3, landed 2026-08-29; this iteration lands after it
and pins the constraint with a test — see Progress task 2.)*
- Multiple stores per process, attach-by-file — held iteration 20's - Multiple stores per process, attach-by-file — held iteration 20's
territory. territory.
@ -73,9 +124,36 @@ readiness: refine
a path whose parent exists and that does not end in `/` is a file to a path whose parent exists and that does not end in `/` is a file to
create; a trailing `/` or existing directory keeps the directory create; a trailing `/` or existing directory keeps the directory
form. Refusing ambiguity loudly (WO exit 2) beats guessing. form. Refusing ambiguity loudly (WO exit 2) beats guessing.
**Settled 2026-09-10 (auto-approved for autonomous execution,
`review_pending`): the leaning IS the rule.** Existing directory or
trailing `/` → `<dir>/shard-0.wal` (byte-identical to today). Otherwise
the path is the log file: created if absent, opened if a regular file.
A parent directory that does not exist → startup refusal, exit 2, one
stderr line naming the path and the missing parent (no `mkdir -p`: a
typo must not create a store somewhere unexpected). A path that exists
but is neither a regular file nor a directory (fifo, socket, device) →
the same refusal shape. `WO_EPHEMERAL` interplay is inherited from
databasev2 2 task 6a unchanged. Compaction and migration already build
their temp as `<log path><suffix>` and sync the log's parent directory
(`database/src/wal.c` `sync_parent_dir`), so the file form inherits
crash safety without engine changes; a test pins that.
## Progress
| # | Task | State |
| --- | --- | --- |
| 1 | `runtime/src/main.c`: stat-based resolution of `WO_DATA` (dir / trailing slash → `shard-0.wal`; else the file), the two refusals (missing parent; not a regular file or directory), message names path and parent | ✅ `b31bd40` |
| 2 | `runtime/test`: pin that compaction and migration temps land beside an arbitrary log path and the parent of THAT path is synced (file-form path with a sibling directory as decoy) | ✅ `ccee2d0` |
| 3 | Contract + memory: `docs/plan/oop-vm/04-db-binding.md` file form; `database/src/CODE-LOGIC.md` or `runtime/src/CODE-LOGIC.md` note on path resolution | ✅ `f1985ba` |
| 4 | Gate: `scripts/residency-accept.sh` or a db-bench durability leg pointed at the file form — seed, restart, kill -9 battery, only-artifact check; refusal checks for the missing parent (codd-cyril) | ✅ `e274f4a` + `aaea6b2` |
## Proposed Solution ## Proposed Solution
Small enough for a bounded slice: brainstorm the one fork in chat, Small enough for a bounded slice: brainstorm the one fork in chat,
implement driver + db-bench file-form leg + docs in one pass, gates implement driver + db-bench file-form leg + docs in one pass, gates
green. No plan document needed unless it grows. green. No plan document needed unless it grows.
## History
- 2026-09-10 tasks 1–3 landed; 4 in flight.
- 2026-09-10 closed: task 4 landed, residency 32/0.