Commit graph

17 commits

Author SHA1 Message Date
296efb52ed docs(db2-ephemeral): databasev2 2 closes — task 6a contract, forks 1–7, the README sweep
- story 02: `status: done`, `review_pending` (forks 1–7 auto-approved for
  autonomy); progress rows 6a ✅, 6b ➡ databasev2 5 Phase A, 7 `a310496`;
  5c/5d rows cite the `dev` hashes (the pre-merge ones were unreachable);
  task 6a's Given/When/Then met; Info records the seven forks (sentinel over
  `:memory:`, its rules, the refusal contract, startup-only, the budget
  leaves for 5, library-owned tables bind consumers, the v8 table bit);
  History keeps the first cut that refused every class-bearing program
- database/src/CODE-LOGIC.md: "Startup refusal + WO_EPHEMERAL" — contract,
  hatch, table bit, measured blast radius, deferred items, proof; the
  dispatcher paragraph no longer says a failed commit un-applies the row
  (fatal since databasev2 4 part A; WO_T_IO unreachable from a write path)
- residency spec + plan: task 6 items annotated with the 2026-09-09
  decisions; the byte budget marked moved to databasev2 5
- README, seven example READMEs and four guides carry the one-line rule
  (durable default refuses without WO_DATA; WO_EPHEMERAL=1; durable:
  false); shop's RAM-only command sets the sentinel

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 2c3531998124042fe736388e8b926abda3841194)
2026-09-15 01:16:24 +02:00
841f6c8f3f feat(db2-keys): gate the residency measurement, close out task 7
- new `residency` leg in db-bench.py driving docs/examples/residency-bench:
  two tables identical except the annotation, control cap + binding cap
- ITS OWN PROGRAM, not a db-bench mode: declaring a resident: keys table
  is a WHOLE-PROGRAM constraint, so the no-WO_DATA refusal fires for
  every mode in the module. Putting those classes in db-bench's shared
  types made growth/ceiling/randread — which run without WO_DATA —
  refuse to start. Caught by running the leg, not by reading it
- gates the RATIOS, waives the absolutes: ops/sec under a cap is swap
  and disk I/O and belongs to the box. Same split randread makes
- rss_ratio 2.55 floor 2.0 tol 10% (structural, like bytes_per_row);
  overcap_vs_swap_x 1.53 floor 1.0; in_ram_cost_x 4.23 ceiling 8.0;
  all_collapse_x 105.4 floor 2.0
- all_collapse_x exists because the leg's FIRST run silently measured
  nothing: at QUICK's 40k rows a 48 MiB cap binds neither mode, so the
  "over-cap" half was not over cap. The cap now scales with N and the
  leg asserts it binds
- verified the gate bites: rss_ratio 1.4, overcap_vs_swap_x 0.6 and
  in_ram_cost_x 12.0 are all rejected
- task 7 closed: both criteria moved to Met with how each was verified

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit a310496664982c372f51b43113465eb8ad9e9fb5)
2026-08-30 20:38:03 +02:00
882c1f7d24 feat(db2-keys): task 7 — measure resident: keys against swapping
- two tables identical except the annotation, 200k rows, 40k reads in
  one key order, WAL on ext4 (not /tmp, which is tmpfs here and would
  have put the log in RAM), rootless cgroup v2 cap
- WIDE shape, 2.55x smaller resident set: 34.4 MB vs 87.5 MB. That is
  the real win and the thing the mode was built for
- under a 48 MB cap (between the two resident sets): keys 19635 ops/s
  vs all 12854 — only 1.53x faster than letting the kernel swap
- degradation is far gentler though: all collapses 105x from its own
  uncapped throughput, keys 16x
- costs 4.2x read throughput when memory is not tight, and writes are
  markedly slower — the keys fill did not finish in 2 min where the
  resident fill plus 40k reads did. No design doc had costed writes
- THE UNANTICIPATED FINDING: cgroup limits charge the PAGE CACHE, so
  moving rows to a file does not escape a container memory limit. WAL
  37 MB + RSS 34 MB cannot both live under a 48 MB cap, so every pread
  reaches disk. The premise "the page cache will hold the hot rows"
  fails in exactly the deployment this targets
- first attempt used Int-only rows and showed parity; recorded, because
  drop_payload frees a field's VALUE and an Int's value is its inline
  slot word, so that shape cannot benefit and would have condemned the
  feature for the wrong reason
- verdict: keep it, to fit ~2.5x more data in given RAM — not to make
  an over-capacity table fast

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 7cba9b1174b0bf581314b3147e25cc49e6f49464)
2026-08-30 20:38:03 +02:00
cfd660a5e6 docs(db2-chains): spec + story for bounding a row's delta chain
- fixes a limitation iteration 2 shipped: compaction was supposed to
  bound chain length, but wo_wal_should_compact triggers on a whole-log
  byte ratio and cannot see one hot row's chain
- tier 1, flatten on update: the update path ALREADY folds the row for
  index maintenance and the fold already walks hop by hop, so it reports
  depth for free. Past a fixed K it writes a full row instead of a
  delta. Read <= K+1 reads, replay O(K^2) per row. No format change, no
  per-row RAM, no new trigger
- tier 2: our compaction policy has a proportional term and a
  SUPPRESSOR misleadingly called a floor; postgres's floor TRIGGERS on
  small absolute garbage. Add that term and a ceiling
- design read from .dev/reference/postgresql, not recalled:
  heap_page_prune_opt gates on an O(1) on-page hint then page fullness
  against Max(fillfactor, BLCKSZ/10); autovacuum uses base + scale *
  reltuples clamped by a max (50, 0.2, 1e8). Neither thresholds on
  new-bytes-versus-old-bytes
- K deliberately does NOT scale with table size: postgres scales a
  table-level aggregate with proportional harm, ours is per-row with
  additive cost, so scaling up would make big databases boot worst
- the story says plainly it should NOT be next: task 7 has still never
  measured whether resident: keys beats the kernel's own paging

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit f667cad2cfbe187b5973440ab1af015b2df288f8)
2026-08-30 20:38:03 +02:00
64035bf177 docs(db2-delta): resident:keys has storage; move done criteria to Met
- "no storage behind it" / "nothing yet stores a table that way" was
  false — CRUD, checkpoint survival and updates all landed; replaced
  with an accurate summary naming task 6/7 as what remains
- the three checked delete/delete-replay/update criteria sat in
  Outstanding despite being done; moved to Met, leaving Outstanding
  holding only genuine task 6/7 work

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit b575678ee95fa3125fa4e8145320a9cdca10ba38)
2026-08-30 20:38:03 +02:00
e08c26309a feat(db2-delta): lift the resident:keys refusal, prove it end to end
- loader.c: delete the INCOMPLETE-update BAIL; durable:false +
  resident:keys stays refused (nowhere to read from)
- table.c: root-cause fix for the Text-index gap — a keys-resident
  borrow now holds ENGINE values, matching wo_row_ptr's contract
  (table.h's "no VM pointer" doctrine), not a VM-decoded row. Fixes
  idx_hash/idx_cols_equal/wo_idx_probe AND db.c's GET_FIELD/PROBE
  arms with one change; reproduced pre-fix as an ASan
  heap-buffer-overflow
- docs/examples/residency: Product is genuinely resident:keys;
  residency-accept.sh's refusal leg replaced by proving the program
  runs and stock survives a restart (11/0)
- test_wal.c: oracle test drives resident:all and resident:keys
  through the same update sequence and asserts identical rows;
  Text-indexed-update test catches the representation bug; five
  pre-existing tests corrected to the fixed contract (4746/0)
- story, README, status board, CODE-LOGIC.md updated; three known
  limitations documented: mid-drain stale reads, O(N^2) replay in
  chain length, compaction blind to per-row chain length

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit b87c68f950f01aa5e572fbb86a0f374adc83d813)
2026-08-30 20:37:49 +02:00
7b39da7eb6 fix(db2-keys): a logged delete must replay on a keys-resident table
- wo_row_remove's keys arm borrows the row from the log to find its
  index entries, and a borrow reads through db->rt->wal. At boot that
  pointer is not wired yet: main.c replays first (main.c:226) and
  assigns rt.wal afterwards (main.c:268)
- so the borrow found no log, the remove failed, and replay reported a
  valid tombstone as CORRUPTION. An UPDATE record would have failed the
  same way, since replay applies it as remove-then-recreate
- replay now lends the runtime a read-only view over the fd it already
  has open, for the replay's duration only, and restores what was there
- broken by the delete fix in 76b8fd9 — deletes worked in-process but
  their tombstones broke the next boot. Unreachable in production only
  because the loader still refuses the annotation
- pinned by test_keys_resident_delete_then_replay, verified failing
  against the unfixed code (2 failures) and clean with it
- found by asking whether the read-modify-append plan was ready, not by
  a gate

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit dc25462461b9f79d70c803f7174adc90fa16c90e)
2026-08-30 20:37:27 +02:00
516bd8362d docs(db2-keys): a runnable example for per-table storage
- docs/examples/residency: one program, two tables filled by the same
  loop, differing only in the annotation. Run twice against one WO_DATA
  and orders replay while sessions do not
- the example checks its own claim (exits 1 if a durable:false table
  survives, or a durable:true one fails to replay) rather than narrating
  it in a print
- resident: keys is written out as a commented block with the loader's
  exact refusal, so the frontier is visible in the example rather than
  only in a story. It documents WHERE the refusal happens: woc compiles
  it and emits a .wob; wovm exits 2, because the annotation is a
  load-time property
- residency-accept gains two legs: the example runs and its restart
  claim holds, and the refusal message the README quotes is checked so
  doc and code cannot drift apart
- the gate writes the example's output to /tmp/residency.log,
  banner-separated, for tail -F
- README commands verified verbatim; they needed mkdir -p because wovm
  will not create WO_DATA

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit c9c7e03e62c3918cb65ef7994d1e33a0c5337b71)
2026-08-30 20:37:27 +02:00
7ad52937b2 fix(db2-keys): delete on a keys-resident table was memory corruption
- wo_row_remove read the id map's value as a slot, but on a keys table
  that value is a LOG OFFSET (hput(t, id, wal_off + 1)). slot_row does
  no bounds check, so a delete indexed t->slabs[] with a byte offset and
  then called db_val_free on whatever it landed on — arbitrary frees,
  not a wrong answer
- keys tables now take their own arm: no slab slot, no bitmap bit, no
  free-list entry to return. The index hook needs the row's values, so
  the row is borrowed from the log for exactly that long
- wo_row_ptr carried the same trap and is public. It cannot refuse keys
  tables outright (insert legitimately calls it while the map still
  holds a slot), so it now detects the offset case — index past the
  slabs, or bitmap bit clear — and returns NULL. Callers all handle NULL
- test_keys_resident_delete pins it; it SEGVs against the old code,
  verified by reverting the fix rather than assumed
- found while auditing every hget() reader before narrowing the loader
  refusal to allow benchmarking. The refusal was justified in the docs
  by "updates are unimplemented" while actually standing in front of
  this too: a guard whose stated reason is narrower than its real one
  gets removed by someone who believes the stated reason

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 76b8fd944af9ed062467bdf9ab93c2e96dd198cf)
2026-08-30 20:37:27 +02:00
533fc5294f feat(db2-keys): rewire remaining readers, survive compaction
- wo_row_read and the @unique shadow probe go through borrow/release;
  release runs before every exit, including wo_row_read's early return
- updates on a keys table refused explicitly in wo_row_update_field and
  the slot variant: no slab slot to mutate, and writing the borrow's
  scratch would discard the write silently. Needs read-modify-append
- compaction walked the bitmap, which a keys row has no bit in — every
  such row would have been dropped from the new log. Now walks
  wo_row_next_id and re-points each row to where it lands
- moves records byte-for-byte (copy_record) rather than decoding: a
  borrowed row holds VM values, enc_val expects engine values, and ASan
  caught that mismatch as a 4294967292-byte memcpy
- wo_row_set_offset updates a value in place and never rehashes, so a
  wo_row_next_id cursor stays valid while compaction re-points
- a compaction that fails after moving rows is fatal: the map would name
  an unlinked temp file, and the intact log replays correctly
- test_keys_resident_survives_compaction pins both failure modes; rows
  rewrite in hash order so offsets really move
- loader still refuses resident: keys — updates are not implemented

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit f606fc9b76d983cac2b348f03f7d4f01433cd905)
2026-08-30 20:36:31 +02:00
a873cf7331 feat(db-bench): random-read-over-cap leg — the 273x collapse
- `randread N R` in the sample: fill N rows, read R across the WHOLE range
- Weyl order `i*2654435761 mod n` — no RNG in the language, none needed;
  both legs read the SAME key order so residency is the only variable
- `randread` driver leg: control (256 MiB, does not bind) vs over-cap
  (6 MiB + swap), sizes kept modest — quick resolves it in ~5s
- gates the RATIO, not the absolutes: over-cap reads/sec belongs to the
  box's swap device, the factor between two runs belongs to the engine
- reads must all resolve (hits == R) or the leg fails; a collapse measured
  over unresolved reads is noise
- 133 checks, 0 failures; gate bites on a doctored collapse_x

Measured — this closes the gap the swap leg left:

- resident 1 851 166 reads/sec, p50 0us p99 1us
- over-cap    6 771 reads/sec, p50 128us p99 487us
- 273x throughput, ~480x p99, all 20 000 reads resolving in both
- so the two access patterns sit ~270x apart under identical pressure:
  append-mostly insert ~1%, random read 273x
- departure is a STEP not a curve (1us -> 487us, nothing between), which
  is why p99_departure_decile finds no knee — there is none

- caveat recorded, NOT inherited: this is demand-paged anonymous memory
  through swap (4 KiB/fault, no readahead). `resident: keys` preads via
  the page cache — should be better, but databasev2 2 task 7 must measure
  its own read path. New criterion added there

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 20:56:19 +02:00
0c9b2c45d8 feat(db-bench): measure the RAM ceiling — databasev2 1
- `Wide` text-heavy reference shape beside Int-only `Item`
- `growth N int|text`: per-decile RSS read from own /proc/self/status
- `growth-verify`: survivor of a crash must be a contiguous intact prefix
- four footprint legs under a rootless cgroup v2 cap, swap on/off
- `ceiling` leg: die at the cap, then replay must come back intact
- footprint read as median-of-marginals; doublings a separate metric
- 121 checks, 0 failures; footprint gated ±10%, kill-timing ±100%

Measured, and it inverted two of the iteration's own predictions:

- footprint 96.5-100 B/row Int vs 320.6-324 B/row text = 3.3x, NOT the
  "order of magnitude" three docs asserted
- table storage has NO checked ceiling: SIGKILL signal 9, not a catchable
  WO_T_OOM. overcommit lets malloc succeed; kernel kills on page touch
- swap is NOT latency collapse: 900k rows 148s capped-with-swap vs 150s
  uncapped. Append-mostly never re-touches cold pages
- ack-after-fsync survives an OOM kill: ~40k rows, no holes, no corruption
- iteration 2's budget dependency is REMOVED not satisfied — there is no
  "swap onset" to derive a fraction from

- fix: subprocess returncode -9 was labelled a "checked refusal"; 137 is
  the shell spelling of the same signal

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 20:18:47 +02:00
1fe808b7a4 docs(stories): add readiness, retire status: refine, sweep all 47 iterations
- `readiness: ready | refine` is a SECOND axis, orthogonal to status.
  `ready` = the brainstorm is complete and the decisions are LOCKED (a spec
  approved, or the forks explicitly confirmed). `refine` = open forks remain
  and it cannot be planned yet
- `status: refine` RETIRED because it carried both meanings at once, so a held
  iteration with an approved spec (language 18, 26) was indistinguishable from
  one nobody had thought about. status is now purely where the WORK is:
  done | in-progress | pending | hold — `pending` was already the board's own
  rendering word, so nothing new was invented
- all 47 iterations classified from EVIDENCE in their own text, not by guess:
  "the four forks are SETTLED" / "spec + plan approved" / "Approved spec:" for
  ready; "Forks the spec must settle" / "no spec exists yet" for refine. Every
  shipped iteration is ready by definition. 19 done, 5 in-progress, 15
  pending, 8 hold; 27 ready, 20 refine
- two iterations moved refine -> in-progress rather than -> pending: language
  31 and 34 are absorbed into 24 and work on them is literally happening, which
  the board already showed as 🔄 while their frontmatter said otherwise. That
  disagreement is now gone
- board legend, board-views' frontmatter contract, and two new Dataview
  queries updated — the useful one being `readiness: ready AND status:
  pending`, the startable set

WHAT THE NEW AXIS IMMEDIATELY SURFACED: of 15 pending iterations, exactly ONE
is startable — databasev2 4, io_uring group-commit, whose forks were confirmed
settled 2026-08-20. Everything else pending needs a brainstorm first. That was
invisible while one key carried both meanings, and it is now on the board.

Also caught by the sweep, unrelated to readiness but found by cross-checking
frontmatter against the board: SIX duplicate rows. Every iteration moved into
databasev2 was still listed in the LANGUAGE pending table under its retired id
(23, 32, 33, 20, 21, 27) as well as its new one. Stale copies removed. And two
databasev2 rows made claims the sweep contradicts — iteration 1 was billed
"startable today" while its forks are open, and 6 still called itself the
ceiling-raiser after 2 took that role.

Docs only. linkcheck 0 broken / 0 anchors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 16:54:45 +02:00
51dd42f9db docs(databasev2): rewrite iteration 2 to match what was designed and built
Flagged by the developer: the iteration still described the pre-brainstorm
three-mode design behind a "superseded in part" banner while four tasks had
landed against it.

- iteration 2 rewritten around the shape as built: two keys
  (`durable: true|false`, `resident: all|keys`), not a `mode:` enum with
  `cold`. status refine -> in-progress
- added a task-by-task progress table with commit hashes, and split the
  acceptance criteria into MET (each with how it was verified, not just that
  it passed — e.g. the goldens-unchanged claim is `git diff` over golden/
  being empty after a WOC_BLESS run, since blessing rewrites all of them) and
  OUTSTANDING with the task that owns each
- kept the history rather than deleting it: the three-mode replacement, the
  "one real rewrite" that was fiction, and the opposite half that turned out
  genuinely deep. An iteration file is where that record belongs
- board row rewritten to agree; the track index's "the lever" section, its
  principle-7 paragraph and its sequence rationale all still taught the dead
  three-mode design

ITERATION 6 IS NOW LARGELY SUPERSEDED, and bannered as such rather than
quietly gutted. `resident: keys` is the ceiling-raiser and it lives in
iteration 2 (tasks 5c/5d). More than relocated: 6's premise — a user-space
resident working set with faulting and 5's eviction policy — was specifically
REJECTED by the spec in favour of the kernel page cache, since a pread against
a cached page is a memcpy. What may still be left is recorded honestly: revisit
only with a measurement showing the page cache insufficient. Its fork list
survives, especially "does the language surface the fault cost at the use
site", which is still open and still the largest question about what writeonce
is. The sequence rationale is amended too — it had 6 as the ceiling-raiser and
5 as a prerequisite on the critical path; neither holds.

Docs only. linkcheck 0 broken / 0 anchors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 13:10:30 +02:00
566559cf70 docs: name the residency value keys, not index (review change)
- `resident: all | keys` replaces `resident: all | index`. Two reasons beyond
  taste: it kills the collision with the `index:` argument
  (`@table(index: [customer], resident: index)` read badly), and it puts both
  values on ONE axis — each now answers "what row data stays resident",
  where `all`/`index` mixed a quantity with a structure name
- accurate as well as clearer: what stays resident is the id->offset map, the
  secondary indexes and the unique shadows — all key structures; row payloads
  are exactly what leaves. `resident: none` was rejected as overclaiming,
  since the indexes very much are resident
- checked for collisions: neither `all` nor `keys` is a keyword or a builtin
  (`key_at`/`val_at` exist, bare `keys` does not)
- the spec's wart note became a recorded decision; the rejected spelling is
  kept quoted so the rationale still reads
- fixes a bug I introduced in the 2026-08-26 track move: all six moved
  iterations carried a banner reading "Part of [Story — the database beyond
  RAM]" whose link pointed at the LANGUAGE arc — correct target, lying text,
  the exact failure mode the link audit warned about. Banners now point at
  the databasev2 story, and the original "Part of" line says plainly which
  track the iteration was authored in before the move
- linkcheck 0 broken / 0 anchors

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 22:47:24 +02:00
da30aa6527 docs: amend principle 7 — the log is authoritative, residency is declared
- driving case: a 120 GB order table on a 32 GB host. Not a tuning problem;
  no eviction policy fixes it. Developer accepted reconsidering the principle
- principle 7 rewritten: durability half UNCHANGED and unconditional
  (WAL-logged, fsync before ack, CRC-dropped torn tail); residency half
  demoted from law to per-table declaration. Old wording quoted in place so
  the amendment is legible, with the reason: a doctrine a real workload
  cannot satisfy gets ignored, and the failure it produced was an OOM kill
- spec: docs/superpowers/specs/2026-08-26-table-residency-design.md
  One log-structured engine — the WAL already holds every row, so keep an
  in-RAM id->offset map and pread rows back. No second engine, no user-space
  row cache (the kernel page cache is the hot copy, which is already this
  repo's stated position and why it avoids O_DIRECT)
- arithmetic that makes it work: 240M rows x 16 B of index = ~3.8 GB
  resident in 32 GB. Indexes stay resident, rows do not. Buys ~2 orders of
  magnitude, not infinity — stated plainly in the spec
- grammar: two optional keys, `durable: true|false` and `resident: all|index`,
  both defaulting to today's behaviour, so all 28 existing @table
  declarations compile untouched and no golden is reblessed
- rejected, with reasons recorded: mmap (rows are pointer-bearing —
  table.c returns (uintptr_t)t as the slot word), buffer pool (the Rust-era
  phase-12 design that died with that track), paged B-tree (stays rejected),
  a three-valued enum, automatic spill, disk-backed-by-default
- self-review caught the budget defaulting to "none" while promising the ERP
  developer a diagnostic instead of the OOM killer — contradiction fixed:
  the budget defaults to a fraction of host memory, and its value comes from
  databasev2 1's swap-onset measurement
- live docs that contradicted the amendment updated (subagent doctrine,
  its guide, discarded.md's two rows, iteration 04's read claim, 07, 38);
  dated specs/plans left as records. linkcheck 0 broken / 0 anchors

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 22:42:27 +02:00
746dc2b42b docs(databasev2): third track — the database beyond RAM, with per-table storage modes
- docs/stories/databasev2/, numbered from 1. Six PENDING database iterations
  moved from the language track and renumbered, keeping the old id in
  `was_language_iteration:` so a search for "iteration 32" still finds it:
  32 -> 3 WAL checkpoint, 23 -> 4 io_uring commit, 33 -> 7 single-file store,
  27 -> 8 query grammar, 20 -> 9 cross-program, 21 -> 10 keypair auth.
  Done work (9, 9b, 22) stays as v1 history; language 18 left whole
- the problem, read off the engine not guessed: rows are malloc'd slabs with
  addresses stable forever, NO eviction/spill/paging anywhere in database/src,
  the WAL never checkpoints so boot replays all history, and durability is one
  process-global WO_DATA so no table can say it matters more than another.
  An allocation failure IS a clean catchable WO_T_OOM — but swap thrash
  arrives first and carries no error signal at all, which is the real hazard
- four new iterations:
  1 measure the ceiling FIRST (curve not cliff; the three exits; kill -9 at
    exhaustion) — every later default should follow from a number
  2 `@table(mode: ram | durable | cold)` — the grammar ask. Small surface
    (Ast.table_cfg gains a key, the parser already rejects unknown args), big
    semantics: `durable` defaults so nothing changes silently, and the
    compiler refuses a durable row holding a `ref` into a ram table
  5 bounded tables + refuse/evict/back-pressure, shedding BEFORE the OS acts
  6 cold tiering — mostly forks, incl. whether the language surfaces the
    fault cost and whether @unique on cold is refused outright. A paged
    B-tree stays rejected: if tiering needs one, reject tiering
- 39 links repointed, link TEXT renumbered to track-local ids; arc gains one
  pointer row replacing the six moved; board + board-views cover three tracks
- linkcheck 0 broken / 0 anchors; no code blocks in any story

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 20:52:48 +02:00