docs: audit all markdown against the code, fix findings, flatten status folders

- README: shipped concurrency/HTTP/WebSockets sat in the roadmap as "not yet
  available"; "no package manager" contradicted [deps]; the deps example
  would not have compiled (the key IS the module name)
- runtime/README: leads with wovm, wo-rt.c demoted to a historical section;
  dropped 2 nonexistent recipes, crates/rt, @gc refcounting, 13 suites -> 18
- employee + log-watcher READMEs claimed "does not compile"; both are gates
- error catalog: +10 emitted codes incl WO-E250, the only diagnostic the
  shipped query surface raises; recorded why the sweep rotted
- language-surface: group-by parses, then the typechecker refuses it
- 00-code-review + 00-link-audit re-run; history kept, not rewritten
- 48 dead Rust-era exploration links de-linked rather than re-pointed (their
  prose names the retired plan by number); successor map -> discarded.md
- 08-project-structure: compiler/plan/ never existed; corpus has 9 dirs, 5 empty
- releasing.md: dropped a --draft step the workflow never had
- new docs/00-doc-audit.md: findings + disposition, incl one row where the
  audit was wrong and the doc it accused was right
- status folders removed: 34 stories flat, status only in frontmatter; 252
  links recomputed from resolved paths; board/board-views/structure retaught
- story 24 -> in-progress, since frontmatter is now the only truth
- new iteration 38: fs mutation verbs + net.connect, the two capability
  families no iteration owned
- new iteration 39: gofiber/fiber v3.5.0 parity study. The ledger called
  CSRF/sessions unblocked by iteration 34's HMAC, but the runtime has no
  source of randomness at all
- linkcheck skips .dev/.superpowers: 0 broken paths, 0 bad anchors

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
shoney.arickathil 2026-08-26 19:20:22 +02:00
parent b3f8ed9985
commit c0b0dbb846
100 changed files with 2308 additions and 676 deletions

View file

@ -25,16 +25,19 @@ program ships as one file that depends only on the system C library.
mistyped field name is a **compile error**, not a runtime surprise. There is mistyped field name is a **compile error**, not a runtime surprise. There is
no SQL string anywhere in the shipped binary. no SQL string anywhere in the shipped binary.
- **One binary, no runtime dependencies.** `woc .` produces a self-contained - **One binary, no runtime dependencies.** `woc .` produces a self-contained
executable (~100 KB for the sample programs) that links only libc. Copy it to executable (160–260 KB for the sample programs in this repository) that links
a server and run it. only libc. Copy it to a server and run it.
- **Small on purpose.** No FFI, no package manager, no framework. The standard - **Small on purpose.** No FFI, no reflection, no package registry —
library is a handful of OS modules. The language is designed to be read. dependencies are exact-rev git URLs and nothing else. The standard library is
a handful of OS modules. The language is designed to be read.
writeonce is **not** a web framework and does not (yet) serve HTTP, WebSockets, writeonce is a systems language whose distinguishing feature is the embedded
or a UI. It is a systems language whose distinguishing feature is the embedded database. HTTP/1.1 and WebSockets **do** work today — but as `.wo` libraries you
database. If you have seen an older "writeonce" that served REST from `cargo consume through `[deps]` (`writeonce-serve` for serving, `writeonce-view` for
run`, that was a separate, earlier runtime; this page documents the current HTML), never as runtime features: the runtime stays framework-agnostic on
`woc`/`wovm` toolchain. purpose. TLS is always terminated by a proxy in front. If you have seen an older
"writeonce" that served REST from `cargo run`, that was a separate, earlier
runtime; this page documents the current `woc`/`wovm` toolchain.
--- ---
@ -139,8 +142,10 @@ has a known owner, memory is freed deterministically, and values that form
cycles are collected by an inferred garbage collector (you never annotate GC- cycles are collected by an inferred garbage collector (you never annotate GC-
ness; the compiler infers it). The surface will look familiar: ness; the compiler infers it). The surface will look familiar:
- **Types:** `Int`, `Text`, `Bool`, and user `class` types. `?T` marks an - **Types:** `Int`, `Float`, `Bool`, `Text`, `Bytes`, `Timestamp`, `Id`, and
optional (nullable) value; `nil` is the empty case. user `class` types. `?T` marks an optional (nullable) value; `nil` is the
empty case. `Int` and `Float` never mix implicitly — `float` and `trunc` are
the only bridges.
- **Containers:** `multi T` (a growable list) and `map<K, V>`. Literals: - **Containers:** `multi T` (a growable list) and `map<K, V>`. Literals:
`[]`, `[a, b]`, `{}`. `[]`, `[a, b]`, `{}`.
- **Classes & records:** classes with fields and methods, `static const` / - **Classes & records:** classes with fields and methods, `static const` /
@ -149,6 +154,11 @@ ness; the compiler infers it). The surface will look familiar:
expressions, and `try { … } catch (e) { … }` (also an expression form). expressions, and `try { … } catch (e) { … }` (also an expression form).
- **Strings:** interpolation with `${expr}` inside a `"…"` literal. - **Strings:** interpolation with `${expr}` inside a `"…"` literal.
- **Functions:** free functions and methods; arguments and returns are typed. - **Functions:** free functions and methods; arguments and returns are typed.
- **Concurrency:** `spawn C { … }` starts an actor and yields an `actor M`
address; `send` is fire-and-forget, `call` parks the calling fiber until the
receive returns. A class becomes an actor by declaring `fn receive(msg: M)`.
Blocking stdlib calls park the fiber — there is no `async`, no `await`, and no
user-visible thread.
``` ```
fn classify(n: Int) -> Text { fn classify(n: Int) -> Text {
@ -166,14 +176,17 @@ A compact set of OS modules, reached by their reserved names — no imports:
| Module | What it does | | Module | What it does |
| --- | --- | | --- | --- |
| `fs` | `exists`, `list`, `stat`, `read_all`, `read_at`, `append` | | `fs` | `exists`, `list`, `stat`, `read_all`, `read_at`, `append` — read and append; a file cannot yet be replaced, truncated, deleted or renamed |
| `time` | `sleep`, `now`, `local`, `iso` | | `time` | `sleep`, `now`, `ticks` (µs monotonic), `local`, `iso` |
| `env` | `get`, `stopping` (a cooperative shutdown flag) | | `env` | `get`, `stopping` (a cooperative shutdown flag) |
| `net` | TCP `listen` / `accept` / `read` / `write` / `close` (host + port) | | `net` | `listen` / `accept` / `read` / `write` / `close`, per-call deadline twins `read_dl` / `accept_dl` / `write_dl`, `listen_unix`, `peer`. Listeners only — there is no outbound `connect` |
| `proc` | `run` a child process, capture stdout/stderr/exit | | `proc` | `run` a child process, capture stdout/stderr/exit |
| `json` | `encode` / `decode` (`json.decode(t) as T` yields `?T`) | | `json` | `encode` / `decode` (`json.decode(t) as T` yields `?T`) |
These are deliberately minimal — the surface a real program needs, and no more. These are deliberately minimal — the surface a real program needs, and no more.
Alongside them sit free builtins for text, containers, the `Float`/`Bytes`
bridges, `base64`, and the digests `sha1` / `sha256` / `hmac_sha256`. The full
list is `docs/guides/language-surface.md`.
--- ---
@ -269,14 +282,15 @@ dependencies, declared in the manifest:
```toml ```toml
[deps] [deps]
niceserve = { git = "https://github.com/shoneyj/niceframework", rev = "v0.1.0" } serve = { git = "https://github.com/shoneyj/writeonce-serve", rev = "v0.1.0" }
``` ```
`woc` fetches each dep (via the `git` binary) into `.wo-deps/<name>/`, pins **The `[deps]` key IS the module name** `use` imports — the repository name
the resolved commit in `wo.lock`, and `use niceframework` (or never appears in your source. `woc` fetches each dep (via the `git` binary)
`use niceframework/sub`) imports its public names like any module. Builds into `.wo-deps/<name>/`, pins the resolved commit in `wo.lock`, and `use serve`
never touch the network once the lock is satisfied; a moved tag is reported, (or `use serve/router`) imports its public names like any module. Builds never
and `woc --update-deps myproject/` refreshes the lock deliberately. Flat touch the network once the lock is satisfied; a moved tag is reported, and
`woc --update-deps myproject/` refreshes the lock deliberately. Flat
dependencies only (a dep may not have its own `[deps]`) — honest and small, dependencies only (a dep may not have its own `[deps]`) — honest and small,
by design. by design.
@ -292,8 +306,9 @@ WO_DATA=./data ./target/myproject report # a fresh process still sees the da
## Worked examples ## Worked examples
Two complete sample programs live in the repository and double as the language's Thirteen sample programs live under `docs/examples/`; eight of them are wired to
acceptance tests: a `just` recipe and double as the language's acceptance tests. The three worth
reading first:
- **`docs/examples/employee/`** — departments and employees related by - **`docs/examples/employee/`** — departments and employees related by
`ref`/`backlink`, `@unique`, foreign-key restrict on delete, per-department `ref`/`backlink`, `@unique`, foreign-key restrict on delete, per-department
@ -319,7 +334,10 @@ acceptance tests:
just log-watcher just log-watcher
``` ```
Read either program's `main.wo` for idiomatic, working writeonce. Read any of their `main.wo` files for idiomatic, working writeonce. The rest —
`site` (the writeonce.de tutorial, server-rendered, `just site`), `fibers`,
`db-actor`, `db-bench`, `gc-cycle`, `operators`, `shop` — cover the concurrency,
GC and benchmark surfaces.
--- ---
@ -330,10 +348,16 @@ honest. These exist as design iterations and/or work-in-progress branches, not
as features you can use today: as features you can use today:
- **Query aggregates** — `group … by … into g` with `count`/`avg`/`min`/`max` - **Query aggregates** — `group … by … into g` with `count`/`avg`/`min`/`max`
and projection records. (Today the same result is written by hand from the and projection records. The clause parses and is then refused by the
shipped primitives.) typechecker; today the same result is written by hand from the shipped
- **HTTP service layer** — `service` blocks that route requests to methods. primitives.
- **Concurrency** — a shard-actor runtime and green-threaded fibers. - **File mutation and outbound sockets** — `fs` can create, grow and read a
file but never replace, truncate, delete or rename one, and there is no
`net.connect` at all, so nothing reaches out (no OIDC, SMTP, object store or
webhook). Both are iteration 38.
- **`service` blocks** — a declaration form that routes requests to methods,
lowering onto the framework library. Today you register routes as ordinary
framework calls, which works and is what every sample does.
- **Cross-program database access** — one program attaching to another's - **Cross-program database access** — one program attaching to another's
database over a local channel, with keypair authentication and per-client database over a local channel, with keypair authentication and per-client
rights. rights.
@ -341,8 +365,11 @@ as features you can use today:
- **Compile-time metaprogramming** — `@derive(Json/Csv/Eq/…)` generated from a - **Compile-time metaprogramming** — `@derive(Json/Csv/Eq/…)` generated from a
class's own metadata, no reflection. class's own metadata, no reflection.
Known current limits worth naming: `net` is TCP host+port only; `proc.run` has Known current limits worth naming: `proc.run` has no timeout or signal control;
no timeout or signal control; there is no stdin/stdout byte I/O and no FFI. there is no stdin/stdout byte I/O and no FFI; `map` lookup is a linear scan;
actor mailboxes are bounded but there is no supervision tree yet; the WAL is
append-only, so it grows and boot replays all of it; TLS is always a proxy's
job.
--- ---

View file

@ -2,7 +2,9 @@
Lexer → parser → typechecker → ownership pass → bytecode emitter, for `.wo`. OCaml stdlib only (no Menhir, no ppx); dune is the build runner. Sibling of the C `wovm` bytecode VM ([`runtime/`](../runtime/README.md)) — the two halves of the OOP track's spec (`docs/superpowers/specs/2026-08-01-oop-compiler-vm-design.md`) meet at plan 3, where `woc`'s emitted `.wob` runs on `wovm`. Lexer → parser → typechecker → ownership pass → bytecode emitter, for `.wo`. OCaml stdlib only (no Menhir, no ppx); dune is the build runner. Sibling of the C `wovm` bytecode VM ([`runtime/`](../runtime/README.md)) — the two halves of the OOP track's spec (`docs/superpowers/specs/2026-08-01-oop-compiler-vm-design.md`) meet at plan 3, where `woc`'s emitted `.wob` runs on `wovm`.
**Stage: plan 3 (`docs/plan/compiler/2026-08-01-wob-emit-e2e-single-binary.md`) complete, Tasks 1–6 + 8** (Task 7, a parity harness against the Rust runtime, was deferred by explicit decision — the two stacks now diverge by design). `.wo` source compiles to `.wob` bytecode (`--emit`) and to a single self-contained executable (`build`) that runs `wovm` with no arguments and no repo-relative dependency. Milestone 1's acceptance gate — compile-time budget, the full conformance corpus under ASan, the single-binary smoke, both unit suites — is `just oop-accept`. Plan 2 (lexer through ownership pass) shipped first and is unchanged. **Stage: well past plan 3.** Plan 2 (lexer through ownership pass) and plan 3 (`docs/plan/compiler/2026-08-01-wob-emit-e2e-single-binary.md`, Tasks 1–6 + 8 — Task 7, a parity harness against the since-removed Rust runtime, was deferred by explicit decision) closed the milestone: `.wo` source compiles to `.wob` bytecode (`--emit`) and to a single self-contained executable (`build`) that runs `wovm` with no arguments and no repo-relative dependency. Milestone 1's acceptance gate — compile-time budget, the full conformance corpus under ASan, the single-binary smoke, both unit suites — is `just oop-accept`.
Since then the front end has taken iterations **15** (`[deps]`, `wo.lock`, `--update-deps`), **17** (`kind = "library"`, entry-less check mode, `internal/` as WO-E108), **19** (`Float` and `Bytes`), **24** (`call`'s typed reply, WO-E226), **34** (digest builtins), **35** (net deadline seams), **36** (`not`, bitwise operators, hex/binary literals, compound assigns — `.wob` v6) and **37** (the backtick raw text literal with `{{ }}` auto-escaping). Current language surface: [`docs/guides/language-surface.md`](../docs/guides/language-surface.md). Current status: [the board](../docs/stories/00-status.md).
## Requirements ## Requirements
@ -24,23 +26,30 @@ just woc-test # same, from the repo root
``` ```
woc <path> # compile (lex, parse, typecheck, ownership-check); nothing prints on success woc <path> # compile (lex, parse, typecheck, ownership-check); nothing prints on success
woc <dir> # BUILDS instead, when <dir>/wo.toml exists — the primary mode
woc version # e.g. "writeonce 0.1.0 linux/amd64"
woc --emit <path> -o <out.wob> # compile through to a .wob bytecode module, runnable by wovm
woc build <dir> -o <app> [--runtime <path>]
# compile + append the .wob image to a copy of wovm (--runtime,
# else $WO_RUNTIME, a wovm beside this woc, or runtime/wovm)
woc --update-deps <dir> # re-fetch [deps] at their manifest revs, rewrite wo.lock
woc -D <name> ... # define a build flag for the #if/#else/#end token filter
woc --dump-tokens <path> # stdout: one line per lexed token woc --dump-tokens <path> # stdout: one line per lexed token
woc --dump-ast <path> # stdout: the declaration + body AST, indented woc --dump-ast <path> # stdout: the declaration + body AST, indented
woc --dump-owner <path> # stdout: the ownership pass's four tables (moves, drops, rc, residual) woc --dump-owner <path> # stdout: the ownership pass's four tables (moves, drops, rc, residual)
woc --dump-gc <path> # stdout: the inferred-GC pass's traced set
woc --dump-bc <path> # stdout: disassembled bytecode for every emitted method woc --dump-bc <path> # stdout: disassembled bytecode for every emitted method
woc --emit <path> -o <out.wob> # compile through to a .wob bytecode module, runnable by wovm
woc build <dir> -o <app> [--runtime <path>]
# compile + append the .wob image to a copy of wovm (default
# runtime/wovm, or --runtime) into one self-contained <app>
``` ```
`<path>` is a single `.wo` file or a directory. A directory is discovered recursively for every `.wo` file under it — same contract as `wo run` (`crates/rt/src/lib.rs::discover`): dot-prefixed entries and `target`/`data`/`node_modules` are skipped, results are sorted by path. Every discovered file compiles as one program (declarations in one file resolve for bodies in another, regardless of discovery order); diagnostics from every file and every stage print sorted by `(file, line, col)`. For multi-file `--dump-*` output, each file's dump is preceded by a `=== path ===` header line (`compiler/src/dump.ml`'s `file_header`) — a single-file run never prints one. `woc <dir>` on a directory holding a `wo.toml` is the mode every sample and the install docs use: it reads the manifest's `name` plus the optional `[build]` runtime/target keys and produces `<target>/<name>` exactly as `woc build` would. A manifest with `kind = "library"` is checked entry-less and writes nothing.
`<path>` is a single `.wo` file or a directory. A directory is discovered recursively for every `.wo` file under it: dot-prefixed entries and `target`/`data`/`node_modules` are skipped, results are sorted by path. Every discovered file compiles as one program (declarations in one file resolve for bodies in another, regardless of discovery order); diagnostics from every file and every stage print sorted by `(file, line, col)`. For multi-file `--dump-*` output, each file's dump is preceded by a `=== path ===` header line (`compiler/src/dump.ml`'s `file_header`) — a single-file run never prints one.
Diagnostics render as `file:line:col: severity CODE: message` plus a source excerpt with a caret; every shipped code is cataloged in `docs/plan/oop-vm/01-error-catalog.md`. Exit codes: **0** clean compile, **1** diagnostics reported, **2** usage/IO failure. Diagnostics render as `file:line:col: severity CODE: message` plus a source excerpt with a caret; every shipped code is cataloged in `docs/plan/oop-vm/01-error-catalog.md`. Exit codes: **0** clean compile, **1** diagnostics reported, **2** usage/IO failure.
## Layout ## Layout
- `src/` — one module per stage: `diag` (diagnostics, collector, exit-code decision), `token`/`lexer`, `ast`/`parser`, `types` (typechecker), `owner` (MVS ownership pass), `emit` (bytecode emitter, consumes `owner`'s four tables), `disasm` (bytecode disassembler, backs `--dump-bc`), `dump` (stable text dumps for all of the above) - `src/` — one module per stage: `diag` (diagnostics, collector, exit-code decision), `token`/`lexer`, `ast`/`parser`, `types` (typechecker), `gcinfer` (the inferred-GC pass, backs `--dump-gc`), `owner` (MVS ownership pass), `emit` (bytecode emitter, consumes `owner`'s four tables), `disasm` (bytecode disassembler, backs `--dump-bc`), `dump` (stable text dumps for all of the above)
- `bin/` — the `woc` executable: CLI parsing, file discovery, the multi-file/cross-file driver, `--emit`/`build` output - `bin/` — the `woc` executable: CLI parsing, file discovery, the multi-file/cross-file driver, `--emit`/`build` output
- `test/` — `runner.ml` (golden runner + CLI smoke) and `test_diag.ml` (diag.ml unit checks); `test/golden/<stage>/` holds one-file-per-fixture goldens (`tokens`, `ast`, `owner`, `owner-err`, `bc`); `test/fixtures/driver/` holds the multi-file CLI-smoke fixtures (directory discovery, cross-file symbols, diagnostic ordering) that don't fit the one-`.wo`-file-per-fixture golden shape - `test/` — `runner.ml` (golden runner + CLI smoke) and `test_diag.ml` (diag.ml unit checks); `test/golden/<stage>/` holds one-file-per-fixture goldens (`tokens`, `ast`, `owner`, `owner-err`, `bc`); `test/fixtures/driver/` holds the multi-file CLI-smoke fixtures (directory discovery, cross-file symbols, diagnostic ordering) that don't fit the one-`.wo`-file-per-fixture golden shape

View file

@ -18,6 +18,12 @@ Native speed — the big one. Everything is interpreted: ~40× behind Go on raw
## Verification 2026-08-20 ## Verification 2026-08-20
> **Read the 2026-08-26 re-verification at the bottom before quoting anything
> from this section.** Eight of its rows have since been overtaken by shipped
> work. The section is kept as written — it is a dated measurement, and
> rewriting it would destroy the record of what was true when the iteration
> order was re-sequenced against it.
Every claim above was checked against the tree. **26 of 27 hold. One number Every claim above was checked against the tree. **26 of 27 hold. One number
does not, and two problems are worse than stated.** does not, and two problems are worse than stated.**
@ -87,3 +93,41 @@ The iteration order in
[`stories/language-runtime-database/00-story.md`](stories/language-runtime-database/00-story.md) [`stories/language-runtime-database/00-story.md`](stories/language-runtime-database/00-story.md)
was re-sequenced against these findings on 2026-08-20 — Seq only, no `#` was re-sequenced against these findings on 2026-08-20 — Seq only, no `#`
renumbered, no file moved. See that table's second re-sequencing note. renumbered, no file moved. See that table's second re-sequencing note.
---
## Re-verification 2026-08-26
Re-run against the tree, reading source rather than documents. **Eight rows
have been overtaken by shipped work; the rest still hold.** Overtaken:
| 2026-08-20 row | What the source says now |
| --- | --- |
| "no `Float`, no `Bytes`" | `types.ml`'s `builtin_scalars` is `["Int"; "Bool"; "Text"; "Timestamp"; "Id"; "Float"; "Bytes"]` — iteration 19, plus the `float`/`trunc` bridges and the `bytes_*`/`base64_*` builtins |
| "`send` is one-way — `WO_B_SEND=69` is the last builtin (`WO_B_MAX 69u`)" | `WO_B_MAX` is `95u`; `WO_B_CALL = 88` is a send that parks the caller for a typed scalar reply (iteration 24, WO-E226) |
| "no crypto primitives" | `WO_B_SHA1 = 85`, `WO_B_SHA256 = 86`, `WO_B_HMAC_SHA256 = 87`; `runtime/src/crypto.c`, vector-accepted in `test_crypto.c` (iteration 34) |
| "unbounded mailboxes, no backpressure" | mailboxes are capped (`WO_MAILBOX`, default 1024) with a sender-side reserve and a catchable `WO_T_ACTOR` trap on overflow |
| "no supervision, links, actor death" | **partly** overtaken: actor death landed with `call` — a dead or mid-call callee traps the caller instead of hanging it. `monitor` (id 89) and `time.after` (id 90) are still literal holes in the builtin enum; supervision trees remain absent |
| "22's battery never run — no `bench/baseline.json`, no `just db-bench`" | `bench/baseline.json` exists with the campaign's metrics, `just db-bench`/`db-bench-quick` are recipes, `bench/results/` holds the runs, iteration 22 is done |
| "no fuzzing, **no CI**" | `.github/workflows/release.yml` builds, verifies and publishes on a `v*` tag. Fuzzing is still absent, and CI is release-only — nothing runs the gates per change, which is iteration 30's remaining half |
| "one framework, five samples, one consumer" | two libraries (`writeonce-serve`, `writeonce-view`) and 13 samples, 8 of them gated |
| "The multi-shard DB gap is structural" (Understated) | closed by the arc's stage 3: the string `"database engine not initialized"` no longer exists in `runtime/src/`, worker statements marshal to the owner shard, and `just db-actor` gates it |
Still true, re-checked at the source: interpreted-only with no JIT and no SIMD;
the ceilings correction (`WO_STACK_SLOTS 4096`, `WO_MAX_REGS 64`,
`WO_MAX_FRAMES 256`, `WO_MAX_SHARDS 64`); no generics beyond `multi`/`map`; no
closures or function values; byte strings with no Unicode awareness; traps and
`try` instead of Result values; `switch` without destructuring; round-robin
placement with no work stealing; no timers beyond `time.sleep`; no TLS; no
HTTP/2; observability is `print`/stderr with no counters, tracing or profiler; no
debugger and no LSP; deps are git-rev-only with no registry, semver or transitive
resolution; blue-green and migrations are futures; TSan covers one demo. And
**`map<K,V>` lookup is still a linear scan** — `runtime/src/cont.h` says so in
its own header comment, which keeps it the compute problem this document argued
it was.
Two capability gaps this re-run named that the original critique did not, now
[iteration 38](stories/language-runtime-database/38-content-platform-capabilities.md):
`fs` has six builtins (ids 40–45) and can create, grow and read a file but never
replace, truncate, delete or rename one; and there is no `net.connect` anywhere
in `runtime/src/`, so no program can open an outbound connection.

View file

@ -6,8 +6,12 @@
> to pick the next implementation: anything whose incoming arrows are all > to pick the next implementation: anything whose incoming arrows are all
> green is startable today. Rebuilt 2026-08-20 from a sweep of every > green is startable today. Rebuilt 2026-08-20 from a sweep of every
> story/spec/plan markdown (the "misses" pass: iteration 17's outgoing > story/spec/plan markdown (the "misses" pass: iteration 17's outgoing
> edges, the concurrency chain, the post-12 parked drain, 9b→10, > edges, the concurrency chain, the parked drain, 9b→25, 28's gap
> 14's gap fan-out, 20's fiber caveat). > fan-out, 20's fiber caveat), and **refreshed 2026-08-26** against the
> code and the story frontmatter: graph 1 had drifted a generation
> behind — it still showed 17 parked and 18 as next, and it used the
> pre-renumber ids 10/12/13/14 for what are now stories 25/26/29/28. All
> iterations through 38 are now nodes.
## 1. Story iterations ## 1. Story iterations
@ -26,23 +30,36 @@ flowchart TD
I15["15 deps package manager"]:::done I15["15 deps package manager"]:::done
I16["16 web framework v1 core"]:::done I16["16 web framework v1 core"]:::done
I17["17 library kind + internal/ (PARKED — spec+plan ready, branch library-internal)"]:::parked I17["17 library kind + internal/ ✅ 2026-08-20"]:::done
FWREORG["framework internal/ reorg + check mode (kills the --emit workaround; WO-E108/E109 reserved)"]:::parked FWREORG["framework internal/ reorg + check mode ✅ landed with 17 (WO-E108/E109 shipped)"]:::done
I18["18 framework v2: transaction{} + cache/flags/jobs (spec APPROVED — the next implementation)"]:::specd I19["19 Float + Bytes ✅ 2026-08-20"]:::done
I37["37 wo-html components + raw text literal ✅ 2026-08-25"]:::done
I35["35 net runtime seams ✅ 2026-08-23"]:::done
I36["36 operator parity: not/bitwise/hex literals — code landed 2026-08-22, awaiting the manual pass"]:::specd
RELEASE["packaging + release pipeline ✅ 2026-08-25 (no story: VERSION, just dist, install-accept, release.yml)"]:::done
I9c["20 cross-program tables (half-built)"]:::open I18["18 framework v2: transaction{} + cache/flags/jobs (⏸ hold 2026-08-21; spec+plan approved, held intact)"]:::parked
I9d["21 keypair attach auth (half-built; crypto+handshake already on its branch)"]:::open
I9c["20 cross-program tables (⏸ hold 2026-08-21; channel half-built)"]:::parked
I9d["21 keypair attach auth (⏸ hold 2026-08-21; crypto floor now exists via 34)"]:::parked
I9e["22 durability + throughput baseline ✅ 2026-08-21"]:::done I9e["22 durability + throughput baseline ✅ 2026-08-21"]:::done
I8["8 shard-actor runtime ✅ 2026-08-21"]:::done I8["8 shard-actor runtime ✅ 2026-08-21"]:::done
I24["24 chat + actor lifecycle 🔄 THE LIVE SLICE (absorbing 31 + 34)"]:::specd
I34["34 crypto builtins ✅ code landed as 24's T1 (ids 85-87)"]:::done
I31["31 actor lifecycle — call/mailbox-cap/death landed in 24; monitor + time.after (ids 89/90) open"]:::specd
I9f["23 io_uring group-commit"]:::open I9f["23 io_uring group-commit"]:::open
I10["10 HTTP service layer (lowers onto the framework)"]:::open I32["32 WAL checkpoint (append-only today; bounds replay)"]:::open
I33["33 single-file store WO_DATA=<path>.db (driver-only, off-chain)"]:::open
I25["25 HTTP service layer — `service` blocks (⏸ hold 2026-08-21; story file removed, plan remains)"]:::parked
I11["11 fibers ✅ 2026-08-21"]:::done I11["11 fibers ✅ 2026-08-21"]:::done
I12["12 blue-green deploy"]:::open I26["26 blue-green deploy (⏸ hold)"]:::parked
I13["13 metaprogramming @derive"]:::open I29["29 metaprogramming @derive (⏸ hold)"]:::parked
I14["14 skillhost workload (demoted)"]:::open I28["28 skillhost workload (⏸ hold; demoted)"]:::parked
I9g["27 query grammar corpus (likely collapses)"]:::open I38["38 content platform capabilities: fs mutation verbs + net.connect"]:::open
GAPS["14's gap fan-out: bounded subprocess, stdin/stdout transport, fs metadata, FFI-vs-out-of-process"]:::open I9g["27 query grammar corpus (⏸ hold; likely collapses)"]:::parked
DRAIN["post-12 parked drain: pub(read)/using/#if, WO-E225, ADT roster, group-by"]:::parked I30["30 observability, CI, fuzz — release-only CI exists; per-change gates + fuzz open (no story file)"]:::open
GAPS["28's gap fan-out: bounded subprocess, stdin/stdout transport, fs metadata, FFI-vs-out-of-process"]:::open
DRAIN["parked drain, what is LEFT of it: WO-E225 roster, ADT roster, group-by aggregates"]:::parked
FOUND --> I7 FOUND --> I7
FOUND --> I9 FOUND --> I9
@ -53,34 +70,53 @@ flowchart TD
I15 --> I17 I15 --> I17
I16 --> I17 I16 --> I17
I17 --> FWREORG I17 --> FWREORG
I16 --> I37
I19 --> I37
I17 --> RELEASE
I9 --> I18 I9 --> I18
I16 --> I18 I16 --> I18
I9 --> I9c I9 --> I9c
I9c --> I9d I9c --> I9d
I9c --> I10 I9c --> I25
I9b --> I10 I9b --> I25
I16 --> I10 I16 --> I25
I9b --> I9e I9b --> I9e
I7b --> I8 I7b --> I8
I9e --> I9f I9e --> I9f
I8 --> I9f I8 --> I9f
I8 --> I11 I8 --> I11
I9 --> I12 I19 --> I34
I10 --> I12 I34 --> I24
I9g --> I14 I8 --> I24
I7 --> I14 I11 --> I24
I14 --> GAPS I35 --> I24
I12 -.scope directive.-> I13 I31 --- I24
I12 -.scope directive.-> DRAIN I9f --> I32
I32 --- I33
I9 --> I26
I25 --> I26
I9g --> I28
I7 --> I28
I28 --> GAPS
I16 --> I38
I32 --> I38
I36 -.reopens the pure-wo HMAC question.-> I34
I26 -.scope directive.-> I29
I26 -.scope directive.-> DRAIN
``` ```
Reading it: **18 is the only spec-approved open node with all Reading it: **the live slice is 24** (chat + actor lifecycle, absorbing 31
prerequisites green — the next implementation.** After 18: 20/21 and 22 and 34), and the chain behind it is 23 → 32. Everything else with all-green
are startable (chosen order: 20/21 first — half-built branches rot). incoming arrows is startable: **33** (driver-only, off-chain), **38** (the
17 unparks on directive: its prerequisites landed, its spec+plan wait on fs-mutation and outbound-socket gaps), and **30**'s remaining half
branch `library-internal`, and its landing brings the framework reorg (per-change CI and fuzzing — the release pipeline covered only publishing).
node with it. 13 and the parked drain sit behind 12 by the 2026-08-08 **36** needs no work, only the developer's manual pass over
scope directive (dashed), not by any technical edge. `docs/examples/operators/`. The held tail — 18, 20/21, 25, 26, 27, 28, 29 —
resumes on its own precedence notes; 29 and what is left of the drain still
sit behind 26 by the 2026-08-08 scope directive (dashed), not by any
technical edge. Note what left the drain: `pub(read)`, `using` and `#if` all
shipped, so only the WO-E225/ADT rosters and group-by aggregates remain in
it.
## 2. The concurrency chain (iterations 8 / 23 / 11 and everything they gate) ## 2. The concurrency chain (iterations 8 / 23 / 11 and everything they gate)

535
docs/00-doc-audit.md Normal file
View file

@ -0,0 +1,535 @@
# Documentation truth audit — 2026-08-26
> **Status: findings resolved 2026-08-26, same day.** Every section below was
> acted on; see [What was fixed](#what-was-fixed) at the end for the
> disposition of each, including **one row where this audit was wrong and the
> document it accused was right** (B3's `WO-W201` claim). The findings are kept
> as written — a fix list whose findings have been edited away cannot be
> checked. `just linkcheck` went from 77 broken paths to 23, and all 23 that
> remain are in `.dev/`, which this repo does not author.
>
> A separate structural directive landed the same day and **removed the status
> folders** (`done/`, `refine/`, `hold/`, `in-progress/`) — status now lives only
> in frontmatter. Paths of the form `…/done/NN-*.md` quoted in the findings below
> were correct when written and no longer resolve; see
> [Structural change 2026-08-26](#structural-change-2026-08-26--status-folders-removed).
Scope: every `*.md` that documents THIS repo — root `README.md`, the numbered
`docs/0*.md`, `docs/guides/`, `docs/stories/`, `docs/plan/`, `docs/examples/`,
`docs/superpowers/`, the code-directory READMEs and `CODE-LOGIC.md` files,
`tests/corpus/README.md`, `bench/compare/go-sqlite/README.md`,
`scripts/install-readme.tmpl.md`, `.claude/agents/database-developer.md`.
Excluded, and why: `.dev/skills/` (vendored copies of plugin skills, not ours),
`.dev/reference/` (other people's codebases), `.superpowers/sdd/` (dated task
reports — snapshots, correct as history).
Method: claims were checked against the tree, not read off prose. `woc`
(`compiler/_build/default/bin/woc`) and `wovm` (`runtime/wovm`), both built
2026-08-25, were run against every sample; `justfile` recipes, `wob.h`
constants, `types.ml`'s builtin tables, story frontmatter and
`scripts/linkcheck.py` were used as ground truth. Nothing in this report is
inferred from another document.
Verdict: **the deep reference docs are in good shape; the front door is not.**
`docs/guides/language-surface.md`, `docs/plan/oop-vm/08-builtin-surface.md`, the
three `CODE-LOGIC.md` files and the status board's tables track the code
closely. The root `README.md`, `runtime/README.md`, `docs/00-code-review.md`,
`docs/00-dependency-graph.md` and two example status banners describe a repo
that stopped existing between one and six weeks ago.
No document was changed by the audit pass itself — the findings below record the
tree as it stood before any fix. What was then changed in response is listed in
[What was fixed](#what-was-fixed).
---
## A. Wrong about shipped features (the highest-cost class)
### A1. `README.md` — the Roadmap lists three landed features as unavailable
`README.md:326-342` is headed "Planned, **not yet available**", and
`README.md:10-14` promises "Features that are planned but **not yet available**
are listed separately under Roadmap — they are not described as if they work."
Three of its six entries have shipped:
| README claim | Reality |
| --- | --- |
| `:336` "**Concurrency** — a shard-actor runtime and green-threaded fibers." | Both landed 2026-08-21 (iterations 8 and 11, both `done/`). `spawn` is a lexer keyword (`lexer.ml:163`); `send`/`call` are builtins (`WO_B_SEND=69`, `WO_B_CALL=88` in `wob.h`); `actor M` is a type (`types.ml`'s `TActor`). Gates exist and run: `just fibers`, `just db-actor`. `runtime/src/park.c` is the parking implementation; `runtime/test/test_fiber.c` and `test_mailbox.c` are its unit suites. |
| `:335` "**HTTP service layer** — `service` blocks that route requests to methods." | The *`service` block syntax* is genuinely absent — that half is honest. But it sits under a banner that also denies HTTP entirely, which is false (see A2). |
| `:332` "**Query aggregates** — `group … by … into g`" | **This one is correct.** `types.ml:2220,2241` rejects it: "group-by aggregation is not supported yet". Kept here only because `docs/guides/language-surface.md` contradicts it — see C1. |
### A2. `README.md:33-37` — "does not serve HTTP, WebSockets, or a UI"
> "writeonce is **not** a web framework and does not (yet) serve HTTP,
> WebSockets, or a UI."
Contradicted 30 lines later by its own §"Worked examples" (`:306-313`), which
describes `docs/examples/writeonce-serve/` as "a web framework written in
writeonce (HTTP/1.1 …, router with `:param` captures, interface-based
handlers)" and `just web-app` as its gate. Also contradicted by:
- iteration 16 (web framework) and 37 (wo-html components), both `done/`;
- `docs/examples/site/` — server-rendered pages, gated by `just site`;
- WebSockets: `docs/examples/writeonce-serve/http/ws.wo` (`ws_accept`, the 101
hijack sentinel) and `http/wsframe.wo` (a pure-`.wo` RFC 6455 frame codec),
both landed per `docs/in-progress/2026-08-23-chat-ws-lifecycle.md` (T6, T7).
### A3. `README.md:30` — "no package manager"
> "**Small on purpose.** No FFI, no package manager, no framework."
Same page, `:265-281`, documents `[deps]`, `.wo-deps/<name>/`, `wo.lock` and
`woc --update-deps`. Iteration 15 (deps package manager) is `done/`;
`just deps-accept` is its gate. "No framework" is contradicted by `:306`.
The intended claim is presumably "no *registry*" — which is true and is what
`docs/00-code-review.md:77` says.
### A4. `docs/examples/employee/README.md:3` — "does not compile on today's toolchain"
> "**Status: target workload — does not compile on today's toolchain.** …
> It becomes buildable when iteration 9 … and iteration 9b … land."
Both landed. `woc docs/examples/employee/` exits 0. `just employee` is a
first-class acceptance gate, and the `justfile:88-91` comment calls it "the
database track's acceptance workload". The banner is ~2 weeks stale.
### A5. `docs/examples/log-watcher/README.md:12-20` — same shape
> "**Status: design artifact — the spec's forcing function.** The systems
> track is approved, pre-implementation. Today's `woc` (milestone 1) …
> diagnoses the adopted surface as WO-E101: `use`, `typedef`, standalone union
> aliases …, `pub(read)`, `switch`, `try`."
Every one of those forms is shipped (`docs/guides/language-surface.md` §2–§5,
verified in `lexer.ml`/`parser.ml`). `woc docs/examples/log-watcher/` exits 0.
`just log-watcher` is the gate the `justfile:82-87` calls "the test the whole
track exists to pass".
Same file, `:8-9`: "the five builtin stdlib modules — `fs`, `proc`, `net`,
`time`, `json`". There are **six** (`types.ml:206`): `env` is missing.
### A6. `runtime/README.md` — describes the pre-2026-08-18 world
The directory's orientation README is still the old `wo-rt-c` prototype page
with the VM bolted on at `:78`. Concretely wrong:
- `:31` "Or from the repo root: `just rt-c-demo`" and `:60` "`just rt-c-bench`"
— **neither recipe exists.** The justfile has 16 recipes plus two `mod`s;
no `rt-c-*` among them.
- `:5,:83` "the production Rust runtime (`crates/rt/`)", `:76` "This file is for
reading; `crates/rt` is for running writeonce" — the Rust runtime was removed
2026-08-18 (`docs/08-project-structure.md:11`).
- `:3` `prototypes/wo-db/`, `:43` `docs/runtime/database/03-inmemory-engine.md`,
`:47` `docs/plan/09-concurrency-scaleout.md` — none of these paths exist
(also in the link audit's sections B/C/E).
- `:84` "**`@gc` reference counting** + budgeted cycle collection … Bacon–Rajan
trial deletion" — retired by iteration 7b. `@gc` on a class is now
**rejected** (`docs/guides/language-surface.md:73`), and
`runtime/src/CODE-LOGIC.md` states the replacement outright: "incremental
tri-color mark-sweep … (iteration 7b — RC and Bacon–Rajan are gone)".
- `:89` "`DB_STUB` traps 'engine not linked' until the DB engine binds (plan
5)" — the engine bound in iteration 9. The opcode survives
(`wob.h:232`, `vm.c:1806`) but the sentence reads as "no database yet".
- `:89` builtin list "`now/print/print_int/words/multi_*/map_*`" — there are
now ~70 free builtins plus six module namespaces (`types.ml:784-849`).
- File map (`:92-107`) omits four of the thirteen sources in `runtime/src/`:
`crypto.c/.h`, `json.c`, `park.c/.h`, `sysio.c`.
- `:105` and `:117` "13 suites" / "one of the 13 ASan test binaries" — there
are **18** (`runtime/test/test_*.c`).
- `:1` "now at **phase E**" vs `:57` "A → B → C → D → E → F, all ✅ shipped"
vs `:60` "Measured (phase F…)" — three answers in one file.
Verified-correct in the same file, for contrast: `WO_HEAP_MB` / 64 MiB arena,
the four `.vscode/launch.json` configs, the exit-code contract, and the
`make -C runtime` targets.
---
## B. Verification tables that no longer verify
### B1. `docs/00-code-review.md` — the 2026-08-20 table has decayed
The doc's value is that it *checked* a critique line by line. Seven rows of
`:55-82` are now false, and the doc carries no superseded banner:
| Row | Then | Now |
| --- | --- | --- |
| "no `Float`, no `Bytes` \| absent from `compiler/src/types.ml`" | true | `types.ml:174` `builtin_scalars = ["Int";"Bool";"Text";"Timestamp";"Id";"Float";"Bytes"]` — iteration 19, `done/` |
| "`send` is one-way \| `WO_B_SEND=69` is the last builtin (`WO_B_MAX 69u`)" | true | `WO_B_MAX 95u`; `WO_B_CALL = 88` is a send that parks for a typed reply |
| "no crypto primitives \| none" | true | `WO_B_SHA1=85`, `WO_B_SHA256=86`, `WO_B_HMAC_SHA256=87`; `runtime/src/crypto.c`; `runtime/test/test_crypto.c` |
| "22's battery never run \| … no `bench/baseline.json`, no `just db-bench`" | true | `bench/baseline.json` exists, `just db-bench` / `db-bench-quick` exist, 30+ result files in `bench/results/`, iteration 22 is `done/` |
| "no fuzzing, no CI \| **no `.github/`**, no fuzz target" | true | `.github/workflows/release.yml` exists (fuzzing still absent) |
| "one framework, five samples, one consumer" | true | 13 sample projects under `docs/examples/` |
| "accept on one shard \| one listener, `SO_REUSEADDR` only" | true | iteration 35 landed `serve_conn` + fiber-per-connection |
| `:46-51` "The multi-shard DB gap is structural … `wo_builtin_db` returns `WO_T_DB` 'database engine not initialized'" | true | that string is gone from `runtime/src/`; arc stage 3 landed the transparent DB actor, gated by `just db-actor` |
Rows that still hold, checked: interpreted-only/no JIT, no SIMD, the
`WO_STACK_SLOTS 4096` / `WO_MAX_REGS 64` / `WO_MAX_FRAMES 256` correction, no
generics, no closures, byte strings, no Result type, switch-not-destructuring,
no supervision, growable mailboxes, no `timerfd`, no TLS, no debugger/LSP,
deps-are-git-rev-only, blue-green is a future, TSan covers one demo. And
**`map<K,V>` lookup is still a linear scan** — `cont.h:1-6` says so in as many
words.
### B2. `docs/00-link-audit.md` — numbers and paths both stale
Dated 2026-08-20. `just linkcheck` today reports **files=235, local=675,
broken=77, bad anchors=0**; the doc's table says 206 / 569 / 88. Its own
sections B–F sum to 77, not the 88 its prose claims twice (`:12`, `:145`) —
an internal arithmetic error independent of the drift.
Its repair table (`:29-37`) references paths that have since moved:
`docs/00-status.md` (now `docs/stories/00-status.md`), and
`refine/{08,11,19,20,21}` (now under `done/` and `hold/`).
Two broken links exist today that the audit does not account for:
- `docs/examples/employee-list/README.md:5,6` → `…/refine/20-cross-program-tables.md`
and `…/refine/21-keypair-attach-auth.md`; both files are now in `hold/`.
- `docs/stories/language-runtime-database/hold/26-blue-green-deploy.md:9` →
`00-story.md`; the sibling stopped being a sibling when 26 moved into `hold/`.
Conversely `docs/00-principles.md:57,77,78,87` — four links the audit lists as
open — resolve now.
### B3. `docs/plan/oop-vm/01-error-catalog.md` — not the complete catalog it claims
`docs/guides/language-surface.md:8` calls it "every diagnostic";
`compiler/README.md:39` says "every shipped code is cataloged" there. Ten
codes the compiler emits are absent:
| Code | Defined at | What it is |
| --- | --- | --- |
| `WO-E003` | `lexer.ml:56` | `#if`/`#else`/`#end` misuse |
| `WO-E108` | `bin/main.ml:277` | `internal/` crossed at a `[deps]` boundary |
| `WO-E109` | `bin/main.ml:275` | unknown `wo.toml` `kind` value |
| `WO-E219`–`WO-E223` | `types.ml` | five type-pass codes |
| `WO-E226` | `types.ml:443` | `call`'s reply type through actor-`M` erasure (iteration 24) |
| `WO-E250` | `types.ml:435` | the whole query surface (iteration 9b) |
`WO-E250` is the notable one: it is the only diagnostic the shipped query
language produces, and it is the code a reader hits first when they mistype a
query. Meanwhile `WO-W201` is still catalogued and no longer exists — the
inferred-GC plan (`docs/superpowers/plans/2026-08-18-inferred-gc-mark-sweep.md:151`)
listed "retire WO-W201" as an amendment; the code went, the catalog entry
stayed.
`WO-E004`/`WO-E005` (raw text literal, iteration 37) *are* catalogued —
checked, since `language-surface.md:29,31` depends on them.
---
## C. Docs that disagree with each other
### C1. Is group-by shipped? Two live docs, two answers
- `README.md:332` — Roadmap, "not yet available". **Correct.**
- `docs/guides/language-surface.md:175` — "Present today: from / where / order
/ take / select **plus group-by aggregation**." **Wrong**, and the same page
opens (`:16-17`) with "Every form listed below was compiled and run against
`woc`/`wovm` while writing this page, not read off the parser and hoped for."
The `group … by … into` clause in its §6 grammar block *parses*
(`parser.ml:1137-1144`) and is then rejected by the typechecker
(`types.ml:2241` "group-by aggregation is not supported yet";
`types.ml:2222` for the navigation form).
Reproduced: `docs/examples/employee-list/main.wo:38-41` uses `group e by
e.dept into g` and fails to compile.
`docs/00-dependency-graph.md:45` correctly still lists group-by under the
parked drain.
### C2. `docs/stories/00-status.md` — the narrative and the table disagree about iteration 24
The board's *Current work* table is right: `:314` records "🔄 **iteration 24
(absorbing 31 + 34): chat + actor lifecycle** — spec + plan approved
2026-08-23 … executing on branch `chat-ws-lifecycle`" and links the marker.
The ▶ NEXT PLAN narrative above it is not:
- `:82-85` "Next slice: **iteration 31, actor lifecycle** — its spec brainstorm
is the next act". 31 was absorbed into 24 by directive, and its
actor-death half already landed (`docs/in-progress/2026-08-23-chat-ws-lifecycle.md:20-23`,
commit `ed69841`).
- `:125` "**Next steps:** 31 (lifecycle spec brainstorm) → 24 (chat) → 23 → 32"
— same stale ordering.
- `:286` iteration 24 marked "⬜ fourth in chain … after 31".
- `:290` iteration 34 marked "⬜ off-chain but GATES 24". T1 crypto landed
(`d14fa9f`); `sha1`/`sha256`/`hmac_sha256` are in both `types.ml:847-849`
and `wob.h:461-463`. The gate is cleared —
`docs/00-dependency-graph.md:169` already says so.
Also missing from the board entirely: the 2026-08-25 packaging/release track.
`VERSION`, `scripts/mkdist.sh`, `just dist`, `just install-accept`,
`.github/workflows/release.yml`, `docs/guides/releasing.md` and `dist/writeonce-0.1.0-linux-amd64.tar.gz`
all exist; the last five commits are that work; no standup entry covers it.
### C3. `docs/00-dependency-graph.md` — the main graph is a generation behind
Its own header (`:3`) defers state to the board, but it paints state inline
anyway, and the first mermaid graph paints it wrong:
- `:29` `I17["17 library kind + internal/ (**PARKED** — spec+plan ready…)"]:::parked`
— story 17 is in `done/` with `status: done`; board `:302` reads
"✅ **landed 2026-08-20**".
- `:31` `I18[… (spec APPROVED — **the next implementation**)]:::specd` — story
18 is in `hold/`; board `:303` reads "⏸ hold (2026-08-21)".
- `:33,34` iterations 20/21 as `open` — both `hold/`.
- `:38,40,41,42` use the pre-renumber ids: "10 HTTP service layer", "12
blue-green deploy", "13 metaprogramming @derive", "14 skillhost workload".
The stories are **26**-blue-green-deploy, **29**-compile-time-metaprogramming,
**28**-skillhost-host-workload; no story numbered 10, 12, 13 or 14 exists.
- `:45` `DRAIN["post-12 parked drain: pub(read)/using/#if, …"]` — `pub(read)`
(`ast.ml:118-123`), `using` (`lexer.ml:164`) and `#if` (`lexer.ml:245-249`)
all shipped.
- The main graph has no node for iterations 19, 24, 31, 32, 33, 35, 36 or 37.
Four of those are `done/`. The later sub-graphs *do* cover 34/35/36
correctly (`:141,164-170`), so the drift is confined to the first graph.
---
## D. Structural claims that don't match the tree
### D1. `docs/08-project-structure.md` — "canonical map", four divergences
- `:39` "`plan/` compiler-track docs: architecture.md + the woc plans" under
`compiler/`. **`compiler/plan/` does not exist**; those docs live at
`docs/plan/compiler/` — which the same file's `:49` links correctly.
- `:22,76-77` `tests/corpus/` as "`run/`, `compile-fail/`, `trap/`, `gc/`".
There are nine directories: also `actor/`, `db/`, `lang/`, `sys/`,
`sample-logwatcher/` — all five **empty**. `tests/corpus/README.md:11-15`
documents them as planned per-plan additions, so the corpus README is the
honest one; the structure doc undercounts and neither mentions that five are
placeholders. (Live fixture counts: run 60, compile-fail 46, trap 5, gc 2.)
- `:79-81` `scripts/` as "`oop-e2e.sh`, `mkdist.sh` + `install-accept.sh`, and
the per-sample acceptance scripts (`employee-accept.sh`,
`log-watcher-accept.sh`)". There are 14 scripts; unmentioned:
`db-actor-accept.sh`, `db-bench.py`, `deps-accept.sh`, `fibers-accept.sh`,
`linkcheck.py`, `single-binary-smoke.sh`, `site-accept.sh`,
`web-app-accept.sh`.
- `:89` `examples/` as "log-watcher/, employee/, employee-list/ samples" —
there are 13.
- `:87` puts status at the `docs/` root; it is `docs/stories/00-status.md`
(the file's own `:5` links it correctly). The root also has
`00-dependency-graph.md` and `00-link-audit.md`, unlisted.
- The one-page map (`:17-29`) omits four tracked root entries: `bench/`,
`dist/`, `.github/`, `.claude/`.
- `docs/examples/db-actor/` has `main.wo`, a `wo.toml` and a gate
(`just db-actor`) but **no README** — the only sample without one.
Correct in the same file, checked: `runtime/wo-rt.c` exists; `.dev/` is
gitignored except `.dev/README.md` (`git ls-files .dev` returns exactly one
path) and `.dev/reference/` does hold `crates/`, `colibri/`, `llama-cpp/`,
`linux/`, `go/`.
### D2. `compiler/README.md` — stage banner and CLI list both behind
- `:5` "**Stage: plan 3 … complete, Tasks 1–6 + 8**". Plan 3 closed in early
August; the front end has since taken iterations 15, 17, 19, 24, 34, 35, 36
and 37. A reader takes this page as the compiler's current extent.
- `:26-34` "Running `woc`" omits four of the nine modes the binary's own
`usage_msg` prints: `--dump-gc`, `--update-deps <dir>`, `version`, and the
`woc <dir>` manifest build (the mode `README.md:111` teaches as the primary
one). `-D <name>`, the `#if` flag setter documented at
`language-surface.md:34`, is in neither the README nor `usage_msg` —
it exists at `bin/main.ml:1162`.
- `:37` "same contract as `wo run` (`crates/rt/src/lib.rs::discover`)" — the
Rust runtime is gone. The identical stale sentence is also the doc comment
at `compiler/bin/main.ml:16-19`. *(Code, not markdown — noted, not
changed.)*
Correct: the `=== path ===` multi-file header (`dump.ml:128`), the five golden
stages, the exit-code contract, OCaml 4.14 / dune 3.14 (`dune-project` says
`(lang dune 3.14)`).
### D3. `runtime/src/CODE-LOGIC.md` — file table missing two sources
`:12-25` is a complete-looking table of "the files, in dependency order" and
omits `park.c/.h` (fiber parking — iteration 11, and central to how blocking
builtins work) and `crypto.c/.h` (iteration 34). The doc is dated 2026-08-14,
before both; nothing marks it as of-that-date beyond the first line.
`database/src/CODE-LOGIC.md` and `compiler/src/CODE-LOGIC.md` were checked
against their sources and hold up.
---
## E. Runbook and instruction errors
`docs/guides/releasing.md`, authored 2026-08-25, contains steps that cannot be
followed:
- `:110` (step 11) "Remove `--draft`, commit, push." **`.github/workflows/release.yml`
contains no `--draft`** — `gh release create` at `:135-139` passes only the
two asset paths, `--title` and `--generate-notes`. Nothing to remove.
- Steps 5, 6 and 10 disagree with each other. `:50-54` (step 5) says the
rehearsal is a `workflow_dispatch` run that "skips the tag guard and the
publish step"; `:56` (step 6) says "nothing to undo — a dry run creates no
tag and no release"; `:89-92` (step 10) then instructs
`gh release delete v0.0.0-test --yes` and two tag deletions. There is no
path in the workflow that creates `v0.0.0-test`.
`.github/workflows/release.yml:1-2` — "this workflow has never run. Authored
2026-08-25 and not executable locally" — is contradicted by `:43` of the same
file ("The first run failed here with `dune: command not found`") and by
commits `05fafd3`, `d66087d`, `4470f03`, which are fixes read off real runs.
*(Code comment, not markdown.)*
Verified correct against the workflow and the built binaries: the asset name
`writeonce-0.1.0-linux-amd64.tar.gz`, the tag↔`VERSION` guard, the
`ubuntu-22.04` pin and its glibc reasoning (this machine's binaries need
`GLIBC_2.38`, matching `:8` step 8's "2.38 from this dev machine"), and
`scripts/install-readme.tmpl.md:24-25` — `woc version` prints
`writeonce 0.1.0 linux/amd64` and `wovm --version` prints `wovm 0.1.0`, exactly
as documented.
---
## F. Small factual errors
| Where | Claim | Actual |
| --- | --- | --- |
| `README.md:28` | "~100 KB for the sample programs" | 163–254 KB. Smallest built sample 163,117 B (`fibers`), largest 253,808 B (`site`); bare `wovm` is 161,848 B, so ~160 KB is the floor |
| `README.md:142` | "**Types:** `Int`, `Text`, `Bool`, and user `class` types" | seven builtin scalars (`types.ml:174`): also `Float`, `Bytes`, `Timestamp`, `Id` |
| `README.md:170` | `time` → "`sleep`, `now`, `local`, `iso`" | also `ticks` (µs monotonic, builtin 84 — iteration 22's one runtime addition) |
| `README.md:172` | `net` → "TCP `listen`/`accept`/`read`/`write`/`close` (host + port)" | also `read_dl`, `accept_dl`, `write_dl`, `listen_unix`, `peer` (ids 91–95, iteration 35) |
| `README.md:344` | "`net` is TCP host+port only" | `net.listen_unix` binds a unix socket (builtin 94) |
| `README.md:272,276-277` | `[deps]` key `niceserve`, then "`use niceframework`" | the `[deps]` KEY *is* the module name — `docs/examples/web-app/wo.toml:19-20` keys it `serve` and `main.wo:9` says `use serve`. The example as written would not compile |
| `README.md:295` vs `:298-320` | "**Two** complete sample programs" | three bullets follow; `:322` "Read **either** program's `main.wo`" compounds it. There are 13 samples, 8 of them gated |
| `docs/guides/language-surface.md:36` | "**Keywords (35)**" | 37. The list printed immediately after is complete and correct against `lexer.ml:147-184` — only the count is wrong |
| `docs/00-principles.md:104-105` | capabilities are "(`fs`, `proc`, `net`, `time`, `json`)" | six modules — `env` missing (`types.ml:206`) |
| `docs/superpowers/plans/2026-08-18-inferred-gc-mark-sweep.md:153-154` | `[x]` "Add a `docs/examples/gc-cycle` acceptance script + `just gc-cycle` recipe" / `[x]` "Verify: `just gc-cycle` green" | **no `gc-cycle` recipe exists** and there is no `scripts/gc-cycle-accept.sh`. `docs/examples/gc-cycle/` has sources, a `target/` and a "Run status" section (`README.md:186`) but no gate. Two checked boxes for work that did not land |
Forward references that are correctly labelled and are *not* findings:
`just chat` (iteration 24, T9 — pending in the marker), `just oop-parity`
(deferred by explicit decision, recorded at `compiler/README.md:5`), and
`docs/examples/employee-list/`, whose banner honestly says it does not compile
(confirmed: `woc` exits 2 on `[connect.employee]`, a section for the `hold/`
iteration-20 feature).
---
## What to fix first
1. **`README.md`** — it is the writeonce.de landing content
(`docs/08-project-structure.md:28`), so A1–A3, F's README rows and the
`use niceframework` example are the highest-value corrections in the repo.
2. **The two example status banners** (A4, A5) — one line each, and they
currently tell a visitor that the repo's two flagship gates don't build.
3. **`runtime/README.md`** (A6) — the largest single body of stale prose. It
wants splitting: `wo-rt.c` is a historical reference card, `runtime/src/` is
the shipped VM, and one page is trying to be both.
4. **`docs/00-code-review.md`** and **`docs/00-link-audit.md`** (B1, B2) — both
are dated verifications whose value depends on being re-run. Either re-run
them or banner them as of-date.
5. **`docs/plan/oop-vm/01-error-catalog.md`** (B3) — it is cited as normative by
two other docs; ten missing codes including the query surface's only one.
6. **`docs/guides/language-surface.md:175`** (C1) — a single false clause on
an otherwise excellent page.
7. **`docs/00-dependency-graph.md`** first graph (C3) and the board's NEXT PLAN
narrative (C2) — both trail their own companion tables.
---
## What was fixed
All on branch `docs-truth-audit-fixes`, 2026-08-26. Docs only — no code changed,
so no gate output changed.
| Finding | Disposition |
| --- | --- |
| **A1** README roadmap listed shipped concurrency | Concurrency entry removed; `spawn`/`send`/`call`/`receive` documented under "Language at a glance" as shipped. The `service`-blocks entry stayed but now says what you write *instead* today. Group-by stayed — it was the one correct entry. |
| **A2** "does not serve HTTP, WebSockets, or a UI" | Rewritten: both work, as `.wo` libraries consumed through `[deps]`, never as runtime features — which is the real (and more interesting) claim. TLS-by-proxy stated. |
| **A3** "no package manager" | Now "no package **registry** — dependencies are exact-rev git URLs and nothing else", which is true and is the distinction the doc meant. |
| **A4** `employee` README "does not compile" | Banner flipped to shipped, `just employee` named as the gate, with the one genuinely-ahead clause (`group … by … into`) called out rather than left to surprise a reader. |
| **A5** `log-watcher` README "design artifact" | Banner flipped to shipped with iteration 7's actual acceptance evidence; the "five stdlib modules" list corrected to six. |
| **A6** `runtime/README.md` a generation stale | **Restructured, not patched.** Leads with `wovm`; `wo-rt.c` demoted to a marked "Historical" section that keeps its measured numbers as the prototype record they are. Fixed: the two nonexistent recipes, `crates/rt`, `prototypes/wo-db`, the `@gc` refcount/Bacon–Rajan description (now inferred mark-sweep), `DB_STUB`, the builtin list, "13 suites" → 18, four missing source files, and the phase E/F contradiction. Three broken links went with it. |
| **B1** `00-code-review.md` decayed | History kept intact with a pointer at the top; a **Re-verification 2026-08-26** section added listing the eight overtaken rows against source, the ~20 that still hold, and the two new gaps (now iteration 38). "No supervision/actor death" is marked *partly* overtaken — death landed, supervision did not. |
| **B2** `00-link-audit.md` stale | Re-run and rewritten. The 48 dead-era exploration links are **resolved by de-linking, not re-pointing** — their prose names the retired plan by number, so re-targeting would have made each sentence lie. A successor map was added to `plan/discarded.md`, which is what that report's own "Still open" note asked for. Three more fixable breaks fixed. 77 → 23. |
| **B3** ten codes missing from the error catalog | Added with definitions read from source: WO-E003, E108, E109, E219–E223, E226, E250. Header's "as of plan 3" scope line corrected. The Completeness method section now records *why* the sweep rotted — codes are built as `<stage>_prefix ^ "NN"`, so grepping for the literal `WO-E250` finds only a comment. **The `WO-W201` half of this finding was wrong:** the catalog already marked it *(retired, iteration 7b)* with "*(no longer emitted)*". The doc was right; the audit misread its own grep. |
| **C1** `language-surface.md` claimed group-by works | Corrected in three places: the clause is marked in the grammar block, the "present today" list drops it, and the page's "every form was compiled and run" promise now names the exception. Keyword count 35 → 37. |
| **C2** board narrative trailed its own tables | ▶ NEXT PLAN rewritten: the live slice is 24 (absorbing 31 + 34), not "31 next". Rows for 24, 31 and 34 updated — 31's remaining surface is cited as the *reserved holes at ids 89/90*, which is machine-checkable. A standup entry for the 2026-08-25 packaging/release track was added; it had none. |
| **C3** dependency graph a generation behind | Graph 1 rebuilt: 17 → done, 18/20/21 → held, the pre-renumber ids 10/12/13/14 replaced by stories 25/26/29/28, nodes added for 19/24/30/31/32/33/34/35/36/37/38, and the parked drain reduced to what is actually left (`pub(read)`/`using`/`#if` all shipped). Node/edge references validated. |
| **D1** `08-project-structure.md` "canonical map" | Fixed: the nonexistent `compiler/plan/`, the corpus's nine directories (four with fixtures, five reserved and empty, with counts), all 14 scripts, the `docs/` subtree, and the four missing root entries (`bench/`, `dist/`, `.github/`, `.claude/`). The sample-acceptance list now names all eight gates. |
| **D2** `compiler/README.md` stage banner + CLI | Banner replaced with the eight iterations the front end has taken since plan 3. The CLI list gained `woc <dir>` (the primary mode), `version`, `--update-deps`, `--dump-gc` and `-D`. `crates/rt/src/lib.rs::discover` reference dropped. `gcinfer` added to the module list. |
| **D3** `runtime/src/CODE-LOGIC.md` file table | `park.c/.h` and `crypto.c/.h` added, dated so the gap is visible rather than papered over. |
| **E** `releasing.md` unfollowable steps | The `--draft` step and the phantom rehearsal cleanup deleted, remaining steps renumbered, and a paragraph added explaining why neither exists (plus how to opt into a draft if you want one). |
| **F** small factual errors | All corrected: binary size ~100 KB → 160–260 KB (measured), the scalar list, `time.ticks`, the five missing `net` members, the `fs` read-and-append limit, the `[deps]` key/`use` mismatch (the example would not have compiled), "two samples" → 13 with 8 gated, the six-module count in `00-principles.md`, and the `just gc-cycle` recipe that two checked boxes claimed. |
| **Later findings** | `00-story.md` gained the missing iteration 36 row; `docs/examples/db-actor/` gained the README it never had. |
### Left deliberately unfixed
- **23 broken links in `.dev/`** — vendored plugin-skill copies and reference
study trees. `.dev/` is gitignored (`git ls-files .dev` returns one path), so
these are per-developer notes. Fixing them means re-vendoring the skills with
their `references/` subdirectories.
- **The `just gc-cycle` gate itself.** The false checkbox is now disclosed in
both the plan and the sample's README, but wiring the acceptance script is
work, not documentation, and belongs to whoever picks up that loose end.
- **`tests/corpus/`'s five empty directories.** Documented as reserved with the
plan each was to be filled by; deleting or filling them is a test decision.
- **Two stale references in code comments**, recorded here rather than edited
because this pass was scoped to markdown: `compiler/bin/main.ml:16-19` and
`compiler/src/parser.ml:202` both still cite `crates/rt/src/*.rs`, removed
2026-08-18; and `.github/workflows/release.yml:1-2` says "this workflow has
never run" while line 43 of the same file reports what its first run failed
with.
---
## Structural change 2026-08-26 — status folders removed
Directive from the developer, applied after the fixes above: **`docs/` no longer
uses directories to encode status.** The four story subfolders
(`done/`, `refine/`, `hold/`, `in-progress/`) and top-level `docs/in-progress/`
are gone. All 34 story iterations sit flat in
`docs/stories/language-runtime-database/`, the slice marker sits flat in `docs/`
as `active-slice-<date>-<topic>.md`, and each file's `status:` frontmatter key is
the single place its state is recorded.
This reverses the 2026-08-20/21 convention ("the folder move IS the status
change"). The reason it is a good trade is visible in this repo's own history:
under the old scheme a status change relocated the file, which invalidated every
relative link in and to it — section A of the 2026-08-20 link audit was nine
instances of exactly that, and B2 above found two more that had accumulated
since. A status change is now a one-line edit that cannot break a link.
What the move required, all verified with `just linkcheck` (23 broken, all in
`.dev/`, unchanged from before the move):
- 34 files relocated with `git mv` so history follows them.
- **252 relative links recomputed in 70 files** — not by string substitution but
by resolving each link to an absolute path from its *old* location, remapping
through the move table, and re-deriving it relative to the file's *new*
location. String surgery would have mangled the `../` depth changes on the
moved files themselves.
- Link *text* and backticked paths that named a status folder stripped
separately — a correct target under stale display text is still a lie.
- Convention prose rewritten where it taught the old rule:
`stories/00-status.md`'s header, `stories/board-views.md` (including its Kanban
caveat, which described status changes as folder moves), and
`08-project-structure.md`'s map plus a new naming-convention entry.
- Phrases of the form "moves to `done/`" rewritten as "sets `status: done`" in
the live docs — including the four open checkboxes in the active plan
`2026-08-23-chat-ws-lifecycle.md`, which would otherwise have instructed a
future session to recreate the folders.
Two things surfaced that the move made visible rather than caused:
1. **A frontmatter collision, caught and fixed.** Giving the marker doc
`iteration: "24"` would have put two files in the repo claiming to be
iteration 24 with contradicting `status:` values. The marker is a progress
log, not a status carrier, so it takes `slice: "24"` and points at the story
that owns the status.
2. **Story 24's frontmatter said `refine` while the board said 🔄 live.** Under
the old scheme that drift was cheap to leave; under this one frontmatter *is*
the answer, so it is now `status: in-progress`. Iterations 31 and 34 keep
`refine` — they are absorbed into 24 but their own closeout is still pending,
which is what 24's T10 exists to do.
Dated records were deliberately left naming the old paths: the findings sections
of this document (which declare themselves a pre-fix snapshot), the history
section of `00-link-audit.md` (which says every path in it is as it was on that
date), and the "Files:" lists of closed plans. Rewriting those would destroy the
record of what was true when each was written.

View file

@ -1,154 +1,156 @@
# Markdown link audit — 2026-08-20 # Markdown link audit — re-run 2026-08-26
Scope: every `*.md` in the repo (`.git` excluded). Scope: every repo-authored `*.md`. `.git`, `target`, `dist`, `node_modules`,
External URLs were not fetched (no network verification performed). `_build` and — since 2026-08-26 — `.dev/` and `.superpowers/` are excluded; see
the note under the table. External URLs are not fetched (no network
verification).
| | files | relative links | broken paths | bad anchors | | | files | relative links | broken paths | bad anchors |
|---|---|---|---|---| |---|---|---|---|---|
| first scan | 207 | 574 | 97 | 0 | | first scan (2026-08-20) | 207 | 574 | 97 | 0 |
| after section A fixes | 206 | 569 | **88** | 0 | | after section A fixes (2026-08-20) | 206 | 569 | 88* | 0 |
| **re-run 2026-08-26, before fixes** | 235 | 675 | 77 | 0 |
| **re-run 2026-08-26, after fixes** | 237 | 652 | 23 | 0 |
| **after scoping the gate to repo-authored docs** | 149 | 656 | **0** | **0** |
Section A is repaired and verified. Sections B–F are pre-existing rot and \* The 2026-08-20 report's prose said 88 twice while its own sections B–F summed
still open — every one of the remaining 88 lives there. to 77. The 77 was right; the 88 was an arithmetic slip, corrected here.
**The gate is now clean: 0 broken, 0 bad anchors.**
The last 23 were all in `.dev/` — vendored plugin-skill copies and cloned
reference projects, neither of which this repo authors. `scripts/linkcheck.py`
now skips `.dev/` and `.superpowers/` alongside `.git`/`target`/`dist`. That was
forced by adding gofiber/fiber as a reference (2026-08-26): its own docs are
Docusaurus pages whose links resolve at site-build time, not on disk, so the
clone alone contributed 21 broken paths and 39 bad anchors. A gate that reports
the same dozens of failures forever is a gate nobody reads. Everything the repo
actually ships — `docs/`, `compiler/`, `runtime/`, `database/`, `tests/`,
`bench/`, `scripts/`, the root README — is still scanned, and is clean.
Re-check with `just linkcheck`. Re-check with `just linkcheck`.
Tool: `linkcheck.py` — walks the tree, strips fenced/inline code, extracts inline Tool: `scripts/linkcheck.py` — walks the tree, strips fenced/inline code,
links and reference definitions, resolves each relative target, and validates extracts inline links and reference definitions, resolves each relative target,
`#fragment` against GitHub-style heading slugs of the target file. and validates `#fragment` against GitHub-style heading slugs of the target file.
--- ---
## A. Regressions from the in-flight renumber — FIXED 2026-08-20 ## What the 2026-08-26 re-run changed
All nine broke because files moved in the working tree; each had a known ### 1. The dead-era exploration links — RESOLVED (48 links, 15 files)
successor. Repaired:
Sections B and C of the 2026-08-20 report left a decision open: the studies under
`docs/plan/exploration/` cite the old flat `docs/plan/NN-*.md` numbering and the
`docs/runtime/database/` tree, both removed with the Rust track on 2026-08-18,
and no successor map existed. That decision is now made.
**De-linked, not re-pointed.** The link *text* in these studies names the retired
plan by number — `[plan 09a]`, `[plan 11]`, ``[`12-engine-disk-cutover.md`]`` —
so aiming those at a story would have made each sentence assert something false
about a document that never said it. The targets were stripped and the text kept
as plain code spans. The studies still read correctly as the dated records they
are, and they no longer claim a file exists.
The successor map lives in
[`plan/discarded.md`](plan/discarded.md#successor-map-for-the-removed-rust-era-plan-paths)
— one row per retired path, naming what carries that work now (or stating
plainly that nothing does, as with `12-engine-disk-cutover.md` and
`08-sendfile-static-assets.md`). That table is what the 2026-08-20 report's
"Still open" note asked for.
Files touched: `assembly/{00-overview,02-writeonce-stance}.md`,
`c-runtime/{00-plan,01-architecture,02-single-binary}.md`,
`linux/{01-epoll,02-eventfd,03-timerfd,04-signalfd,05-inotify,06-sendfile,07-io_uring,08-mmap,11-memfd_create,12-pwrite-fsync}.md`.
### 2. `runtime/README.md` — RESOLVED (3 links)
`prototypes/wo-db/`, `docs/runtime/database/03-inmemory-engine.md` and
`docs/plan/09-concurrency-scaleout.md` all went when that README was restructured
to lead with `wovm` and demote `wo-rt.c` to a clearly-marked historical section.
It also carried two recipes that do not exist (`just rt-c-demo`,
`just rt-c-bench`) — not a link problem, fixed in the same pass. See
[`00-doc-audit.md`](00-doc-audit.md) §A6.
### 3. Two breaks the 2026-08-20 report did not have — RESOLVED
Both were caused by story files moving between status folders after that report:
| Source | Was | Now | | Source | Was | Now |
|---|---|---| |---|---|---|
| `docs/00-status.md:167` | `stories/language-runtime-database/05-language-surface.md` | `…/done/05-language-surface.md` | | `docs/examples/employee-list/README.md:5,6` | `…/refine/20-cross-program-tables.md`, `…/refine/21-keypair-attach-auth.md` | `…/hold/…` (both stories moved to `hold/` 2026-08-21) |
| `docs/00-status.md:187` | `stories/language-runtime-database/18-memory-db-features.md` | `…/hold/18-memory-db-features.md` | | `docs/stories/…/hold/26-blue-green-deploy.md:9` | `00-story.md` | `../00-story.md` (the sibling stopped being a sibling when 26 moved into `hold/`) |
| `docs/stories/language-runtime-database/00-story.md:60` | `05-language-surface.md` | `done/05-language-surface.md` |
| `docs/stories/language-runtime-database/00-story.md:69` | `18-memory-db-features.md` | `hold/18-memory-db-features.md` |
| `docs/stories/language-runtime-database/25-http-service.md:4` | `../00-story.md` | `00-story.md` |
| `docs/stories/language-runtime-database/26-blue-green-deploy.md:4` | `../00-story.md` | `00-story.md` |
| `.../refine/08-shard-actor-runtime.md:98` | `../hold/09e-durability-throughput-scale.md` | `22-durability-throughput-scale.md` |
| `.../refine/08-shard-actor-runtime.md:100` | `09f-io-uring-commit.md` | `23-io-uring-commit.md` |
| `.../refine/20-cross-program-tables.md:143` | `../hold/09d-keypair-attach-auth.md` | `21-keypair-attach-auth.md` |
The `25`/`26` pair used `../00-story.md` while `00-story.md` is a sibling — the This is the recurring shape: **a story folder move breaks every relative link
`refine/`-relative form pasted into files one level up. in and to that file.** Section A of the 2026-08-20 report was nine instances of
it; these are two more. Worth a check in whatever moves a story.
Link labels were renumbered with their targets, since the old IDs contradicted ### 4. Stale paths inside the report itself — RESOLVED
the new paths: `9e`→`22` and `9f`→`23` in `refine/08` (both the "Gated by the
benchmark" note and settled decision 4, "Order: 22 → the 8+11 arc → 23").
## B. Dead era: the old flat `docs/plan/NN-*.md` numbering (48 links) The 2026-08-20 repair table cited `docs/00-status.md` (now
`docs/stories/00-status.md`) and `refine/{08,11,19,20,21}` (now under `done/` and
`hold/`). That table has been retired into the history section below rather than
carried forward with paths that no longer resolve.
`docs/plan/` now holds only `compiler/`, `exploration/`, `oop-vm/`, ### 5. Four links the report listed as open had already been fixed
`discarded.md`, `learnings.md`. Every flat-numbered plan doc is gone, and no
successor path was recorded. Missing targets, by inbound count:
- `09-concurrency-scaleout.md` — 12 `docs/00-principles.md:57,77,78,87` resolved before this re-run — including the
- `11-wal-and-recovery.md` — 9 `examples/blog/README.md` reference that section D called a never-created file.
- `12-engine-disk-cutover.md` — 8 Section D's other entries stand.
- `10-storage-foundations.md` — 8
- `done/02-event-loop-epoll.md` — 4
- `13-class-model-live-pricing.md` — 3
- `07-inotify-content-watcher.md` — 3
- `08-sendfile-static-assets.md` — 2
- `15-mcp-streamable-http.md`, `16-postgres-mirror.md`,
`done/03-hand-rolled-http.md`, `done/04-cutover-remove-tokio-axum.md` — 1 each
Inbound from: all of `docs/plan/exploration/{linux,postgresql,c-runtime,assembly}/`,
plus `docs/00-principles.md:57,77,78`, `runtime/README.md:47`,
`.dev/reference/README.md:55,56,58`.
**Decision needed** — these exploration docs still cite a plan structure that no
longer exists. Either map each to its story successor
(e.g. concurrency-scaleout → `stories/.../refine/08-shard-actor-runtime.md`,
wal/storage → `refine/22-durability-throughput-scale.md`,
io_uring → `refine/23-io-uring-commit.md`) or strip the links and keep prose.
## C. Dead era: the `docs/runtime/database/` tree (7 links)
`docs/runtime/` does not exist. Missing targets:
- `03-inmemory-engine.md` — 5 (incl. one `#recovery` anchor)
- `02-wo-language.md` — 2 (incl. one `#concurrency-model` anchor)
- `07-wo-seg-migration.md` — 1
Inbound from `docs/plan/exploration/linux/{07-io_uring,08-mmap,11-memfd_create}.md`,
`docs/plan/exploration/{assembly/02-writeonce-stance,c-runtime/02-single-binary}.md`,
`runtime/README.md:43`, `.dev/reference/README.md:31`.
## D. Never-created / removed siblings (5 links)
| Source | Target | Note |
|---|---|---|
| `docs/plan/exploration/linux/06-sendfile.md:10` | `./07-splice.md` | slot 07 is `07-io_uring.md`; no splice doc was written |
| `docs/plan/exploration/assembly/00-overview.md:19` | `../../../.dev/reference/go/src/runtime/atomic_amd64.s` | wrong depth **and** file absent from the vendored Go tree |
| `docs/00-principles.md:87` | `examples/blog/README.md` | `docs/examples/blog/` never existed |
| `.dev/reference/rest/README.md:76` | `../../docs/examples/blog/README.md` | same missing example |
| `.dev/reference/README.md:41,59` | `../docs/plan/exploration/colibri/00-colibri-and-mixtral.md` | `exploration/colibri/` absent (2 links) |
## E. `prototypes/` tree gone (4 links)
`prototypes/` is not in the repo. Referenced as `prototypes/wo-db/` from
`docs/plan/exploration/c-runtime/00-plan.md:88`, `02-single-binary.md:83`,
`runtime/README.md:5`, and `prototypes/llama-moe-stream` from
`.dev/reference/README.md:59`.
## F. Vendored skill copies — not ours to fix (13 links)
`.dev/skills/` holds flattened copies of plugin skills. The originals ship as
directories with sibling reference files; flattening dropped them.
- `.dev/skills/context-mode/context-mode.md:297-300` → `./references/{patterns-javascript,patterns-python,patterns-shell,anti-patterns}.md`
- `.dev/skills/superpowers/requesting-code-review.md:34,95` → `code-reviewer.md`
- `.dev/skills/superpowers/subagent-driven-development.md:232,300,345,400,410` → `implementer-prompt.md`, `task-reviewer-prompt.md`, `re-review-prompt.md` (×2), `../requesting-code-review/code-reviewer.md`
- `.dev/skills/superpowers/test-driven-development.md:206` → `writing-good-tests.md`
- `.dev/skills/superpowers/writing-skills.md:12,587` → `../using-superpowers/references/{codex,gemini}-tools.md`, `testing-skills-with-subagents.md`
Leave as-is, or re-vendor the skills with their `references/` subdirectories.
--- ---
## Structural problems found alongside the links ## Out of gate scope — `.dev/` (was 23 links, now unscanned)
1. **Iteration 19 was double-booked — RESOLVED.** Recorded so the knowledge is not lost, but no longer reported by
`refine/19-chat-websocket-workload.md` and `refine/24-chat-websocket-workload.md` `just linkcheck`. Not ours to fix, unchanged in character from the 2026-08-20
were the same document, differing only in the `# Iteration NN` heading, while report's section F.
- **`.dev/skills/` (15 links)** — flattened copies of plugin skills. The
originals ship as directories with sibling `references/` files; flattening
dropped them. `context-mode.md:297-300`, `subagent-driven-development.md` (5),
`writing-skills.md` (3), `requesting-code-review.md` (2),
`test-driven-development.md:206`. Leave as-is, or re-vendor the skills with
their subdirectories.
- **`.dev/reference/` (8 links)** — `README.md` (7) points at the removed
`docs/plan/{linux,assembly}/` and `15-mcp-streamable-http.md`, the absent
`exploration/colibri/`, and `prototypes/llama-moe-stream`;
`rest/README.md:76` points at `docs/examples/blog/`, which never existed.
`.dev/` is gitignored (`git ls-files .dev` returns only `.dev/README.md`), so
these are per-developer notes, not repo content.
---
## History — the 2026-08-20 first pass
Kept for the record; every path below is as it was on that date.
### A. Regressions from the in-flight renumber — FIXED 2026-08-20
Nine links broke because files moved in the working tree; each had a known
successor. Sources: `docs/00-status.md:167,187`,
`docs/stories/language-runtime-database/00-story.md:60,69`, the `25`/`26` story
pair (which used `../00-story.md` while `00-story.md` was a sibling — the
`refine/`-relative form pasted into files one level up), `refine/08-shard-actor-runtime.md:98,100`,
and `refine/20-cross-program-tables.md:143`. Link labels were renumbered with
their targets, since the old IDs contradicted the new paths: `9e`→`22` and
`9f`→`23`.
### Structural problems found alongside the links
1. **Iteration 19 was double-booked — RESOLVED.** `refine/19-chat-websocket-workload.md`
and `refine/24-chat-websocket-workload.md` were the same document while
`19-missing-scalar-types.md` also claimed 19. `00-story.md`'s mapping line `19-missing-scalar-types.md` also claimed 19. `00-story.md`'s mapping line
(`24←19(chat)`) and table row 20 make **24 canonical**, so the 19 copy was made **24** canonical, so the 19 copy was deleted after repointing
deleted. `refine/11-fibers.md:13` had been pointing at the 19 copy — repointed `refine/11-fibers.md:13` at 24.
to 24 first, so the delete broke nothing. Prose in `refine/08` that named 2. **`08-shard-actor-runtime.md` existed twice — RESOLVED.** 58 lines at the
"iteration 19" for chat now says 24 (4 places). stories root vs 110 in `refine/`. The `refine/` copy superseded it outright
(the root copy still required `@gc`, retired by 7b, and cited
2. **`08-shard-actor-runtime.md` existed twice — RESOLVED.** `runtime/wo-rt.c`, removed with the Rust runtime). Root copy deleted.
58 lines at the stories root vs 110 in `refine/`. The `refine/` copy supersedes
it outright: same acceptance criteria plus the 2026-08-20 settled decisions, the
inferred-GC restatement (7b retired `@gc`, which the root copy still required),
and the corrected substrate path (the root copy cited `runtime/wo-rt.c`, removed
with the Rust runtime). Root copy deleted; the one inbound link,
`docs/00-status.md:171`, now points at `refine/`. Six other referrers already did.
3. **Unresolved merge-conflict markers were committed** into 3. **Unresolved merge-conflict markers were committed** into
`refine/20-cross-program-tables.md:139-145` — `<<<<<<<< HEAD:… / ======== / `refine/20-cross-program-tables.md:139-145`, from a rename-conflicted merge —
>>>>>>>> language-surface-strictness:…/hold/09c-cross-program-tables.md`, from a which is what produced that file's broken `09d` link. Resolved in favour of
rename-conflicted merge. This is what produced that file's broken `09d` link: HEAD. `grep` confirmed no other conflict markers under `docs/`.
the stale side was still in the file. Resolved in favour of HEAD (the renumbered 4. **A status disagreement, not a link problem:** `docs/00-status.md:171` showed
`21` text). `grep` confirms no other conflict markers under `docs/`. iteration 8 as ⬜ while `00-story.md:68` recorded arc stages 1+2 as landed.
Both now read landed.
## Still open
- Sections B–F above: 88 broken links, all pre-existing.
- `docs/plan/discarded.md` and `docs/plan/learnings.md` are the only survivors of
the old flat plan layout, which is why B and C have no successor map. A rename
table in one of them would let the exploration docs be repaired mechanically
rather than by guesswork.
- `docs/00-status.md:171` still shows iteration 8 as ⬜ while `00-story.md:68`
records arc stages 1+2 as landed 2026-08-20. Not a link problem — a status
disagreement between the two index docs. Left alone.
- `refine/23-io-uring-commit.md:26` still quotes the old order as
"9e → 8+11 → 9f" in a dated note. No link involved; left as historical record.

View file

@ -101,9 +101,10 @@ directly instead of the lowest common denominator.
## 10. Capabilities are typed builtins — no FFI ## 10. Capabilities are typed builtins — no FFI
Programs reach the system only through audited stdlib builtins (`fs`, Programs reach the system only through audited stdlib builtins — six
`proc`, `net`, `time`, `json`): bounded reads, args-array-only process reserved namespaces (`fs`, `proc`, `net`, `time`, `json`, `env`): bounded
runs, handles that close on drop. There is no `extern`, no escape hatch. reads, args-array-only process runs, handles that close on drop. There is
no `extern`, no escape hatch.
*Why:* one FFI hole voids the entire memory-safety and security story; *Why:* one FFI hole voids the entire memory-safety and security story;
typed capabilities make the safe path the only path. typed capabilities make the safe path the only path.
*Enforced by:* [the systems-track spec Parts 2–3](superpowers/specs/2026-08-01-systems-track-design.md). *Enforced by:* [the systems-track spec Parts 2–3](superpowers/specs/2026-08-01-systems-track-design.md).

View file

@ -16,18 +16,25 @@ project.
``` ```
writeonce-all/ writeonce-all/
├── compiler/ OCaml `woc` — lexer→parser→types→owner→emit; produces the compiler binary ├── compiler/ OCaml `woc` — lexer→parser→types→gcinfer→owner→emit; produces the compiler binary
├── runtime/ C `wovm` — the register VM that runs .wob images (src/); phase A–F C reference (wo-rt.c, bench/) ├── runtime/ C `wovm` — the register VM that runs .wob images (src/); retired io_uring reference (wo-rt.c, bench/)
├── database/ C embedded engine — class-shaped tables, secondary indexes, typed WAL + recovery ├── database/ C embedded engine — class-shaped tables, secondary indexes, typed WAL + recovery
├── tests/ corpus/ — conformance fixtures: run / compile-fail / trap / gc ├── tests/ corpus/ — conformance fixtures: run / compile-fail / trap / gc (+ five reserved, still empty)
├── scripts/ oop-e2e.sh (corpus runner), mkdist.sh / install-accept.sh (packaging), sample acceptance ├── scripts/ the corpus runner, packaging, linkcheck, and one acceptance script per sample
├── docs/ ALL documentation: numbered docs, stories/, plan/, examples/, superpowers/ ├── bench/ baseline.json (the db-bench gate's thresholds) + results/ + compare/ (Go+SQLite peer)
├── docs/ ALL documentation: numbered docs, stories/, plan/, examples/, guides/, superpowers/
├── dist/ `just dist` output: writeonce-<ver>-linux-amd64.tar.gz + .sha256
├── .github/ workflows/release.yml — builds, verifies and publishes on a `v*` tag push
├── .claude/ agents/ — project subagent definitions (see docs/guides/database-developer-subagent.md)
├── .dev/ gitignored per-developer links + reference study trees (v1 crates, colibri, llama-cpp) ├── .dev/ gitignored per-developer links + reference study trees (v1 crates, colibri, llama-cpp)
├── justfile task runner: woc-/wovm-build, the *-test gates, oop-accept, dist, install-accept ├── justfile task runner: woc-/wovm-build, the *-test gates, oop-accept, dist, install-accept
├── VERSION single-sourced toolchain version (stamped into woc/wovm; asserted by `just dist`) ├── VERSION single-sourced toolchain version (stamped into woc/wovm; asserted by `just dist`)
└── README.md the getting-started front door (also the writeonce.de landing content) └── README.md the getting-started front door (also the writeonce.de landing content)
``` ```
`target/` and `.vscode/` are local build and editor state, not part of the
project layout.
## Root directories in detail ## Root directories in detail
### `compiler/` — the OCaml `woc` compiler ### `compiler/` — the OCaml `woc` compiler
@ -36,13 +43,19 @@ writeonce-all/
compiler/ compiler/
├── dune-project ├── dune-project
├── README.md orientation: pipeline map, build/test commands ├── README.md orientation: pipeline map, build/test commands
├── plan/ compiler-track docs: architecture.md + the woc plans
├── src/ one module per stage: diag, token, lexer, ast, parser, ├── src/ one module per stage: diag, token, lexer, ast, parser,
│ types, owner, emit, disasm, dump │ types, gcinfer, owner, emit, disasm, dump
├── bin/main.ml the woc executable (check / --emit / build / version modes) ├── bin/main.ml the woc executable — check / build-from-manifest / --emit /
└── test/ golden runner + golden/ fixtures per stage │ build / version / --update-deps / the --dump-* modes / -D
└── test/ golden runner + golden/ fixtures per stage (tokens, ast,
owner, owner-err, bc) + fixtures/driver/ CLI-smoke cases
``` ```
The compiler-track plan docs live under
[`plan/compiler/`](plan/compiler/architecture.md) in this `docs/` tree, not
inside `compiler/` — the repo rule below applies to the compiler like everything
else.
Doctrine: OCaml stdlib only — no Menhir, no ppx, no opam libraries; handwritten Doctrine: OCaml stdlib only — no Menhir, no ppx, no opam libraries; handwritten
lexer and recursive-descent parser. Build: `just woc-build`; gate: lexer and recursive-descent parser. Build: `just woc-build`; gate:
`just woc-test`. Architecture map: `just woc-test`. Architecture map:
@ -73,22 +86,34 @@ compiler (`emit.ml`), lowered to engine builtins — no SQL text in the image.
### `tests/`, `scripts/` ### `tests/`, `scripts/`
- `tests/corpus/` — the conformance spine: `run/`, `compile-fail/`, `trap/`, - `tests/corpus/` — the conformance spine. Four directories carry fixtures:
`gc/`. Exact-outcome matching: byte-equal stdout, exact `WO-E###`, exact trap `run/` (60), `compile-fail/` (46), `trap/` (5), `gc/` (2). Five more —
code. Driven by `scripts/oop-e2e.sh` (`just oop-e2e`). `actor/`, `db/`, `lang/`, `sys/`, `sample-logwatcher/` — are reserved slots
- `scripts/` — `oop-e2e.sh` (corpus), `mkdist.sh` + `install-accept.sh` from the original plan and still **empty**; see
(tarball packaging), and the per-sample acceptance scripts [`tests/corpus/README.md`](../tests/corpus/README.md) for which plan each was
(`employee-accept.sh`, `log-watcher-accept.sh`). to be filled by. Exact-outcome matching: byte-equal stdout, exact `WO-E###`,
exact trap code. Driven by `scripts/oop-e2e.sh` (`just oop-e2e`).
- `scripts/` — the corpus runner (`oop-e2e.sh`) and
`single-binary-smoke.sh`; packaging (`mkdist.sh`, `install-accept.sh`); the
docs gate (`linkcheck.py`); the benchmark campaign (`db-bench.py`); and one
acceptance script per sample — `employee-accept.sh`, `log-watcher-accept.sh`,
`web-app-accept.sh`, `site-accept.sh`, `fibers-accept.sh`,
`db-actor-accept.sh`, `deps-accept.sh`.
### `docs/` — all documentation ### `docs/` — all documentation
``` ```
docs/ docs/
├── 00-*, 01-problem.md, 08-*.md status / principles / code-review / problem / structure ├── 00-*.md, 01-problem.md, 08-*.md principles / code-review / dependency-graph /
├── stories/ the canonical iteration arc (language-runtime-database/) │ link-audit / doc-audit / problem / structure
├── examples/ log-watcher/, employee/, employee-list/ samples ├── stories/ 00-status.md (the board) + board-views.md +
│ the canonical iteration arc (language-runtime-database/,
│ FLAT — status lives in each story's frontmatter)
├── active-slice-*.md the live slice's one marker doc, deleted when it lands
├── guides/ runbooks: releasing, language-surface, subagents
├── examples/ 13 sample projects, 8 of them wired to a `just` recipe
├── plan/ compiler/ plans, oop-vm/ contracts, exploration/ studies, ├── plan/ compiler/ plans, oop-vm/ contracts, exploration/ studies,
│ discarded.md + learnings.md registers │ perf-targets.md, discarded.md + learnings.md registers
└── superpowers/ specs/ (approved designs) + plans/ (implementation plans) └── superpowers/ specs/ (approved designs) + plans/ (implementation plans)
``` ```
@ -106,15 +131,25 @@ gates.
`just woc-build` + `just wovm-build` produce the two binaries; `just oop-accept` `just woc-build` + `just wovm-build` produce the two binaries; `just oop-accept`
runs the full milestone gate (compile-time budget, conformance corpus under runs the full milestone gate (compile-time budget, conformance corpus under
ASan, single-binary smoke, both unit gates). Sample acceptance: ASan, single-binary smoke, both unit gates). Sample acceptance: `just employee`
`just employee` (database), `just log-watcher` (systems stdlib). Packaging: (database), `just log-watcher` (systems stdlib), `just web-app` and `just site`
`just dist` → `writeonce-<ver>-linux-amd64.tar.gz`, proven by (the framework consumed through `[deps]`), `just fibers` and `just db-actor`
`just install-accept`. (the concurrency arc), `just deps-accept` (the package manager),
`just db-bench` / `db-bench-quick` (the benchmark campaign, gated against
`bench/baseline.json`). Docs gate: `just linkcheck`. Packaging: `just dist` →
`writeonce-<ver>-linux-amd64.tar.gz`, proven by `just install-accept`;
publishing is `.github/workflows/release.yml` on a `v*` tag
(see [`guides/releasing.md`](guides/releasing.md)).
## Naming conventions ## Naming conventions
- Binaries: `woc` (OCaml compiler), `wovm` (C VM); a `woc build` / `woc <dir>` - Binaries: `woc` (OCaml compiler), `wovm` (C VM); a `woc build` / `woc <dir>`
output is named by the project's `wo.toml`. output is named by the project's `wo.toml`.
- Story iterations: `<NN>-<topic>.md`, flat in
`docs/stories/language-runtime-database/`. **No directory encodes status**
(directive 2026-08-26) — each story's `status:` frontmatter key is the only
place state is recorded, so a status change is a one-line edit and never
moves a file or breaks a link.
- Plan/spec files: `YYYY-MM-DD-<topic>.md` under `docs/superpowers/{specs,plans}/`; - Plan/spec files: `YYYY-MM-DD-<topic>.md` under `docs/superpowers/{specs,plans}/`;
compiler plans under `docs/plan/compiler/`; normative contracts under compiler plans under `docs/plan/compiler/`; normative contracts under
`docs/plan/oop-vm/`. `docs/plan/oop-vm/`.

View file

@ -1,9 +1,15 @@
# In progress — chat + actor lifecycle (iteration 24, absorbing 31 + 34) ---
slice: "24" # the story that owns the status; see stories/24-chat-websocket-workload.md
status: in-progress
---
Active slice, branch `chat-ws-lifecycle`. Spec: # Active slice — chat + actor lifecycle (iteration 24, absorbing 31 + 34)
[`../superpowers/specs/2026-08-23-chat-websocket-actor-lifecycle-design.md`](../superpowers/specs/2026-08-23-chat-websocket-actor-lifecycle-design.md)
Branch `chat-ws-lifecycle`. Spec:
[`superpowers/specs/2026-08-23-chat-websocket-actor-lifecycle-design.md`](superpowers/specs/2026-08-23-chat-websocket-actor-lifecycle-design.md)
· plan: · plan:
[`../superpowers/plans/2026-08-23-chat-ws-lifecycle.md`](../superpowers/plans/2026-08-23-chat-ws-lifecycle.md). [`superpowers/plans/2026-08-23-chat-ws-lifecycle.md`](superpowers/plans/2026-08-23-chat-ws-lifecycle.md)
· board: [`stories/00-status.md`](stories/00-status.md).
## Progress (2026-08-23) ## Progress (2026-08-23)
@ -49,4 +55,6 @@ Every landed task: full battery 12/12, fresh-built.
standup entry, graph nodes, framework README ledger rows, runtime + standup entry, graph nodes, framework README ledger rows, runtime +
chat CODE-LOGIC sections, delete this marker. Final battery. chat CODE-LOGIC sections, delete this marker. Final battery.
This file is deleted when the slice lands (board convention). This file is deleted when the slice lands (board convention). It lives flat in
`docs/` rather than a status folder — since 2026-08-26 no directory in this repo
encodes state; `status:` above is the only place it is recorded.

View file

@ -0,0 +1,62 @@
# `db-actor` — the database reached from any shard
> **Status: shipped — arc stage 3's acceptance gate.** Run it with
> `just db-actor`. Landed 2026-08-21 with the shard-fiber arc
> ([story 8](../../stories/language-runtime-database/08-shard-actor-runtime.md)
> · [plan](../../superpowers/plans/2026-08-20-shard-fiber-arc.md)).
The database lives on **one** shard — the owner, shard 0 — because RAM is
authoritative and a single writer is what makes the WAL's ordering meaningful.
That is a problem the moment actors are placed round-robin across cores: a
`spawn`ed actor has no say in which shard it lands on, and before stage 3 a
worker-shard `insert` trapped `WO_T_DB` with "database engine not initialized".
Stage 3's answer is a **transparent DB actor**: statements issued off the owner
shard marshal to it, execute there, and materialize their replies back. The
program's source says nothing about any of it — the same `insert` and the same
`from … select` work wherever the actor happens to run. This sample exists to
prove exactly that, which is why its acceptance criterion is *placement
independence* rather than any particular output.
## What it does
`Note` is a `@table` with a secondary index on `tag`. `Writer` is an actor: each
one inserts a row, then scans the whole table and prints the sum it sees. `main`
spawns two writers, waits, then scans once itself.
With the default shard count, round-robin placement puts at least one writer off
the owner shard — so one of those inserts and one of those scans travel the RPC
path under test, and the other does not. Both must produce the same shape.
```bash
just db-actor # the gate
woc docs/examples/db-actor/ # or build it by hand
WO_SHARDS=1 ./docs/examples/db-actor/target/db-actor # force the local path
```
## What the gate proves
`scripts/db-actor-accept.sh`, 8 checks:
| Check | Why it is shaped that way |
| --- | --- |
| multi-shard, three rounds | The writer lines are asserted as a **set**, not a sequence — scheduling decides their order, and pinning it would be testing the scheduler, not the RPC. The `main` line is exact. |
| both `WO_IO` backends forced | The reply park has to be plane-independent: io_uring and epoll must give the same answer, or the parking is leaking into semantics. |
| single shard, byte-exact | The local path is untouched by stage 3. Any drift here means the RPC changed the non-RPC case. |
| `WO_DATA` restart pair | A worker's insert must commit on the **owner's** WAL before its ack, so a restart replays it: 2 rows, then 2+2 after a second run. This is the durability claim the RPC could most easily break. |
Run under `wovm_asan` and `wovm_tsan` as well — cross-shard message passing is
exactly where a data race would hide, and TSan covering this demo is the one
place it runs.
## Read it for
- **How little the source knows.** Compare `Writer.receive` here against the
same statements in [`employee`](../employee/): identical. Transparency is the
feature.
- **Why `main` waits.** `main` is not an actor and has no mailbox, so it sleeps
rather than awaiting — the gap iteration 31's `call` closes for actors and
[24's marker](../../active-slice-2026-08-23-chat-ws-lifecycle.md) tracks.
Reasoning under the engine side: [`database/src/CODE-LOGIC.md`](../../../database/src/CODE-LOGIC.md).
Contract: [`plan/oop-vm/04-db-binding.md`](../../plan/oop-vm/04-db-binding.md).

View file

@ -2,8 +2,8 @@
> **Status: target workload — does not compile on today's toolchain.** > **Status: target workload — does not compile on today's toolchain.**
> Written ahead of iterations > Written ahead of iterations
> [20 (cross-program tables)](../../stories/language-runtime-database/refine/20-cross-program-tables.md) > [20 (cross-program tables)](../../stories/language-runtime-database/20-cross-program-tables.md)
> and [21 (keypair attach auth)](../../stories/language-runtime-database/refine/21-keypair-attach-auth.md), > and [21 (keypair attach auth)](../../stories/language-runtime-database/21-keypair-attach-auth.md),
> the way every acceptance sample here precedes its features. It also leans > the way every acceptance sample here precedes its features. It also leans
> on 9/9b (the [employee sample](../employee/) it attaches to must run > on 9/9b (the [employee sample](../employee/) it attaches to must run
> first). > first).

View file

@ -1,14 +1,16 @@
# employee — the database track's acceptance workload # employee — the database track's acceptance workload
> **Status: target workload — does not compile on today's toolchain.** > **Status: shipped — this is the database track's acceptance gate.** Run it
> This sample is written *ahead of* the features it exercises, exactly as > with `just employee`. The sample was written *ahead of* the features it
> log-watcher was written ahead of iterations 5–7: the sample is the test, > exercises, exactly as log-watcher was written ahead of iterations 5–7: the
> and the plans compile toward it. It becomes buildable when iteration 9 > sample is the test, and the plans compiled toward it. Both landed — iteration
> (engine: [`2026-08-01-db-engine-binding.md`](../../superpowers/plans/2026-08-01-db-engine-binding.md)) > 9 (engine: [`2026-08-01-db-engine-binding.md`](../../superpowers/plans/2026-08-01-db-engine-binding.md))
> and iteration 9b (query surface: > and iteration 9b (query surface:
> [`2026-08-15-employee-relations-query.md`](../../plan/compiler/2026-08-15-employee-relations-query.md)) > [`2026-08-15-employee-relations-query.md`](../../plan/compiler/2026-08-15-employee-relations-query.md)).
> land. Normative semantics: > Normative semantics:
> [the 9b spec](../../superpowers/specs/2026-08-15-table-relations-query-design.md). > [the 9b spec](../../superpowers/specs/2026-08-15-table-relations-query-design.md).
> One clause below is still ahead of the compiler and marked where it appears:
> `group … by … into` parses and is then refused by the typechecker.
Two `@table` classes and every 9b feature load-bearing: Two `@table` classes and every 9b feature load-bearing:

View file

@ -185,8 +185,15 @@ inferred). The developer writes no memory annotations for either.
## Run status ## Run status
Iteration 7b is landing in phases (plan: Iteration 7b landed 2026-08-18 (plan:
[`../../superpowers/plans/2026-08-18-inferred-gc-mark-sweep.md`](../../superpowers/plans/2026-08-18-inferred-gc-mark-sweep.md)). [`../../superpowers/plans/2026-08-18-inferred-gc-mark-sweep.md`](../../superpowers/plans/2026-08-18-inferred-gc-mark-sweep.md)).
This sample compiles and runs on today's toolchain — `woc
docs/examples/gc-cycle/` then `./docs/examples/gc-cycle/target/gc-cycle`.
> **It has no `just` recipe.** The plan's phase 4 checked off a
> `just gc-cycle` acceptance that never landed; the sample is the one
> compiling example in the repo with no gate behind it. Run it by hand,
> and see the plan's 2026-08-26 disclosure note.
**Phase 1 (landed).** The inference pass classifies each class; `woc --dump-gc **Phase 1 (landed).** The inference pass classifies each class; `woc --dump-gc
docs/examples/gc-cycle` prints: docs/examples/gc-cycle` prints:

View file

@ -6,17 +6,17 @@ ported file for file, per the approved
(Part 4). A single-binary systems daemon: log-tail watcher, cron.d (Part 4). A single-binary systems daemon: log-tail watcher, cron.d
supervisor, flock/pgrep probes, hand-rolled MCP-over-HTTP server, JSONL supervisor, flock/pgrep probes, hand-rolled MCP-over-HTTP server, JSONL
detection sink. Program mode (`fn main`, blocking legal, one shard) plus the detection sink. Program mode (`fn main`, blocking legal, one shard) plus the
five builtin stdlib modules — `fs`, `proc`, `net`, `time`, `json` — carry stdlib modules it needs — `fs`, `proc`, `net`, `time`, `json`, `env` — carry
all of it; read each `.wo` next to its `.hx` sibling. all of it; read each `.wo` next to its `.hx` sibling.
> **Status: design artifact — the spec's forcing function.** The systems > **Status: shipped — the systems track's acceptance gate.** Run it with
> track is approved, pre-implementation. Today's `woc` (milestone 1) > `just log-watcher` (`just log-watcher::build` / `::soak 60` for the rest).
> recovers the `class`/`fn` skeletons in these files (`--dump-ast` lists > Landed with iteration 7 on 2026-08-15: executable, not merely compilable —
> every Watcher method) but diagnoses the adopted surface as WO-E101: > zero ASan leaks in all three modes, SIGTERM ends parked syscalls, fds flat,
> `use`, `typedef`, standalone union aliases (`type CronResult = …`), > `LW_SOAK` gate. The sample existed to force the grammar it uses (the
> `pub(read)`, `switch`, `try`. This sample exists to force that grammar > blog/ecommerce/pricing precedent), and every form it needed — `use`,
> (the blog/ecommerce/pricing precedent) and becomes the track's acceptance > `typedef`, standalone union aliases (`type CronResult = …`), `pub(read)`,
> test: it compiles and detects a real silent death when the track ships. > `switch`, `try` — is now shipped surface.
## The mapping ## The mapping

View file

@ -85,6 +85,13 @@ first (pure `.wo` cannot express it yet).
### Transport ### Transport
> Parity reference: [the Fiber v3.5.0 study](../../plan/exploration/fiber/00-fiber-parity.md)
> read all 32 of Fiber's middleware packages against this framework on
> 2026-08-26. **Nine already have a working counterpart here** (CORS, basic
> auth, key/bearer auth, security headers, ETag, static files, logger, host
> authorization, recover-as-500). The rows below marked ⛔/⏸ are what it found
> missing, each with an owner.
| Item | State | | Item | State |
| --- | --- | | --- | --- |
| HTTP/1.1 parsing | ✅ parses + 400-and-survive; duplicate `Content-Length` rejected outright (RFC 9112 §6.3, slice 2); BODY_MAX bounds headers and body | | HTTP/1.1 parsing | ✅ parses + 400-and-survive; duplicate `Content-Length` rejected outright (RFC 9112 §6.3, slice 2); BODY_MAX bounds headers and body |
@ -152,7 +159,14 @@ first (pure `.wo` cannot express it yet).
| --- | --- | | --- | --- |
| base64 | ✅ pure `.wo` (`http/auth.wo`) | | base64 | ✅ pure `.wo` (`http/auth.wo`) |
| SHA-1 · SHA-256 · HMAC-SHA256 | ✅ C runtime builtins (iteration 34, ids 85–87, RFC-vector gated); SHA-512/CRC32 wait for a consumer | | SHA-1 · SHA-256 · HMAC-SHA256 | ✅ C runtime builtins (iteration 34, ids 85–87, RFC-vector gated); SHA-512/CRC32 wait for a consumer |
| Unlocks (signed cookies, CSRF, session integrity, webhook verification, JWT HS256) | ⬜ UNBLOCKED (the primitives exist since iteration 34); each is its own slice; **hard stop at JWT HS256** — no RS256, no JOSE zoo | | Unlocks — signed cookies, webhook verification, JWT HS256 **verification** | ⬜ genuinely unblocked (integrity only needs iteration 34's HMAC); each its own slice; **hard stop at JWT HS256** — no RS256, no JOSE zoo |
| Unlocks — CSRF, session integrity, JWT **issuing** | ⛔ **BLOCKED, corrected 2026-08-26.** This row previously read "UNBLOCKED (the primitives exist since iteration 34)" and that was wrong: HMAC lets you *authenticate* a token, not *mint* one, and **writeonce has no source of randomness at all** (no `getrandom`, no CSPRNG builtin — grep the runtime). An HMAC over a guessable session id is a signed guess. A random-bytes builtin is [iteration 39](../../stories/language-runtime-database/39-web-framework-parity.md)'s first goal |
| Cookies (read + `Set-Cookie`) | ⛔ absent in BOTH directions, and `Resp.headers` is a `map<Text,Text>` so it structurally cannot carry two `Set-Cookie` lines — [iteration 39](../../stories/language-runtime-database/39-web-framework-parity.md) |
| Sessions · CSRF · rate limiting · idempotency | ⬜ [iteration 39](../../stories/language-runtime-database/39-web-framework-parity.md). Limiter and idempotency need only a `@table` + `time.ticks` and are the cheapest wins available; sessions and CSRF wait on randomness + cookies. `@table` gives all four a **durable** store, where Fiber ships in-memory and expects Redis |
| Compression · SSE · byte ranges · chunked bodies | ⏸ all four sit on the parked streaming seam (`serialize()` always emits `Content-Length`). Chunked REQUEST bodies are deliberately refused today (`internal/parse.wo:153-157`, request-smuggling note) — that refusal must survive whoever implements them |
| Typed binding of query/params/form/headers | ⏸ Fiber's `Bind` reflects over struct tags; principle 13 forbids reflection, so the answer is [iteration 29's `@derive`](../../stories/language-runtime-database/29-compile-time-metaprogramming.md). JSON bodies already work via `json.decode(t) as T` |
| PATCH/OPTIONS/HEAD/ALL helpers · named routes · per-route body limit · request id · `Location`/`Vary`/`Attachment` | ⬜ [iteration 39](../../stories/language-runtime-database/39-web-framework-parity.md) — registration and response sugar; `BODY_MAX = 1048576` is currently one compile-time number for the whole server |
| `proxy` middleware | ⛔ impossible today — no `net.connect` anywhere in the runtime ([iteration 38](../../stories/language-runtime-database/38-content-platform-capabilities.md)) |
## Layout and privacy (iteration 17) ## Layout and privacy (iteration 17)

View file

@ -1,8 +1,9 @@
# The writeonce language surface — everything a `.wo` file may contain # The writeonce language surface — everything a `.wo` file may contain
Derived from the front end as it stands on 2026-08-24, by reading Derived from the front end as it stands on 2026-08-24 and re-verified
`compiler/src/lexer.ml`, `parser.ml`, `ast.ml` and `types.ml` — not a against it on 2026-08-26, by reading `compiler/src/lexer.ml`,
spec. Where this disagrees with the compiler, the compiler is right. `parser.ml`, `ast.ml` and `types.ml` — not a spec. Where this disagrees
with the compiler, the compiler is right.
The normative companions are The normative companions are
[`08-builtin-surface.md`](../plan/oop-vm/08-builtin-surface.md) (what the [`08-builtin-surface.md`](../plan/oop-vm/08-builtin-surface.md) (what the
runtime offers) and [`01-error-catalog.md`](../plan/oop-vm/01-error-catalog.md) runtime offers) and [`01-error-catalog.md`](../plan/oop-vm/01-error-catalog.md)
@ -14,7 +15,9 @@ matching answer to "what will the compiler refuse?", which is just as
much part of the surface. much part of the surface.
Every form listed below was compiled and run against `woc`/`wovm` while Every form listed below was compiled and run against `woc`/`wovm` while
writing this page, not read off the parser and hoped for. writing this page, not read off the parser and hoped for — with the one
exception §6 calls out by name (`group … by … into` parses and is then
refused).
--- ---
@ -33,7 +36,7 @@ writing this page, not read off the parser and hoped for.
| newline | significant | terminates a statement (or `;`). A line ending in `..` continues on the next — the one newline suppression | | newline | significant | terminates a statement (or `;`). A line ending in `..` continues on the next — the one newline suppression |
| build flags | `#if name` / `#else` / `#end` | token-level filter, flag NAMES only (no expressions); nesting allowed; set with `woc -D name` | | build flags | `#if name` / `#else` / `#end` | token-level filter, flag NAMES only (no expressions); nesting allowed; set with `woc -D name` |
**Keywords** (35): `type class interface fn let mut take return if else **Keywords** (37): `type class interface fn let mut take return if else
while for in true false use spawn using pub break continue do const and while for in true false use spawn using pub break continue do const and
or not inline switch case default typedef try catch nil as INSERT or not inline switch case default typedef try catch nil as INSERT
SELECT`. SELECT`.
@ -164,7 +167,7 @@ no `&&`/`||`/`!`/`~` anywhere in the language.
``` ```
from <var> in <source> from <var> in <source>
where <expr> -- zero or more where <expr> -- zero or more
group <expr> by <key> into <gvar> group <expr> by <key> into <gvar> -- PARSES, THEN REFUSED (see below)
order by <expr> [desc] order by <expr> [desc]
take <expr> take <expr>
select <expr> select <expr>
@ -172,9 +175,15 @@ from <var> in <source>
The source is either a table class (`from p in Product`) or a The source is either a table class (`from p in Product`) or a
navigation (`from s in dept.staff` — a `backlink` or a `multi`). Present navigation (`from s in dept.staff` — a `backlink` or a `multi`). Present
today: from / where / order / take / select plus group-by aggregation. today: **from / where / order / take / select**. A query is an
Joins are not in the slice. A query is an expression and is also what expression and is also what `for x in <query>` iterates.
`for x in <query>` iterates.
`group … by … into` is the one clause above that is grammar without
semantics: the parser accepts it (`parser.ml`) and the typechecker then
rejects it with WO-E250 — "group-by aggregation is not supported yet",
or "group-by on a navigation query is not supported yet" for the
navigation form (`types.ml`). It is listed because the syntax is
settled, not because it runs. Joins are not in the slice at all.
## 7. Concurrency ## 7. Concurrency

View file

@ -72,24 +72,23 @@ ignored by this workflow.
Publishing binaries whose floor differs from the page is the one Publishing binaries whose floor differs from the page is the one
failure a user cannot debug. failure a user cannot debug.
10 Clean up the rehearsal: 10 Ship for real:
gh release delete v0.0.0-test --yes
git push origin :refs/tags/v0.0.0-test
git tag -d v0.0.0-test
11 Remove --draft, commit, push.
12 Ship for real:
git tag -a v0.1.0 -m "writeonce 0.1.0" git tag -a v0.1.0 -m "writeonce 0.1.0"
git push origin v0.1.0 git push origin v0.1.0
13 Verify the link a stranger clicks (should print 200): 11 Verify the link a stranger clicks (should print 200):
curl -sIL -o /dev/null -w '%{http_code}\n' \ curl -sIL -o /dev/null -w '%{http_code}\n' \
https://github.com/shoneyj/writeonce/releases/download/v0.1.0/writeonce-0.1.0-linux-amd64.tar.gz https://github.com/shoneyj/writeonce/releases/download/v0.1.0/writeonce-0.1.0-linux-amd64.tar.gz
14 Refresh the site's mirror from $WO_DIST (section 7 below). 12 Refresh the site's mirror from $WO_DIST (section 7 below).
``` ```
There is no rehearsal to clean up and no draft flag to remove: a
`workflow_dispatch` run creates neither a tag nor a release, and
`gh release create` in the workflow publishes directly — the file has never
carried `--draft`. If you *want* a draft first, add `--draft` to that step
yourself and remember to remove it again.
Known first-run risks, in the order they are likely to bite: Known first-run risks, in the order they are likely to bite:
`ocaml/setup-ocaml@v3` resolving OCaml 4.14 on the 22.04 image; the `ocaml/setup-ocaml@v3` resolving OCaml 4.14 on the 22.04 image; the
`objdump` in the glibc-floor step needing `binutils` (present on GitHub `objdump` in the glibc-floor step needing `binutils` (present on GitHub

View file

@ -154,10 +154,10 @@ Deliberately excluded: `.dev/reference/colibri`, `.dev/reference/llama-cpp`,
## 7. Governing docs ## 7. Governing docs
- Spec: [`docs/superpowers/specs/2026-08-01-oop-compiler-vm-design.md`](../../superpowers/specs/2026-08-01-oop-compiler-vm-design.md) - Spec: [`docs/superpowers/specs/2026-08-01-oop-compiler-vm-design.md`](../../superpowers/specs/2026-08-01-oop-compiler-vm-design.md)
- Plan 2 — compiler front: [`2026-08-01-woc-compiler-front.md`](./2026-08-01-woc-compiler-front.md) - Plan 2 — compiler front: [`2026-08-01-woc-compiler-front.md`](2026-08-01-woc-compiler-front.md)
- Plan 3 — emit + e2e + single binary: [`2026-08-01-wob-emit-e2e-single-binary.md`](./2026-08-01-wob-emit-e2e-single-binary.md) - Plan 3 — emit + e2e + single binary: [`2026-08-01-wob-emit-e2e-single-binary.md`](2026-08-01-wob-emit-e2e-single-binary.md)
- Plan 8 — Haxe-parity language surface: [`2026-08-01-haxe-parity-language.md`](./2026-08-01-haxe-parity-language.md) - Plan 8 — Haxe-parity language surface: [`2026-08-01-haxe-parity-language.md`](2026-08-01-haxe-parity-language.md)
- Format contract: [`docs/plan/oop-vm/00-wob-format.md`](../../plan/oop-vm/00-wob-format.md) - Format contract: [`docs/plan/oop-vm/00-wob-format.md`](../oop-vm/00-wob-format.md)
- VM counterpart (shipped): [`docs/superpowers/plans/2026-08-01-wob-format-and-vm-core.md`](../../superpowers/plans/2026-08-01-wob-format-and-vm-core.md) - VM counterpart (shipped): [`docs/superpowers/plans/2026-08-01-wob-format-and-vm-core.md`](../../superpowers/plans/2026-08-01-wob-format-and-vm-core.md)
> **Docs-location note:** compiler plan documents live here in > **Docs-location note:** compiler plan documents live here in

View file

@ -56,3 +56,32 @@ Status board: [`00-status.md`](../stories/00-status.md) · Doctrine: [`../00-pri
| **Old-runtime "front door" + v1 design docs** | 2026-08-17: removed `writeonce-pl.md`, `runtime/wo-language.md`, `future-scope/ai-agents-content-management.md`, the numbered v1 set `02-recovery`/`03-data`/`04-ui`/`05-datalayer`/`06-markdown-render`/`07-ssl`, and `runtime/database/05-go-sdk.md`. They pitched the old Rust `wo` runtime (REST + LiveView + SQL/Cypher) as the current language and contradicted the shipped woc/wovm toolchain. | | **Old-runtime "front door" + v1 design docs** | 2026-08-17: removed `writeonce-pl.md`, `runtime/wo-language.md`, `future-scope/ai-agents-content-management.md`, the numbered v1 set `02-recovery`/`03-data`/`04-ui`/`05-datalayer`/`06-markdown-render`/`07-ssl`, and `runtime/database/05-go-sdk.md`. They pitched the old Rust `wo` runtime (REST + LiveView + SQL/Cypher) as the current language and contradicted the shipped woc/wovm toolchain. |
| **The 2026-08-01 shard-actor plan (epoll-based)** | 2026-08-21: [`superpowers/plans/2026-08-01-shard-actor-vm-runtime.md`](../superpowers/plans/2026-08-01-shard-actor-vm-runtime.md) marked discarded, file kept as reference. Superseded by the arc plan of record ([`2026-08-20-shard-fiber-arc.md`](../superpowers/plans/2026-08-20-shard-fiber-arc.md), stages 1+2 landed): io_uring is a MUST and the epoll-based approach is discarded — the old plan's "epoll now / io_uring later" premise is inverted, and its substrate (`runtime/wo-rt.c`) left with the Rust track 2026-08-18. | | **The 2026-08-01 shard-actor plan (epoll-based)** | 2026-08-21: [`superpowers/plans/2026-08-01-shard-actor-vm-runtime.md`](../superpowers/plans/2026-08-01-shard-actor-vm-runtime.md) marked discarded, file kept as reference. Superseded by the arc plan of record ([`2026-08-20-shard-fiber-arc.md`](../superpowers/plans/2026-08-20-shard-fiber-arc.md), stages 1+2 landed): io_uring is a MUST and the epoll-based approach is discarded — the old plan's "epoll now / io_uring later" premise is inverted, and its substrate (`runtime/wo-rt.c`) left with the Rust track 2026-08-18. |
| **The entire Rust `wo` runtime track** | 2026-08-18: removed `crates/` (the Stage-2 Rust runtime), `Cargo.toml`/`Cargo.lock`, `prototypes/` (wo-rt-c stale duplicate + wo-db C++ ref), the `rt-c-*` justfile recipes, the Rust engineering plans (`docs/plan/05..16`, `docs/plan/done/`), and `docs/runtime/` (the old runtime overview + 7-phase DB design series + async/fibers/gc/surreal essays). It was the prior, abandoned architecture — fully independent of the woc/wovm stack. Master now reflects only the current single-language project; the removed track lives in git history if ever needed as reference. Kept: the syscall/postgres/assembly/c-runtime **exploration studies** (they fed the current C runtime) and the discarded/learnings registers. | | **The entire Rust `wo` runtime track** | 2026-08-18: removed `crates/` (the Stage-2 Rust runtime), `Cargo.toml`/`Cargo.lock`, `prototypes/` (wo-rt-c stale duplicate + wo-db C++ ref), the `rt-c-*` justfile recipes, the Rust engineering plans (`docs/plan/05..16`, `docs/plan/done/`), and `docs/runtime/` (the old runtime overview + 7-phase DB design series + async/fibers/gc/surreal essays). It was the prior, abandoned architecture — fully independent of the woc/wovm stack. Master now reflects only the current single-language project; the removed track lives in git history if ever needed as reference. Kept: the syscall/postgres/assembly/c-runtime **exploration studies** (they fed the current C runtime) and the discarded/learnings registers. |
### Successor map for the removed Rust-era plan paths
Added 2026-08-26. The exploration studies under
[`exploration/`](exploration/linux/00-linux.md) were written against the old
flat `docs/plan/NN-*.md` numbering and the `docs/runtime/database/` tree, both
removed with the Rust track above. Those 48 dangling links were **de-linked, not
re-pointed** — their prose names the retired plan by number ("plan 09a", "plan
11"), so aiming them at a story would have made the sentence lie. The studies
still read correctly; the names are now plain text. This table is where a reader
goes to find what took each one's place.
| Retired path | What carries that work now |
| --- | --- |
| `09-concurrency-scaleout.md` | [`08-shard-actor-runtime.md`](../stories/language-runtime-database/08-shard-actor-runtime.md) + [`11-fibers.md`](../stories/language-runtime-database/11-fibers.md) — the arc, landed 2026-08-21 |
| `10-storage-foundations.md`, `11-wal-and-recovery.md` | [`09-database-engine.md`](../stories/language-runtime-database/09-database-engine.md) (typed WAL + replay) and [`22-durability-throughput-scale.md`](../stories/language-runtime-database/22-durability-throughput-scale.md) (the measurements) |
| `12-engine-disk-cutover.md` | Nothing — RAM stays authoritative by doctrine (principle 7). The disk story is the WAL; reclamation is [`32-wal-checkpoint.md`](../stories/language-runtime-database/32-wal-checkpoint.md) |
| `13-class-model-live-pricing.md` | [`09b-table-relations-query.md`](../stories/language-runtime-database/09b-table-relations-query.md) — `@table`, `ref`/`backlink`, the compiler-checked query surface |
| `07-inotify-content-watcher.md` | [`07-logwatcher-proof.md`](../stories/language-runtime-database/07-logwatcher-proof.md) — the log-watcher sample polls via `fs.stat`; inotify was never surfaced as a builtin |
| `08-sendfile-static-assets.md` | Nothing. `sendfile` is not exposed; static assets are served as `Text` through `net.write` |
| `15-mcp-streamable-http.md` | [`28-skillhost-host-workload.md`](../stories/language-runtime-database/28-skillhost-host-workload.md) — MCP transport is that story's Blocker B |
| `16-postgres-mirror.md` | Nothing — the mirror-is-backup doctrine holds, but no iteration owns it and there are no outbound sockets to reach a mirror with ([`refine/38`](../stories/language-runtime-database/38-content-platform-capabilities.md)) |
| `02-event-loop-epoll.md`, `03-hand-rolled-http.md` | `runtime/src/park.c` (io_uring with an epoll fallback) and the `.wo` framework `writeonce-serve` |
| `04-cutover-remove-tokio-axum.md` | Completed by the Rust-track removal itself — nothing left to cut over |
| `runtime/database/03-inmemory-engine.md` | [`database/src/CODE-LOGIC.md`](../../database/src/CODE-LOGIC.md) + [`plan/oop-vm/04-db-binding.md`](oop-vm/04-db-binding.md) |
| `runtime/database/02-wo-language.md` | [`docs/guides/language-surface.md`](../guides/language-surface.md) |
| `runtime/database/07-wo-seg-migration.md` | Nothing — segment migration was a Rust-engine concept with no analogue here |
| `prototypes/wo-db/` (C++ query-layer ref) | Removed with the Rust track. The query layer lives in `compiler/src/emit.ml`, lowered to engine builtins |
| `exploration/linux/07-splice.md` | Never written. Slot 07 is `07-io_uring.md` |

View file

@ -16,7 +16,7 @@ The biggest category. The language's calling convention — how arguments are pa
Atomics, memory barriers, and some hardware-accelerated primitives need specific instruction sequences. A compiler that sees `a = *b` can't know whether you wanted a relaxed load or an acquire fence without annotation — and the *right* instruction on x86 vs ARM vs RISC-V is different. Atomics, memory barriers, and some hardware-accelerated primitives need specific instruction sequences. A compiler that sees `a = *b` can't know whether you wanted a relaxed load or an acquire fence without annotation — and the *right* instruction on x86 vs ARM vs RISC-V is different.
**Atomic CAS / load-acquire / store-release.** On x86 it's `LOCK CMPXCHG`; on ARM it's `LDXR` / `STXR` with a retry loop; on RISC-V it's `LR.W.AQ` / `SC.W.RL`. Go emits these from [`reference/go/src/runtime/atomic_amd64.s`](../../../.dev/reference/go/src/runtime/atomic_amd64.s) (and its per-arch siblings) because a portable compiler can't. **Atomic CAS / load-acquire / store-release.** On x86 it's `LOCK CMPXCHG`; on ARM it's `LDXR` / `STXR` with a retry loop; on RISC-V it's `LR.W.AQ` / `SC.W.RL`. Go emits these from `reference/go/src/runtime/atomic_amd64.s` (and its per-arch siblings) because a portable compiler can't.
**Memory barriers.** `MFENCE`, `LFENCE`, `SFENCE` on x86; `DMB` / `DSB` / `ISB` on ARM. Used by Go's `publicationBarrier`, `procyield`, and friends. Per-arch asm files carry them. **Memory barriers.** `MFENCE`, `LFENCE`, `SFENCE` on x86; `DMB` / `DSB` / `ISB` on ARM. Used by Go's `publicationBarrier`, `procyield`, and friends. Per-arch asm files carry them.
@ -34,10 +34,10 @@ Go does these in asm because it cannot rely on libc — Go's scheduler needs to
**Rust + libc covers all three categories** for the specific workload the `rt` crate serves. No custom scheduler means no stack switching. `std::sync::atomic::*` emits the right arch-specific instructions per target. `libc::syscall(SYS_*, ...)` hits the kernel through glibc's own trampolines — we don't need our own because we don't need fine-grained control over scheduler park/unpark (there's no scheduler to park). `signalfd` (see [`../linux/04-signalfd.md`](../linux/04-signalfd.md)) makes signal-handler asm unnecessary. **Rust + libc covers all three categories** for the specific workload the `rt` crate serves. No custom scheduler means no stack switching. `std::sync::atomic::*` emits the right arch-specific instructions per target. `libc::syscall(SYS_*, ...)` hits the kernel through glibc's own trampolines — we don't need our own because we don't need fine-grained control over scheduler park/unpark (there's no scheduler to park). `signalfd` (see [`../linux/04-signalfd.md`](../linux/04-signalfd.md)) makes signal-handler asm unnecessary.
The next document, [`01-go-runtime-asm.md`](./01-go-runtime-asm.md), catalogues Go's asm in concrete detail. The one after that, [`02-writeonce-stance.md`](./02-writeonce-stance.md), spells out the policy: **no custom assembly in `crates/rt`** — and lists the three edge cases where a future profiling run might force the decision. The next document, [`01-go-runtime-asm.md`](01-go-runtime-asm.md), catalogues Go's asm in concrete detail. The one after that, [`02-writeonce-stance.md`](02-writeonce-stance.md), spells out the policy: **no custom assembly in `crates/rt`** — and lists the three edge cases where a future profiling run might force the decision.
## Reading order ## Reading order
1. **This doc** — the abstract "why asm exists in runtimes." 1. **This doc** — the abstract "why asm exists in runtimes."
2. [`01-go-runtime-asm.md`](./01-go-runtime-asm.md) — concrete Go inventory with reference paths. 2. [`01-go-runtime-asm.md`](01-go-runtime-asm.md) — concrete Go inventory with reference paths.
3. [`02-writeonce-stance.md`](./02-writeonce-stance.md) — the writeonce policy + escape hatches. 3. [`02-writeonce-stance.md`](02-writeonce-stance.md) — the writeonce policy + escape hatches.

View file

@ -1,6 +1,6 @@
# 01 — Go's runtime assembly, catalogued # 01 — Go's runtime assembly, catalogued
The Go runtime ships ~72 `TEXT` functions in `asm_amd64.s` alone, ~43 in `sys_linux_amd64.s`, and per-architecture variants of both for `386`, `arm`, `arm64`, `loong64`, `mips(64)x`, `ppc64x`, `riscv64`, `s390x`, `wasm`. This doc inventories them by purpose so a reader can map each Go asm concern to the writeonce equivalent (spoiler: usually "Rust stdlib does it"). Follow-on reading: [`02-writeonce-stance.md`](./02-writeonce-stance.md). The Go runtime ships ~72 `TEXT` functions in `asm_amd64.s` alone, ~43 in `sys_linux_amd64.s`, and per-architecture variants of both for `386`, `arm`, `arm64`, `loong64`, `mips(64)x`, `ppc64x`, `riscv64`, `s390x`, `wasm`. This doc inventories them by purpose so a reader can map each Go asm concern to the writeonce equivalent (spoiler: usually "Rust stdlib does it"). Follow-on reading: [`02-writeonce-stance.md`](02-writeonce-stance.md).
All paths are inside [`reference/go/src/runtime/`](../../../../.dev/reference/go/src/runtime/). All paths are inside [`reference/go/src/runtime/`](../../../../.dev/reference/go/src/runtime/).
@ -86,6 +86,6 @@ Hookable entry points for sanitisers. Not relevant to writeonce.
## What's NOT in asm ## What's NOT in asm
Everything else in Go's runtime is plain Go: the scheduler's policy (`proc.go`), the garbage collector (`mgc.go` etc.), `netpoll` dispatch (`netpoll.go` — *Go code*; the platform-specific backends like `netpoll_epoll.go` are also pure Go that call into the asm `epollwait` trampoline). The asm is strictly the three categories in [`00-overview.md`](./00-overview.md): calling-convention-breaking operations, arch-specific instructions, and syscall trampolines. Everything else in Go's runtime is plain Go: the scheduler's policy (`proc.go`), the garbage collector (`mgc.go` etc.), `netpoll` dispatch (`netpoll.go` — *Go code*; the platform-specific backends like `netpoll_epoll.go` are also pure Go that call into the asm `epollwait` trampoline). The asm is strictly the three categories in [`00-overview.md`](00-overview.md): calling-convention-breaking operations, arch-specific instructions, and syscall trampolines.
The three categories writeonce **also** needs a solution for — but writeonce gets all three from Rust stdlib + libc. The next doc catalogues those mappings. The three categories writeonce **also** needs a solution for — but writeonce gets all three from Rust stdlib + libc. The next doc catalogues those mappings.

View file

@ -4,11 +4,11 @@
## Why the policy works ## Why the policy works
Each Go asm category from [`01-go-runtime-asm.md`](./01-go-runtime-asm.md) maps to a Rust-stdlib equivalent that is already correct on every supported architecture: Each Go asm category from [`01-go-runtime-asm.md`](01-go-runtime-asm.md) maps to a Rust-stdlib equivalent that is already correct on every supported architecture:
| Go asm need | What writeonce uses | Why it covers the gap | | Go asm need | What writeonce uses | Why it covers the gap |
| --- | --- | --- | | --- | --- | --- |
| Scheduler stack switching (`gogo`, `mcall`, `systemstack`) | — nothing — | Single-threaded event loop through phases 02–08 (see Phase 2 [Concurrency Model](../../../runtime/database/02-wo-language.md#concurrency-model)). [Phase 09](../../09-concurrency-scaleout.md) introduces **thread-per-core** scaling for the 10k-user ecommerce workload — but still no Go-style stack switching: each thread runs its own event loop, connections are pinned for their lifetime, and cross-thread work is message-passing, not scheduler-stealing. No goroutines, no `g0`, even at scale. | | Scheduler stack switching (`gogo`, `mcall`, `systemstack`) | — nothing — | Single-threaded event loop through phases 02–08 (see Phase 2 `Concurrency Model`). `Phase 09` introduces **thread-per-core** scaling for the 10k-user ecommerce workload — but still no Go-style stack switching: each thread runs its own event loop, connections are pinned for their lifetime, and cross-thread work is message-passing, not scheduler-stealing. No goroutines, no `g0`, even at scale. |
| Preemption (`asyncPreempt`) | — nothing — | No preemption through phases 02–08. Phase 09's thread-per-core model keeps this property: handlers run to completion on whichever thread owns their connection. | | Preemption (`asyncPreempt`) | — nothing — | No preemption through phases 02–08. Phase 09's thread-per-core model keeps this property: handlers run to completion on whichever thread owns their connection. |
| Atomic operations (`Load`, `Store`, `Cas`, `Xadd`, ...) | [`std::sync::atomic`](https://doc.rust-lang.org/std/sync/atomic/) | The compiler emits the right instruction per target — `LOCK CMPXCHG` on x86, `LDXR/STXR` on ARM, `LR.W/SC.W` on RISC-V. Ordering is in the type signature (`Ordering::Acquire`, `Release`, `SeqCst`). | | Atomic operations (`Load`, `Store`, `Cas`, `Xadd`, ...) | [`std::sync::atomic`](https://doc.rust-lang.org/std/sync/atomic/) | The compiler emits the right instruction per target — `LOCK CMPXCHG` on x86, `LDXR/STXR` on ARM, `LR.W/SC.W` on RISC-V. Ordering is in the type signature (`Ordering::Acquire`, `Release`, `SeqCst`). |
| Memory barriers (`MFENCE` etc.) | [`std::sync::atomic::fence(Ordering)`](https://doc.rust-lang.org/std/sync/atomic/fn.fence.html) | One call, one fence, arch-neutral. | | Memory barriers (`MFENCE` etc.) | [`std::sync::atomic::fence(Ordering)`](https://doc.rust-lang.org/std/sync/atomic/fn.fence.html) | One call, one fence, arch-neutral. |
@ -69,7 +69,7 @@ No asm has been written under this policy yet. The expectation is it stays that
## Cross-references ## Cross-references
- [`00-overview.md`](./00-overview.md) — why runtimes ever need asm at all (three categories). - [`00-overview.md`](00-overview.md) — why runtimes ever need asm at all (three categories).
- [`01-go-runtime-asm.md`](./01-go-runtime-asm.md) — Go's asm inventory, by file. - [`01-go-runtime-asm.md`](01-go-runtime-asm.md) — Go's asm inventory, by file.
- [`../linux/04-signalfd.md`](../linux/04-signalfd.md) — the specific primitive that obviates Go's `sigtramp` asm. - [`../linux/04-signalfd.md`](../linux/04-signalfd.md) — the specific primitive that obviates Go's `sigtramp` asm.
- [`../02-event-loop-epoll.md`](../../done/02-event-loop-epoll.md) — phase 02, where the `runtime/` module actually lands. - `../02-event-loop-epoll.md` — phase 02, where the `runtime/` module actually lands.

View file

@ -2,7 +2,7 @@
> **Status: ✅ done** — phases A–F all shipped with measured exit evidence below. Board: [00-status.md](../../../stories/00-status.md) > **Status: ✅ done** — phases A–F all shipped with measured exit evidence below. Board: [00-status.md](../../../stories/00-status.md)
**Context sources:** [`prototypes/wo-rt-c/wo-rt.c`](../../../../runtime/wo-rt.c) (phase 0 — the single-threaded epoll baseline), [`../../09-concurrency-scaleout.md`](../../09-concurrency-scaleout.md) (the thread-per-core doctrine every phase here miniaturizes), [`../../10-storage-foundations.md`](../../10-storage-foundations.md) / [`11-wal-and-recovery.md`](../../11-wal-and-recovery.md) / [`12-engine-disk-cutover.md`](../../12-engine-disk-cutover.md) (the storage track), kernel reference cards [`../linux/07-io_uring.md`](../linux/07-io_uring.md), [`08-mmap.md`](../linux/08-mmap.md), [`09-fallocate.md`](../linux/09-fallocate.md), [`12-pwrite-fsync.md`](../linux/12-pwrite-fsync.md), [`02-eventfd.md`](../linux/02-eventfd.md). **Context sources:** [`prototypes/wo-rt-c/wo-rt.c`](../../../../runtime/wo-rt.c) (phase 0 — the single-threaded epoll baseline), `../../09-concurrency-scaleout.md` (the thread-per-core doctrine every phase here miniaturizes), `../../10-storage-foundations.md` / `11-wal-and-recovery.md` / `12-engine-disk-cutover.md` (the storage track), kernel reference cards [`../linux/07-io_uring.md`](../linux/07-io_uring.md), [`08-mmap.md`](../linux/08-mmap.md), [`09-fallocate.md`](../linux/09-fallocate.md), [`12-pwrite-fsync.md`](../linux/12-pwrite-fsync.md), [`02-eventfd.md`](../linux/02-eventfd.md).
## Goal ## Goal
@ -21,7 +21,7 @@ Each phase is the executable proving ground for the matching Rust plan (09–12)
- *Consistency* — invariants checked in-thread before the RAM apply; an aborted op writes nothing. - *Consistency* — invariants checked in-thread before the RAM apply; an aborted op writes nothing.
- *Isolation* — one thread is one serial execution stream: per-shard serializability by construction. - *Isolation* — one thread is one serial execution stream: per-shard serializability by construction.
- *Durability* — the HTTP ack is sent only after the fsync completion for the commit's WAL tick. - *Durability* — the HTTP ack is sent only after the fsync completion for the commit's WAL tick.
Cross-shard transactions (2PC) belong to the Rust track ([plan 09e](../../09-concurrency-scaleout.md)) — non-scope here. Cross-shard transactions (2PC) belong to the Rust track (`plan 09e`) — non-scope here.
6. **Dual-write order.** Commit = apply to the RAM slot → append WAL record (write SQE) → one `fdatasync` SQE per loop tick covering all of that tick's commits (group commit) → ack. Boot = replay per-shard WAL/snapshot from disk into the arena, in parallel, before listeners open. 6. **Dual-write order.** Commit = apply to the RAM slot → append WAL record (write SQE) → one `fdatasync` SQE per loop tick covering all of that tick's commits (group commit) → ack. Boot = replay per-shard WAL/snapshot from disk into the arena, in parallel, before listeners open.
## Phase sequence ## Phase sequence
@ -29,14 +29,14 @@ Each phase is the executable proving ground for the matching Rust plan (09–12)
One phase per implementation pass; `make` + the smoke endpoints stay green after every phase. One phase per implementation pass; `make` + the smoke endpoints stay green after every phase.
### Phase A — thread-per-core skeleton — ✅ shipped ### Phase A — thread-per-core skeleton — ✅ shipped
*Maps to [plan 09a](../../09-concurrency-scaleout.md); cards: `SO_REUSEPORT`, [`eventfd`](../linux/02-eventfd.md).* *Maps to `plan 09a`; cards: `SO_REUSEPORT`, [`eventfd`](../linux/02-eventfd.md).*
`WO_THREADS` pthreads, each pinned to its core; per-thread epoll loop (io_uring arrives in C), per-thread `SO_REUSEPORT` listener on the same port (kernel balances accepts by 4-tuple), per-thread `conns[]` and request counters. Shutdown: thread 0 owns the `signalfd`; on signal it writes each thread's `eventfd`, every loop exits, `pthread_join` all. Store stays per-thread arrays until B. Responses gain `"shard":t`; `/` reports `"threads":N` and per-shard counters. `WO_THREADS` pthreads, each pinned to its core; per-thread epoll loop (io_uring arrives in C), per-thread `SO_REUSEPORT` listener on the same port (kernel balances accepts by 4-tuple), per-thread `conns[]` and request counters. Shutdown: thread 0 owns the `signalfd`; on signal it writes each thread's `eventfd`, every loop exits, `pthread_join` all. Store stays per-thread arrays until B. Responses gain `"shard":t`; `/` reports `"threads":N` and per-shard counters.
**Exit (met):** all endpoints green at `WO_THREADS=4`; `/proc/<pid>/task/*/status` shows each thread pinned to its own core (tid→cpu 0,1,2,3); `/` counters proved 200 concurrent requests spread `[68,36,44,53]` across 4 shards; SIGTERM broadcast joined all shards cleanly; `WO_THREADS=1` behaves like phase 0 (shard 0, sequential ids). Note ids are now interleaved per shard (t+1, t+1+N, …) for coordination-free global uniqueness. **Exit (met):** all endpoints green at `WO_THREADS=4`; `/proc/<pid>/task/*/status` shows each thread pinned to its own core (tid→cpu 0,1,2,3); `/` counters proved 200 concurrent requests spread `[68,36,44,53]` across 4 shards; SIGTERM broadcast joined all shards cleanly; `WO_THREADS=1` behaves like phase 0 (shard 0, sequential ids). Note ids are now interleaved per shard (t+1, t+1+N, …) for coordination-free global uniqueness.
### Phase B — the RAM arena — ✅ shipped ### Phase B — the RAM arena — ✅ shipped
*Maps to [plan 10](../../10-storage-foundations.md); cards: [`mmap`](../linux/08-mmap.md), [`fallocate`](../linux/09-fallocate.md).* *Maps to `plan 10`; cards: [`mmap`](../linux/08-mmap.md), [`fallocate`](../linux/09-fallocate.md).*
One arena: a header page (magic, version, shard count, slot geometry) + N shard slices of fixed-size row slots + a per-shard allocation bitmap. `mmap(MAP_ANONYMOUS|MAP_PRIVATE [|MAP_HUGETLB], MAP_POPULATE)` then `mlock` (graceful fallback + warning if `RLIMIT_MEMLOCK` refuses). Rows move from arrays into slots; every access is a typed pointer into the owning thread's slice — decision 2 made literal. One arena: a header page (magic, version, shard count, slot geometry) + N shard slices of fixed-size row slots + a per-shard allocation bitmap. `mmap(MAP_ANONYMOUS|MAP_PRIVATE [|MAP_HUGETLB], MAP_POPULATE)` then `mlock` (graceful fallback + warning if `RLIMIT_MEMLOCK` refuses). Rows move from arrays into slots; every access is a typed pointer into the owning thread's slice — decision 2 made literal.
@ -50,21 +50,21 @@ Replace each thread's epoll loop with a raw ring: `io_uring_setup`, mmap SQ/CQ,
**Exit (met):** four requests over one socket (`curl` reported `num_connects: 1, 0, 0, 0`), and a create+list pair on one connection lands on the same shard with both rows visible; `strace -c` over 60 keep-alive requests showed **124 `io_uring_enter` and zero `epoll_wait`/`recvfrom`/`sendto`/`accept`** — the only `read`/`write` calls were the signalfd/eventfd shutdown path; SIGTERM broadcast joined all shards cleanly. Raw ring (`io_uring_setup` + SINGLE_MMAP rings + `io_uring_enter`), multishot accept with `CQE_F_MORE` re-arm, one outstanding SQE per connection, pipelined-tail carry-over. **Exit (met):** four requests over one socket (`curl` reported `num_connects: 1, 0, 0, 0`), and a create+list pair on one connection lands on the same shard with both rows visible; `strace -c` over 60 keep-alive requests showed **124 `io_uring_enter` and zero `epoll_wait`/`recvfrom`/`sendto`/`accept`** — the only `read`/`write` calls were the signalfd/eventfd shutdown path; SIGTERM broadcast joined all shards cleanly. Raw ring (`io_uring_setup` + SINGLE_MMAP rings + `io_uring_enter`), multishot accept with `CQE_F_MORE` re-arm, one outstanding SQE per connection, pipelined-tail carry-over.
### Phase D — WAL dual write: RAM first, then the hard drive — ✅ shipped ### Phase D — WAL dual write: RAM first, then the hard drive — ✅ shipped
*Maps to [plan 11](../../11-wal-and-recovery.md) + 09c; cards: [`pwrite/fsync`](../linux/12-pwrite-fsync.md), [`fallocate`](../linux/09-fallocate.md).* *Maps to `plan 11` + 09c; cards: [`pwrite/fsync`](../linux/12-pwrite-fsync.md), [`fallocate`](../linux/09-fallocate.md).*
Per-shard `shard-<t>.wal`, preallocated with `fallocate`. The commit path is decision 6 verbatim: RAM apply → framed record append (write SQE at the shard's tail offset) → one `fdatasync` SQE per tick → ack on the fsync CQE. CRC32 hand-rolled. Acks for all commits in a tick ride the same fsync — group commit, exactly the Rust runtime's doctrine. Per-shard `shard-<t>.wal`, preallocated with `fallocate`. The commit path is decision 6 verbatim: RAM apply → framed record append (write SQE at the shard's tail offset) → one `fdatasync` SQE per tick → ack on the fsync CQE. CRC32 hand-rolled. Acks for all commits in a tick ride the same fsync — group commit, exactly the Rust runtime's doctrine.
**Exit (met):** crash test — 60 concurrent POSTs, `kill -9` mid-stream — showed **60/60 acked writes present and CRC-valid in the WALs, zero acked-but-missing** (offline verification via the new `wo-rt wal-check <file>` mode, which is also phase E's replay skeleton); a copy truncated mid-record reported `TORN at byte 2584 — 17 whole records before it`, dropping the partial whole; 200 concurrent durable commits in 102 ms (~1,960 commits/s, curl-process-bound — each tick's fsync covers every commit staged that tick via double-buffered write→fsync `IOSQE_IO_LINK` pairs). Failed fsync closes the batch's connections without acking — a client never sees a 201 for a non-durable write. Phase D boots with `O_TRUNC` (fresh log); replay lands in E. **Exit (met):** crash test — 60 concurrent POSTs, `kill -9` mid-stream — showed **60/60 acked writes present and CRC-valid in the WALs, zero acked-but-missing** (offline verification via the new `wo-rt wal-check <file>` mode, which is also phase E's replay skeleton); a copy truncated mid-record reported `TORN at byte 2584 — 17 whole records before it`, dropping the partial whole; 200 concurrent durable commits in 102 ms (~1,960 commits/s, curl-process-bound — each tick's fsync covers every commit staged that tick via double-buffered write→fsync `IOSQE_IO_LINK` pairs). Failed fsync closes the batch's connections without acking — a client never sees a 201 for a non-durable write. Phase D boots with `O_TRUNC` (fresh log); replay lands in E.
### Phase E — first load: hard drive → RAM — ✅ shipped ### Phase E — first load: hard drive → RAM — ✅ shipped
*Maps to plans [11](../../11-wal-and-recovery.md)/[12](../../12-engine-disk-cutover.md).* *Maps to plans `11`/`12`.*
Boot, before any listener opens: each thread replays its own WAL into its arena slice — parallel recovery — validating frame CRC + commit marker, truncating at the first torn record. Clean shutdown writes a snapshot (`pwrite` of live slots to `shard-<t>.data`) and truncates the WAL; boot prefers snapshot + WAL tail. Recovery time printed at startup. Boot, before any listener opens: each thread replays its own WAL into its arena slice — parallel recovery — validating frame CRC + commit marker, truncating at the first torn record. Clean shutdown writes a snapshot (`pwrite` of live slots to `shard-<t>.data`) and truncates the WAL; boot prefers snapshot + WAL tail. Recovery time printed at startup.
**Exit (met):** the full cycle verified — (1) 40 writes + `kill -9` + restart: `shard_used [10,7,15,8]` identical, replayed from WAL alone (`recovered 0 snapshot rows + N wal records in 1 ms` per shard); (2) SIGTERM wrote four snapshots and truncated the WALs; (3) restart loaded the snapshots instantly; (4) 5 more writes + `kill -9` + restart recovered **snapshot + WAL tail combined** (`recovered 7 snapshot rows + 2 wal records`), totals exact at 45/45; (5) a restart with `WO_THREADS=8` against a 4-shard data dir **refuses to boot** (`meta` guard — resharding is 09f, never silent data loss). Recovery 1–3 ms at demo geometry; large-arena timing rides phase F's harness, which can generate volume natively (geometry is `-D` overridable: `SLOTS_PER_SHARD`/`SLOT_SIZE`). **Exit (met):** the full cycle verified — (1) 40 writes + `kill -9` + restart: `shard_used [10,7,15,8]` identical, replayed from WAL alone (`recovered 0 snapshot rows + N wal records in 1 ms` per shard); (2) SIGTERM wrote four snapshots and truncated the WALs; (3) restart loaded the snapshots instantly; (4) 5 more writes + `kill -9` + restart recovered **snapshot + WAL tail combined** (`recovered 7 snapshot rows + 2 wal records`), totals exact at 45/45; (5) a restart with `WO_THREADS=8` against a 4-shard data dir **refuses to boot** (`meta` guard — resharding is 09f, never silent data loss). Recovery 1–3 ms at demo geometry; large-arena timing rides phase F's harness, which can generate volume natively (geometry is `-D` overridable: `SLOTS_PER_SHARD`/`SLOT_SIZE`).
### Phase F — million-scale harness + ACID verification — ✅ shipped ### Phase F — million-scale harness + ACID verification — ✅ shipped
*Maps to [plan 09's verification-targets table](../../09-concurrency-scaleout.md).* *Maps to `plan 09's verification-targets table`.*
`setrlimit(RLIMIT_NOFILE)` raised at boot. A small C load client under `prototypes/wo-rt-c/bench/` (keep-alive, pipelined GETs, latency timestamps — `wrk` would be an external dep). Measure honestly on the dev box and commit the numbers to the prototype README: aggregate read req/s across cores (goal order 10⁶/s on 8–16 cores), concurrent open connections (goal order 10⁵–10⁶; ~8 KB/conn + fd limits are the ceiling), commits/s under group fsync, p99 read latency under write load. ACID scripts: torn-WAL injection (atomicity), single-shard interleaving probe (isolation), the phase-D crash test under load (durability). A `just rt-c-bench` recipe runs it all. `setrlimit(RLIMIT_NOFILE)` raised at boot. A small C load client under `prototypes/wo-rt-c/bench/` (keep-alive, pipelined GETs, latency timestamps — `wrk` would be an external dep). Measure honestly on the dev box and commit the numbers to the prototype README: aggregate read req/s across cores (goal order 10⁶/s on 8–16 cores), concurrent open connections (goal order 10⁵–10⁶; ~8 KB/conn + fd limits are the ceiling), commits/s under group fsync, p99 read latency under write load. ACID scripts: torn-WAL injection (atomicity), single-shard interleaving probe (isolation), the phase-D crash test under load (durability). A `just rt-c-bench` recipe runs it all.
@ -80,9 +80,9 @@ Boot, before any listener opens: each thread replays its own WAL into its arena
## Cross-references ## Cross-references
- [`../../09-concurrency-scaleout.md`](../../09-concurrency-scaleout.md) — the doctrine; this prototype is its executable proving ground (A↔09a, C↔09 decision 4, D↔09c). - `../../09-concurrency-scaleout.md` — the doctrine; this prototype is its executable proving ground (A↔09a, C↔09 decision 4, D↔09c).
- [`../../10-storage-foundations.md`](../../10-storage-foundations.md), [`11-wal-and-recovery.md`](../../11-wal-and-recovery.md), [`12-engine-disk-cutover.md`](../../12-engine-disk-cutover.md) — the storage track phases B/D/E miniaturize. - `../../10-storage-foundations.md`, `11-wal-and-recovery.md`, `12-engine-disk-cutover.md` — the storage track phases B/D/E miniaturize.
- [`../../../../prototypes/wo-rt-c/README.md`](../../../../runtime/README.md) — current state and module map (phase 0). - [`../../../../prototypes/wo-rt-c/README.md`](../../../../runtime/README.md) — current state and module map (phase 0).
- [`./01-architecture.md`](./01-architecture.md) — the target architecture traced through one memory address at million-connection concurrency, plus improvement proposals (seqlock reads, registered buffers, SEND_ZC, SQPOLL) that slot into phases C/F. - [`./01-architecture.md`](01-architecture.md) — the target architecture traced through one memory address at million-connection concurrency, plus improvement proposals (seqlock reads, registered buffers, SEND_ZC, SQPOLL) that slot into phases C/F.
- [`./02-single-binary.md`](./02-single-binary.md) — the end goal: how the `wo build` single binary runs on this runtime environment (Go model, not JVM — the kernel is statically linked into every app; the embedding contract between compiler payload and runtime kernel). - [`./02-single-binary.md`](02-single-binary.md) — the end goal: how the `wo build` single binary runs on this runtime environment (Go model, not JVM — the kernel is statically linked into every app; the embedding contract between compiler payload and runtime kernel).
- [`../../../../prototypes/wo-db/`](../../../../prototypes/wo-db/) — the query-layer sibling; one day a phase-G could splice its engine on top of this runtime. - `../../../../prototypes/wo-db/` — the query-layer sibling; one day a phase-G could splice its engine on top of this runtime.

View file

@ -1,6 +1,6 @@
# wo-rt-c architecture — one memory address, two spaces, a million connections # wo-rt-c architecture — one memory address, two spaces, a million connections
This document defines the runtime's architecture by following **one memory address** through user space, kernel space, and hardware, under a million connections reading and writing it concurrently — then suggests improvements. Companion docs: [`00-plan.md`](./00-plan.md) (the phases that build this), [`README.md`](../../../../runtime/README.md) (phase-0 module map). This document defines the runtime's architecture by following **one memory address** through user space, kernel space, and hardware, under a million connections reading and writing it concurrently — then suggests improvements. Companion docs: [`00-plan.md`](00-plan.md) (the phases that build this), [`README.md`](../../../../runtime/README.md) (phase-0 module map).
## The cast: one address ## The cast: one address
@ -14,7 +14,7 @@ Three facts define everything that follows:
1. **User space sees a virtual address.** `0x7f3a2c001000` is an entry in this process's page tables; the kernel resolved it to one physical RAM frame at fault time (`MAP_POPULATE` faults it in at boot, before any request). 1. **User space sees a virtual address.** `0x7f3a2c001000` is an entry in this process's page tables; the kernel resolved it to one physical RAM frame at fault time (`MAP_POPULATE` faults it in at boot, before any request).
2. **The kernel pins the frame.** `mlock` guarantees the physical page is never swapped — a load from this address is always a RAM access, never disk I/O in disguise. 2. **The kernel pins the frame.** `mlock` guarantees the physical page is never swapped — a load from this address is always a RAM access, never disk I/O in disguise.
3. **Exactly one thread owns writes to it.** The address lies inside shard 0's slice; thread 0 is the only code in the process that may store to it ([00-plan.md decision 2](./00-plan.md)). Data exists once — ownership, not copying, is the concurrency model. 3. **Exactly one thread owns writes to it.** The address lies inside shard 0's slice; thread 0 is the only code in the process that may store to it ([00-plan.md decision 2](00-plan.md)). Data exists once — ownership, not copying, is the concurrency model.
## The two spaces ## The two spaces
@ -55,7 +55,7 @@ The arrows worth staring at: the thread's access to the database (`MOV`) and to
## Write path — the address changes ## Write path — the address changes
One of the million connections POSTs a new value. Dual-write order per [00-plan.md decision 6](./00-plan.md): RAM first, then the hard drive, ack only after the disk confirms. One of the million connections POSTs a new value. Dual-write order per [00-plan.md decision 6](00-plan.md): RAM first, then the hard drive, ack only after the disk confirms.
```mermaid ```mermaid
sequenceDiagram sequenceDiagram
@ -110,7 +110,7 @@ do { v1 = atomic_load_acquire(&slot->ver); /* spin only while odd */
} while (v1 != v2 || (v1 & 1)); } while (v1 != v2 || (v1 & 1));
``` ```
A hot row becomes readable by all N cores **with zero duplication — same physical frame, same address** — and writes stay serial, so ACID isolation is untouched. Cost: two atomic increments per write, a retry loop per read (C11 atomics, no library). Doctrine note: this relaxes "only the owner touches the slice" to "only the owner *writes* the slice"; contrast with [plan 13e's hot-row read replicas](../../13-class-model-live-pricing.md), which solve the same bottleneck by *copying* rows per thread — seqlock is the no-duplication answer the replica design isn't. A hot row becomes readable by all N cores **with zero duplication — same physical frame, same address** — and writes stay serial, so ACID isolation is untouched. Cost: two atomic increments per write, a retry loop per read (C11 atomics, no library). Doctrine note: this relaxes "only the owner touches the slice" to "only the owner *writes* the slice"; contrast with `plan 13e's hot-row read replicas`, which solve the same bottleneck by *copying* rows per thread — seqlock is the no-duplication answer the replica design isn't.
### 2. Registered buffers and files (`IORING_REGISTER_BUFFERS` / `_FILES`) ### 2. Registered buffers and files (`IORING_REGISTER_BUFFERS` / `_FILES`)
@ -138,12 +138,12 @@ On multi-socket boxes, bind each shard slice's pages to the owning core's NUMA n
### Deliberately not suggested ### Deliberately not suggested
Work stealing (breaks single-writer ACID), shared-heap locking (the doctrine exists to avoid it — and at 1M readers a mutex on the row would serialize everything the seqlock parallelizes), liburing (the prototype's value is the raw syscall sequence), and multi-node distribution (plan 09's single-box stance). See [plan 09 § Non-scope](../../09-concurrency-scaleout.md). Work stealing (breaks single-writer ACID), shared-heap locking (the doctrine exists to avoid it — and at 1M readers a mutex on the row would serialize everything the seqlock parallelizes), liburing (the prototype's value is the raw syscall sequence), and multi-node distribution (plan 09's single-box stance). See `plan 09 § Non-scope`.
## Cross-references ## Cross-references
- [`00-plan.md`](./00-plan.md) — phases A–F that build the architecture described here; improvements 1–7 slot into phases C/F or follow them. - [`00-plan.md`](00-plan.md) — phases A–F that build the architecture described here; improvements 1–7 slot into phases C/F or follow them.
- [`../../docs/plan/09-concurrency-scaleout.md`](../../09-concurrency-scaleout.md) — the thread-per-core doctrine. - `../../docs/plan/09-concurrency-scaleout.md` — the thread-per-core doctrine.
- [`../../docs/plan/exploration/linux/07-io_uring.md`](../linux/07-io_uring.md), [`08-mmap.md`](../linux/08-mmap.md) — the two shared-page mechanisms. - [`../../docs/plan/exploration/linux/07-io_uring.md`](../linux/07-io_uring.md), [`08-mmap.md`](../linux/08-mmap.md) — the two shared-page mechanisms.
- [`../../docs/plan/13-class-model-live-pricing.md`](../../13-class-model-live-pricing.md) — 13e's read-replica alternative, contrasted in improvement 1. - `../../docs/plan/13-class-model-live-pricing.md` — 13e's read-replica alternative, contrasted in improvement 1.
- [`README.md`](../../../../README.md) — the C/assembly "one address" pedagogy the single-binary story extends to a full runtime. - [`README.md`](../../../../README.md) — the C/assembly "one address" pedagogy the single-binary story extends to a full runtime.

View file

@ -1,6 +1,6 @@
# 02 — The end goal: the writeonce single binary on this runtime environment # 02 — The end goal: the writeonce single binary on this runtime environment
**Context sources:** [`00-plan.md`](./00-plan.md) (the runtime-environment phases, A–B ✅), [`01-architecture.md`](./01-architecture.md) (the one-address trace), `../../../runtime/wo-language.md` ("one binary per project; no runtime to install on the target host"; `.wo` has "its own lexer, parser, analyzer, and bytecode"), [`../../09-concurrency-scaleout.md`](../../09-concurrency-scaleout.md)–[`12`](../../12-engine-disk-cutover.md) (the Rust product track this proves out), [`../../../runtime/database/02-wo-language.md`](../../../runtime/database/02-wo-language.md) (catalog + transaction semantics the payload carries). **Context sources:** [`00-plan.md`](00-plan.md) (the runtime-environment phases, A–B ✅), [`01-architecture.md`](01-architecture.md) (the one-address trace), `../../../runtime/wo-language.md` ("one binary per project; no runtime to install on the target host"; `.wo` has "its own lexer, parser, analyzer, and bytecode"), `../../09-concurrency-scaleout.md`–`12` (the Rust product track this proves out), `../../../runtime/database/02-wo-language.md` (catalog + transaction semantics the payload carries).
## The end goal, stated once ## The end goal, stated once
@ -80,11 +80,11 @@ Steps 1–2 and 4–6 exist in `wo-rt-c` today with the notes store standing in
- **`wo-rt-c` (C)** — proves each kernel syscall sequence first: threads/arena (✅), io_uring, WAL, recovery, bench. It will never parse `.wo`; its notes store is the stand-in payload. - **`wo-rt-c` (C)** — proves each kernel syscall sequence first: threads/arena (✅), io_uring, WAL, recovery, bench. It will never parse `.wo`; its notes store is the stand-in payload.
- **`crates/rt` (Rust)** — the product: owns the compiler front-end today (lexer→catalog, Stage 2 shipped) and absorbs each proven kernel sequence per plans 09–12, where ownership makes the shard discipline a compile-time guarantee. - **`crates/rt` (Rust)** — the product: owns the compiler front-end today (lexer→catalog, Stage 2 shipped) and absorbs each proven kernel sequence per plans 09–12, where ownership makes the shard discipline a compile-time guarantee.
- **Optional phase G** (named in [`00-plan.md`](./00-plan.md)): splice the [`wo-db`](../../../../prototypes/wo-db/) C++ query engine onto `wo-rt-c` as an end-to-end C-family demonstrator of this document — valuable as proof, never the product. - **Optional phase G** (named in [`00-plan.md`](00-plan.md)): splice the `wo-db` C++ query engine onto `wo-rt-c` as an end-to-end C-family demonstrator of this document — valuable as proof, never the product.
## Cross-references ## Cross-references
- [`00-plan.md`](./00-plan.md) — the kernel phases; [`01-architecture.md`](./01-architecture.md) — the one-address trace through the same stack. - [`00-plan.md`](00-plan.md) — the kernel phases; [`01-architecture.md`](01-architecture.md) — the one-address trace through the same stack.
- [`README.md`](../../../../README.md) — the user-facing single-binary promise this document implements. - [`README.md`](../../../../README.md) — the user-facing single-binary promise this document implements.
- [`../../13-class-model-live-pricing.md`](../../13-class-model-live-pricing.md) (13b methods) — the payload-side track. - `../../13-class-model-live-pricing.md` (13b methods) — the payload-side track.
- [`../../09-concurrency-scaleout.md`](../../09-concurrency-scaleout.md) — shard-key routing and 2PC the contract defers to. - `../../09-concurrency-scaleout.md` — shard-key routing and 2PC the contract defers to.

View file

@ -0,0 +1,204 @@
# Fiber parity study — what a mainstream Go web framework ships that `writeonce-serve` does not
Reference: [gofiber/fiber](https://github.com/gofiber/fiber) **v3.5.0**, read
2026-08-26 from a shallow clone at `.dev/reference/fiber` (gitignored — re-clone
with `git clone --depth 1 https://github.com/gofiber/fiber .dev/reference/fiber`).
Read against `docs/examples/writeonce-serve` as it stands the same day.
Why Fiber and not Express or Axum: it is the closest structural analogue in the
reference set. Compiled language, no runtime, one binary, an explicit
`Ctx`-per-request, and middleware as an ordered chain — the same shape
`writeonce-serve` already has. Where it differs, the difference is a feature
decision rather than a paradigm gap, which is what makes the comparison useful.
Express would have contributed mostly "you have no closures".
What was actually read: `app.go` (routing methods, the 53-field `Config`),
`ctx.go` / `req.go` / `res.go` (the request and response surface), `bind.go`
(binding), `hooks.go` (lifecycle), and the `Config` struct of every one of the
**32** packages under `middleware/`.
**Headline: `writeonce-serve` is further along than its size suggests.** Of
Fiber's 32 middleware packages, 9 already have a working `writeonce-serve`
counterpart (CORS, basic auth, key/bearer auth, helmet-style security headers,
ETag, static files, logger, host authorization, recover-as-500). The gaps are
real but they are mostly *breadth*, and they cluster around four things:
**cookies** (absent entirely, and half the list sits on top of them),
**streaming** (absent, and SSE/compression/chunked all sit on that),
**binding** (blocked by doctrine until `@derive`), and **one missing runtime
primitive nobody had noticed**.
---
## 0. The blocker the existing ledger gets wrong
`docs/examples/writeonce-serve/README.md`'s crypto row currently reads:
> Unlocks (signed cookies, CSRF, session integrity, webhook verification, JWT
> HS256) | ⬜ **UNBLOCKED** (the primitives exist since iteration 34)
**That is not true for three of the five.** SHA-256 and HMAC-SHA256 let you
*authenticate* a token. They do not let you *mint* one, because
**writeonce has no source of randomness at all** — `grep -inE
'random|rand|urandom|getrandom'` over `compiler/src/types.ml`,
`runtime/src/wob.h`, `sysio.c` and `crypto.c` returns nothing. There is no
`getrandom(2)`, no `/dev/urandom` read (`fs.read_at` could reach it, but `fs`
has no way to open a character device meaningfully and the result would be a
`Text` of raw bytes with no API contract), and no CSPRNG builtin.
Fiber's session store defaults its `KeyGenerator` to a UUID; its CSRF
middleware mints a token per request. Both are unguessability requirements, not
integrity requirements. An HMAC over a *predictable* session id is not a
session — it is a signed guess.
So the honest dependency order is: **a random-bytes builtin comes before
sessions and CSRF, not after them.** Signed cookies over an
application-supplied value and webhook verification (where the secret comes
from config and the nonce comes from the *sender*) genuinely are unblocked; JWT
HS256 is unblocked for verification and blocked for issuing anything with a
random `jti`.
This is the study's most valuable single finding and it is the reason iteration
39 leads with the primitive rather than the middleware.
---
## 1. Cookies — absent, and foundational
`writeonce-serve` has **no cookie support in either direction**. `Req` has
`headers: map<Text, Text>` and nothing parses `Cookie:`; `Resp` has
`headers: map<Text, Text>` and there is no `Set-Cookie` builder — and because
`Resp.headers` is a *map*, it structurally cannot carry the two `Set-Cookie`
lines a login-plus-flash response needs. That map is a real design constraint
this iteration has to confront, not a missing function.
Fiber, for comparison: `Req.Cookies(key)`, `Res.Cookie(*Cookie)` with
`ClearCookie`, and a `Cookie` struct carrying Path, Domain, MaxAge, Expires,
Secure, HTTPOnly, SameSite, Partitioned and SessionOnly.
| Piece | Fiber | writeonce-serve |
| --- | --- | --- |
| read request cookies | `Req.Cookies(key)` | — |
| set a response cookie | `Res.Cookie(&Cookie{...})` | — |
| clear | `Res.ClearCookie(key...)` | — |
| attributes | Path/Domain/MaxAge/Expires/Secure/HTTPOnly/SameSite/Partitioned | — |
| multiple `Set-Cookie` per response | native (header list) | **impossible** — `Resp.headers` is `map<Text,Text>` |
| signed / encrypted | `encryptcookie` middleware | — (HMAC exists; see §0 for the minting problem) |
Everything in §2 depends on this section landing first.
## 2. Session, CSRF, rate limiting, idempotency — the store-backed chain
All four are one shape in Fiber: a middleware plus a `Storage` interface. All
four are pure-`.wo` work in writeonce *once cookies and randomness exist*, and
writeonce has an unusual advantage here — `@table` gives a **durable,
WAL-backed, crash-recoverable** store for free, where Fiber ships an in-memory
default and makes you bolt on Redis for anything real.
| Middleware | Fiber's config knobs (the shape to translate) | writeonce status |
| --- | --- | --- |
| `session` | Storage, KeyGenerator, IdleTimeout, AbsoluteTimeout, CookieDomain/Path/SameSite/Secure/HTTPOnly/SessionOnly, Extractor | absent; needs §0 + §1 |
| `csrf` | Storage, Session, KeyGenerator, TrustedOrigins, SingleUseToken, CookieName + the cookie attrs, IdleTimeout, Extractor | absent; needs §0 + §1 |
| `limiter` | Storage, Max, Expiration, KeyGenerator, LimitReached, SkipFailed/SkipSuccessful, DisableHeaders | absent; needs only a store + `time.ticks` — **unblocked today** |
| `idempotency` | Storage, Lock, KeyHeader + validator, Lifetime, KeepResponseHeaders | absent; store + `time.ticks` — **unblocked today** |
`limiter` and `idempotency` are the two cheapest real wins in this whole
document: no new primitive, no cookie, just a `@table` and a clock that already
exists.
## 3. Streaming — absent, and three features sit on it
`internal/serve.wo` builds a whole response as one `Text` and writes it with a
single `net.write`; `serialize()` always emits `Content-Length`. There is no
flush, no chunked framing, no way to write a response incrementally.
`internal/parse.wo:153-157` **explicitly refuses** chunked request bodies, with
a correct note that silently treating a chunked request as body-less is request
smuggling — that refusal is good engineering and should stay until chunked is
implemented properly.
Blocked on this one seam:
- **SSE** (Fiber: `middleware/sse` with Retry, HeartbeatInterval, OnClose) —
the natural fit for writeonce's actor model, since a room actor already has
the fan-out shape. Wants iteration 24's chat work beside it.
- **Compression** (Fiber: `middleware/compress`, Level) — gzip/deflate/brotli.
Iteration 36 landed the bitwise operators, so a pure-`.wo` DEFLATE is now
*expressible*; whether it should be `.wo` or a C builtin is a genuine fork,
and the honest answer probably depends on whether anything else ever wants
zlib.
- **`SendFile` / `SendStream` / byte ranges** — Fiber's static ships ByteRange,
Browse, MaxAge, CacheDuration, IndexNames, Download. `http/files.wo` serves
whole small files only. Range requests are what make video and large
downloads work.
The framework README already lists "lazy body streaming + backpressure ·
streaming responses · explicit commit point" as ⏸ unblocked-by-the-arc. This
study's contribution is naming what *else* falls out of it.
## 4. Binding — blocked by doctrine, and that is fine
Fiber's `Bind` is 16 methods: `Body`, `JSON`, `XML`, `CBOR`, `MsgPack`, `Form`,
`Query`, `URI`, `Header`, `Cookie`, `RespHeader`, `All`, `Custom`, plus
validator hooks. It fills a struct by reflecting over tags.
writeonce has `json.decode(t) as T` for JSON bodies and **nothing** for query,
path params, form or header — those hand you `map<Text, Text>` and you assign
field by field. Principle 13 forbids reflection, so this cannot be closed the
way Fiber closes it.
The right owner is **[iteration 29, `@derive`](../../../stories/language-runtime-database/29-compile-time-metaprogramming.md)**:
compile-time generation from the class table gives typed binding with no
runtime reflection. This is a genuine parity gap with a real answer that is
already on the roadmap, so iteration 39 records it and does not attempt it.
Same verdict, same reason, for the codec spread: Fiber ships XML, CBOR and
MsgPack encoders/decoders in `Config`. writeonce ships JSON. That is the small
stdlib doing its job, not a defect.
## 5. Routing and response ergonomics — mostly sugar, cheap to close
| Gap | Fiber | writeonce-serve |
| --- | --- | --- |
| method helpers | Get/Post/Put/Delete/Patch/Head/Options/Trace/Connect/All/Add | `get`/`post`/`put`/`delete_` only — a `Route { method: "PATCH" }` literal works, so this is registration sugar, but its absence is felt |
| route names + URL building | `Name()`, `GetRouteURL()` | — (no named routes, no reverse routing) |
| route introspection | `GetRoutes()`, `Stack()`, `HandlersCount()` | — |
| mount / sub-app | `Use(prefix, subApp)` | `Group { prefix }` covers the common case ✅ |
| case sensitivity / strict slash | `CaseSensitive`, `StrictRouting` | — (always case-sensitive, always lenient) |
| per-route body limit | `Config.BodyLimit` | `const BODY_MAX = 1048576` in `internal/parse.wo` — one compile-time number for the whole server |
| per-handler timeout | `middleware/timeout` (Timeout, OnTimeout) | conn-level `read_ms`/`idle_ms` only; bounding a *handler* needs cancellation, which the README already parks |
| `Location`, `Vary`, `Links`, `Append`, `Attachment`/`Download` | ✅ each a `Res` method | — (`set_header` by hand) |
| `Format`/`AutoFormat` content negotiation on the way out | ✅ | `accepts()` exists; no format dispatch helper |
| q-value **ranking** | ✅ | 🔶 already in the ledger: q-values stripped, not ranked |
| request id | `middleware/requestid` (Header, Generator) | `req.ctx` bag exists to carry it; no generator — and see §0 |
| healthcheck / favicon / redirect / rewrite / skip | five small middleware | — (each a handful of lines) |
| `earlydata`, `paginate`, `responsetime`, `envvar`, `expvar`, `pprof` | ✅ | — (`expvar`/`pprof` belong to iteration 30, which has no story file) |
| lifecycle hooks | 11 hook families (OnRoute, OnListen, OnPreShutdown, OnPostShutdown, …) | — README already notes "no user teardown hooks yet" 🔶 |
| `proxy` middleware | ✅ | **impossible today** — no `net.connect`; owned by [iteration 38](../../../stories/language-runtime-database/38-content-platform-capabilities.md) |
| `recover` with stack trace | `EnableStackTrace`, `StackTraceHandler` | trap → 500 and the server lives ✅; no backtrace primitive exists |
## 6. Deliberate divergences — listed so nobody re-opens them
Not gaps. Each was decided and the reasoning is on file.
| Fiber feature | writeonce position |
| --- | --- |
| `Views` / `Render` / `ReloadViews` / `PassLocalsToViews` — a runtime template engine | **Rejected.** Markup is a compile-time literal (iteration 37's raw text literal + `writeonce-view`'s `Component`) or it does not exist. A per-request file read is the already-rejected engine — see `docs/plan/discarded.md`. |
| TLS config, `SetTLSHandler`, HTTP/2 | **Proxy-terminated, forever** (principle: TLS is not the app's job). h2c stays parked behind iteration 23. |
| `adaptor` (net/http interop) | No FFI, no foreign handler ecosystem to adapt to. |
| `Concurrency`, `ReadBufferSize`, prefork/`OnFork` | The shard-actor runtime owns placement; there is no worker-pool knob to expose. |
| Closures as handlers | Handlers are classes satisfying `Handler` (`router/router.wo`'s own note: "no function values in this language, by doctrine — a handler is a CLASS, its fields are the closure substitute"). |
| `SharedState` / `Locals` as an untyped bag | `req.ctx` is `map<Text,Text>` on purpose; typed per-request state is a `@table` row or a field on the handler class. |
## 7. What this study feeds
[**Iteration 39 — web framework parity**](../../../stories/language-runtime-database/39-web-framework-parity.md)
takes §0–§2 and the cheap half of §5, in that order, because §0 gates §2 and §1
gates most of it.
Explicitly *not* iteration 39's, with owners:
- streaming, SSE, compression, byte ranges (§3) — the parked streaming slice
- typed binding (§4) — [iteration 29](../../../stories/language-runtime-database/29-compile-time-metaprogramming.md)
- TTL cache middleware — [iteration 18](../../../stories/language-runtime-database/18-memory-db-features.md)
- `proxy` — [iteration 38](../../../stories/language-runtime-database/38-content-platform-capabilities.md)
- `pprof`/`expvar`/metrics — iteration 30
- everything in §6 — closed by doctrine

View file

@ -3,8 +3,8 @@
> Exploration/reference note (no status banner by board convention). > Exploration/reference note (no status banner by board convention).
> The normative decisions live in the arc spec > The normative decisions live in the arc spec
> ([`2026-08-20-shard-fiber-arc-design.md`](../../../superpowers/specs/2026-08-20-shard-fiber-arc-design.md)) > ([`2026-08-20-shard-fiber-arc-design.md`](../../../superpowers/specs/2026-08-20-shard-fiber-arc-design.md))
> and iterations [8](../../../stories/language-runtime-database/done/08-shard-actor-runtime.md) / > and iterations [8](../../../stories/language-runtime-database/08-shard-actor-runtime.md) /
> [11](../../../stories/language-runtime-database/done/11-fibers.md); this > [11](../../../stories/language-runtime-database/11-fibers.md); this
> page explains the WHY at doctrine depth. Written 2026-08-20, when this > page explains the WHY at doctrine depth. Written 2026-08-20, when this
> file was also the target of a dangling reference from iteration 11 — > file was also the target of a dangling reference from iteration 11 —
> it exists now. > it exists now.

View file

@ -8,18 +8,18 @@ Each primitive has its own numbered file with the kernel source path (into [`ref
| # | Primitive | Used by | | # | Primitive | Used by |
| --- | --- | --- | | --- | --- | --- |
| [01](./01-epoll.md) | `epoll` — event-driven I/O multiplexing | every runtime phase | | [01](01-epoll.md) | `epoll` — event-driven I/O multiplexing | every runtime phase |
| [02](./02-eventfd.md) | `eventfd` — counter as fd, cross-flow wake | phase 02, subscription wakeup | | [02](02-eventfd.md) | `eventfd` — counter as fd, cross-flow wake | phase 02, subscription wakeup |
| [03](./03-timerfd.md) | `timerfd` — timers as fds | phase 02, phase 07 debounce | | [03](03-timerfd.md) | `timerfd` — timers as fds | phase 02, phase 07 debounce |
| [04](./04-signalfd.md) | `signalfd` — signals as fds, graceful shutdown | phase 04 | | [04](04-signalfd.md) | `signalfd` — signals as fds, graceful shutdown | phase 04 |
| [05](./05-inotify.md) | `inotify` — filesystem events as fds | phase 07, future register! subscription | | [05](05-inotify.md) | `inotify` — filesystem events as fds | phase 07, future register! subscription |
| [06](./06-sendfile.md) | `sendfile` — zero-copy file → socket | phase 08 | | [06](06-sendfile.md) | `sendfile` — zero-copy file → socket | phase 08 |
| [07](./07-io_uring.md) | `io_uring` — async I/O ring buffers | phase 3 (WAL fsync), future HTTP | | [07](07-io_uring.md) | `io_uring` — async I/O ring buffers | phase 3 (WAL fsync), future HTTP |
| [08](./08-mmap.md) | `mmap` + `madvise` — memory-mapped files, page-cache hints | phase 3 (storage engine) | | [08](08-mmap.md) | `mmap` + `madvise` — memory-mapped files, page-cache hints | phase 3 (storage engine) |
| [09](./09-fallocate.md) | `fallocate` + `pread` + `pwritev2` — positional I/O & pre-allocation | phase 3 (WAL + SSTables) | | [09](09-fallocate.md) | `fallocate` + `pread` + `pwritev2` — positional I/O & pre-allocation | phase 3 (WAL + SSTables) |
| [10](./10-pidfd.md) | `pidfd` — process as fd, race-free supervision | future supervisor | | [10](10-pidfd.md) | `pidfd` — process as fd, race-free supervision | future supervisor |
| [11](./11-memfd_create.md) | `memfd_create` — anonymous shared memory | phase 3 (index build) | | [11](11-memfd_create.md) | `memfd_create` — anonymous shared memory | phase 3 (index build) |
| [12](./12-pwrite-fsync.md) | `pwrite` + `fsync`/`fdatasync`/`sync_file_range`/`posix_fadvise` — durability barriers | phases 10, 11, 12 (storage foundations, WAL, engine cutover) | | [12](12-pwrite-fsync.md) | `pwrite` + `fsync`/`fdatasync`/`sync_file_range`/`posix_fadvise` — durability barriers | phases 10, 11, 12 (storage foundations, WAL, engine cutover) |
The list below is the original overview kept for context and for a handful of adjacent primitives (`fanotify`, `splice`/`tee`) that don't yet have their own reference card. The list below is the original overview kept for context and for a handful of adjacent primitives (`fanotify`, `splice`/`tee`) that don't yet have their own reference card.

View file

@ -68,7 +68,7 @@ unsafe {
## Used by ## Used by
Every runtime phase that touches I/O: [`02-event-loop-epoll.md`](../../done/02-event-loop-epoll.md), [`03-hand-rolled-http.md`](../../done/03-hand-rolled-http.md), [`07-inotify-content-watcher.md`](../../07-inotify-content-watcher.md), [`08-sendfile-static-assets.md`](../../08-sendfile-static-assets.md). Every runtime phase that touches I/O: `02-event-loop-epoll.md`, `03-hand-rolled-http.md`, `07-inotify-content-watcher.md`, `08-sendfile-static-assets.md`.
## v1 port source ## v1 port source

View file

@ -55,11 +55,11 @@ unsafe {
- **Always 8-byte `read` / `write`.** Short reads/writes return `EINVAL` — the counter is `u64`, full word or nothing. - **Always 8-byte `read` / `write`.** Short reads/writes return `EINVAL` — the counter is `u64`, full word or nothing.
- **Writing `u64::MAX`** returns `EINVAL`; the counter can't hold more than `u64::MAX - 1`. - **Writing `u64::MAX`** returns `EINVAL`; the counter can't hold more than `u64::MAX - 1`.
- **Multiple writers are OK**; the kernel serialises. But reads race — use `EFD_SEMAPHORE` if you want one consumer per write. - **Multiple writers are OK**; the kernel serialises. But reads race — use `EFD_SEMAPHORE` if you want one consumer per write.
- **Not async-signal-safe.** Don't `write(fd, ...)` from a signal handler; use `signalfd` instead (see [04-signalfd.md](./04-signalfd.md)). - **Not async-signal-safe.** Don't `write(fd, ...)` from a signal handler; use `signalfd` instead (see [04-signalfd.md](04-signalfd.md)).
## Used by ## Used by
[`02-event-loop-epoll.md`](../../done/02-event-loop-epoll.md) — wake the loop for shutdown or internal work. Future `sub` crate ([`09-native-subscriptions`], not yet planned) uses it to signal that a subscriber queue has drained. `02-event-loop-epoll.md` — wake the loop for shutdown or internal work. Future `sub` crate ([`09-native-subscriptions`], not yet planned) uses it to signal that a subscriber queue has drained.
## v1 port source ## v1 port source

View file

@ -70,7 +70,7 @@ unsafe {
## Used by ## Used by
[`02-event-loop-epoll.md`](../../done/02-event-loop-epoll.md) — housekeeping timers. [`07-inotify-content-watcher.md`](../../07-inotify-content-watcher.md) — 150 ms debounce window after an inotify burst. Future subscription phase — keepalive pings to long-lived connections. `02-event-loop-epoll.md` — housekeeping timers. `07-inotify-content-watcher.md` — 150 ms debounce window after an inotify burst. Future subscription phase — keepalive pings to long-lived connections.
## v1 port source ## v1 port source

View file

@ -66,11 +66,11 @@ unsafe {
- **Per-thread mask.** `sigprocmask` is per-thread. In a single-threaded runtime that's fine; if you ever spawn threads, use `pthread_sigmask` on each so the main loop gets the signals. - **Per-thread mask.** `sigprocmask` is per-thread. In a single-threaded runtime that's fine; if you ever spawn threads, use `pthread_sigmask` on each so the main loop gets the signals.
- **`signalfd_siginfo` is big** (128 bytes). Read into an aligned buffer; partial reads return `EINVAL`. - **`signalfd_siginfo` is big** (128 bytes). Read into an aligned buffer; partial reads return `EINVAL`.
- **Doesn't catch `SIGKILL` or `SIGSTOP`.** Nothing does. Those bypass everything. - **Doesn't catch `SIGKILL` or `SIGSTOP`.** Nothing does. Those bypass everything.
- **Child exit notifications (`SIGCHLD`)** work via signalfd but `pidfd` (see [10-pidfd.md](./10-pidfd.md)) is usually the better fit for clean child supervision. - **Child exit notifications (`SIGCHLD`)** work via signalfd but `pidfd` (see [10-pidfd.md](10-pidfd.md)) is usually the better fit for clean child supervision.
## Used by ## Used by
[`04-cutover-remove-tokio-axum.md`](../../done/04-cutover-remove-tokio-axum.md) — replaces `tokio::signal::ctrl_c()` for graceful shutdown. Every subsequent phase inherits this pattern. `04-cutover-remove-tokio-axum.md` — replaces `tokio::signal::ctrl_c()` for graceful shutdown. Every subsequent phase inherits this pattern.
## v1 port source ## v1 port source

View file

@ -75,13 +75,13 @@ unsafe {
- **Per-directory watches, not per-file.** Watching individual files wastes descriptors and misses `IN_CREATE`/`IN_DELETE` for new entries. Watch the directory; filter by event `name` in userspace. - **Per-directory watches, not per-file.** Watching individual files wastes descriptors and misses `IN_CREATE`/`IN_DELETE` for new entries. Watch the directory; filter by event `name` in userspace.
- **Recursive watching is manual.** Walk the tree at init and add a watch per directory. React to `IN_CREATE | IN_ISDIR` by adding a watch for the new subdirectory — and to `IN_MOVED_TO | IN_ISDIR` too. - **Recursive watching is manual.** Walk the tree at init and add a watch per directory. React to `IN_CREATE | IN_ISDIR` by adding a watch for the new subdirectory — and to `IN_MOVED_TO | IN_ISDIR` too.
- **`fs.inotify.max_user_watches`** defaults to 8192 on most distros. Recursive watches over a big node_modules or target dir exhaust it fast. Filter aggressively before adding. - **`fs.inotify.max_user_watches`** defaults to 8192 on most distros. Recursive watches over a big node_modules or target dir exhaust it fast. Filter aggressively before adding.
- **Editors burst events.** Tmp-file + rename + delete is 3–4 events per logical save. Debounce 100–200 ms with [`timerfd`](./03-timerfd.md). - **Editors burst events.** Tmp-file + rename + delete is 3–4 events per logical save. Debounce 100–200 ms with [`timerfd`](03-timerfd.md).
- **Reading less than a full event is an `EINVAL`.** Use a buffer ≥ `sizeof(inotify_event) + NAME_MAX + 1` (≈ 4 KiB is a safe size). - **Reading less than a full event is an `EINVAL`.** Use a buffer ≥ `sizeof(inotify_event) + NAME_MAX + 1` (≈ 4 KiB is a safe size).
- **`wd` is stable per-watch but reused after `rm_watch`.** Keep a `wd → path` map; remove from it on `IN_IGNORED`. - **`wd` is stable per-watch but reused after `rm_watch`.** Keep a `wd → path` map; remove from it on `IN_IGNORED`.
## Used by ## Used by
[`07-inotify-content-watcher.md`](../../07-inotify-content-watcher.md) — the Stage-3 hot-reload feature. Future `sub` crate — the register-macro subscription model in [`00-linux.md § Database Subscription`](./00-linux.md#database-subscription). `07-inotify-content-watcher.md` — the Stage-3 hot-reload feature. Future `sub` crate — the register-macro subscription model in [`00-linux.md § Database Subscription`](00-linux.md#database-subscription).
## v1 port source ## v1 port source

View file

@ -7,7 +7,7 @@ Zero-copy transfer from a file fd to a socket fd. The kernel splices pages direc
| Path | What | | Path | What |
| --- | --- | | --- | --- |
| [`reference/linux/fs/read_write.c`](../../../../.dev/reference/linux/fs/read_write.c) | `SYSCALL_DEFINE4(sendfile, ...)` and `SYSCALL_DEFINE4(sendfile64, ...)`. Modern glibc aliases the first to the second; the syscalls are distinguished by the offset type. | | [`reference/linux/fs/read_write.c`](../../../../.dev/reference/linux/fs/read_write.c) | `SYSCALL_DEFINE4(sendfile, ...)` and `SYSCALL_DEFINE4(sendfile64, ...)`. Modern glibc aliases the first to the second; the syscalls are distinguished by the offset type. |
| [`reference/linux/fs/splice.c`](../../../../.dev/reference/linux/fs/splice.c) | Internally `sendfile` delegates to `splice_direct_to_actor`. Related — see [07-splice.md](./07-splice.md) if you ever need the more general fd-to-fd pipe path. | | [`reference/linux/fs/splice.c`](../../../../.dev/reference/linux/fs/splice.c) | Internally `sendfile` delegates to `splice_direct_to_actor`. Related — see `07-splice.md` if you ever need the more general fd-to-fd pipe path. |
## Man pages ## Man pages
@ -71,7 +71,7 @@ unsafe {
## Used by ## Used by
[`08-sendfile-static-assets.md`](../../08-sendfile-static-assets.md) — the `GET /static/...` handler. Future `##ui` SSR output bundles go through the same path. `08-sendfile-static-assets.md` — the `GET /static/...` handler. Future `##ui` SSR output bundles go through the same path.
## v1 port source ## v1 port source

View file

@ -2,7 +2,7 @@
Ring-buffer based async I/O (Linux 5.1+, mature 5.11+). Two lock-free SPSC rings shared between userspace and kernel: submissions (SQEs) go in one, completions (CQEs) come out of the other. Batched, zero-syscall submission (with SQPOLL), zero-copy where the underlying op allows. Successor to `epoll` + `libaio` for the storage engine's WAL fsync path and — eventually — the HTTP server's accept/recv/send path. Ring-buffer based async I/O (Linux 5.1+, mature 5.11+). Two lock-free SPSC rings shared between userspace and kernel: submissions (SQEs) go in one, completions (CQEs) come out of the other. Batched, zero-syscall submission (with SQPOLL), zero-copy where the underlying op allows. Successor to `epoll` + `libaio` for the storage engine's WAL fsync path and — eventually — the HTTP server's accept/recv/send path.
**Not on the runtime's critical path in phases 02–08.** Phase 02 uses `epoll`. `io_uring` comes in during [Phase 3 — In-Memory Engine](../../../runtime/database/03-inmemory-engine.md) for the WAL's group-commit fsync loop. This card is the reference for that phase. **Not on the runtime's critical path in phases 02–08.** Phase 02 uses `epoll`. `io_uring` comes in during `Phase 3 — In-Memory Engine` for the WAL's group-commit fsync loop. This card is the reference for that phase.
## Kernel source ## Kernel source
@ -90,7 +90,7 @@ Full working code is ~200 LOC including error handling — see `liburing` source
## Used by ## Used by
Phase 3 of the database series — see [`docs/runtime/database/03-inmemory-engine.md`](../../../runtime/database/03-inmemory-engine.md). Specifically the WAL fsync path: link `WRITE` → `FSYNC` SQEs, submit many per tick, reap completions to ack committed transactions. Also the natural upgrade target for the HTTP server once Phase 4 adds the native wire protocol. Phase 3 of the database series — see `docs/runtime/database/03-inmemory-engine.md`. Specifically the WAL fsync path: link `WRITE` → `FSYNC` SQEs, submit many per tick, reap completions to ack committed transactions. Also the natural upgrade target for the HTTP server once Phase 4 adds the native wire protocol.
## v1 port source ## v1 port source

View file

@ -93,7 +93,7 @@ unsafe {
## Used by ## Used by
Phase 3 of the database series — see [`docs/runtime/database/03-inmemory-engine.md`](../../../runtime/database/03-inmemory-engine.md) § Linux Tuning Checklist. The relational B+ tree, the LSM memtables' on-disk segments, and the document store's arenas all live behind `mmap`. Phase 3 of the database series — see `docs/runtime/database/03-inmemory-engine.md` § Linux Tuning Checklist. The relational B+ tree, the LSM memtables' on-disk segments, and the document store's arenas all live behind `mmap`.
## v1 port source ## v1 port source

View file

@ -2,7 +2,7 @@
`fallocate` pre-allocates disk space for a file without writing any bytes — lets the filesystem commit to a contiguous extent, so later writes don't fragment and can't fail mid-operation due to disk pressure. `pread` / `pwritev2` read and write at an explicit offset without touching the file's cursor — letting many concurrent readers share one fd safely. `fallocate` pre-allocates disk space for a file without writing any bytes — lets the filesystem commit to a contiguous extent, so later writes don't fragment and can't fail mid-operation due to disk pressure. `pread` / `pwritev2` read and write at an explicit offset without touching the file's cursor — letting many concurrent readers share one fd safely.
Together they form the backbone of the storage engine's on-disk layout: segment files are pre-allocated to their target size at creation, then written into via `pwritev2`; readers hit them via `pread` or `mmap` (see [08-mmap.md](./08-mmap.md)). Together they form the backbone of the storage engine's on-disk layout: segment files are pre-allocated to their target size at creation, then written into via `pwritev2`; readers hit them via `pread` or `mmap` (see [08-mmap.md](08-mmap.md)).
## Kernel source ## Kernel source
@ -89,7 +89,7 @@ unsafe {
## Used by ## Used by
Phase 3 of the database series — WAL pre-allocation, SSTable extent reservation, segment punching for compaction. Also [`07-io_uring.md`](./07-io_uring.md) pairs beautifully with positional I/O: `IORING_OP_WRITE` / `IORING_OP_READ` take an offset, so they're `pwrite`/`pread` under the hood. Phase 3 of the database series — WAL pre-allocation, SSTable extent reservation, segment punching for compaction. Also [`07-io_uring.md`](07-io_uring.md) pairs beautifully with positional I/O: `IORING_OP_WRITE` / `IORING_OP_READ` take an offset, so they're `pwrite`/`pread` under the hood.
## v1 port source ## v1 port source

View file

@ -95,7 +95,7 @@ unsafe {
## Used by ## Used by
Phase 3 of the database series — index-build-then-swap (mentioned in [03-inmemory-engine.md § Recovery](../../../runtime/database/03-inmemory-engine.md#recovery) as "build a .seg index in memory before atomically swapping it to disk"). Also any future IPC story with worker processes (Phase 6 full-stack with multiple render workers, say). Phase 3 of the database series — index-build-then-swap (mentioned in `03-inmemory-engine.md § Recovery` as "build a .seg index in memory before atomically swapping it to disk"). Also any future IPC story with worker processes (Phase 6 full-stack with multiple render workers, say).
## v1 port source ## v1 port source

View file

@ -1,6 +1,6 @@
# 12 — `pwrite` + `fsync` durability syscalls # 12 — `pwrite` + `fsync` durability syscalls
The previous cards cover positional I/O ([`09-fallocate.md`](./09-fallocate.md)) and the page cache backstory ([`08-mmap.md`](./08-mmap.md)) but skip the actual durability primitives. This card fills the gap. Every persistent-storage phase (10, 11, 12) leans on these. The previous cards cover positional I/O ([`09-fallocate.md`](09-fallocate.md)) and the page cache backstory ([`08-mmap.md`](08-mmap.md)) but skip the actual durability primitives. This card fills the gap. Every persistent-storage phase (10, 11, 12) leans on these.
## The four syscalls ## The four syscalls
@ -141,9 +141,9 @@ Writeonce trades the memcpy for the recovery speed and the simpler programming m
## Used by ## Used by
- [`docs/plan/10-storage-foundations.md`](../../10-storage-foundations.md) — segment append uses `pwrite` + later `fdatasync`. - `docs/plan/10-storage-foundations.md` — segment append uses `pwrite` + later `fdatasync`.
- [`docs/plan/11-wal-and-recovery.md`](../../11-wal-and-recovery.md) — group-commit uses `pwrite` + `fdatasync`; control file uses `fsync` + `rename` + parent-dir `fsync`. - `docs/plan/11-wal-and-recovery.md` — group-commit uses `pwrite` + `fdatasync`; control file uses `fsync` + `rename` + parent-dir `fsync`.
- [`docs/plan/12-engine-disk-cutover.md`](../../12-engine-disk-cutover.md) — checkpoint uses `fsync` per active segment fd. - `docs/plan/12-engine-disk-cutover.md` — checkpoint uses `fsync` per active segment fd.
Pair with [`postgresql/wal.md`](../postgresql/wal.md), [`postgresql/buffer-and-checkpoint.md`](../postgresql/buffer-and-checkpoint.md) for the design context. Pair with [`postgresql/wal.md`](../postgresql/wal.md), [`postgresql/buffer-and-checkpoint.md`](../postgresql/buffer-and-checkpoint.md) for the design context.

View file

@ -14,12 +14,12 @@ Gitignored — see [`.gitignore`](../../../../.gitignore). Pair it with [`refere
| # | Postgres area | What writeonce takes | What writeonce skips | | # | Postgres area | What writeonce takes | What writeonce skips |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| [wal](./wal.md) | `access/transam/xlog*.c` | append-only sequential log, LSN-as-byte-offset, segment rollover, group commit, `pwrite` + `fsync` at commit | replication/archiver, multi-process WAL writer, GUC matrix | | [wal](wal.md) | `access/transam/xlog*.c` | append-only sequential log, LSN-as-byte-offset, segment rollover, group commit, `pwrite` + `fsync` at commit | replication/archiver, multi-process WAL writer, GUC matrix |
| [smgr-and-md](./smgr-and-md.md) | `storage/smgr/{md,smgr,bulk_write}.c` | one file per relation, segments capped at `RELSEG_SIZE`, immediate vs deferred fsync | multi-fork abstraction (main/fsm/vm), shared-memory descriptor cache | | [smgr-and-md](smgr-and-md.md) | `storage/smgr/{md,smgr,bulk_write}.c` | one file per relation, segments capped at `RELSEG_SIZE`, immediate vs deferred fsync | multi-fork abstraction (main/fsm/vm), shared-memory descriptor cache |
| [buffer-and-checkpoint](./buffer-and-checkpoint.md) | `storage/buffer/{bufmgr,freelist}.c` + `postmaster/{checkpointer,bgwriter}.c` | page cache + dirty bit + LRU; checkpoint flushes then advances control-file LSN | shared-buffer pinning/unpinning, separate writer processes, latches | | [buffer-and-checkpoint](buffer-and-checkpoint.md) | `storage/buffer/{bufmgr,freelist}.c` + `postmaster/{checkpointer,bgwriter}.c` | page cache + dirty bit + LRU; checkpoint flushes then advances control-file LSN | shared-buffer pinning/unpinning, separate writer processes, latches |
| [page-format](./page-format.md) | `storage/page/{bufpage,checksum}.c` | page header (LSN, checksum, free-space markers); CRC32C trailers | MVCC visibility (xmin/xmax/ctid), access-method-specific opaque space | | [page-format](page-format.md) | `storage/page/{bufpage,checksum}.c` | page header (LSN, checksum, free-space markers); CRC32C trailers | MVCC visibility (xmin/xmax/ctid), access-method-specific opaque space |
| [constraints-and-grammar](./constraints-and-grammar.md) | `parser/gram.y`, `catalog/pg_constraint.h`, `utils/adt/ri_triggers.c` | PK = blessed unique index; FK forward-only catalog + inline-check semantics; ON DELETE action set; backlink-implies-index (our improvement) | trigger machinery, deferrable constraints, MATCH PARTIAL, composite keys | | [constraints-and-grammar](constraints-and-grammar.md) | `parser/gram.y`, `catalog/pg_constraint.h`, `utils/adt/ri_triggers.c` | PK = blessed unique index; FK forward-only catalog + inline-check semantics; ON DELETE action set; backlink-implies-index (our improvement) | trigger machinery, deferrable constraints, MATCH PARTIAL, composite keys |
| [indexing-and-point-lookup](./indexing-and-point-lookup.md) | `access/{nbtree,hash}/README`, `optimizer/path/costsize.c`, `storage/itemptr.h` | hash-bucket point lookup (expected O(1)), index-entry-as-row-address (TID ↔ our slot), selectivity beats seqscan by arithmetic | btree/gin/gist/spgist/brin AMs, cost-based planner, index paging | | [indexing-and-point-lookup](indexing-and-point-lookup.md) | `access/{nbtree,hash}/README`, `optimizer/path/costsize.c`, `storage/itemptr.h` | hash-bucket point lookup (expected O(1)), index-entry-as-row-address (TID ↔ our slot), selectivity beats seqscan by arithmetic | btree/gin/gist/spgist/brin AMs, cost-based planner, index paging |
## The lift-vs-skip filter ## The lift-vs-skip filter
@ -46,11 +46,11 @@ The implementation phases that lean on this material:
- The Rust-era consumers (plans 10/11/12: storage foundations, WAL and - The Rust-era consumers (plans 10/11/12: storage foundations, WAL and
recovery, disk cutover) were removed with that track 2026-08-18; their recovery, disk cutover) were removed with that track 2026-08-18; their
ideas shipped in `database/src/` (typed WAL + replay) and the rest wait ideas shipped in `database/src/` (typed WAL + replay) and the rest wait
on [iteration 32](../../../stories/language-runtime-database/refine/32-wal-checkpoint.md) on [iteration 32](../../../stories/language-runtime-database/32-wal-checkpoint.md)
(checkpoint) — the wal/buffer cards are its entry material. (checkpoint) — the wal/buffer cards are its entry material.
- Current consumers: [constraints-and-grammar](./constraints-and-grammar.md) - Current consumers: [constraints-and-grammar](constraints-and-grammar.md)
(the `@table` PK/FK grammar direction) and (the `@table` PK/FK grammar direction) and
[indexing-and-point-lookup](./indexing-and-point-lookup.md) (the [indexing-and-point-lookup](indexing-and-point-lookup.md) (the
O(1) read-path slice iteration 22's numbers demand). O(1) read-path slice iteration 22's numbers demand).
Pair each card with [`docs/plan/exploration/linux/12-pwrite-fsync.md`](../linux/12-pwrite-fsync.md) for the actual syscalls — these cards are about *design patterns*, that one is about *kernel calls*. Pair each card with [`docs/plan/exploration/linux/12-pwrite-fsync.md`](../linux/12-pwrite-fsync.md) for the actual syscalls — these cards are about *design patterns*, that one is about *kernel calls*.

View file

@ -79,4 +79,4 @@ Slotted pages are the answer when those assumptions break. Until then, the frami
- `docs/plan/10-storage-foundations.md` (Rust-era, removed 2026-08-18) — record framing borrows the **header + checksum** pattern from `bufpage.h`. - `docs/plan/10-storage-foundations.md` (Rust-era, removed 2026-08-18) — record framing borrows the **header + checksum** pattern from `bufpage.h`.
- `docs/plan/12-engine-disk-cutover.md` (Rust-era, removed 2026-08-18) — when reading rows back from disk, CRC verification is the silent-corruption safety net the page header gives Postgres. - `docs/plan/12-engine-disk-cutover.md` (Rust-era, removed 2026-08-18) — when reading rows back from disk, CRC verification is the silent-corruption safety net the page header gives Postgres.
Pair with [`wal.md`](./wal.md) for the LSN convention and [`buffer-and-checkpoint.md`](./buffer-and-checkpoint.md) for the dirty-page semantics that pages need. Pair with [`wal.md`](wal.md) for the LSN convention and [`buffer-and-checkpoint.md`](buffer-and-checkpoint.md) for the dirty-page semantics that pages need.

View file

@ -1,7 +1,12 @@
# The `woc` diagnostic catalog — normative reference # The `woc` diagnostic catalog — normative reference
Every `WO-E###`/`WO-W###` code the `woc` front end (`compiler/`) actually Every `WO-E###`/`WO-W###` code the `woc` front end (`compiler/`) actually
emits, as of plan 2 tasks 2–8 and plan 3 tasks 1–2. Code ranges are reserved emits, as of plan 2 tasks 2–8, plan 3 tasks 1–2, and the iterations that
added codes afterwards — 9b (WO-E250), 15/17 (WO-E106–E109), the
language-surface-strictness branch (WO-E219/E220), the shard-fiber arc
(WO-E221/E222), 24 (WO-E226) and 36 (WO-E223). Re-swept against the source
2026-08-26; the eight codes that sweep found missing are now in the tables
below. Code ranges are reserved
per stage (`compiler/src/diag.ml`): `WO-E0xx` lexing, `WO-E1xx` parsing, per stage (`compiler/src/diag.ml`): `WO-E0xx` lexing, `WO-E1xx` parsing,
`WO-E2xx` types, `WO-E3xx` ownership, `WO-E4xx` the bytecode emitter, `WO-E2xx` types, `WO-E3xx` ownership, `WO-E4xx` the bytecode emitter,
`WO-W2xx` warnings from the types stage. This `WO-W2xx` warnings from the types stage. This
@ -26,6 +31,7 @@ half of the story ("moved here" / "borrowed here" / etc.).
| --- | --- | --- | | --- | --- | --- |
| WO-E001 | an input byte the lexer doesn't recognize as the start of any token. Reported once per bad byte, which is then skipped — one bad byte never stops the whole file. | `unknown character '$'` | | WO-E001 | an input byte the lexer doesn't recognize as the start of any token. Reported once per bad byte, which is then skipped — one bad byte never stops the whole file. | `unknown character '$'` |
| WO-E002 | a string literal's backslash escape is the last byte of the file, with no character left to escape (a plain unterminated string with no dangling backslash is *not* an error — rt parity). | `unterminated string escape` | | WO-E002 | a string literal's backslash escape is the last byte of the file, with no character left to escape (a plain unterminated string with no dangling backslash is *not* an error — rt parity). | `unterminated string escape` |
| WO-E003 | haxe-parity Task 8 (build flags). Misuse of the token-level `#if`/`#else`/`#end` filter: a `#if` with no flag name, a second `#else` in one section, a stray `#else`/`#end` with nothing open, a `#if` left open at end of file, or an unknown directive word. `Eof` always survives the filter so the parser still terminates after a reported error. Flags are NAMES only — no expressions — and are set with `woc -D <name>`. | ``unknown directive `#unless` — the build-flag directives are #if <flag>, #else, #end`` |
| WO-E004 | a raw text literal (backtick-delimited) that runs off the end of the file. Reported at the OPENING backtick — unlike a plain `"..."` string this is never silent, because multi-line is this form's normal case and a missing close would swallow every remaining line. | `unterminated raw text literal` | | WO-E004 | a raw text literal (backtick-delimited) that runs off the end of the file. Reported at the OPENING backtick — unlike a plain `"..."` string this is never silent, because multi-line is this form's normal case and a missing close would swallow every remaining line. | `unterminated raw text literal` |
| WO-E005 | a raw newline inside a `"..."` or `'...'` string. The scan stops at the newline without consuming it, so the `Newline` token still terminates the statement and only one line is lost. Multi-line text is spelled with a raw literal instead. | ``newline in string literal (use a `...` raw text literal for multi-line text)`` | | WO-E005 | a raw newline inside a `"..."` or `'...'` string. The scan stops at the newline without consuming it, so the `Newline` token still terminates the statement and only one line is lost. Multi-line text is spelled with a raw literal instead. | ``newline in string literal (use a `...` raw text literal for multi-line text)`` |
@ -38,6 +44,8 @@ half of the story ("moved here" / "borrowed here" / etc.).
| WO-E103 | haxe-parity Task 2. `inline fn ...` — the haxe keyword verdict table's own reject half of the `inline` row (`const` values are the adopted half). The whole declaration is discarded by the usual top-level recovery, same as any other bad declaration. | `` `inline fn` is rejected — optimization is the compiler's job `` | | WO-E103 | haxe-parity Task 2. `inline fn ...` — the haxe keyword verdict table's own reject half of the `inline` row (`const` values are the adopted half). The whole declaration is discarded by the usual top-level recovery, same as any other bad declaration. | `` `inline fn` is rejected — optimization is the compiler's job `` |
| WO-E106 | iteration 15 (deps, 2026-08-18). A dependency fetch/shape failure, driver-level: missing `git` binary, clone/checkout failure, a locked commit missing from the remote, cache/lock drift (a moved `rev` — the message names both SHAs and points at `woc --update-deps`), a fetched dep that is not a writeonce project, or a dep declaring its own `[deps]` (transitive — refused flat-only). One code; the message names the dependency and the failing step. | `` dependency `niceframework`: lock drift — wo.lock pins <sha> but .wo-deps has <sha> (a moved `rev`?); run `woc --update-deps` or remove .wo-deps/niceframework `` | | WO-E106 | iteration 15 (deps, 2026-08-18). A dependency fetch/shape failure, driver-level: missing `git` binary, clone/checkout failure, a locked commit missing from the remote, cache/lock drift (a moved `rev` — the message names both SHAs and points at `woc --update-deps`), a fetched dep that is not a writeonce project, or a dep declaring its own `[deps]` (transitive — refused flat-only). One code; the message names the dependency and the failing step. | `` dependency `niceframework`: lock drift — wo.lock pins <sha> but .wo-deps has <sha> (a moved `rev`?); run `woc --update-deps` or remove .wo-deps/niceframework `` |
| WO-E107 | iteration 15 (deps). A `[deps]` name collides with a local top-level module directory of the same name — `use <name>` would be ambiguous, so the build refuses instead of silently picking one. | `` dependency `niceframework` collides with the local module directory `niceframework/` `` | | WO-E107 | iteration 15 (deps). A `[deps]` name collides with a local top-level module directory of the same name — `use <name>` would be ambiguous, so the build refuses instead of silently picking one. | `` dependency `niceframework` collides with the local module directory `niceframework/` `` |
| WO-E108 | iteration 17 (library kind + `internal/`, 2026-08-20). A `use` path reaches a module under a dependency's `internal/` — Go's rule, enforced at the *consumer's* own `use` and only across the `[deps]` boundary: a library imports its own interior freely. Driver-level (`compiler/bin/main.ml`), reported at the offending `use`. | `` `serve/internal/parse` is internal to the dependency `serve` — a module under `internal/` is the library's own business and cannot be imported across the `[deps]` boundary `` |
| WO-E109 | iteration 17. An unknown `kind` value in `wo.toml`. Manifest-level: printed directly and exits 2 rather than joining the collector, the same as WO-E106/E107. `program` is the default, so every manifest written before the key existed stays byte-identical. | ``woc: wo.toml: error WO-E109: unknown `kind` value `lib` — expected "program" (the default) or "library"`` |
| WO-E105 | iteration 5 strictness (2026-08-18). A rejected Haxe keyword used where it would otherwise misparse — or, worst, compile clean (`return super.f()` used to): `extends`/`implements` after a class name, and `extends`/`implements`/`super`/`override`/`cast`/`Dynamic`/`untyped`/`macro`/`extern`/`operator` as an expression head or a top-level declaration head. Each cites the systems-track verdict table's doctrine reason. (`Dynamic`/`untyped` as a *type name* fire WO-E225 with the same doctrine message.) | `` `extends` is rejected: no inheritance, ever — is-a is a tagged union, has-a is composition, polymorphism is structural interfaces (principle 4) `` | | WO-E105 | iteration 5 strictness (2026-08-18). A rejected Haxe keyword used where it would otherwise misparse — or, worst, compile clean (`return super.f()` used to): `extends`/`implements` after a class name, and `extends`/`implements`/`super`/`override`/`cast`/`Dynamic`/`untyped`/`macro`/`extern`/`operator` as an expression head or a top-level declaration head. Each cites the systems-track verdict table's doctrine reason. (`Dynamic`/`untyped` as a *type name* fire WO-E225 with the same doctrine message.) | `` `extends` is rejected: no inheritance, ever — is-a is a tagged union, has-a is composition, polymorphism is structural interfaces (principle 4) `` |
| WO-E104 | iteration 7b. `@gc` on a class — the annotation is gone: GC-ness is inferred (structural cycles + demand promotion; `woc --dump-gc`). The annotation is skipped for recovery and the class classifies by inference. | `` `@gc` is not a valid annotation: GC-ness is inferred by the compiler (run `woc --dump-gc`). Remove it. `` | | WO-E104 | iteration 7b. `@gc` on a class — the annotation is gone: GC-ness is inferred (structural cycles + demand promotion; `woc --dump-gc`). The annotation is skipped for recovery and the class classifies by inference. | `` `@gc` is not a valid annotation: GC-ness is inferred by the compiler (run `woc --dump-gc`). Remove it. `` |
@ -64,6 +72,13 @@ half of the story ("moved here" / "borrowed here" / etc.).
| WO-E217 | haxe-parity Task 1 (modules). A qualified reference (`alias.name(...)`) names a real declaration in a real, `use`d module, but that declaration has no `pub` marker — private to its own module. | `` `hidden` is not `pub` in module `secret` `` | | WO-E217 | haxe-parity Task 1 (modules). A qualified reference (`alias.name(...)`) names a real declaration in a real, `use`d module, but that declaration has no `pub` marker — private to its own module. | `` `hidden` is not `pub` in module `secret` `` |
| WO-E218 | haxe-parity Task 1 (modules). A bare (unqualified) name resolves as `pub` in *more than one* used module — "collisions diagnose rather than shadow silently" (the plan's own words): resolution never silently picks a winner among used modules, it fails loudly and names every alias that matched. | `` `thing` is ambiguous — exported `pub` by more than one used module (a, b) `` | | WO-E218 | haxe-parity Task 1 (modules). A bare (unqualified) name resolves as `pub` in *more than one* used module — "collisions diagnose rather than shadow silently" (the plan's own words): resolution never silently picks a winner among used modules, it fails loudly and names every alias that matched. | `` `thing` is ambiguous — exported `pub` by more than one used module (a, b) `` |
| WO-E225 | a field's declared type name isn't a builtin scalar, a declared class (typedef records included), a declared interface, or (haxe-parity Task 4) a declared union. Also checked, same shape, on a payload variant's field types. A dotted name whose head is a reserved stdlib namespace (`json.Value`) is accepted as UNKNOWN-BUT-RESERVED — the same plan-9 convention `fs.stat(...)` calls get; any other dotted name is as unknown as a misspelling. Checked once per field declaration, at the field's own position. | `unknown type \`Wdiget\`` | | WO-E225 | a field's declared type name isn't a builtin scalar, a declared class (typedef records included), a declared interface, or (haxe-parity Task 4) a declared union. Also checked, same shape, on a payload variant's field types. A dotted name whose head is a reserved stdlib namespace (`json.Value`) is accepted as UNKNOWN-BUT-RESERVED — the same plan-9 convention `fs.stat(...)` calls get; any other dotted name is as unknown as a misspelling. Checked once per field declaration, at the field's own position. | `unknown type \`Wdiget\`` |
| WO-E219 | language-surface-strictness branch. A `pub(read)` field written from outside its declaring class — the marker means readable anywhere, writable only inside. Reported at the write site. | `` field `total` of class `Cart` is pub(read) — readable anywhere, writable only inside `Cart` `` |
| WO-E220 | language-surface-strictness branch. A name is both a real method of the receiver's class and a `using` extension in scope — an extension never overrides a method, so the collision is refused rather than silently resolved one way. | `` `render` is both a real method of `Page` and a `using` extension — rename one; an extension never overrides a method `` |
| WO-E221 | the shard-fiber arc (iteration 8+11). A `spawn C { ... }` whose target class declares no `fn receive(msg: M)`, or declares one whose `M` is not a class, record or union. `receive` is an ordinary identifier, not a keyword — declaring it is what makes a class an actor. | `` `spawn Worker { ... }`: no `fn receive` — an actor is a class with `fn receive(msg: M)` where M is a class, record, or union `` |
| WO-E222 | the shard-fiber arc. A traced (inferred-GC) type, or a type containing one, used as an actor message or as actor state. Aliased object graphs cannot cross shard heaps, and `spawn` placement makes every actor potentially remote, so this is refused statically rather than trapped at the send. | `` message type `Graph` is traced (or contains a traced class) — aliased graphs cannot cross shard heaps; spawn placement makes every actor potentially remote `` |
| WO-E223 | iteration 36 (operators). A literal shift count outside `0..63` on `<<` or `>>`. A non-literal count is not caught here — the VM traps it at run time (`WOP_SHL`/`WOP_SHR`). | ``shift count is out of range 0..63 for `<<` `` |
| WO-E226 | iteration 24 (`call`). `call`'s reply type has to survive actor-`M` erasure, so every `fn receive(msg: M)` program-wide must declare the SAME return type, and in v1 that type must be a copyable scalar. Fires when two `receive` declarations disagree, or when the agreed type is not scalar. | `` `call` on `actor Msg` needs one reply type, but `Room` and `Registry` declare different `receive` returns `` |
| WO-E250 | iteration 9b (the query surface). Every diagnostic the language-integrated query grammar raises, one code: a `from v in C` whose `C` is not a declared table class, a navigation source that is not a `backlink`/`multi` of a table class, and the two not-yet-supported clauses — `group … by … into` on a table query and on a navigation query. The group-by rows are why the clause parses and still cannot run. | ``group-by aggregation is not supported yet`` |
| WO-W202 *(warning)* | haxe-parity Task 1 (modules). A file's own `use` clause is never actually referenced — neither a bare name resolving through it nor a qualified `alias.name(...)` call — anywhere in that file's surviving parse tree. | `` unused `use fs` `` | | WO-W202 *(warning)* | haxe-parity Task 1 (modules). A file's own `use` clause is never actually referenced — neither a bare name resolving through it nor a qualified `alias.name(...)` call — anywhere in that file's surviving parse tree. | `` unused `use fs` `` |
| WO-W203 *(warning)* | haxe-parity Task 3 review fix (Critical 1). A `switch`'s `default` arm is not textually last — no longer a silent dead-code trap (`default` is lowered last regardless of source position, `Ast.switch_lowering_order`), but still surprising source; fired once per switch, at `default`'s own position. | `` `default` is not the last arm -- a `case` written after it still matches (this compiler evaluates `default` last regardless of source position), which reads as dead code `` | | WO-W203 *(warning)* | haxe-parity Task 3 review fix (Critical 1). A `switch`'s `default` arm is not textually last — no longer a silent dead-code trap (`default` is lowered last regardless of source position, `Ast.switch_lowering_order`), but still surprising source; fired once per switch, at `default`'s own position. | `` `default` is not the last arm -- a `case` written after it still matches (this compiler evaluates `default` last regardless of source position), which reads as dead code `` |
@ -165,3 +180,15 @@ enumeration, not a dig: `diag.ml` already reserves the numeric ranges,
so the only open question per stage was *which* reserved codes actually so the only open question per stage was *which* reserved codes actually
fire — answered by exhaustive grep, not inspection of a handful of fire — answered by exhaustive grep, not inspection of a handful of
samples. samples.
**The method is sound; re-running it is the maintenance.** The sweep was
not repeated between plan 3 and 2026-08-26, and eight codes accumulated
outside the table in that window (WO-E003, E108, E109, E219–E223, E226,
E250) — one of them, WO-E250, the only diagnostic the entire shipped query
surface raises. Note the extra indirection the re-sweep had to follow:
codes are built as `<stage>_prefix ^ "NN"`, so a grep for the literal
string `WO-E250` finds only the comment beside the constant, never the
emission. Sweep for `_prefix ^ "` and resolve each constant, plus the
handful of driver codes that `Printf.eprintf` a literal `WO-E1NN` and exit
2 without touching the collector at all (WO-E106/E107/E109). Any iteration
that adds a code adds its row here in the same change.

View file

@ -145,8 +145,8 @@ stop is not a trap and cannot be caught.
| concern | where it is normative | | concern | where it is normative |
| --- | --- | | --- | --- |
| shards, envelopes, ownership-move sends, WO-E221/E222, placement | [arc spec](../../superpowers/specs/2026-08-20-shard-fiber-arc-design.md) + [arc plan deviations](../../superpowers/plans/2026-08-20-shard-fiber-arc.md) | | shards, envelopes, ownership-move sends, WO-E221/E222, placement | [arc spec](../../superpowers/specs/2026-08-20-shard-fiber-arc-design.md) + [arc plan deviations](../../superpowers/plans/2026-08-20-shard-fiber-arc.md) |
| request/response, bounded mailboxes, actor death, timers | [iteration 31](../../stories/language-runtime-database/refine/31-actor-lifecycle.md) — not built yet | | request/response, bounded mailboxes, actor death, timers | [iteration 31](../../stories/language-runtime-database/31-actor-lifecycle.md) — not built yet |
| the DB actor (stage 3) | [story 8's guarantee contract](../../stories/language-runtime-database/done/08-shard-actor-runtime.md) — landed 2026-08-21 | | the DB actor (stage 3) | [story 8's guarantee contract](../../stories/language-runtime-database/08-shard-actor-runtime.md) — landed 2026-08-21 |
| builtin ids and their park behavior | [`08-builtin-surface.md`](08-builtin-surface.md) | | builtin ids and their park behavior | [`08-builtin-surface.md`](08-builtin-surface.md) |
## 7. Rejected alternatives — settled, argue against the reason ## 7. Rejected alternatives — settled, argue against the reason

View file

@ -7,33 +7,83 @@ incoming arrows is startable. This board carries the STATES.
The single place to learn where this project stands. Organised in six buckets: The single place to learn where this project stands. Organised in six buckets:
**stories** (the narrative arc), **in progress**, **done**, **pending**, **stories** (the narrative arc), **in progress**, **done**, **pending**,
**discarded**, **learnings**. The buckets are **sections of this board, not **discarded**, **learnings**. The buckets are **sections of this board, not
folders** — a doc stays where it was authored when its work lands; only its folders** — a doc stays where it was authored, and only its frontmatter, its
banner and this board change. ONE exception by directive (2026-08-20): banner and this board change.
story iteration files move physically — landed ones into
`stories/language-runtime-database/done/`, brainstorm-needing ones into
`refine/`, held ones into `hold/`, the active slice's stories into
`stories/language-runtime-database/in-progress/` (both 2026-08-21);
ready ones stay at the root. Second exception (2026-08-21): the active
slice's one marker doc lives in `docs/in-progress/` and is deleted when
the slice lands. Every plan and phase doc opens with a
`> **Status:**` banner linking back here; normative contracts
(`plan/oop-vm/`), exploration studies, reference docs and the
discarded/learnings registers carry none by design.
Update this board in the same change that finishes work — move the item to done **Status lives in frontmatter, nowhere else** (directive 2026-08-26). Every
with _what actually landed_, set the next in-progress item, and record any story iteration file sits flat in
rejection in [`discarded.md`](../plan/discarded.md) with its reason. [`language-runtime-database/`](language-runtime-database/00-story.md) and
carries `status:` in its YAML header; the active slice's marker doc sits flat
in `docs/`. **No directory anywhere encodes state.** This replaces the
2026-08-20/21 convention under which files moved between `done/`, `refine/`,
`hold/` and `in-progress/` — those folders are gone. A status change is now a
one-line edit, not a move, which is the point: the old scheme broke every
relative link in and to a file each time its status changed, and the two link
audits ([`00-link-audit.md`](../00-link-audit.md)) were mostly that.
Statuses: ✅ **done** · 🔄 **in progress** · ⬜ **pending** · ⏸ **hold** Every plan and phase doc opens with a `> **Status:**` banner linking back here;
normative contracts (`plan/oop-vm/`), exploration studies, reference docs and
the discarded/learnings registers carry none by design.
Story files carry YAML frontmatter (`iteration`/`status`/`chain`) — the Update this board in the same change that finishes work — set the item's
machine-readable truth behind this board; live Obsidian Dataview views: `status:`, record _what actually landed_ here, set the next in-progress item,
and record any rejection in [`discarded.md`](../plan/discarded.md) with its
reason.
Statuses, the closed set `status:` may take: `done` · `in-progress` · `refine`
(needs a brainstorm before it can be planned) · `hold`. This board renders them
as ✅ **done** · 🔄 **in progress** · ⬜ **pending** · ⏸ **hold**.
Story frontmatter (`iteration`/`status`/`chain`) is the machine-readable truth
behind this board; live Obsidian Dataview views:
[`board-views.md`](board-views.md) (Kanban = view only, never edits status). [`board-views.md`](board-views.md) (Kanban = view only, never edits status).
--- ---
## ▶ NEXT PLAN ## ▶ NEXT PLAN
### Landed 2026-08-25 — packaging + release pipeline (off-chain, no story)
**Implemented last time (2026-08-25):** the toolchain became installable
by a stranger. `VERSION` as the single source (0.1.0, asserted against
both binaries by `scripts/mkdist.sh`), `just dist` producing
`writeonce-<ver>-linux-amd64.tar.gz` + `.sha256` with
`scripts/install-readme.tmpl.md` inside it, `just install-accept`
proving a from-scratch project builds against the extracted tarball's
own binaries, and `.github/workflows/release.yml` publishing on a `v*`
tag push. Runbook: [`guides/releasing.md`](../guides/releasing.md).
**Key findings (measured, not asserted):** the build host's glibc caps
what the shipped binaries can import, and that cap becomes every user's
floor — so `runs-on` is `ubuntu-22.04` (2.35) deliberately, not
`ubuntu-latest`; built on this dev machine the binaries need
`GLIBC_2.38`, which would silently exclude Ubuntu 22.04, Debian 12 and
RHEL 9. `ocaml/setup-ocaml@v3` gives a compiler and opam but **not**
dune, and pinning `dune.3.14.0` is a downgrade the solver refuses — take
whatever it provides, since any dune ≥ 3.14 satisfies `(lang dune 3.14)`.
**Learned:** the asset filename is load-bearing. `/install` links one
exact URL, so the workflow asserts tag = `VERSION` = asset name and
fails rather than publishing a download button that 404s. A measurement
that only prints is not a gate — the glibc floor is printed from the
artefact about to ship, so the claim on the page can be checked against
a build log instead of trusted.
**Dependencies unblocked:** nothing in the chain; this is the
distribution seam. It does make `docs/examples/site`'s `/install` page
truthful, which iteration 37's site restructure had left pointing at an
asset nobody had built.
**Next steps:** the live slice is iteration 24, untouched by this. CI is
release-only — no workflow runs the gates per change, which remains the
open half of iteration 30 (observability, CI, fuzz — still no story
file).
**`.dev/reference` used:** none — GitHub Actions' own docs and the
runner images' glibc versions were the only sources.
---
### Landed 2026-08-25 — iteration 37, wo-html components (off-chain) ### Landed 2026-08-25 — iteration 37, wo-html components (off-chain)
**Implemented last time (2026-08-25):** iteration 37 CLOSED, both **Implemented last time (2026-08-25):** iteration 37 CLOSED, both
@ -78,11 +128,18 @@ no reference project was consulted for the implementation).
--- ---
**The concurrency + fiber chain — ✅ stage 3 → ✅ 22 → 31 → 24 → 23 → 32** **The concurrency + fiber chain — ✅ stage 3 → ✅ 22 → 🔄 24 (absorbing
(directive 2026-08-21). Next slice: **iteration 31, actor lifecycle** — 31 + 34) → 23 → 32.** The chain's original order put 31 before 24; the
its spec brainstorm is the next act (four forks in 2026-08-23 directive absorbed 31 INTO 24, and 34 resolved with it, so
[the story](language-runtime-database/refine/31-actor-lifecycle.md); those three are one slice. **The live slice is iteration 24** — spec and
the mailbox-cap/ring decisions now HAVE their mutex-inbox number). plan approved 2026-08-23, executing on branch `chat-ws-lifecycle`, five
of ten tasks landed. Its running state is the marker doc
([`2026-08-23-chat-ws-lifecycle.md`](../active-slice-2026-08-23-chat-ws-lifecycle.md)),
which is the file to read for what is done and what is next; stories
[31](language-runtime-database/31-actor-lifecycle.md) and
[34](language-runtime-database/34-crypto-builtins.md) keep
`status: refine` until 24's T10 closeout sets all three to `status: done`
together.
**Implemented last time (2026-08-21):** **iteration 22 landed — the **Implemented last time (2026-08-21):** **iteration 22 landed — the
measurement backbone exists and every performance claim is now measurement backbone exists and every performance claim is now
@ -122,7 +179,9 @@ numbers need a real disk.
machinery), and every future optimization (the gate that catches machinery), and every future optimization (the gate that catches
regressions is live). regressions is live).
**Next steps:** 31 (lifecycle spec brainstorm) → 24 (chat) → 23 **Next steps:** finish 24 (T4 `monitor` id 89, T5 `time.after` id 90 —
both still literal holes in `wob.h`'s builtin enum; then T8 the chat
sample, T9 its gate, T10 closeout setting 24/31/34 to `status: done`) → 23
(io_uring group-commit — target: close the 4.5k→297k durable gap) → (io_uring group-commit — target: close the 4.5k→297k durable gap) →
32 (WAL checkpoint). Held tail resumes on its own precedence notes. 32 (WAL checkpoint). Held tail resumes on its own precedence notes.
@ -234,7 +293,7 @@ itself, and all six landed:
quarantine warm-up. quarantine warm-up.
Plan: [`plan/compiler/2026-08-14-logwatcher-executable.md`](../plan/compiler/2026-08-14-logwatcher-executable.md) · Plan: [`plan/compiler/2026-08-14-logwatcher-executable.md`](../plan/compiler/2026-08-14-logwatcher-executable.md) ·
Story slice: [`docs/stories/language-runtime-database/done/07-logwatcher-proof.md`](language-runtime-database/done/07-logwatcher-proof.md) Story slice: [`docs/stories/language-runtime-database/07-logwatcher-proof.md`](language-runtime-database/07-logwatcher-proof.md)
**Deferred by name, with the measurement that says so:** **Deferred by name, with the measurement that says so:**
@ -268,39 +327,41 @@ that sequences its tasks. Read one, approve, then the next starts.
| # | Iteration | State | | # | Iteration | State |
| --- | -------------------------------------------------------------------------------------------- | ---------------------------- | ---- | | --- | -------------------------------------------------------------------------------------------- | ---------------------------- | ---- |
| 1 | [Principles doc](language-runtime-database/done/01-principles-doc.md) | ✅ | | 1 | [Principles doc](language-runtime-database/01-principles-doc.md) | ✅ |
| 2 | [VM core (`wovm`)](language-runtime-database/done/02-vm-core.md) | ✅ | | 2 | [VM core (`wovm`)](language-runtime-database/02-vm-core.md) | ✅ |
| 3 | [Compiler front (`woc`)](language-runtime-database/done/03-compiler-front.md) | ✅ (known gaps below) | | 3 | [Compiler front (`woc`)](language-runtime-database/03-compiler-front.md) | ✅ (known gaps below) |
| 4 | [Single binary end-to-end](language-runtime-database/done/04-single-binary-e2e.md) | ✅ (known gaps below) | | 4 | [Single binary end-to-end](language-runtime-database/04-single-binary-e2e.md) | ✅ (known gaps below) |
| 5 | [Language surface](language-runtime-database/done/05-language-surface.md) | 🔄 grammar done; **`?T` forced handling ✅ + reject rows ✅ + WO-E205 ✅ (2026-08-18)**; `pub(read)`/`using`/`#if` still ⏸ | | 5 | [Language surface](language-runtime-database/05-language-surface.md) | 🔄 grammar done; **`?T` forced handling ✅ + reject rows ✅ + WO-E205 ✅ (2026-08-18)**; `pub(read)`/`using`/`#if` still ⏸ |
| 6 | [Program mode + stdlib](language-runtime-database/done/06-program-mode-stdlib.md) | ✅ (the surface log-watcher uses) | | 6 | [Program mode + stdlib](language-runtime-database/06-program-mode-stdlib.md) | ✅ (the surface log-watcher uses) |
| 7 | [log-watcher proof](language-runtime-database/done/07-logwatcher-proof.md) | ✅ **landed 2026-08-15** — executable, not merely compilable: zero ASan leaks in all three modes, SIGTERM ends parked syscalls, fds flat, `LW_SOAK` gate; `just log-watcher` 7/0 | | 7 | [log-watcher proof](language-runtime-database/07-logwatcher-proof.md) | ✅ **landed 2026-08-15** — executable, not merely compilable: zero ASan leaks in all three modes, SIGTERM ends parked syscalls, fds flat, `LW_SOAK` gate; `just log-watcher` 7/0 |
| 7b | [Inferred GC + mark-sweep](language-runtime-database/done/07b-inferred-gc-mark-sweep.md) | ✅ **landed 2026-08-18** — `@gc` gone (WO-E104), GC-ness inferred, RC replaced by incremental mark-sweep, `.wob` v4; supersedes iteration 2's RC memory model | | 7b | [Inferred GC + mark-sweep](language-runtime-database/07b-inferred-gc-mark-sweep.md) | ✅ **landed 2026-08-18** — `@gc` gone (WO-E104), GC-ness inferred, RC replaced by incremental mark-sweep, `.wob` v4; supersedes iteration 2's RC memory model |
| 8 | [Shard-actor runtime](language-runtime-database/done/08-shard-actor-runtime.md) | ✅ **landed 2026-08-21** — the arc complete: stages 1+2 (fibers/budget/actors/io_uring plane, shards, envelopes, WO-E222) + stage 3's transparent DB actor (`just db-actor` 8/0, ASan/TSan clean, WAL replay pair) | | 8 | [Shard-actor runtime](language-runtime-database/08-shard-actor-runtime.md) | ✅ **landed 2026-08-21** — the arc complete: stages 1+2 (fibers/budget/actors/io_uring plane, shards, envelopes, WO-E222) + stage 3's transparent DB actor (`just db-actor` 8/0, ASan/TSan clean, WAL replay pair) |
| 9 | [Database engine](language-runtime-database/done/09-database-engine.md) | 🔄 engine complete (storage/WAL/indexes/insert-update-delete); reads land with 9b | | 9 | [Database engine](language-runtime-database/09-database-engine.md) | 🔄 engine complete (storage/WAL/indexes/insert-update-delete); reads land with 9b |
| 9b | [`@table`, relations, query](language-runtime-database/done/09b-table-relations-query.md) | 🔄 query surface + relations + FK done (branch query-surface); group-by parked | | 9b | [`@table`, relations, query](language-runtime-database/09b-table-relations-query.md) | 🔄 query surface + relations + FK done (branch query-surface); group-by parked |
| 19 | [Float + Bytes](language-runtime-database/done/19-missing-scalar-types.md) | ✅ **landed 2026-08-20** — `.wob` v5: Float constant tag, field kinds 6/7, opcodes 34-41 (IEEE-quiet f64), builtins 70-83. Full stack: literals, arithmetic, `@table` column, WAL bit-exact replay, json fractions in / shortest-round-trip out, `?Float` reserved-NaN nil, total-order index (NaN last, `-0.0` == `+0.0`), Bytes + base64. No implicit Int/Float mixing (WO-E201); `float`/`trunc` are the only bridges. Proof: web-app price is a real Float (`{"price":9.99}`), `just web-app` 23/0; corpus 103/0 | | 19 | [Float + Bytes](language-runtime-database/19-missing-scalar-types.md) | ✅ **landed 2026-08-20** — `.wob` v5: Float constant tag, field kinds 6/7, opcodes 34-41 (IEEE-quiet f64), builtins 70-83. Full stack: literals, arithmetic, `@table` column, WAL bit-exact replay, json fractions in / shortest-round-trip out, `?Float` reserved-NaN nil, total-order index (NaN last, `-0.0` == `+0.0`), Bytes + base64. No implicit Int/Float mixing (WO-E201); `float`/`trunc` are the only bridges. Proof: web-app price is a real Float (`{"price":9.99}`), `just web-app` 23/0; corpus 103/0 |
| 11 | [Fibers](language-runtime-database/done/11-fibers.md) | ✅ **landed 2026-08-21** with the arc (`just fibers` 10/0); fs-park re-scoped out of v1, disclosed in the story | | 11 | [Fibers](language-runtime-database/11-fibers.md) | ✅ **landed 2026-08-21** with the arc (`just fibers` 10/0); fs-park re-scoped out of v1, disclosed in the story |
| 22 | [Durability, throughput, scale](language-runtime-database/done/22-durability-throughput-scale.md) | ✅ **landed 2026-08-21** — db-bench + baseline.json (74 metrics) + restart/kill -9 proofs both shard counts; durable 4.5k vs ram 297k inserts/s, reads O(table), msgrate 13.4M/2.45M | | 22 | [Durability, throughput, scale](language-runtime-database/22-durability-throughput-scale.md) | ✅ **landed 2026-08-21** — db-bench + baseline.json (74 metrics) + restart/kill -9 proofs both shard counts; durable 4.5k vs ram 297k inserts/s, reads O(table), msgrate 13.4M/2.45M |
| 31 | [Actor lifecycle](language-runtime-database/refine/31-actor-lifecycle.md) | ⬜ needs a spec first — third in chain (story written 2026-08-21) | | 31 | [Actor lifecycle](language-runtime-database/31-actor-lifecycle.md) | 🔄 **absorbed into 24** (directive 2026-08-23) and half landed there: `call` request/response with a typed scalar reply (`WO_B_CALL = 88`, WO-E226), bounded mailboxes (`WO_MAILBOX`, cap 1024, catchable `WO_T_ACTOR`), and actor death that traps callers instead of hanging them. Still open: `monitor` and `time.after` — ids **89 and 90 are reserved holes** in `wob.h`, which is the machine-checkable proof of what is left. Supervision trees stay out of v1 |
| 24 | [chat: WebSocket workload](language-runtime-database/refine/24-chat-websocket-workload.md) | ⬜ fourth in chain — the arc's acceptance; after 31 | | 24 | [chat: WebSocket workload](language-runtime-database/24-chat-websocket-workload.md) | 🔄 **the live slice** (absorbing 31 + 34, directive 2026-08-23) — branch `chat-ws-lifecycle`, 5/10 tasks landed: crypto, bounded mailboxes, WS upgrade, frame codec, `call`/reply + actor death. Pending: `monitor`, `time.after`, the chat sample, its gate, closeout. State lives in [the marker](../active-slice-2026-08-23-chat-ws-lifecycle.md) |
| 23 | [io_uring group-commit](language-runtime-database/refine/23-io-uring-commit.md) | ⬜ fifth in chain, after stage 3 + 22 | | 23 | [io_uring group-commit](language-runtime-database/23-io-uring-commit.md) | ⬜ fifth in chain, after stage 3 + 22 |
| 32 | [WAL checkpoint](language-runtime-database/refine/32-wal-checkpoint.md) | ⬜ last in chain, after 23 — disk reclamation + bounded replay (story written 2026-08-21) | | 32 | [WAL checkpoint](language-runtime-database/32-wal-checkpoint.md) | ⬜ last in chain, after 23 — disk reclamation + bounded replay (story written 2026-08-21) |
| 33 | [Single-file store](language-runtime-database/refine/33-single-file-db.md) | ⬜ off-chain, small — `WO_DATA=<path>.db` file form; driver-only (story written 2026-08-22) | | 33 | [Single-file store](language-runtime-database/33-single-file-db.md) | ⬜ off-chain, small — `WO_DATA=<path>.db` file form; driver-only (story written 2026-08-22) |
| 34 | [Crypto builtins](language-runtime-database/refine/34-crypto-builtins.md) | ⬜ off-chain but GATES 24 (WS handshake needs SHA-1) — digests + HMAC as vector-verified C builtins (story written 2026-08-22) | | 34 | [Crypto builtins](language-runtime-database/34-crypto-builtins.md) | 🔄 **code landed** as 24's T1 (`d14fa9f`): `sha1`/`sha256`/`hmac_sha256`, ids 85–87 in `wob.h`, `runtime/src/crypto.c`, RFC/FIPS vectors 18/0, corpus pin. The 24 gate that once needed it is cleared. Frontmatter keeps `status: refine` only until 24's T10 closeout sets it to `done` |
| 37 | [wo-html components](language-runtime-database/done/37-wo-html-components.md) | ✅ off-chain — LANDED 2026-08-25. Raw text literal (backtick, margin stripped at lex time, `{{ }}` auto-escapes) + the component layer: `Component`/`render_all`/`Layout` in wo-html, `ok_html` moved into the framework, site and shop both migrated | | 38 | [Content platform capabilities](language-runtime-database/38-content-platform-capabilities.md) | ⬜ off-chain, needs a spec — the two capability families no iteration owns, confirmed against `runtime/src/wob.h`: `fs` mutation verbs (six fs builtins, ids 40–45; `append` creates-if-absent, so nothing is ever replaced, truncated, deleted or renamed) and `net.connect` (ids 51–55 + 91–95, no connect, and no `connect()` anywhere in `runtime/src/` — so no OIDC/SMTP/object-store/webhook/federation). Driven by a `docs/examples/vault` content-collaboration workload, in 28's mould. New builtins from 96 (89/90 reserved for 31); no `.wob` bump (`WOB_VERSION 6u`, last moved by 36). Story written 2026-08-26 from the "can it build a Nextcloud?" ask |
| 35 | [net runtime seams](language-runtime-database/done/35-net-runtime-seams.md) | ⬜ off-chain — fd deadlines on the park plane, Unix sockets, peer address; owns the ledger's three 🔧 rows (story written 2026-08-22) | | 39 | [Web framework parity](language-runtime-database/39-web-framework-parity.md) | ⬜ off-chain, needs a spec — from [the Fiber v3.5.0 study](../plan/exploration/fiber/00-fiber-parity.md) (all 32 of its middleware read against `writeonce-serve`; **nine already have a counterpart**). Leads with a **random-bytes builtin**: the framework ledger claimed CSRF/sessions were unblocked by iteration 34's HMAC, but HMAC authenticates a token and cannot mint one — there is no RNG anywhere in the runtime. Then cookies (absent both ways; `Resp.headers` being a map cannot carry two `Set-Cookie` lines), then limiter/idempotency (cheapest wins — `@table` + `time.ticks`, nothing new), sessions, CSRF, and the routing/response sugar. Streaming/SSE/compression, `@derive` binding, TTL cache, `proxy` and metrics all excluded with owners named |
| 20 | [Cross-program tables](language-runtime-database/hold/20-cross-program-tables.md) | ⏸ hold (2026-08-21); channel done (branch ipc-attach keeps its manifest) | | 37 | [wo-html components](language-runtime-database/37-wo-html-components.md) | ✅ off-chain — LANDED 2026-08-25. Raw text literal (backtick, margin stripped at lex time, `{{ }}` auto-escapes) + the component layer: `Component`/`render_all`/`Layout` in wo-html, `ok_html` moved into the framework, site and shop both migrated |
| 21 | [Keypair attach auth](language-runtime-database/hold/21-keypair-attach-auth.md) | ⏸ hold (2026-08-21); crypto+handshake done (branch keypair-auth keeps its manifest) | | 35 | [net runtime seams](language-runtime-database/35-net-runtime-seams.md) | ⬜ off-chain — fd deadlines on the park plane, Unix sockets, peer address; owns the ledger's three 🔧 rows (story written 2026-08-22) |
| 20 | [Cross-program tables](language-runtime-database/20-cross-program-tables.md) | ⏸ hold (2026-08-21); channel done (branch ipc-attach keeps its manifest) |
| 21 | [Keypair attach auth](language-runtime-database/21-keypair-attach-auth.md) | ⏸ hold (2026-08-21); crypto+handshake done (branch keypair-auth keeps its manifest) |
| 25 | [HTTP service layer](../superpowers/plans/2026-08-01-http-service-layer.md) | ⏸ hold (2026-08-21) — story file removed; the plan doc remains | | 25 | [HTTP service layer](../superpowers/plans/2026-08-01-http-service-layer.md) | ⏸ hold (2026-08-21) — story file removed; the plan doc remains |
| 26 | [Blue-green deploy](language-runtime-database/hold/26-blue-green-deploy.md) | ⏸ hold (2026-08-21) | | 26 | [Blue-green deploy](language-runtime-database/26-blue-green-deploy.md) | ⏸ hold (2026-08-21) |
| 27 | [Query grammar corpus](language-runtime-database/hold/27-query-grammar-corpus.md) | ⏸ hold (2026-08-21) | | 27 | [Query grammar corpus](language-runtime-database/27-query-grammar-corpus.md) | ⏸ hold (2026-08-21) |
| 28 | [skillhost host workload](language-runtime-database/hold/28-skillhost-host-workload.md) | ⏸ hold (2026-08-21); gaps recorded (branch query-grammar found skillhost needs no new query grammar) | | 28 | [skillhost host workload](language-runtime-database/28-skillhost-host-workload.md) | ⏸ hold (2026-08-21); gaps recorded (branch query-grammar found skillhost needs no new query grammar) |
| 29 | [Compile-time metaprogramming](language-runtime-database/hold/29-compile-time-metaprogramming.md) | ⏸ hold (2026-08-21) | | 29 | [Compile-time metaprogramming](language-runtime-database/29-compile-time-metaprogramming.md) | ⏸ hold (2026-08-21) |
| 15 | [deps: `wo.toml [deps]`](language-runtime-database/done/15-deps-package-manager.md) | ✅ **landed 2026-08-18** (branch web-framework): [deps] inline tables, git-binary fetch, wo.lock pinning, offline-when-locked, --update-deps, WO-E106/E107; `just deps-accept` 8/0 | | 15 | [deps: `wo.toml [deps]`](language-runtime-database/15-deps-package-manager.md) | ✅ **landed 2026-08-18** (branch web-framework): [deps] inline tables, git-binary fetch, wo.lock pinning, offline-when-locked, --update-deps, WO-E106/E107; `just deps-accept` 8/0 |
| 16 | [web framework](language-runtime-database/done/16-web-framework.md) | ✅ **landed 2026-08-19** — writeonce-framework (HTTP/1.1 + router + Handler/Middleware) consumed by web-app through [deps]; h2c parked (§C) behind 8/23/11. **v1 polish landed 2026-08-20** (branch framework-v1): get/post/put/delete_ helpers, 405+Allow, HEAD, Logging middleware, set_header; `just web-app` 16/0; fixed the interp-borrowed-field emitter crash en route. **Auth-in-core landed 2026-08-20**: http/auth.wo (Bearer/Basic, ct_eq, req.principal), web-app dogfoods BearerAuth, gate 17/0 | | 16 | [web framework](language-runtime-database/16-web-framework.md) | ✅ **landed 2026-08-19** — writeonce-framework (HTTP/1.1 + router + Handler/Middleware) consumed by web-app through [deps]; h2c parked (§C) behind 8/23/11. **v1 polish landed 2026-08-20** (branch framework-v1): get/post/put/delete_ helpers, 405+Allow, HEAD, Logging middleware, set_header; `just web-app` 16/0; fixed the interp-borrowed-field emitter crash en route. **Auth-in-core landed 2026-08-20**: http/auth.wo (Bearer/Basic, ct_eq, req.principal), web-app dogfoods BearerAuth, gate 17/0 |
| 17 | [library projects + `internal/`](language-runtime-database/done/17-library-projects-internal.md) | ✅ **landed 2026-08-20** — `kind = "library"` in `wo.toml` (default `program`, so every existing manifest is byte-identical; unknown value = WO-E109 exit 2); `woc <dir>` on a library runs the FULL pipeline entry-less and writes nothing, retiring iteration 16's `--emit` workaround; the no-entry build error names the kind; lib+bin dual works. Go's `internal/` rule as **WO-E108** at the consumer's own `use`, dep-boundary-only — the library imports its own interior freely. Framework reorganized: `internal/{parse,serve}.wo` behind the line, `http/form.wo` split out to keep `media_type`/`form_values` public. Driver-only change; VM/`.wob`/GC untouched. `just web-app` **26/0** (3 new checks), every standing gate unchanged | | 17 | [library projects + `internal/`](language-runtime-database/17-library-projects-internal.md) | ✅ **landed 2026-08-20** — `kind = "library"` in `wo.toml` (default `program`, so every existing manifest is byte-identical; unknown value = WO-E109 exit 2); `woc <dir>` on a library runs the FULL pipeline entry-less and writes nothing, retiring iteration 16's `--emit` workaround; the no-entry build error names the kind; lib+bin dual works. Go's `internal/` rule as **WO-E108** at the consumer's own `use`, dep-boundary-only — the library imports its own interior freely. Framework reorganized: `internal/{parse,serve}.wo` behind the line, `http/form.wo` split out to keep `media_type`/`form_values` public. Driver-only change; VM/`.wob`/GC untouched. `just web-app` **26/0** (3 new checks), every standing gate unchanged |
| 18 | [framework v2: memory-rich features](language-runtime-database/hold/18-memory-db-features.md) | ⏸ hold (2026-08-21); spec approved + plan authored, both held intact ([spec](../superpowers/specs/2026-08-20-memory-db-features-design.md), [plan](../superpowers/plans/2026-08-20-framework-v2-memory-features.md)): TTL cache + @table flags + durable job queue (drain-on-request) + `transaction { }` over the WAL's staged batch; pub/sub rejection expired with the arc (8/11 landed 2026-08-21) — revisit on unhold | | 18 | [framework v2: memory-rich features](language-runtime-database/18-memory-db-features.md) | ⏸ hold (2026-08-21); spec approved + plan authored, both held intact ([spec](../superpowers/specs/2026-08-20-memory-db-features-design.md), [plan](../superpowers/plans/2026-08-20-framework-v2-memory-features.md)): TTL cache + @table flags + durable job queue (drain-on-request) + `transaction { }` over the WAL's staged batch; pub/sub rejection expired with the arc (8/11 landed 2026-08-21) — revisit on unhold |
--- ---
@ -308,14 +369,16 @@ that sequences its tasks. Read one, approve, then the next starts.
| Track | Item | Where | | Track | Item | Where |
| -------- | --------------------------------------------------------------------------- | ---------------------------------------------------------- | | -------- | --------------------------------------------------------------------------- | ---------------------------------------------------------- |
| Language | 🔄 [iteration 36 — operator parity](language-runtime-database/in-progress/36-operator-parity.md): `not`, bitwise `& \| ^ << >>`, hex/binary/`_` literals, compound assigns — CODE LANDED 2026-08-22 (branch operator-parity, `.wob` v6, all gates green; reference project `.dev/reference/go` drove the design). Awaiting the developer's MANUAL pass on `docs/examples/operators/` (no test fixtures by directive); unblocks story 34's pure-`.wo` HMAC question | [plan](../superpowers/plans/2026-08-22-operator-parity.md) | | Language | 🔄 [iteration 36 — operator parity](language-runtime-database/36-operator-parity.md): `not`, bitwise `& \| ^ << >>`, hex/binary/`_` literals, compound assigns — CODE LANDED 2026-08-22 (branch operator-parity, `.wob` v6, all gates green; reference project `.dev/reference/go` drove the design). Awaiting the developer's MANUAL pass on `docs/examples/operators/` (no test fixtures by directive); unblocks story 34's pure-`.wo` HMAC question | [plan](../superpowers/plans/2026-08-22-operator-parity.md) |
| Language | the framework v1-polish slice landed 2026-08-20 (branch framework-v1, awaiting merge); next per the order: brainstorm 20/21's forks | [order](#implementation-order-re-sequenced-2026-08-21--concurrency-chain) | | Language | the framework v1-polish slice landed 2026-08-20 (branch framework-v1, awaiting merge); next per the order: brainstorm 20/21's forks | [order](#implementation-order-re-sequenced-2026-08-21--concurrency-chain) |
| Runtime | ✅ **iteration 35 landed 2026-08-23** (branch `framework-v1b`, with framework v1 slice 2 + the serving slice): net deadlines/unix/peer (ids 91–95), fiber pooling, serve_conn + web-app fiber-per-connection — web-app gate 41/0, both WO_IO backends | [design](../superpowers/specs/2026-08-23-net-seams-park-design.md) | | Runtime | ✅ **iteration 35 landed 2026-08-23** (branch `framework-v1b`, with framework v1 slice 2 + the serving slice): net deadlines/unix/peer (ids 91–95), fiber pooling, serve_conn + web-app fiber-per-connection — web-app gate 41/0, both WO_IO backends | [design](../superpowers/specs/2026-08-23-net-seams-park-design.md) |
| Runtime | 🔄 **iteration 24 (absorbing 31 + 34): chat + actor lifecycle** — spec + plan approved 2026-08-23 (24 absorbs 31 by directive; 34 resolved C-builtins); executing on branch `chat-ws-lifecycle` | [marker](../in-progress/2026-08-23-chat-ws-lifecycle.md) · [plan](../superpowers/plans/2026-08-23-chat-ws-lifecycle.md) | | Runtime | 🔄 **iteration 24 (absorbing 31 + 34): chat + actor lifecycle** — spec + plan approved 2026-08-23 (24 absorbs 31 by directive; 34 resolved C-builtins); executing on branch `chat-ws-lifecycle` | [marker](../active-slice-2026-08-23-chat-ws-lifecycle.md) · [plan](../superpowers/plans/2026-08-23-chat-ws-lifecycle.md) |
The active slice's marker doc lives in [`in-progress/`](../in-progress/) — The active slice's marker doc is
one file, deleted when the slice lands. Everything else pending is the [`docs/active-slice-2026-08-23-chat-ws-lifecycle.md`](../active-slice-2026-08-23-chat-ws-lifecycle.md)
concurrency chain (see *Pending* below); the held tail is in `hold/`. — one file, deleted when the slice lands. Everything else pending is the
concurrency chain (see *Pending* below); the held tail is every story
whose frontmatter reads `status: hold`.
### Landed 2026-08-14 — the compile-and-run milestone ### Landed 2026-08-14 — the compile-and-run milestone
@ -360,7 +423,7 @@ Gates at the end of that session: corpus 71/0, `woc` runtest 565/0, every
| Status | Item | Doc | What actually landed | | Status | Item | Doc | What actually landed |
| ------ | ------------------------------------ | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------ | ------------------------------------ | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ✅ | iteration 37 — wo-html components | [37](language-runtime-database/done/37-wo-html-components.md) | Two halves. **Grammar (2026-08-24):** the backtick raw text literal — content verbatim, common margin removed at lex time, `${ }` raw and `{{ }}` auto-escaping to a call on the `esc` in scope; WO-E004/WO-E005 added; lexer + one parser desugar only, nothing downstream. **Library (2026-08-25):** `Component`/`render_all`/`Layout` in wo-html, `ok_html` moved into `framework/http` beside `ok_text`/`ok_json`, site migrated onto `Layout` + a reused `ChapterNav`, shop onto `AppShell` + `multi Component` children with queries in the controllers; the site was then restructured onto the program template's MVC layout (model / layout / view modules / one controller per feature / bootstrap-only main). `multi Component` needs no wrapper record — the framework's Mw/Aw shape is not a language requirement. Gates: `just site` 11/0, `just web-app` 46/0, `woc-test` 556/0, `oop-e2e` 116/0 | | ✅ | iteration 37 — wo-html components | [37](language-runtime-database/37-wo-html-components.md) | Two halves. **Grammar (2026-08-24):** the backtick raw text literal — content verbatim, common margin removed at lex time, `${ }` raw and `{{ }}` auto-escaping to a call on the `esc` in scope; WO-E004/WO-E005 added; lexer + one parser desugar only, nothing downstream. **Library (2026-08-25):** `Component`/`render_all`/`Layout` in wo-html, `ok_html` moved into `framework/http` beside `ok_text`/`ok_json`, site migrated onto `Layout` + a reused `ChapterNav`, shop onto `AppShell` + `multi Component` children with queries in the controllers; the site was then restructured onto the program template's MVC layout (model / layout / view modules / one controller per feature / bootstrap-only main). `multi Component` needs no wrapper record — the framework's Mw/Aw shape is not a language requirement. Gates: `just site` 11/0, `just web-app` 46/0, `woc-test` 556/0, `oop-e2e` 116/0 |
| ✅ | Principles | [`../00-principles.md`](../00-principles.md) | 13 principles, each with a why and a link to the doc that enforces it | | ✅ | Principles | [`../00-principles.md`](../00-principles.md) | 13 principles, each with a why and a link to the doc that enforces it |
| ✅ | `wovm` VM core | [plan 1](../superpowers/plans/2026-08-01-wob-format-and-vm-core.md) | `.wob` v1 loader with full static validation, register interpreter (computed-goto + ISO-C fallback), arena with size-class free lists, borrow word, RC + budgeted Bacon–Rajan cycle collector, drop-map trap unwinding, containers, builtins, ICALL, CLI. 13 suites × 2 dispatch flavors + CLI smoke, ASan/UBSan clean | | ✅ | `wovm` VM core | [plan 1](../superpowers/plans/2026-08-01-wob-format-and-vm-core.md) | `.wob` v1 loader with full static validation, register interpreter (computed-goto + ISO-C fallback), arena with size-class free lists, borrow word, RC + budgeted Bacon–Rajan cycle collector, drop-map trap unwinding, containers, builtins, ICALL, CLI. 13 suites × 2 dispatch flavors + CLI smoke, ASan/UBSan clean |
| ✅ | `.wob` format contract | [`oop-vm/00-wob-format.md`](../plan/oop-vm/00-wob-format.md) | Normative; twinned with `runtime/src/wob.h` | | ✅ | `.wob` format contract | [`oop-vm/00-wob-format.md`](../plan/oop-vm/00-wob-format.md) | Normative; twinned with `runtime/src/wob.h` |
@ -514,7 +577,7 @@ precedence notes for resumption.
costs). It has never run — no `bench/baseline.json`, no `just db-bench`; costs). It has never run — no `bench/baseline.json`, no `just db-bench`;
the arc's stages 1+2 delta is recorded retroactively. the arc's stages 1+2 delta is recorded retroactively.
3. **31** — actor lifecycle 3. **31** — actor lifecycle
([story](language-runtime-database/refine/31-actor-lifecycle.md), ([story](language-runtime-database/31-actor-lifecycle.md),
written 2026-08-21): request/response (`send` is one-way and callers written 2026-08-21): request/response (`send` is one-way and callers
`sleep` to await), bounded mailboxes (the FIFO only grows), actor `sleep` to await), bounded mailboxes (the FIFO only grows), actor
death/supervision, timers beyond `time.sleep`. death/supervision, timers beyond `time.sleep`.
@ -523,7 +586,7 @@ precedence notes for resumption.
5. **23** — io_uring group-commit; the WAL's WRITE+FSYNC chains ride the 5. **23** — io_uring group-commit; the WAL's WRITE+FSYNC chains ride the
arc's per-shard ring (T4); after 22's baseline — the payoff, measured. arc's per-shard ring (T4); after 22's baseline — the payoff, measured.
6. **32** — WAL checkpoint 6. **32** — WAL checkpoint
([story](language-runtime-database/refine/32-wal-checkpoint.md), ([story](language-runtime-database/32-wal-checkpoint.md),
written 2026-08-21): the WAL is append-only forever — snapshot + written 2026-08-21): the WAL is append-only forever — snapshot +
truncate reclaims disk and bounds replay; after 23 (composes with truncate reclaims disk and bounds replay; after 23 (composes with
group-commit), policy set by 22's aged-store numbers. group-commit), policy set by 22's aged-store numbers.
@ -532,9 +595,9 @@ precedence notes for resumption.
story file); slots in when scheduled — nothing in the chain depends on it. story file); slots in when scheduled — nothing in the chain depends on it.
⏸ **Held** (2026-08-21, developer decision): 18, 20, 21, 25, 26, 27, 28, ⏸ **Held** (2026-08-21, developer decision): 18, 20, 21, 25, 26, 27, 28,
29 — stories in 29 — every story carrying `status: hold` in its frontmatter (25's story
[`hold/`](language-runtime-database/hold/) (25's story file file removed; its
removed; its [plan doc](../superpowers/plans/2026-08-01-http-service-layer.md) [plan doc](../superpowers/plans/2026-08-01-http-service-layer.md)
remains). Half-done branches (ipc-attach, keypair-auth) keep their remains). Half-done branches (ipc-attach, keypair-auth) keep their
manifests. manifests.

View file

@ -6,16 +6,23 @@ is the source of truth**:
```yaml ```yaml
--- ---
iteration: "8" # immutable id (string: "7b", "9b" exist) iteration: "8" # immutable id (string: "7b", "9b" exist)
status: in-progress # done | in-progress | refine | hold — mirrors its folder status: in-progress # done | in-progress | refine | hold — the ONLY place status lives
chain: 1 # concurrency-chain position, chain stories only (1–6) chain: 1 # concurrency-chain position, chain stories only (1–6)
--- ---
``` ```
The folder move IS the status change: moving a story between `done/`, **Editing `status:` IS the status change.** Story files sit flat in
`in-progress/`, `refine/`, `hold/` must update its `status:` in the same `docs/stories/language-runtime-database/`; no directory encodes state, so
change — the two never disagree. The prose board there is nothing to move and nothing that can disagree. This replaced the
([`00-status.md`](00-status.md)) stays the standup narrative; these 2026-08-20/21 folder scheme on **2026-08-26** — under that scheme a status
queries are the live views over the same facts. change moved the file, which broke every relative link in and to it, and
the repo's two link audits were largely the cleanup.
The closed set `status:` may take is `done`, `in-progress`, `refine` (needs
a brainstorm before it can be planned) and `hold`. A value outside it will
simply not appear in the lanes below, which is the cheapest possible
validation. The prose board ([`00-status.md`](00-status.md)) stays the
standup narrative; these queries are the live views over the same facts.
Adjust the `FROM` path to your vault root (queries below assume the Adjust the `FROM` path to your vault root (queries below assume the
vault opens at the repo root). vault opens at the repo root).
@ -60,7 +67,6 @@ WHERE status = "in-progress"
The Kanban plugin stores board state in its own markdown file — a The Kanban plugin stores board state in its own markdown file — a
second copy of status. To keep frontmatter the single source of truth: second copy of status. To keep frontmatter the single source of truth:
**use Dataview for querying; treat any Kanban board as a VIEW, never **use Dataview for querying; treat any Kanban board as a VIEW, never
the place status is edited.** Status changes happen by moving the story the place status is edited.** A status change is one edit to one
file between folders + updating its `status:` key (one commit); a `status:` key; a Kanban card drag that only rewrites the Kanban file is a
Kanban card drag that only rewrites the Kanban file is a lie the next lie the next query won't see.
query won't see.

View file

@ -79,47 +79,50 @@ still pending IS the runtime-concurrency chain; order:
measurement: a multi-shard program touching the database traps measurement: a multi-shard program touching the database traps
`WO_T_DB` today, and fixing that first lets one benchmark campaign `WO_T_DB` today, and fixing that first lets one benchmark campaign
cover single- and multi-shard honestly. cover single- and multi-shard honestly.
- **31 has its story file** ([refine/31-actor-lifecycle.md](refine/31-actor-lifecycle.md)); - **31 has its story file** ([31-actor-lifecycle.md](31-actor-lifecycle.md));
30 stays a row until it is scheduled. 30 stays a row until it is scheduled.
- **Holds landed** (developer decision, 2026-08-21): 18, 20, 21, 26, 27, - **Holds landed** (developer decision, 2026-08-21): 18, 20, 21, 26, 27,
28, 29 moved to `hold/`; 25's story file removed (its plan doc remains 28, 29 set to `status: hold`; 25's story file removed (its plan doc remains
in superpowers). 19 landed 2026-08-20. in superpowers). 19 landed 2026-08-20.
| Seq | # | Iteration | Delivers | | Seq | # | Iteration | Delivers |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| 1 | 1 | [Principles doc](done/01-principles-doc.md) | `docs/00-principles.md` — the doctrine page every later slice links back to | | 1 | 1 | [Principles doc](01-principles-doc.md) | `docs/00-principles.md` — the doctrine page every later slice links back to |
| 2 | 2 | [VM core](done/02-vm-core.md) | `wovm`: `.wob` loader, register interpreter, arena, borrow word, `@gc` collector | | 2 | 2 | [VM core](02-vm-core.md) | `wovm`: `.wob` loader, register interpreter, arena, borrow word, `@gc` collector |
| 3 | 3 | [Compiler front](done/03-compiler-front.md) | `woc`: lexer → parser → typechecker → ownership pass, diagnostics | | 3 | 3 | [Compiler front](03-compiler-front.md) | `woc`: lexer → parser → typechecker → ownership pass, diagnostics |
| 4 | 4 | [Single binary end-to-end](done/04-single-binary-e2e.md) | emitter + conformance corpus + `woc build` self-contained binary | | 4 | 4 | [Single binary end-to-end](04-single-binary-e2e.md) | emitter + conformance corpus + `woc build` self-contained binary |
| 5 | 5 | [Language surface](done/05-language-surface.md) | Haxe-parity adoptions, grammar + strictness halves (landed in waves through 2026-08-20) | | 5 | 5 | [Language surface](05-language-surface.md) | Haxe-parity adoptions, grammar + strictness halves (landed in waves through 2026-08-20) |
| 6 | 6 | [Program mode + stdlib](done/06-program-mode-stdlib.md) | `fn main`, exit codes, `fs`/`proc`/`net`/`time`/`json` builtins | | 6 | 6 | [Program mode + stdlib](06-program-mode-stdlib.md) | `fn main`, exit codes, `fs`/`proc`/`net`/`time`/`json` builtins |
| 7 | 7 | [log-watcher proof](done/07-logwatcher-proof.md) | the driving workload compiled, executable, soak-proven (landed 2026-08-15) | | 7 | 7 | [log-watcher proof](07-logwatcher-proof.md) | the driving workload compiled, executable, soak-proven (landed 2026-08-15) |
| 8 | 7b | [Inferred GC + mark-sweep](done/07b-inferred-gc-mark-sweep.md) | `@gc` removed; GC-ness inferred; RC replaced by incremental per-shard tri-color mark-sweep | | 8 | 7b | [Inferred GC + mark-sweep](07b-inferred-gc-mark-sweep.md) | `@gc` removed; GC-ness inferred; RC replaced by incremental per-shard tri-color mark-sweep |
| 9 | 9 | [Database engine](done/09-database-engine.md) | class-shaped tables, typed WAL + recovery, `insert`/`select` execute | | 9 | 9 | [Database engine](09-database-engine.md) | class-shaped tables, typed WAL + recovery, `insert`/`select` execute |
| 10 | 9b | [`@table`, relations, query](done/09b-table-relations-query.md) | `@table` real storage; `ref`/`backlink`/`multi`; compiler-checked queries | | 10 | 9b | [`@table`, relations, query](09b-table-relations-query.md) | `@table` real storage; `ref`/`backlink`/`multi`; compiler-checked queries |
| 11 | 15 | [deps: `wo.toml [deps]`](done/15-deps-package-manager.md) | exact-rev git deps + `wo.lock` + `.wo-deps`; flat-only, offline once locked | | 11 | 15 | [deps: `wo.toml [deps]`](15-deps-package-manager.md) | exact-rev git deps + `wo.lock` + `.wo-deps`; flat-only, offline once locked |
| 12 | 16 | [web framework](done/16-web-framework.md) | the `.wo` framework v1 (router, middleware, auth, all three body hooks) consumed via `[deps]` | | 12 | 16 | [web framework](16-web-framework.md) | the `.wo` framework v1 (router, middleware, auth, all three body hooks) consumed via `[deps]` |
| 13 | 8+11 | [Shard-actor runtime](done/08-shard-actor-runtime.md) · [Fibers](done/11-fibers.md) | ✅ **THE ARC LANDED 2026-08-21** — stages 1+2 (fibers/budget/actors/io_uring plane; pinned shards, envelope sends, home-routed frees, WO-E222) + stage 3's transparent DB actor: worker statements marshal to shard 0, ack-after-owner-fsync, materialized replies (`just db-actor` 8/0, ASan/TSan, WAL replay pair). fs-park re-scoped out (disclosed in story 11). | | 13 | 8+11 | [Shard-actor runtime](08-shard-actor-runtime.md) · [Fibers](11-fibers.md) | ✅ **THE ARC LANDED 2026-08-21** — stages 1+2 (fibers/budget/actors/io_uring plane; pinned shards, envelope sends, home-routed frees, WO-E222) + stage 3's transparent DB actor: worker statements marshal to shard 0, ack-after-owner-fsync, materialized replies (`just db-actor` 8/0, ASan/TSan, WAL replay pair). fs-park re-scoped out (disclosed in story 11). |
| 14 | 22 | [Durability, throughput, scale](done/22-durability-throughput-scale.md) | ✅ **LANDED 2026-08-21** — db-bench sample + `time.ticks` (builtin 84) + campaign driver + `bench/baseline.json` (74 metrics, tolerance-tuned); restart + 3× kill -9 proofs at both shard counts, gate bites. Measured: durable 4.5k vs ram 297k inserts/s (23's case); reads O(table) ≈1.5k/s (new candidate slice); mixread 1,280→21 ops/s single→multi (the arc's price); msgrate 13.4M/2.45M (deviation 4's number). *(was 9e)* | | 14 | 22 | [Durability, throughput, scale](22-durability-throughput-scale.md) | ✅ **LANDED 2026-08-21** — db-bench sample + `time.ticks` (builtin 84) + campaign driver + `bench/baseline.json` (74 metrics, tolerance-tuned); restart + 3× kill -9 proofs at both shard counts, gate bites. Measured: durable 4.5k vs ram 297k inserts/s (23's case); reads O(table) ≈1.5k/s (new candidate slice); mixread 1,280→21 ops/s single→multi (the arc's price); msgrate 13.4M/2.45M (deviation 4's number). *(was 9e)* |
| 15 | 30 | Observability, CI, fuzz *(no story file yet)* | **NEW** — runtime counters + a profiler hook, 22's harness wired to run per change instead of by hand, and a fuzz target on the parser and `.wob` loader. The whole proof-maturity gap had no iteration to point at. | | 15 | 30 | Observability, CI, fuzz *(no story file yet)* | **NEW** — runtime counters + a profiler hook, 22's harness wired to run per change instead of by hand, and a fuzz target on the parser and `.wob` loader. The whole proof-maturity gap had no iteration to point at. |
| 16 | 19 | [Float + Bytes](done/19-missing-scalar-types.md) | **LANDED 2026-08-20** — `.wob` v5; the full stack: IEEE-quiet f64 through literals/VM/@table/WAL/json + Bytes as the binary carrier, no implicit mixing, total-order indexes. Unblocks 24 (WS frames) and the crypto fork (digests). *(was 20)* | | 16 | 19 | [Float + Bytes](19-missing-scalar-types.md) | **LANDED 2026-08-20** — `.wob` v5; the full stack: IEEE-quiet f64 through literals/VM/@table/WAL/json + Bytes as the binary carrier, no implicit mixing, total-order indexes. Unblocks 24 (WS frames) and the crypto fork (digests). *(was 20)* |
| 17 | 31 | [Actor lifecycle](refine/31-actor-lifecycle.md) | request/response (today `send` is one-way and callers `sleep` to await), bounded mailboxes with backpressure (today the FIFO just grows), actor death/supervision, and timers beyond `time.sleep`. 24 cannot be written honestly without these. *(story written 2026-08-21)* | | 17 | 31 | [Actor lifecycle](31-actor-lifecycle.md) | request/response (today `send` is one-way and callers `sleep` to await), bounded mailboxes with backpressure (today the FIFO just grows), actor death/supervision, and timers beyond `time.sleep`. 24 cannot be written honestly without these. *(story written 2026-08-21)* |
| 18 | 24 | [chat: WebSocket workload](refine/24-chat-websocket-workload.md) | the arc's acceptance: WS upgrade + frames (SHA-1 via crypto fork, Bytes via 19), rooms/broadcast, 1k clients, drain-clean. *(was 19)* | | 18 | 24 | [chat: WebSocket workload](24-chat-websocket-workload.md) | the arc's acceptance: WS upgrade + frames (SHA-1 via crypto fork, Bytes via 19), rooms/broadcast, 1k clients, drain-clean. *(was 19)* |
| 19 | 23 | [io_uring group-commit](refine/23-io-uring-commit.md) | WAL WRITE+FSYNC chains on the arc's per-shard rings; fsync fallback kept (after 22 + the arc). *(was 9f)* | | 19 | 23 | [io_uring group-commit](23-io-uring-commit.md) | WAL WRITE+FSYNC chains on the arc's per-shard rings; fsync fallback kept (after 22 + the arc). *(was 9f)* |
| 20 | 32 | [WAL checkpoint](refine/32-wal-checkpoint.md) | **NEW 2026-08-21** (stage-3 guarantee refinement found the hole) — the WAL is append-only forever: snapshot + truncate reclaims disk and bounds replay time; every durability guarantee byte-identical; crash mid-checkpoint recovers from the previous snapshot + full tail. After 23 (composes with group-commit); RAM slot-reuse already contracted in `04-db-binding.md`. | | 20 | 32 | [WAL checkpoint](32-wal-checkpoint.md) | **NEW 2026-08-21** (stage-3 guarantee refinement found the hole) — the WAL is append-only forever: snapshot + truncate reclaims disk and bounds replay time; every durability guarantee byte-identical; crash mid-checkpoint recovers from the previous snapshot + full tail. After 23 (composes with group-commit); RAM slot-reuse already contracted in `04-db-binding.md`. |
| 21 | 33 | [Single-file store](refine/33-single-file-db.md) | **NEW 2026-08-22** — `WO_DATA=<path>.db`: a file path IS the wal (the store already lives in exactly one file; this makes the surface say so). Driver-only, independent of the chain; composes with 32's rename-swap. | | 21 | 33 | [Single-file store](33-single-file-db.md) | **NEW 2026-08-22** — `WO_DATA=<path>.db`: a file path IS the wal (the store already lives in exactly one file; this makes the surface say so). Driver-only, independent of the chain; composes with 32's rename-swap. |
| 22 | 34 | [Crypto builtins](refine/34-crypto-builtins.md) | **NEW 2026-08-22** — SHA-1/SHA-256/HMAC-SHA256 as C builtins over Bytes (no bitwise ops in the language, hand-rolled per doctrine, vector-verified). GATES 24's WS handshake; digest floor for held 21 and the ETag row. | | 22 | 34 | [Crypto builtins](34-crypto-builtins.md) | **NEW 2026-08-22** — SHA-1/SHA-256/HMAC-SHA256 as C builtins over Bytes (no bitwise ops in the language, hand-rolled per doctrine, vector-verified). GATES 24's WS handshake; digest floor for held 21 and the ETag row. |
| 23 | 37 | [wo-html components](done/37-wo-html-components.md) | **NEW 2026-08-23** — an MVC-shaped view layer in the wo-html LIBRARY (framework stays micro): structural `Component` interface (`render() -> Text`), layout components with slots, the site sample migrated as acceptance. Angular's component FORMAT studied and translated to server-rendered no-JS `.wo`; DI/bindings rejected. **LANDED 2026-08-25.** Raw text literal 2026-08-24 (backtick, verbatim content, margin stripped at lex time, `${ }` raw / `{{ }}` auto-escaping; WO-E004/WO-E005) — lexer plus one parser desugar, nothing downstream. Component layer 2026-08-25: `Component`/`render_all`/`Layout` in wo-html, `ok_html` moved into the framework, site and shop both migrated. | | 23 | 37 | [wo-html components](37-wo-html-components.md) | **NEW 2026-08-23** — an MVC-shaped view layer in the wo-html LIBRARY (framework stays micro): structural `Component` interface (`render() -> Text`), layout components with slots, the site sample migrated as acceptance. Angular's component FORMAT studied and translated to server-rendered no-JS `.wo`; DI/bindings rejected. **LANDED 2026-08-25.** Raw text literal 2026-08-24 (backtick, verbatim content, margin stripped at lex time, `${ }` raw / `{{ }}` auto-escaping; WO-E004/WO-E005) — lexer plus one parser desugar, nothing downstream. Component layer 2026-08-25: `Component`/`render_all`/`Layout` in wo-html, `ok_html` moved into the framework, site and shop both migrated. |
| 23 | 35 | [net runtime seams](done/35-net-runtime-seams.md) | **NEW 2026-08-22** — the ledger's three 🔧 rows owned: fd deadlines composing with the park plane, Unix-socket listeners, peer address (trusted-proxy check). Framework knobs stay framework slices; pairs naturally with 24 (dead-client eviction). | | 23 | 35 | [net runtime seams](35-net-runtime-seams.md) | **NEW 2026-08-22** — the ledger's three 🔧 rows owned: fd deadlines composing with the park plane, Unix-socket listeners, peer address (trusted-proxy check). Framework knobs stay framework slices; pairs naturally with 24 (dead-client eviction). |
| 23 | 36 | [operator parity](36-operator-parity.md) | **NEW 2026-08-22** — `not`, the five bitwise operators (`& \| ^ << >>`, Int-only, riding the additive/multiplicative rungs Go-style), hex/binary/`_` literals, and compound assigns wired to the written-out form. **Code landed 2026-08-22** on branch `operator-parity`: `.wob` v6, opcodes 42–46 with the 0..63 shift trap (WO-E223), all gates green; reference project `.dev/reference/go` drove the operator-precedence design. Awaiting the developer's MANUAL pass over `docs/examples/operators/` (no test fixtures, by directive). Unblocks story 34's pure-`.wo` HMAC question. |
| 24 | 25 | [HTTP service layer](../../superpowers/plans/2026-08-01-http-service-layer.md) | `service` blocks lower onto the framework (after 9b + 20 by their own precedence notes). **HELD 2026-08-21** — story file removed; the plan doc remains. *(was 10)* | | 24 | 25 | [HTTP service layer](../../superpowers/plans/2026-08-01-http-service-layer.md) | `service` blocks lower onto the framework (after 9b + 20 by their own precedence notes). **HELD 2026-08-21** — story file removed; the plan doc remains. *(was 10)* |
| 25 | 18 | [framework v2: memory-rich features](hold/18-memory-db-features.md) | spec+plan approved: TTL cache, @table flags, durable job queue, `transaction { }` over the WAL's staged batch. **Demoted from seq 14**: more surface on a framework with one consumer, and the cache still stores `Text` because there are no generics | | 25 | 18 | [framework v2: memory-rich features](18-memory-db-features.md) | spec+plan approved: TTL cache, @table flags, durable job queue, `transaction { }` over the WAL's staged batch. **Demoted from seq 14**: more surface on a framework with one consumer, and the cache still stores `Text` because there are no generics |
| 26 | 27 | [Query grammar corpus](hold/27-query-grammar-corpus.md) | grow the query grammar from real corpora; likely collapses to "confirm `len(query)` + add `exists`"; precedes 28. *(was 9g)* | | 26 | 27 | [Query grammar corpus](27-query-grammar-corpus.md) | grow the query grammar from real corpora; likely collapses to "confirm `len(query)` + add `exists`"; precedes 28. *(was 9g)* |
| 27 | 26 | [Blue-green deploy](hold/26-blue-green-deploy.md) | two VM slots, in-runtime compile, atomic switch, resident rollback (plan authored after 9 + 25). *(was 12)* | | 27 | 26 | [Blue-green deploy](26-blue-green-deploy.md) | two VM slots, in-runtime compile, atomic switch, resident rollback (plan authored after 9 + 25). *(was 12)* |
| 28 | 20 | [Cross-program tables](hold/20-cross-program-tables.md) | attach to a running program's database over local IPC; owner stays the single writer (channel half-built). **Demoted from seq 16**: new distribution surface while there is no TLS, no crypto, and the multi-shard DB still traps. *(was 9c)* | | 28 | 20 | [Cross-program tables](20-cross-program-tables.md) | attach to a running program's database over local IPC; owner stays the single writer (channel half-built). **Demoted from seq 16**: new distribution surface while there is no TLS, no crypto, and the multi-shard DB still traps. *(was 9c)* |
| 29 | 21 | [Keypair attach auth](hold/21-keypair-attach-auth.md) | program identity is a keypair; mutual challenge–response at attach (crypto half-built; plan folds into 20's). **Demoted with 20** — and it needs crypto primitives that do not exist. *(was 9d)* | | 29 | 21 | [Keypair attach auth](21-keypair-attach-auth.md) | program identity is a keypair; mutual challenge–response at attach (crypto half-built; plan folds into 20's). **Demoted with 20** — and it needs crypto primitives that do not exist. *(was 9d)* |
| 30 | 28 | [skillhost host workload](hold/28-skillhost-host-workload.md) | host-shaped driving workload naming runtime gaps — demoted with the framework goal. *(was 14)* | | 30 | 28 | [skillhost host workload](28-skillhost-host-workload.md) | host-shaped driving workload naming runtime gaps — demoted with the framework goal. *(was 14)* |
| 31 | 29 | [Compile-time metaprogramming](hold/29-compile-time-metaprogramming.md) | `@derive(...)` from class-table metadata; held with the parked drain by the 2026-08-08 scope directive. *(was 13)* | | 31 | 29 | [Compile-time metaprogramming](29-compile-time-metaprogramming.md) | `@derive(...)` from class-table metadata; held with the parked drain by the 2026-08-08 scope directive. *(was 13)* |
| ✅ | 17 | [library projects + `internal/`](done/17-library-projects-internal.md) | **LANDED 2026-08-20** — `kind = "library"` + entry-less check mode (retires the `--emit` workaround) and Go's `internal/` rule as WO-E108 at the consumer's `use`; driver-only, VM/GC untouched. `just web-app` 26/0 | | 32 | 38 | [Content platform capabilities](38-content-platform-capabilities.md) | **NEW 2026-08-26** — the two capability families nothing owns, read off `wob.h`: `fs` mutation (`write`/`remove`/`rename`/`mkdir` — the table holds exactly six fs builtins, ids 40–45, where `append` creates-if-absent and grows, so a file is never replaced, truncated, deleted or renamed) and `net.connect` (ids 51–55 + 91–95, no connect; no `connect()` call in `runtime/src/` at all — which is every identity/notification/federation story at once; 35 deferred connect-side timeouts as "no workload asks yet" — this is the ask). Driven by a `docs/examples/vault` content-collaboration workload in 28's mould; WebDAV, CRDT editing, previews and FTS each get a written verdict instead of an implication. New builtins start at 96 (89/90 are 31's reserved holes); no `.wob` bump. Off-chain, wants a spec. |
| 33 | 39 | [Web framework parity](39-web-framework-parity.md) | **NEW 2026-08-26** — gofiber/fiber v3.5.0 added as the web-framework reference and read end to end ([the study](../../plan/exploration/fiber/00-fiber-parity.md)); nine of its 32 middleware already have a `writeonce-serve` counterpart, so the gaps are breadth, not foundations — with one exception. The study's sharpest finding: the framework's own ledger said CSRF and sessions were UNBLOCKED because iteration 34 landed HMAC, but **there is no source of randomness in the runtime at all**, and an HMAC over a guessable session id is a signed guess. So 39 leads with a random-bytes builtin, then cookies (absent in both directions; `Resp.headers: map<Text,Text>` structurally cannot emit two `Set-Cookie` lines), then the store-backed chain — limiter and idempotency first since they need only `@table` + `time.ticks`. Off-chain, wants a spec. |
| ✅ | 17 | [library projects + `internal/`](17-library-projects-internal.md) | **LANDED 2026-08-20** — `kind = "library"` + entry-less check mode (retires the `--emit` workaround) and Go's `internal/` rule as WO-E108 at the consumer's `use`; driver-only, VM/GC untouched. `just web-app` 26/0 |
Review protocol: the developer reads one iteration, approves or amends; Review protocol: the developer reads one iteration, approves or amends;

View file

@ -6,7 +6,7 @@ status: done
# Iteration 1 — the principles doc # Iteration 1 — the principles doc
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
## Goals ## Goals

View file

@ -6,7 +6,7 @@ status: done
# Iteration 2 — VM core (`wovm`) # Iteration 2 — VM core (`wovm`)
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
## Goals ## Goals

View file

@ -6,7 +6,7 @@ status: done
# Iteration 3 — compiler front (`woc`) # Iteration 3 — compiler front (`woc`)
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
## Goals ## Goals

View file

@ -6,7 +6,7 @@ status: done
# Iteration 4 — single binary end-to-end # Iteration 4 — single binary end-to-end
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
## Goals ## Goals

View file

@ -6,7 +6,7 @@ status: done
# Iteration 5 — language surface (Haxe-parity adoptions) # Iteration 5 — language surface (Haxe-parity adoptions)
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> **Status: ✅ COMPLETE 2026-08-20.** The *grammar* half landed 2026-08-14 — > **Status: ✅ COMPLETE 2026-08-20.** The *grammar* half landed 2026-08-14 —

View file

@ -6,7 +6,7 @@ status: done
# Iteration 6 — program mode + systems stdlib # Iteration 6 — program mode + systems stdlib
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> **Status (2026-08-14):** ✅ landed for the surface the driving workload uses — > **Status (2026-08-14):** ✅ landed for the surface the driving workload uses —
@ -16,7 +16,7 @@ status: done
> `Proc` records, plus `json` encode/decode over `.wob` v2 class metadata. > `Proc` records, plus `json` encode/decode over `.wob` v2 class metadata.
> What the workload never calls was not written. Two lifetime defects found > What the workload never calls was not written. Two lifetime defects found
> here are being fixed as part of > here are being fixed as part of
> [`plan/compiler/2026-08-14-logwatcher-executable.md`](../../../plan/compiler/2026-08-14-logwatcher-executable.md): > [`plan/compiler/2026-08-14-logwatcher-executable.md`](../../plan/compiler/2026-08-14-logwatcher-executable.md):
> the runtime's own argv container is never freed, and blocking `accept`/`read` > the runtime's own argv container is never freed, and blocking `accept`/`read`
> ignore the stop signal. > ignore the stop signal.

View file

@ -6,7 +6,7 @@ status: done
# Iteration 7 — log-watcher proof workload # Iteration 7 — log-watcher proof workload
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
## Goals ## Goals
@ -24,7 +24,7 @@ status: done
> SIGTERM ends parked syscalls, fds flat across 200 requests, and the > SIGTERM ends parked syscalls, fds flat across 200 requests, and the
> `LW_SOAK` gate holds RSS/descriptors flat under sustained load. > `LW_SOAK` gate holds RSS/descriptors flat under sustained load.
> `just log-watcher`: 7 checks, 0 failures. The work is recorded in > `just log-watcher`: 7 checks, 0 failures. The work is recorded in
> [`plan/compiler/2026-08-14-logwatcher-executable.md`](../../../plan/compiler/2026-08-14-logwatcher-executable.md) > [`plan/compiler/2026-08-14-logwatcher-executable.md`](../../plan/compiler/2026-08-14-logwatcher-executable.md)
> and nothing else blocks this iteration. > and nothing else blocks this iteration.
## Acceptance Criteria ## Acceptance Criteria
@ -74,7 +74,7 @@ status: done
- The authoring plan (`docs/superpowers/plans/2026-08-01-log-watcher-sample.md`) - The authoring plan (`docs/superpowers/plans/2026-08-01-log-watcher-sample.md`)
is spent: the `.wo` files exist and compile. is spent: the `.wo` files exist and compile.
- What remains is - What remains is
[`plan/compiler/2026-08-14-logwatcher-executable.md`](../../../plan/compiler/2026-08-14-logwatcher-executable.md) [`plan/compiler/2026-08-14-logwatcher-executable.md`](../../plan/compiler/2026-08-14-logwatcher-executable.md)
— six tasks, every one traced to a measurement on this sample: the ownership — six tasks, every one traced to a measurement on this sample: the ownership
pass learning stdlib return types, dropping a projected temporary, the pass learning stdlib return types, dropping a projected temporary, the
runtime's own argv container, honouring the stop signal in blocking calls, runtime's own argv container, honouring the stop signal in blocking calls,

View file

@ -6,7 +6,7 @@ status: done
# Iteration 7b — inferred GC + incremental mark-sweep # Iteration 7b — inferred GC + incremental mark-sweep
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-11**, after the plan was first drawn — hence `7b` rather > **Inserted 2026-08-11**, after the plan was first drawn — hence `7b` rather
> than a renumber. It sits here because the log-watcher critical path > than a renumber. It sits here because the log-watcher critical path
@ -15,13 +15,13 @@ status: done
> **Status (2026-08-18): LANDED** (branch `inferred-gc`; plan > **Status (2026-08-18): LANDED** (branch `inferred-gc`; plan
> [`2026-08-18-inferred-gc-mark-sweep.md`](../../../superpowers/plans/2026-08-18-inferred-gc-mark-sweep.md)). > [`2026-08-18-inferred-gc-mark-sweep.md`](../../superpowers/plans/2026-08-18-inferred-gc-mark-sweep.md)).
> The front end infers GC-ness (structural SCC + demand promotion, > The front end infers GC-ness (structural SCC + demand promotion,
> `woc --dump-gc`), `@gc` in source is WO-E104, and the runtime's RC + > `woc --dump-gc`), `@gc` in source is WO-E104, and the runtime's RC +
> Bacon–Rajan collector is replaced by an incremental per-shard tri-color > Bacon–Rajan collector is replaced by an incremental per-shard tri-color
> mark-sweep with a Yuasa deletion barrier — `.wob` is v4, opcodes 27–28 > mark-sweep with a Yuasa deletion barrier — `.wob` is v4, opcodes 27–28
> reserved. The worked example is > reserved. The worked example is
> [`docs/examples/gc-cycle`](../../../examples/gc-cycle/README.md): its ring > [`docs/examples/gc-cycle`](../../examples/gc-cycle/README.md): its ring
> compiles with no annotation, runs, and is reclaimed in budgeted slices, > compiles with no annotation, runs, and is reclaimed in budgeted slices,
> ASan-clean. All four recorded `@gc`/RC defects are deleted by > ASan-clean. All four recorded `@gc`/RC defects are deleted by
> construction; `just oop-accept` is fully green (criterion 3's ASan clause > construction; `just oop-accept` is fully green (criterion 3's ASan clause
@ -93,11 +93,11 @@ status: done
## Info ## Info
- Governing spec: [`docs/superpowers/specs/2026-08-11-inferred-gc-mark-sweep-design.md`](../../../superpowers/specs/2026-08-11-inferred-gc-mark-sweep-design.md). - Governing spec: [`docs/superpowers/specs/2026-08-11-inferred-gc-mark-sweep-design.md`](../../superpowers/specs/2026-08-11-inferred-gc-mark-sweep-design.md).
- **Gated by the benchmark (2026-08-15):** this is the "implement garbage - **Gated by the benchmark (2026-08-15):** this is the "implement garbage
collection" lever of the performance arc — tri-color mark-sweep replacing collection" lever of the performance arc — tri-color mark-sweep replacing
RC changes the write path's tail latency, so landing it means re-running RC changes the write path's tail latency, so landing it means re-running
iteration [22](../done/22-durability-throughput-scale.md) and recording the iteration [22](22-durability-throughput-scale.md) and recording the
delta (does tracing help or hurt p99 under write load?). delta (does tracing help or hurt p99 under write load?).
- **Constraint added by the database track (2026-08-15):** a GC-managed value - **Constraint added by the database track (2026-08-15):** a GC-managed value
in a `@table` field is a compile error (the engine/heap bulkhead — 9b in a `@table` field is a compile error (the engine/heap bulkhead — 9b

View file

@ -7,7 +7,7 @@ chain: 1
# Iteration 8 — shard-actor runtime (the 8+11 concurrency arc, part 1) # Iteration 8 — shard-actor runtime (the 8+11 concurrency arc, part 1)
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **✅ LANDED 2026-08-21** — the arc is complete. Stage 3 closed the > **✅ LANDED 2026-08-21** — the arc is complete. Stage 3 closed the
> `WO_T_DB` hole: worker-shard DB statements marshal to the owner shard > `WO_T_DB` hole: worker-shard DB statements marshal to the owner shard
@ -33,8 +33,8 @@ chain: 1
> >
> **RE-SEQUENCED 2026-08-21** (developer decision): the brainstorm → > **RE-SEQUENCED 2026-08-21** (developer decision): the brainstorm →
> spec → plan happened. The arc's plan of record is > spec → plan happened. The arc's plan of record is
> [`2026-08-20-shard-fiber-arc.md`](../../../superpowers/plans/2026-08-20-shard-fiber-arc.md) > [`2026-08-20-shard-fiber-arc.md`](../../superpowers/plans/2026-08-20-shard-fiber-arc.md)
> (spec: [`2026-08-20-shard-fiber-arc-design.md`](../../../superpowers/specs/2026-08-20-shard-fiber-arc-design.md)), > (spec: [`2026-08-20-shard-fiber-arc-design.md`](../../superpowers/specs/2026-08-20-shard-fiber-arc-design.md)),
> and **stages 1+2 LANDED 2026-08-20** on branch `concurrency-arc` > and **stages 1+2 LANDED 2026-08-20** on branch `concurrency-arc`
> (T1–T6, seven disclosed deviations recorded in the plan). Remaining > (T1–T6, seven disclosed deviations recorded in the plan). Remaining
> scope: **stage 3, the transparent DB actor** — a correctness fix, not > scope: **stage 3, the transparent DB actor** — a correctness fix, not
@ -163,7 +163,7 @@ the slice's marker doc when it landed):
| Durability | ✅ fsync-per-commit, ack-after-durable; the ack crosses shards only AFTER the owner's fsync (`just db-actor`'s WAL pair). Power-loss rides fdatasync semantics; 22's kill battery is the scripted proof. | | Durability | ✅ fsync-per-commit, ack-after-durable; the ack crosses shards only AFTER the owner's fsync (`just db-actor`'s WAL pair). Power-loss rides fdatasync semantics; 22's kill battery is the scripted proof. |
| Crash recovery | ✅ boot replay, torn-tail drop, index rebuild; replay completes on the primary before any worker serves (main.c boots the engine before the shards). 22 scripts the restart proof. | | Crash recovery | ✅ boot replay, torn-tail drop, index rebuild; replay completes on the primary before any worker serves (main.c boots the engine before the shards). 22 scripts the restart proof. |
| Concurrency control | ✅ stage 3 — the DB actor serializes every statement; replies are materialized copies, no torn read by construction. Cross-statement snapshots arrive with 18. | | Concurrency control | ✅ stage 3 — the DB actor serializes every statement; replies are materialized copies, no torn read by construction. Cross-statement snapshots arrive with 18. |
| Space reclamation | RAM ✅ (deleted rows free their slot — ids never reused, slots are); disk ✖ → [story 32](../refine/32-wal-checkpoint.md), end of chain. | | Space reclamation | RAM ✅ (deleted rows free their slot — ids never reused, slots are); disk ✖ → [story 32](32-wal-checkpoint.md), end of chain. |
- The C proving ground (`docs/plan/exploration/c-runtime/`, phases A–F: - The C proving ground (`docs/plan/exploration/c-runtime/`, phases A–F:
epoll loops, eventfd mail) is the substrate this lifts into `wovm`. epoll loops, eventfd mail) is the substrate this lifts into `wovm`.
@ -172,19 +172,19 @@ the slice's marker doc when it landed):
- The VM's object header has carried a shard id since iteration 2 — no - The VM's object header has carried a shard id since iteration 2 — no
relayout. relayout.
- **Gated by the benchmark:** landing the arc means re-running - **Gated by the benchmark:** landing the arc means re-running
[22](../done/22-durability-throughput-scale.md) at the concurrency [22](22-durability-throughput-scale.md) at the concurrency
scale it unlocks and recording the before/after delta; it is also scale it unlocks and recording the before/after delta; it is also
where [23](../refine/23-io-uring-commit.md) gets a thread to overlap where [23](23-io-uring-commit.md) gets a thread to overlap
durability against. durability against.
## Proposed Solution ## Proposed Solution
Execute stage 3 of the plan of record Execute stage 3 of the plan of record
([`2026-08-20-shard-fiber-arc.md`](../../../superpowers/plans/2026-08-20-shard-fiber-arc.md)): ([`2026-08-20-shard-fiber-arc.md`](../../superpowers/plans/2026-08-20-shard-fiber-arc.md)):
the DB-actor migration — engine calls off the owner shard become message the DB-actor migration — engine calls off the owner shard become message
sends with parked replies, closing the `WO_T_DB` hole. Stages 1+2 sends with parked replies, closing the `WO_T_DB` hole. Stages 1+2
(scheduler, fibers, unified spawn/send, WO-E222 traced-send rejection, (scheduler, fibers, unified spawn/send, WO-E222 traced-send rejection,
cross-shard envelopes with home-routed frees) landed 2026-08-20. After cross-shard envelopes with home-routed frees) landed 2026-08-20. After
stage 3: 22 measures, then iteration 24 proves the arc. Actor lifecycle stage 3: 22 measures, then iteration 24 proves the arc. Actor lifecycle
(request/response, backpressure, supervision, timers) is deliberately (request/response, backpressure, supervision, timers) is deliberately
NOT the arc's scope — it is [iteration 31](../refine/31-actor-lifecycle.md). NOT the arc's scope — it is [iteration 31](31-actor-lifecycle.md).

View file

@ -6,7 +6,7 @@ status: done
# Iteration 9 — database engine # Iteration 9 — database engine
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
## Goals ## Goals
@ -59,7 +59,7 @@ status: done
counts references. The full analysis (row views as borrows without a counts references. The full analysis (row views as borrows without a
runtime net, cursor stability, GC-pause interaction) lives in the 9b runtime net, cursor stability, GC-pause interaction) lives in the 9b
design's section 6: design's section 6:
[`2026-08-15-table-relations-query-design.md`](../../../superpowers/specs/2026-08-15-table-relations-query-design.md). [`2026-08-15-table-relations-query-design.md`](../../superpowers/specs/2026-08-15-table-relations-query-design.md).
## Proposed Solution ## Proposed Solution

View file

@ -6,7 +6,7 @@ status: done
# Iteration 9b — `@table`, relations, and language-integrated query # Iteration 9b — `@table`, relations, and language-integrated query
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-11**, hence `9b` rather than a renumber. It follows > **Inserted 2026-08-11**, hence `9b` rather than a renumber. It follows
> iteration 9 because a query surface needs tables that actually execute, and > iteration 9 because a query surface needs tables that actually execute, and
@ -14,13 +14,13 @@ status: done
> results. > results.
> >
> **Spec exists (2026-08-15):** > **Spec exists (2026-08-15):**
> [`2026-08-15-table-relations-query-design.md`](../../../superpowers/specs/2026-08-15-table-relations-query-design.md) > [`2026-08-15-table-relations-query-design.md`](../../superpowers/specs/2026-08-15-table-relations-query-design.md)
> settles the three forks recorded in *Info* below (kept as the decision > settles the three forks recorded in *Info* below (kept as the decision
> record): the SQL/Cypher layer is superseded as the program surface, > record): the SQL/Cypher layer is superseded as the program surface,
> the syntax is a compiler-desugared comprehension, and the references > the syntax is a compiler-desugared comprehension, and the references
> contribute vocabulary + semantics (System.Linq) and execution + integrity > contribute vocabulary + semantics (System.Linq) and execution + integrity
> vocabulary (PostgreSQL, surveyed with the spec). Plan: > vocabulary (PostgreSQL, surveyed with the spec). Plan:
> [`2026-08-15-employee-relations-query.md`](../../../plan/compiler/2026-08-15-employee-relations-query.md). > [`2026-08-15-employee-relations-query.md`](../../plan/compiler/2026-08-15-employee-relations-query.md).
## Goals ## Goals

View file

@ -7,7 +7,7 @@ chain: 1
# Iteration 11 — fibers (the 8+11 concurrency arc, part 2) # Iteration 11 — fibers (the 8+11 concurrency arc, part 2)
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **✅ LANDED 2026-08-21** with the arc's stage 3 (fiber substance landed > **✅ LANDED 2026-08-21** with the arc's stage 3 (fiber substance landed
> stages 1+2; stage 3 added the plane-less reply park `WO_PARK_INBOX` — > stages 1+2; stage 3 added the plane-less reply park `WO_PARK_INBOX` —
@ -26,7 +26,7 @@ chain: 1
> The spawn-surface question below is SETTLED: one unified actor-address > The spawn-surface question below is SETTLED: one unified actor-address
> surface (`spawn` returns an address, fiber or remote alike; `send` > surface (`spawn` returns an address, fiber or remote alike; `send`
> moves ownership; same-heap sends take the cheap path). The arc's > moves ownership; same-heap sends take the cheap path). The arc's
> driving workload is [iteration 24: chat](../refine/24-chat-websocket-workload.md) > driving workload is [iteration 24: chat](24-chat-websocket-workload.md)
> — fiber-per-WebSocket-connection is the serving model that retires the > — fiber-per-WebSocket-connection is the serving model that retires the
> framework's close-when-idle keep-alive policy. > framework's close-when-idle keep-alive policy.
> >
@ -43,7 +43,7 @@ chain: 1
> move, round-robin placement, home-routed frees, WO-E222 on EVERY > move, round-robin placement, home-routed frees, WO-E222 on EVERY
> spawn/send (placement makes any actor potentially remote), TID-verified > spawn/send (placement makes any actor potentially remote), TID-verified
> shard context. Deviations disclosed in the plan of record > shard context. Deviations disclosed in the plan of record
> ([`2026-08-20-shard-fiber-arc.md`](../../../superpowers/plans/2026-08-20-shard-fiber-arc.md)): > ([`2026-08-20-shard-fiber-arc.md`](../../superpowers/plans/2026-08-20-shard-fiber-arc.md)):
> the inbox is a mutex-guarded list + eventfd (rings arrive only if > the inbox is a mutex-guarded list + eventfd (rings arrive only if
> iteration 22 measures the mutex as a cost), the deterministic corpus > iteration 22 measures the mutex as a cost), the deterministic corpus
> pins `WO_SHARDS=1`, two TSan races and one teardown SEGV fixed. > pins `WO_SHARDS=1`, two TSan races and one teardown SEGV fixed.
@ -113,13 +113,13 @@ chain: 1
- Normative contract (added 2026-08-21): stackless coroutine model, - Normative contract (added 2026-08-21): stackless coroutine model,
park/resume protocols, the no-`async` rule — park/resume protocols, the no-`async` rule —
[`docs/plan/oop-vm/03-concurrency-coroutines.md`](../../../plan/oop-vm/03-concurrency-coroutines.md). [`docs/plan/oop-vm/03-concurrency-coroutines.md`](../../plan/oop-vm/03-concurrency-coroutines.md).
- Research note: [`docs/plan/exploration/fibers/00-fibers.md`](../../../plan/exploration/fibers/00-fibers.md) - Research note: [`docs/plan/exploration/fibers/00-fibers.md`](../../plan/exploration/fibers/00-fibers.md)
— kernel's-eye evidence (task_struct costs, CFS collapse at high task — kernel's-eye evidence (task_struct costs, CFS collapse at high task
counts) and the precedent survey (BEAM reductions adopted; Go stack counts) and the precedent survey (BEAM reductions adopted; Go stack
copying and Tokio coloring rejected; Loom's park-under-blocking-API copying and Tokio coloring rejected; Loom's park-under-blocking-API
matches the stdlib posture). matches the stdlib posture).
- Vision origin: [blue-green vision §3](../../../plan/exploration/blue-green-vm/00-vision.md); - Vision origin: [blue-green vision §3](../../plan/exploration/blue-green-vm/00-vision.md);
iteration 8's scheduler is the substrate this extends. iteration 8's scheduler is the substrate this extends.
- Open questions — REDUCED AGAIN 2026-08-21: the spawn surface, budget - Open questions — REDUCED AGAIN 2026-08-21: the spawn surface, budget
size/granularity (back-edge accounting — see the plan's livelock size/granularity (back-edge accounting — see the plan's livelock
@ -128,13 +128,13 @@ chain: 1
implementation. Still open: how a parked fiber's borrow state interacts implementation. Still open: how a parked fiber's borrow state interacts
with the shard's GC safepoints — tracked for stage 3 / the collector's with the shard's GC safepoints — tracked for stage 3 / the collector's
next pass. Lifecycle surface (request/response, backpressure, next pass. Lifecycle surface (request/response, backpressure,
supervision, timers) is [iteration 31](../refine/31-actor-lifecycle.md)'s, not supervision, timers) is [iteration 31](31-actor-lifecycle.md)'s, not
this story's. this story's.
## Proposed Solution ## Proposed Solution
- The plan exists and its fiber stages are landed: - The plan exists and its fiber stages are landed:
[`2026-08-20-shard-fiber-arc.md`](../../../superpowers/plans/2026-08-20-shard-fiber-arc.md) [`2026-08-20-shard-fiber-arc.md`](../../superpowers/plans/2026-08-20-shard-fiber-arc.md)
(stages 1+2 complete 2026-08-20). This story closes when the arc's (stages 1+2 complete 2026-08-20). This story closes when the arc's
stage 3 lands and iteration 22 records the delta; the chat workload stage 3 lands and iteration 22 records the delta; the chat workload
([iteration 24](../refine/24-chat-websocket-workload.md)) is the proof. ([iteration 24](24-chat-websocket-workload.md)) is the proof.

View file

@ -6,7 +6,7 @@ status: done
# Iteration 15 — dependencies: `wo.toml [deps]`, git fetch, `wo.lock` # Iteration 15 — dependencies: `wo.toml [deps]`, git fetch, `wo.lock`
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-18. LANDED the same day** (branch `web-framework`): all > **Inserted 2026-08-18. LANDED the same day** (branch `web-framework`): all
> acceptance criteria met — `just deps-accept` 8/0 (cold fetch + lock, use > acceptance criteria met — `just deps-accept` 8/0 (cold fetch + lock, use
@ -16,9 +16,9 @@ status: done
> The enabler for code shared between writeonce repositories — the web > The enabler for code shared between writeonce repositories — the web
> framework (iteration 16) is the driving consumer. > framework (iteration 16) is the driving consumer.
> >
> **Spec exists:** [`2026-08-18-web-framework-design.md`](../../../superpowers/specs/2026-08-18-web-framework-design.md) > **Spec exists:** [`2026-08-18-web-framework-design.md`](../../superpowers/specs/2026-08-18-web-framework-design.md)
> section A is normative for this iteration. **Plan:** > section A is normative for this iteration. **Plan:**
> [`2026-08-18-deps-package-manager.md`](../../../superpowers/plans/2026-08-18-deps-package-manager.md) > [`2026-08-18-deps-package-manager.md`](../../superpowers/plans/2026-08-18-deps-package-manager.md)
> (5 tasks: manifest inline-table + [deps]; resolver fetch/cache/lock; > (5 tasks: manifest inline-table + [deps]; resolver fetch/cache/lock;
> multi-root discovery + module mapping + entry restriction; the > multi-root discovery + module mapping + entry restriction; the
> `just deps-accept` gate over file:// remotes; docs closeout). > `just deps-accept` gate over file:// remotes; docs closeout).

View file

@ -6,7 +6,7 @@ status: done
# Iteration 16 — the web framework: a `.wo` library, HTTP/1.1 behind a proxy # Iteration 16 — the web framework: a `.wo` library, HTTP/1.1 behind a proxy
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-18. LANDED 2026-08-19** (branch `web-framework`): > **Inserted 2026-08-18. LANDED 2026-08-19** (branch `web-framework`):
> `just web-app` 14/0 — deps chain, auth middleware, CRUD with @unique 409 / > `just web-app` 14/0 — deps chain, auth middleware, CRUD with @unique 409 /
@ -66,9 +66,9 @@ status: done
> in the toolchain. h2c is the parked successor (after iterations 8/23/11, > in the toolchain. h2c is the parked successor (after iterations 8/23/11,
> when multiplexing has a scheduler to pay off on). > when multiplexing has a scheduler to pay off on).
> >
> **Spec exists:** [`2026-08-18-web-framework-design.md`](../../../superpowers/specs/2026-08-18-web-framework-design.md) > **Spec exists:** [`2026-08-18-web-framework-design.md`](../../superpowers/specs/2026-08-18-web-framework-design.md)
> sections B (normative) and C (the parked h2c successor). **Plan:** > sections B (normative) and C (the parked h2c successor). **Plan:**
> [`2026-08-19-web-framework.md`](../../../superpowers/plans/2026-08-19-web-framework.md) > [`2026-08-19-web-framework.md`](../../superpowers/plans/2026-08-19-web-framework.md)
> (6 tasks: types/builders; HTTP/1.1 parse+serve; router+interfaces+App; > (6 tasks: types/builders; HTTP/1.1 parse+serve; router+interfaces+App;
> the web-app storefront; the `just web-app` gate; docs closeout). Two > the web-app storefront; the `just web-app` gate; docs closeout). Two
> enabling risks retired before planning: interface-field dispatch (probe) > enabling risks retired before planning: interface-field dispatch (probe)

View file

@ -6,7 +6,7 @@ status: done
# Iteration 17 — library projects and dependency privacy (`kind`, `internal/`) # Iteration 17 — library projects and dependency privacy (`kind`, `internal/`)
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-20, needs further refinement** (developer decision: keep > **Inserted 2026-08-20, needs further refinement** (developer decision: keep
> as an iteration, do not implement yet). > as an iteration, do not implement yet).

View file

@ -7,12 +7,12 @@ status: hold
> **Scope label (2026-08-20): this iteration is FRAMEWORK V2.** Framework > **Scope label (2026-08-20): this iteration is FRAMEWORK V2.** Framework
> v1 is the transport/routing/body/security surface tracked in the > v1 is the transport/routing/body/security surface tracked in the
> [framework README's status ledger](../../../examples/writeonce-serve/README.md); > [framework README's status ledger](../../examples/writeonce-serve/README.md);
> v2 is what the embedded store adds on top. v1 gaps land before or > v2 is what the embedded store adds on top. v1 gaps land before or
> alongside v2 as slices, per the ledger. > alongside v2 as slices, per the ledger.
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-20, forks settled the same day** (developer decisions > **Inserted 2026-08-20, forks settled the same day** (developer decisions
> below). Next step: spec + plan, implementation on approval — the > below). Next step: spec + plan, implementation on approval — the
@ -119,6 +119,17 @@ worked around.
reads it, **then** the cached read reflects the write (bump reads it, **then** the cached read reflects the write (bump
invalidation), and `just web-app` stays green throughout. invalidation), and `just web-app` stays green throughout.
> **Not this iteration (noted 2026-08-26):** the Fiber v3.5.0 parity study
> ([`plan/exploration/fiber/00-fiber-parity.md`](../../plan/exploration/fiber/00-fiber-parity.md))
> found a second axis of framework gaps — cookies, sessions, CSRF, rate
> limiting, idempotency, request ids, response helpers. Those went to
> [iteration 39](39-web-framework-parity.md) rather than here, deliberately:
> this iteration's spec is approved for the memory-rich half (TTL cache,
> `@table` flags, durable job queue, `transaction { }`) and widening it would
> invalidate that approval. The one overlap is the **TTL cache**, which stays
> this iteration's — 39's out-of-scope list points back here for it.
## Out Of Scope ## Out Of Scope
Pub/sub and WebSockets (behind 8/11); streaming job payloads; cross-node Pub/sub and WebSockets (behind 8/11); streaming job payloads; cross-node

View file

@ -6,7 +6,7 @@ status: done
# Iteration 19 — the missing scalar types: Float and Bytes # Iteration 19 — the missing scalar types: Float and Bytes
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-20, forks settled the same day** (developer > **Inserted 2026-08-20, forks settled the same day** (developer
> decisions below). Next: spec, then plan — a new storage kind touches > decisions below). Next: spec, then plan — a new storage kind touches
@ -14,7 +14,7 @@ status: done
> >
> **LANDED 2026-08-20.** Both scalars shipped full-stack as `.wob` v5. The > **LANDED 2026-08-20.** Both scalars shipped full-stack as `.wob` v5. The
> format decisions the story asked a spec for are recorded normatively in > format decisions the story asked a spec for are recorded normatively in
> [`docs/plan/oop-vm/00-wob-format.md`](../../../plan/oop-vm/00-wob-format.md) > [`docs/plan/oop-vm/00-wob-format.md`](../../plan/oop-vm/00-wob-format.md)
> §"v5: Float and Bytes" — written in the same change as the code, per this > §"v5: Float and Bytes" — written in the same change as the code, per this
> iteration's own last acceptance criterion — with the reasoning-under-the-code > iteration's own last acceptance criterion — with the reasoning-under-the-code
> in `runtime/src/CODE-LOGIC.md` and `compiler/src/CODE-LOGIC.md`. No separate > in `runtime/src/CODE-LOGIC.md` and `compiler/src/CODE-LOGIC.md`. No separate

View file

@ -6,7 +6,7 @@ status: hold
# Iteration 20 — cross-program tables: attach to a running program's database # Iteration 20 — cross-program tables: attach to a running program's database
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-15**, hence `20`. It follows 9b because a program > **Inserted 2026-08-15**, hence `20`. It follows 9b because a program
> attaching to another's tables wants the same typed statements and queries > attaching to another's tables wants the same typed statements and queries

View file

@ -6,7 +6,7 @@ status: hold
# Iteration 21 — keypair authentication for cross-program attach # Iteration 21 — keypair authentication for cross-program attach
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-15.** Promotes iteration 20's identity fork (Info, > **Inserted 2026-08-15.** Promotes iteration 20's identity fork (Info,
> fork 3) to its own iteration: the name + unix-uid lean is the milestone > fork 3) to its own iteration: the name + unix-uid lean is the milestone

View file

@ -7,7 +7,7 @@ chain: 2
# Iteration 22 — durability proof, throughput, and scale under load # Iteration 22 — durability proof, throughput, and scale under load
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-15.** The measurement backbone. Everything after the > **Inserted 2026-08-15.** The measurement backbone. Everything after the
> functional engine (9/9b) is an *optimization*, and an optimization without > functional engine (9/9b) is an *optimization*, and an optimization without
@ -42,8 +42,8 @@ chain: 2
> decisions: the vehicle is a NEW sample `docs/examples/db-bench` > decisions: the vehicle is a NEW sample `docs/examples/db-bench`
> (employee stays a teaching sample) and `time.ticks` (CLOCK_MONOTONIC > (employee stays a teaching sample) and `time.ticks` (CLOCK_MONOTONIC
> µs) is the iteration's one runtime addition. Spec: > µs) is the iteration's one runtime addition. Spec:
> [`2026-08-21-db-bench-design.md`](../../../superpowers/specs/2026-08-21-db-bench-design.md) > [`2026-08-21-db-bench-design.md`](../../superpowers/specs/2026-08-21-db-bench-design.md)
> · plan: [`2026-08-21-db-bench.md`](../../../superpowers/plans/2026-08-21-db-bench.md) > · plan: [`2026-08-21-db-bench.md`](../../superpowers/plans/2026-08-21-db-bench.md)
> — **in progress** (second slice of the chain). > — **in progress** (second slice of the chain).
> >
> **RE-SEQUENCED 2026-08-21** (developer decision): runs AFTER the arc's > **RE-SEQUENCED 2026-08-21** (developer decision): runs AFTER the arc's

View file

@ -7,7 +7,7 @@ chain: 5
# Iteration 23 — io_uring group-commit write path # Iteration 23 — io_uring group-commit write path
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-15.** The write-path optimization, and deliberately the > **Inserted 2026-08-15.** The write-path optimization, and deliberately the
> LAST database performance iteration: it only earns its complexity once > LAST database performance iteration: it only earns its complexity once

View file

@ -1,13 +1,13 @@
--- ---
iteration: "24" iteration: "24"
status: refine status: in-progress
chain: 4 chain: 4
--- ---
# Iteration 24 — chat: the WebSocket pub/sub driving workload # Iteration 24 — chat: the WebSocket pub/sub driving workload
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-20** (concurrency-chain refinement): the 8+11 arc's > **Inserted 2026-08-20** (concurrency-chain refinement): the 8+11 arc's
> driving workload, the role log-watcher played for iterations 3–7. Needs > driving workload, the role log-watcher played for iterations 3–7. Needs

View file

@ -6,7 +6,7 @@ status: hold
# Iteration 27 — query grammar, driven by real embedded-DB corpora # Iteration 27 — query grammar, driven by real embedded-DB corpora
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-16.** A query-surface iteration in the 9b family: the > **Inserted 2026-08-16.** A query-surface iteration in the 9b family: the
> language-integrated query grows to cover the grammar that *real > language-integrated query grows to cover the grammar that *real

View file

@ -6,7 +6,7 @@ status: hold
# Iteration 28 — skillhost: a host-shaped workload, and the capability gaps it exposes # Iteration 28 — skillhost: a host-shaped workload, and the capability gaps it exposes
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-16.** A driving-workload iteration, the way iteration 7's > **Inserted 2026-08-16.** A driving-workload iteration, the way iteration 7's
> log-watcher drove the systems stdlib. The workload is a writeonce port of > log-watcher drove the systems stdlib. The workload is a writeonce port of

View file

@ -6,11 +6,11 @@ status: hold
# Iteration 29 — compile-time metaprogramming (derive from the class table) # Iteration 29 — compile-time metaprogramming (derive from the class table)
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-16.** A language-capability iteration, deliberately > **Inserted 2026-08-16.** A language-capability iteration, deliberately
> numbered to echo the principle it lives inside: > numbered to echo the principle it lives inside:
> [principle 13, "statically typed, all the way to the register"](../../../00-principles.md). > [principle 13, "statically typed, all the way to the register"](../../00-principles.md).
> It comes late because it earns its keep only once there are enough > It comes late because it earns its keep only once there are enough
> types worth deriving over (the `@table` classes of iteration 9/9b, the > types worth deriving over (the `@table` classes of iteration 9/9b, the
> records the query surface projects), and it must never be the excuse that > records the query surface projects), and it must never be the excuse that

View file

@ -7,7 +7,7 @@ chain: 3
# Iteration 31 — actor lifecycle: request/response, backpressure, death, timers # Iteration 31 — actor lifecycle: request/response, backpressure, death, timers
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-21** (concurrency-chain re-sequence; the iteration > **Inserted 2026-08-21** (concurrency-chain re-sequence; the iteration
> was named as "new 31" in the 2026-08-20 code-review re-sequence — this > was named as "new 31" in the 2026-08-20 code-review re-sequence — this
@ -98,10 +98,10 @@ Forks the spec must settle:
Sources: the 2026-08-20 code-review findings (the gaps this iteration Sources: the 2026-08-20 code-review findings (the gaps this iteration
answers), the arc plan's stage-2 deviations answers), the arc plan's stage-2 deviations
([`2026-08-20-shard-fiber-arc.md`](../../../superpowers/plans/2026-08-20-shard-fiber-arc.md) ([`2026-08-20-shard-fiber-arc.md`](../../superpowers/plans/2026-08-20-shard-fiber-arc.md)
— the unbounded FIFO is deviation 4's mutex inbox), and BEAM precedent — the unbounded FIFO is deviation 4's mutex inbox), and BEAM precedent
already surveyed in already surveyed in
[`docs/plan/exploration/fibers/00-fibers.md`](../../../plan/exploration/fibers/00-fibers.md). [`docs/plan/exploration/fibers/00-fibers.md`](../../plan/exploration/fibers/00-fibers.md).
## Proposed Solution ## Proposed Solution

View file

@ -7,14 +7,14 @@ chain: 6
# Iteration 32 — WAL checkpoint: disk space reclamation and bounded replay # Iteration 32 — WAL checkpoint: disk space reclamation and bounded replay
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-21** (stage-3 guarantee refinement found the hole): > **Inserted 2026-08-21** (stage-3 guarantee refinement found the hole):
> the WAL is append-only FOREVER — no checkpoint, no truncation exists > the WAL is append-only FOREVER — no checkpoint, no truncation exists
> in the engine or anywhere on the roadmap. Disk grows without bound and > in the engine or anywhere on the roadmap. Disk grows without bound and
> replay time grows with history, so restart cost rises with every write > replay time grows with history, so restart cost rises with every write
> the program ever made. RAM reclamation already exists (deleted rows > the program ever made. RAM reclamation already exists (deleted rows
> free their slot — [`04-db-binding.md`](../../../plan/oop-vm/04-db-binding.md): > free their slot — [`04-db-binding.md`](../../plan/oop-vm/04-db-binding.md):
> "Ids are never reused; slots are"); this iteration is the DISK half. > "Ids are never reused; slots are"); this iteration is the DISK half.
> LAST in the concurrency chain: > LAST in the concurrency chain:
> **stage 3 → 22 → 31 → 24 → 23 → 32** — it wants 22's measured > **stage 3 → 22 → 31 → 24 → 23 → 32** — it wants 22's measured
@ -87,7 +87,7 @@ Forks the spec must settle:
4. **Composition with 23** — the snapshot's durability barrier rides 4. **Composition with 23** — the snapshot's durability barrier rides
the same per-shard ring (WRITE+FSYNC chain, then the truncate); the same per-shard ring (WRITE+FSYNC chain, then the truncate);
ordering vs in-flight group commits must be stated normatively in ordering vs in-flight group commits must be stated normatively in
[`04-db-binding.md`](../../../plan/oop-vm/04-db-binding.md)'s WAL [`04-db-binding.md`](../../plan/oop-vm/04-db-binding.md)'s WAL
section. section.
## Proposed Solution ## Proposed Solution

View file

@ -6,7 +6,7 @@ status: refine
# Iteration 33 — `WO_DATA=<path>.db`: the persistent store as one file # Iteration 33 — `WO_DATA=<path>.db`: the persistent store as one file
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-22** (developer ask: "can the persistent db be in > **Inserted 2026-08-22** (developer ask: "can the persistent db be in
> file.db form?"). The truth is already almost there: `WO_DATA=<dir>` > file.db form?"). The truth is already almost there: `WO_DATA=<dir>`

View file

@ -6,7 +6,7 @@ status: refine
# Iteration 34 — crypto builtins: digests and HMAC in the runtime # Iteration 34 — crypto builtins: digests and HMAC in the runtime
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-22** — the framework ledger's oldest unowned gap > **Inserted 2026-08-22** — the framework ledger's oldest unowned gap
> gets an owner. PREMISE UPDATE (same day, post-merge): iteration 36 > gets an owner. PREMISE UPDATE (same day, post-merge): iteration 36
@ -25,7 +25,7 @@ Four consumers already wait on it, none able to proceed:
(`Sec-WebSocket-Accept` = base64(SHA-1(key + GUID)) — SHA-1 (`Sec-WebSocket-Accept` = base64(SHA-1(key + GUID)) — SHA-1
specifically, not a choice); the framework's ETag/conditional-request specifically, not a choice); the framework's ETag/conditional-request
row (wants a content hash); HMAC-signed tokens the auth core can grow; row (wants a content hash); HMAC-signed tokens the auth core can grow;
and held [iteration 21](../hold/21-keypair-attach-auth.md), whose and held [iteration 21](21-keypair-attach-auth.md), whose
challenge–response needs primitives that "do not exist" (its demotion challenge–response needs primitives that "do not exist" (its demotion
note). Bytes and base64 landed with iteration 19 — the carriers exist, note). Bytes and base64 landed with iteration 19 — the carriers exist,
only the digests are missing. only the digests are missing.

View file

@ -6,7 +6,7 @@ status: done
# Iteration 35 — `net` runtime seams: timeouts, Unix sockets, peer address # Iteration 35 — `net` runtime seams: timeouts, Unix sockets, peer address
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **LANDED 2026-08-23** (branch `framework-v1b`, with the serving slice > **LANDED 2026-08-23** (branch `framework-v1b`, with the serving slice
> riding it): per-call `_dl` deadlines (nil/false = the expected > riding it): per-call `_dl` deadlines (nil/false = the expected
@ -14,7 +14,7 @@ status: done
> `net.peer` (id 95). Plane: shard-tick TIMEOUT + expiry sweep + > `net.peer` (id 95). Plane: shard-tick TIMEOUT + expiry sweep +
> POLL_REMOVE tombstone on uring, extended deadline scan on epoll, > POLL_REMOVE tombstone on uring, extended deadline scan on epoll,
> fibers POOLED against stale-CQE UAF — the full design in > fibers POOLED against stale-CQE UAF — the full design in
> [the review spec](../../../superpowers/specs/2026-08-23-net-seams-park-design.md). > [the review spec](../../superpowers/specs/2026-08-23-net-seams-park-design.md).
> Proof: all five seams probe-verified on BOTH `WO_IO` backends; > Proof: all five seams probe-verified on BOTH `WO_IO` backends;
> `serve_conn` + web-app's fiber-per-connection pattern gate parallel > `serve_conn` + web-app's fiber-per-connection pattern gate parallel
> requests, idle eviction, and slow-loris tearing (web-app 41 checks). > requests, idle eviction, and slow-loris tearing (web-app 41 checks).
@ -100,7 +100,7 @@ Forks the spec must settle:
POLL_ADD and a TIMEOUT in flight (io_uring linked ops vs two POLL_ADD and a TIMEOUT in flight (io_uring linked ops vs two
submissions + first-wins cancel; epoll fallback = the existing submissions + first-wins cancel; epoll fallback = the existing
deadline scan). The park protocol contract deadline scan). The park protocol contract
([`03-concurrency-coroutines.md`](../../../plan/oop-vm/03-concurrency-coroutines.md)) ([`03-concurrency-coroutines.md`](../../plan/oop-vm/03-concurrency-coroutines.md))
gains the rule. gains the rule.
3. **Surface shape**: per-call deadline argument vs per-fd setting 3. **Surface shape**: per-call deadline argument vs per-fd setting
(`net.set_deadline(fd, ms)`); leaning per-call — no hidden fd state, (`net.set_deadline(fd, ms)`); leaning per-call — no hidden fd state,

View file

@ -6,7 +6,7 @@ status: in-progress
# Iteration 36 — operator parity: `not`, bitwise, hex literals, compound assigns # Iteration 36 — operator parity: `not`, bitwise, hex literals, compound assigns
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-22, forks settled the same day** (decisions below, > **Inserted 2026-08-22, forks settled the same day** (decisions below,
> story-19 discipline). Driver: a gap survey of the operator surface > story-19 discipline). Driver: a gap survey of the operator surface
@ -26,7 +26,7 @@ status: in-progress
> pure-`.wo` base64 does with division/modulo what shifts and masks say > pure-`.wo` base64 does with division/modulo what shifts and masks say
> directly. > directly.
> >
> **Plan:** [`2026-08-22-operator-parity.md`](../../../superpowers/plans/2026-08-22-operator-parity.md) > **Plan:** [`2026-08-22-operator-parity.md`](../../superpowers/plans/2026-08-22-operator-parity.md)
> (5 tasks: lexer/tokens; AST/parser incl. compound-assign desugar; > (5 tasks: lexer/tokens; AST/parser incl. compound-assign desugar;
> type checker incl. literal shift-count rejection; emit + VM opcodes > type checker incl. literal shift-count rejection; emit + VM opcodes
> `.wob` v6; corpus/gates/docs closeout). > `.wob` v6; corpus/gates/docs closeout).

View file

@ -6,7 +6,7 @@ status: done
# Iteration 37 — wo-html components: an MVC-shaped view layer (Angular's format, studied) # Iteration 37 — wo-html components: an MVC-shaped view layer (Angular's format, studied)
> Format: `product/story-iteration-template`. Part of > Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md). > [Story — one language, one runtime, one database, one binary](00-story.md).
> >
> **Inserted 2026-08-23** (developer ask: "enhance wo-html like MVC; > **Inserted 2026-08-23** (developer ask: "enhance wo-html like MVC;
> understand Angular format"). Grows the wo-html LIBRARY, never the > understand Angular format"). Grows the wo-html LIBRARY, never the
@ -36,7 +36,7 @@ status: done
> no-JS posture — forms and POSTs instead. Still a COMPILER iteration, > no-JS posture — forms and POSTs instead. Still a COMPILER iteration,
> parked behind the standing "no compiler/VM/database changes yet" > parked behind the standing "no compiler/VM/database changes yet"
> directive. The shop template > directive. The shop template
> ([`docs/examples/shop`](../../../examples/shop/README.md)) is the > ([`docs/examples/shop`](../../examples/shop/README.md)) is the
> consumer: each `render()` body becomes one raw template literal, and > consumer: each `render()` body becomes one raw template literal, and
> its README's recorded gaps ride along (gap #1: `pub` + `@table` > its README's recorded gaps ride along (gap #1: `pub` + `@table`
> cannot combine — blocks a shared model module; gap #2: `@view` > cannot combine — blocks a shared model module; gap #2: `@view`

View file

@ -0,0 +1,204 @@
---
iteration: "38"
status: refine
---
# Iteration 38 — content collaboration workload: the file-mutation and outbound-socket gaps
> Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](00-story.md).
>
> **Inserted 2026-08-26** (developer ask: "can the language create an
> application like Nextcloud, a content collaboration platform?"). The
> answer was no, and the reason was not missing `.wo` effort: two whole
> capability families are unowned by any iteration. Read off the runtime
> contract, not off prose — `runtime/src/wob.h` allocates exactly six
> `fs` builtins, ids 40–45 (`exists`, `list`, `stat`, `read_all`,
> `read_at`, `append`; `append` "creates the file if absent"), so a file
> can be **created and grown and then never replaced, truncated, deleted
> or renamed**. It allocates ten `net` builtins — 51–55
> (`listen`/`accept`/`read`/`write`/`close`) and 91–95 (the three `_dl`
> deadline twins, `listen_unix`, `peer`) — and **no `connect`**;
> `grep -rn 'connect(' runtime/src/` returns nothing, so a program cannot
> open an outbound connection to anything, ever. This iteration is the
> workload that forces both into the open, in the mould of
> [iteration 28](28-skillhost-host-workload.md).
## Goals
- **A writeonce program shaped like a content platform.** `docs/examples/vault`:
authenticated users, folders and documents as `@table` classes related by
`ref`/`backlink`, multipart upload intake, content-addressed blobs, share
grants with expiry, a version chain per document, a trash bucket, an
activity feed, and a server-rendered UI over `writeonce-serve` +
`writeonce-view`. Everything above the storage line is expressible on
today's toolchain and is the part that ships.
- **`fs` grows the mutation verbs.** The gap is not ergonomic. A blob store
that can only `append` can never reclaim a byte: a cancelled upload, a
deleted document and a superseded version all leak their file forever,
and there is no atomic-publish primitive (write-temp-then-`rename`) so
every write is torn-visible to a concurrent reader. Name the minimum set
the workload actually needs and no more. The syscalls are not the work —
`sysio.c:669` already calls `unlink()` on the `listen_unix` path to clear
a stale socket, so `remove` is a builtin-table row over a call the
runtime already links.
- **`net` grows the client side.** Every federation, identity and
notification story in a collaboration platform is an outbound call —
OIDC token exchange, SMTP for share mail, an object-store backend, a
webhook, an antivirus or preview service. All of them are one missing
builtin. [Iteration 35](35-net-runtime-seams.md) declared
connect-side timeouts out of scope "no workload asks yet" — this is the
workload asking.
- **Block honestly on the rest, in writing.** WebDAV (the protocol every
desktop and mobile sync client speaks), collaborative editing, previews
and full-text search each get a named verdict in this story — owned
elsewhere, deliberately rejected, or deferred with the reason — so the
sample's README never implies a feature the stack cannot serve.
## Acceptance Criteria
- **Given** the `vault` sample and today's `fs`, **when** a user deletes a
document and the acceptance script inspects the blob directory, **then**
the blob is gone from disk and the disk footprint returns to its
pre-upload size — the criterion that is impossible today and is the
whole point of the fs half.
- **Given** two concurrent readers of a document while a new version is
being written, **when** the write completes, **then** every read
returned either the whole old version or the whole new one and never a
partial file — atomic publish, proven by a racing reader, not asserted.
- **Given** an upload that is abandoned mid-stream (client disconnect),
**when** the next request arrives, **then** no partial blob and no
orphan row survive — cleanup is reachable from `.wo`.
- **Given** a share notification configured against a local SMTP sink and
an identity provider stub, **when** a share is created, **then** the
program opened an outbound connection, spoke the protocol and recorded
delivery — the connect half proven end to end, offline, with a fixture
server the gate starts itself (the `deps-accept`/`web-app` precedent:
no network in CI).
- **Given** an outbound peer that accepts the connection and then never
answers, **when** the configured deadline expires, **then** the call
returns the expected-timeout shape, the fd is closed, and the fiber is
not parked forever — the same cleanup contract iteration 35 set for the
listen side.
- **Given** the full sample under the standing battery, **when**
`oop-accept`, `web-app`, `site` and the new gate run, **then** all are
green and ASan-clean at both shard counts.
## Out Of Scope
- **Raw stdin/stdout byte I/O, bounded/killable subprocesses, recursive
directory walk, executable-bit and realpath confinement** — every one of
these is [iteration 28](28-skillhost-host-workload.md)'s
Blockers B and C and its gap list. This story must not re-own them. It
*does* depend on 28's killable-subprocess work for previews, which is
exactly why previews are deferred here rather than attempted.
- **WAL checkpoint and disk reclamation** —
[iteration 32](32-wal-checkpoint.md). A metadata store whose boot replays
every write ever made is a real ceiling for this workload, and naming it
here is the point; fixing it is 32's. This story's gate should record the
replay time it observes so 32 inherits a number.
- **WebDAV.** Sync clients speak PROPFIND/PROPPATCH/MKCOL/MOVE/LOCK over
XML bodies. The language has `json` and no XML parser, and `MKCOL`/`MOVE`
have nothing to land on until the fs half exists. Verdict for the spec to
confirm: out of this slice, revisited only once file mutation ships, and
never as a compiler concern.
- **Collaborative editing (OT/CRDT).** Wants ordered trees and cheap
lookups; `multi` is an array, `map` is a documented linear scan, there
are no closures and no generics beyond those two. Deliberately not
attempted in `.wo` at this stage — and the external-document-server
route Nextcloud itself takes is unreachable until `connect` lands.
- **Full-text search.** The engine indexes equality probes on declared
columns; there is no prefix scan or FTS. Query-grammar growth is
[iteration 27](27-query-grammar-corpus.md)'s.
- **TLS** — proxy-terminated, by doctrine, unchanged. The outbound half
therefore speaks plaintext to a local sidecar or a trusted-network peer,
and the story says so out loud rather than implying HTTPS clients.
- **A plugin/app ecosystem.** In-runtime recompile is
[iteration 26](26-blue-green-deploy.md)'s; nothing here loads
code at run time.
- **Scale claims.** RAM is authoritative (64 MiB arena by default,
`WO_HEAP_MB`), so this is a team-scale platform and the README states the
row ceiling it was measured at.
## Info
The gap ledger this story exists to resolve, with current ownership:
Every "today" cell below was read from the source named beside it, not
from another document.
| Capability | Today | Read from | Owner |
| --- | --- | --- | --- |
| overwrite / write-at-offset a file | absent; `append` (id 45) creates-if-absent and grows, nothing more | `wob.h:353-358` | **this story** |
| remove, rename, mkdir, truncate | absent from the builtin table | `wob.h:353-358` | **this story** |
| outbound TCP / unix connect | absent; no `connect()` call in the runtime at all | `wob.h:364-368,477-485`; `grep` over `runtime/src/` | **this story** |
| outbound deadline + cleanup | `_dl` seam exists, listen side only (ids 91–93) | `wob.h:477-482` | **this story**, on 35's contract |
| stdin/stdout bytes | no builtin; only `<stdint.h>` matches `stdin` | `sysio.c`, `wob.h` | 28, Blocker B |
| bounded, killable subprocess | `proc.run` (id 56) takes `(cmd, args, cls)` — no deadline, no signal | `wob.h:369`; `sysio.c:718` | 28, Blocker C |
| directory walk, `X_OK`, realpath | absent | `sysio.c` | 28, gap list |
| WAL checkpoint / bounded replay | no checkpoint, snapshot or truncate anywhere in the WAL; `fdatasync` per commit | `wal.c:403`, `wal.h` | 32 |
| io_uring on the WAL write path | rings exist in the fiber/net plane only; the WAL is plain `fdatasync` | `park.c:22-54` vs `wal.c:403` | 23 |
| `WO_DATA` as a file | hardcoded `"%s/shard-0.wal"` directory form | `main.c:202` | 33 |
| prefix scan / FTS / aggregates | equality probe only; group-by rejected in the typechecker | `types.ml:2220,2241` | 27, parked drain |
Builtin id allocation, so this story's work does not collide: `WO_B_MAX`
is `95u` and ids **89 and 90 are reserved holes** — iteration 31's
`monitor` and `time.after`, named in the active slice's marker and absent
from both `wob.h` and `types.ml`. New builtins here start at **96**. No
`.wob` version bump is implied: `WOB_VERSION` is `6u` and moved last for
iteration 36's opcodes 42–46; stdlib builtins are a table row, exactly as
iteration 35's 91–95 were.
What already exists and needs no new capability, verified against the tree:
`http/multipart` and `http/auth` in `writeonce-serve`; `sha1`/`sha256`/
`hmac_sha256` (builtin ids 85–87) for content addressing, ETags and session
tokens; `base64_encode`/`decode` and the `Bytes` carrier; `ws_accept` plus
the pure-`.wo` RFC 6455 frame codec for the activity feed; `Component`/
`Layout` with `{{ }}` escaping for the UI; `@table` with `@unique`, `ref`,
`backlink` and FK-restrict for the whole permission and version model.
Forks the spec must settle:
1. **How small is the fs set?** Two candidate shapes. Minimal-atomic:
`fs.write` (create-or-replace whole file), `fs.remove`, `fs.rename`,
`fs.mkdir` — four verbs, no offsets, atomic publish by write-temp +
rename, and a blob store is expressible. Streaming: add
`fs.write_at`/`fs.truncate` so a large upload can be resumed and a
sparse file assembled. Leaning minimal-atomic: it closes the deletion
hole, it is the one shape that composes with 32's own rename-swap, and
offsets can be argued later by a workload that measures the need.
2. **What does a handle look like — or is there one?** Today every `fs`
member takes a path and does the whole operation. A `write` that takes a
path keeps that property and keeps handles out of the ownership model;
a resumable upload wants an open fd, which means a droppable
`fs.File` scalar in the shape of `net.Conn`. This fork decides fork 1.
3. **Does `connect` return `net.Conn` and nothing more?** Cheapest honest
answer is yes: `net.connect(host, port)` and `net.connect_unix(path)`
yielding the same fd-scalar `accept` already yields, so every existing
`read`/`write`/`close`/`_dl` member composes unchanged and the client
side costs two builtins, not a subsystem.
4. **Path confinement.** A content platform maps user-supplied names onto
paths. Without realpath (28's gap) the sample must confine by
construction — content-addressed filenames the user never names — or the
fs half hands `.wo` code a traversal footgun the day it lands. This is
the security fork and it gates the fs half, not the sample.
5. **Where does the workload stop?** The sample is the acceptance test, so
its edge is the story's edge. Leaning: upload / download / delete /
rename / version / share / trash / activity feed, one share
notification over `connect`, and no sync-client protocol at all.
## Proposed Solution
Two capability halves and a workload, and the workload is written first so
the capability set is argued from a program that needs it rather than from
a wish list. Order: brainstorm the five forks (fork 1 and 2 together, fork
4 before any fs code), then the `vault` sample against today's toolchain to
establish exactly where it blocks, then the fs verbs, then `connect`, each
with corpus fixtures and its own gate leg. Big enough to want a spec and a
plan document — this is not a bounded slice like
[33](33-single-file-db.md).
Precedence: independent of the concurrency chain (stage 3 → 22 → 31 → 24 →
23 → 32) and startable beside it, with one caveat — the honest disk story
needs [32](32-wal-checkpoint.md), so if this lands first its gate records
the replay number rather than claiming the platform is operationally done.

View file

@ -0,0 +1,191 @@
---
iteration: "39"
status: refine
---
# Iteration 39 — web framework parity: randomness, cookies, and the store-backed middleware chain
> Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](00-story.md).
>
> **Inserted 2026-08-26** (developer ask: add gofiber/fiber as a reference and
> find the basic features the web framework lacks). Derived from
> [the Fiber v3.5.0 parity study](../../plan/exploration/fiber/00-fiber-parity.md),
> which read Fiber's routing surface, `Req`/`Res` API, binder and all 32 of its
> `middleware/` packages against `docs/examples/writeonce-serve`. This iteration
> takes that study's §0–§2 plus the cheap half of §5; the study names an owner
> for everything it leaves out.
## Goals
- **A random-bytes builtin, first, because it gates the rest.** The framework
README's crypto row claims signed cookies, CSRF and session integrity are
"UNBLOCKED — the primitives exist since iteration 34". For CSRF and sessions
**that is wrong**: SHA-256 and HMAC let a program *authenticate* a token, not
*mint* one, and writeonce has no source of randomness anywhere — no
`getrandom(2)`, no CSPRNG builtin, nothing. An HMAC over a guessable session
id is a signed guess. Close this before writing a line of session code, and
correct the ledger row that says otherwise.
- **Cookies, in both directions.** Absent entirely today: nothing parses a
`Cookie:` request header and there is no `Set-Cookie` builder. This is the
foundation the next goal stands on, and it forces a design decision the
framework has so far avoided — `Resp.headers` is a `map<Text, Text>`, so it
**structurally cannot** carry the two `Set-Cookie` lines a login-plus-flash
response needs. Deciding what replaces or supplements that map is the real
work of this goal; the parsing is the easy half.
- **The store-backed middleware chain, in dependency order**: rate limiting and
idempotency first (they need only a store and `time.ticks`, both of which
exist — the cheapest real wins available), then sessions, then CSRF. Each is
an ordinary `.wo` middleware class, and each gets a **durable** store for
free from `@table` — where Fiber ships an in-memory default and expects you
to bolt on Redis. That is a genuine writeonce advantage and the samples should
show it.
- **Close the routing and response sugar that is merely missing.** `PATCH` /
`OPTIONS` / `HEAD` / `ALL` registration helpers (a `Route { method: "PATCH" }`
literal already works, so this is registration ergonomics), a per-route body
limit instead of one compile-time `BODY_MAX = 1048576` for the whole server, a
request-id middleware, and the response helpers every framework has and this
one writes by hand: `Location`, `Vary`, `Attachment`/`Download`.
- **Say what is still missing, with an owner.** The study's §3–§6 stay out of
scope; this iteration's closing act is updating the framework README's ledger
so each row points at whoever owns it rather than reading as an oversight.
## Acceptance Criteria
- **Given** the random-bytes builtin, **when** the acceptance script draws many
values across separate processes, **then** no value repeats and none is
derivable from the clock — and the builtin is refused, loudly, if the kernel
source is unavailable rather than silently falling back to something weaker.
A CSPRNG that degrades quietly is worse than no CSPRNG.
- **Given** a login handler that sets a session cookie and a flash cookie on
one response, **when** the response is serialized, **then** **two** distinct
`Set-Cookie` headers reach the wire — the criterion the current
`map<Text, Text>` cannot satisfy, and the reason this iteration touches
`Resp`.
- **Given** a request carrying a `Cookie:` header with several pairs, quoted
values and stray whitespace, **when** it is parsed, **then** each value is
recovered exactly, and a malformed header is a 400 rather than a silent
partial parse.
- **Given** a session cookie whose HMAC is altered by one bit, **when** the
session middleware reads it, **then** the session is rejected in constant
time (`ct_eq`, which already exists) and the request proceeds unauthenticated
— never as a *different* user.
- **Given** the limiter configured to N requests per window, **when** a client
exceeds it, **then** it receives 429 with the rate-limit headers set, the
window expires on `time.ticks`, and the counters **survive a restart** — the
durability the `@table` store buys, proven by a restart in the gate.
- **Given** a CSRF-protected form flow, **when** a request arrives with a
missing, stale or foreign-origin token, **then** each is refused distinctly;
**when** the token is valid, the request proceeds. Single-use tokens are not
double-spendable.
- **Given** the same POST replayed with an identical idempotency key, **when**
it is handled, **then** the stored response is returned and the handler does
not run twice — proven by a side effect that would be visible if it had.
- **Given** a route registered with each new method helper and a per-route body
limit, **when** the matrix runs, **then** methods dispatch correctly, an
oversized body is refused per-route rather than per-server, and every standing
gate (`just web-app`, `just site`, `oop-accept`) is unchanged.
## Out Of Scope
Every item below is a real Fiber feature and a real writeonce gap. Each is
excluded because someone else owns it — the study's §7 is the full map.
- **Streaming, SSE, compression, byte-range requests** — all four sit on one
missing seam: `internal/serve.wo` builds a whole response as one `Text` and
`serialize()` always emits `Content-Length`. The framework README already
parks "lazy body streaming + backpressure · streaming responses · explicit
commit point"; these belong to that slice. Note `internal/parse.wo:153-157`
**deliberately refuses** chunked request bodies with a request-smuggling note
— that refusal is correct and must survive whoever implements chunked.
- **Typed binding of query / params / form / headers into a class** — Fiber's
`Bind` reflects over struct tags; principle 13 forbids reflection, so the
answer is compile-time generation:
[iteration 29's `@derive`](29-compile-time-metaprogramming.md). Recorded, not
attempted. Same for XML/CBOR/MsgPack codecs — JSON only is the small stdlib
working as intended.
- **TTL cache middleware** — [iteration 18](18-memory-db-features.md) owns it,
spec already approved.
- **`proxy` middleware** — needs an outbound socket, which does not exist:
[iteration 38](38-content-platform-capabilities.md).
- **`pprof` / `expvar` / metrics / stack traces on trap** — iteration 30
(observability, CI, fuzz — still no story file).
- **Everything the study's §6 lists as a deliberate divergence**: a runtime
template engine (markup is a compile-time literal or it does not exist), TLS
and HTTP/2 (proxy-terminated by doctrine), `net/http` interop (no FFI),
prefork and buffer-size knobs (the shard runtime owns placement), closures as
handlers (a handler is a class; its fields are the closure substitute). These
are settled — the study lists them so nobody re-opens them as "missing".
- **A radix-tree router.** Path matching is a linear scan, already 🔶 in the
ledger pending a *measurement*. Iteration 22 built the harness but benched the
database, not the router. Still waiting on a number, not on this iteration.
## Info
Fiber v3.5.0, `.dev/reference/fiber` (gitignored; the study carries the clone
command). Of its 32 middleware packages, **nine already have a working
`writeonce-serve` counterpart** — CORS, basic auth, key/bearer auth,
helmet-style security headers, ETag, static files, logger, host authorization,
and recover-as-500. The framework is further along than its size suggests; the
gaps are breadth, not foundations, with the single exception of randomness.
Dependency order, which is the whole point of the iteration:
```
random bytes ──▶ cookies ──▶ sessions ──▶ CSRF
│ │
│ └──▶ (signed cookies, JWT HS256 issuing)
└──▶ request-id
limiter, idempotency ──▶ need only @table + time.ticks (start here)
```
Forks the spec must settle:
1. **What replaces `Resp.headers: map<Text, Text>`?** Options: a
`multi Text` of raw header lines beside the map; a dedicated
`cookies: multi Cookie` field on `Resp` that `serialize()` renders; or a
general repeated-header list. The first is smallest, the second is the most
typed, the third is the most honest about HTTP. This fork decides how much
of the framework's public surface moves, so it comes first.
2. **What shape is the random builtin?** `random_bytes(n) -> Bytes` is the
obvious one and composes with everything iteration 19 and 34 added. Decide
whether a convenience `random_hex`/`random_id` rides along or whether
`base64_encode` is enough, and decide the failure mode when the kernel source
is unavailable — the criterion above says refuse, not degrade.
3. **Is there a `Store` interface, or does each middleware own its `@table`?**
Fiber abstracts `Storage` so the same middleware runs on memory or Redis.
writeonce has one store, and interfaces are structural — an abstraction with
exactly one implementor is decoration (iteration 37 learned this the hard way
about `Component`). Leaning: concrete `@table` per middleware until a second
backend actually exists.
4. **Where does session state live — cookie or table?** A signed cookie
carrying the whole payload needs no store and cannot be revoked; a table row
keyed by a random id can be revoked and costs a lookup. Leaning: table, since
`@table` is the language's whole thesis and revocation is not optional for a
real login.
5. **Does the request-id middleware trust an inbound header?** Fiber's
`requestid` accepts one by default. Behind a trusted proxy that is what you
want; on an open port it lets a client forge correlation ids. `client_ip` and
`net.peer` already exist for the trust decision — reuse that judgement rather
than inventing a second one.
## Proposed Solution
Wants a spec: fork 1 changes public framework types, and fork 2 adds a runtime
builtin. Order follows the dependency chain above, and the first slice is
deliberately the *cheapest* rather than the most foundational — limiter and
idempotency need nothing new, so they prove the store pattern and the gate shape
before the risky work starts.
Then: the random builtin (runtime, with its own corpus fixtures), cookies (the
`Resp` decision plus parse and serialize), sessions, CSRF, and the routing and
response sugar last since it is independent of everything else. Each slice ends
green on `just web-app` and `just site`, and the closing act corrects the
framework README's crypto row and re-points every ledger row this study touched
at its actual owner.
Off-chain and independent of the concurrency chain, with one caveat worth
stating: the SSE work this study defers is a natural companion to
[iteration 24's](24-chat-websocket-workload.md) chat sample, since a room actor
already has the fan-out shape. If 24 lands first, SSE gets cheaper.

View file

@ -25,7 +25,7 @@ toolchain may invoke). Acceptance via a shell gate over local `file://`
remotes — the suite stays network-free. remotes — the suite stays network-free.
**Spec:** [`../specs/2026-08-18-web-framework-design.md`](../specs/2026-08-18-web-framework-design.md) **Spec:** [`../specs/2026-08-18-web-framework-design.md`](../specs/2026-08-18-web-framework-design.md)
section A (normative). Story: [`15-deps-package-manager.md`](../../stories/language-runtime-database/done/15-deps-package-manager.md). section A (normative). Story: [`15-deps-package-manager.md`](../../stories/language-runtime-database/15-deps-package-manager.md).
## Global Constraints ## Global Constraints

View file

@ -150,8 +150,18 @@ is the largest phase and lands with Phase 2's front-end.
**Files:** `docs/00-principles.md` (principle 3), the OOP spec (decision table, §3 rule 5, §4 memory model), `docs/plan/oop-vm/00-wob-format.md`, `01-error-catalog.md` (retire WO-W201, update WO-E304 wording, add the new WO-E1xx), `08-builtin-surface.md` (delete the `push` special case + `set` gap), `docs/00-status.md` (record 7b superseding iteration 2's memory model), `docs/stories/language-runtime-database/done/07b-inferred-gc-mark-sweep.md` (status → done), `docs/examples/gc-cycle/README.md` (flip "Run status" to shipped + wire a `just gc-cycle` acceptance). **Files:** `docs/00-principles.md` (principle 3), the OOP spec (decision table, §3 rule 5, §4 memory model), `docs/plan/oop-vm/00-wob-format.md`, `01-error-catalog.md` (retire WO-W201, update WO-E304 wording, add the new WO-E1xx), `08-builtin-surface.md` (delete the `push` special case + `set` gap), `docs/00-status.md` (record 7b superseding iteration 2's memory model), `docs/stories/language-runtime-database/done/07b-inferred-gc-mark-sweep.md` (status → done), `docs/examples/gc-cycle/README.md` (flip "Run status" to shipped + wire a `just gc-cycle` acceptance).
- [x] Apply each amendment in the spec's §8 migration table. Add a `docs/examples/gc-cycle` acceptance script + `just gc-cycle` recipe running `--dump-gc` + ring/owned demos under `WO_GC_TRACE`. - [x] Apply each amendment in the spec's §8 migration table.
- [x] Verify: `just oop-accept` green; `just gc-cycle` green; `git grep '@gc' -- '*.wo'` returns nothing (success criterion 1). Commit. - [ ] **NOT DONE** — add a `docs/examples/gc-cycle` acceptance script + `just gc-cycle` recipe running `--dump-gc` + ring/owned demos under `WO_GC_TRACE`.
- [x] Verify: `just oop-accept` green; `git grep '@gc' -- '*.wo'` returns nothing (success criterion 1). Commit.
> **Disclosure added 2026-08-26 (doc audit).** The two boxes above were both
> checked when this plan closed, but the `gc-cycle` acceptance never landed:
> there is no `just gc-cycle` recipe in the `justfile` and no
> `scripts/gc-cycle-accept.sh`. `docs/examples/gc-cycle/` has its sources, a
> `wo.toml` and a `target/`, and it compiles — it is simply ungated, the only
> sample in that state besides the two that are deliberately ahead of the
> toolchain. The rest of phase 4 did land, including the `WO-W201` retirement
> and the `@gc` sweep. Wiring the gate is a loose end, not a regression.
--- ---

View file

@ -39,7 +39,7 @@ keep-alive with `Content-Length` bodies only.
**Spec:** [`../specs/2026-08-18-web-framework-design.md`](../specs/2026-08-18-web-framework-design.md) **Spec:** [`../specs/2026-08-18-web-framework-design.md`](../specs/2026-08-18-web-framework-design.md)
section B (normative; §C's h2c stays parked). Story: section B (normative; §C's h2c stays parked). Story:
[`16-web-framework.md`](../../stories/language-runtime-database/done/16-web-framework.md). [`16-web-framework.md`](../../stories/language-runtime-database/16-web-framework.md).
## Global Constraints ## Global Constraints

View file

@ -1,7 +1,7 @@
# Iteration 18 — framework v2 (transaction{} + cache/flags/jobs): implementation plan # Iteration 18 — framework v2 (transaction{} + cache/flags/jobs): implementation plan
> **Status: ⏸ hold (2026-08-21, developer decision)** — story iteration 18 > **Status: ⏸ hold (2026-08-21, developer decision)** — story iteration 18
> sits in `stories/language-runtime-database/hold/`; plan was ready to > carries `status: hold`; plan was ready to
> execute (2026-08-20) and stays intact for resumption. Board: > execute (2026-08-20) and stays intact for resumption. Board:
> [docs/00-status.md](../../stories/00-status.md). > [docs/00-status.md](../../stories/00-status.md).
@ -33,7 +33,7 @@ pure `.wo` (framework), bash gates.
**Spec:** [`../specs/2026-08-20-memory-db-features-design.md`](../specs/2026-08-20-memory-db-features-design.md) **Spec:** [`../specs/2026-08-20-memory-db-features-design.md`](../specs/2026-08-20-memory-db-features-design.md)
(approved 2026-08-20, normative). Story: (approved 2026-08-20, normative). Story:
[`18-memory-db-features.md`](../../stories/language-runtime-database/hold/18-memory-db-features.md). [`18-memory-db-features.md`](../../stories/language-runtime-database/18-memory-db-features.md).
## Global Constraints ## Global Constraints

View file

@ -32,7 +32,7 @@ build path grows a check branch, and the dep-use resolution walk in
**Spec:** [`../specs/2026-08-20-library-kind-internal-design.md`](../specs/2026-08-20-library-kind-internal-design.md) **Spec:** [`../specs/2026-08-20-library-kind-internal-design.md`](../specs/2026-08-20-library-kind-internal-design.md)
(normative). Story: (normative). Story:
[`17-library-projects-internal.md`](../../stories/language-runtime-database/done/17-library-projects-internal.md). [`17-library-projects-internal.md`](../../stories/language-runtime-database/17-library-projects-internal.md).
## Global Constraints ## Global Constraints

View file

@ -47,8 +47,8 @@ every standing gate green before the next begins.
**Architecture:** see the spec (normative): **Architecture:** see the spec (normative):
[`../specs/2026-08-20-shard-fiber-arc-design.md`](../specs/2026-08-20-shard-fiber-arc-design.md). [`../specs/2026-08-20-shard-fiber-arc-design.md`](../specs/2026-08-20-shard-fiber-arc-design.md).
Stories: [8](../../stories/language-runtime-database/done/08-shard-actor-runtime.md) · Stories: [8](../../stories/language-runtime-database/08-shard-actor-runtime.md) ·
[11](../../stories/language-runtime-database/done/11-fibers.md). [11](../../stories/language-runtime-database/11-fibers.md).
**Tech Stack:** C11 libc-only (`wovm`), OCaml stdlib-only (`woc`), bash **Tech Stack:** C11 libc-only (`wovm`), OCaml stdlib-only (`woc`), bash
gates; TSan added to the corpus harness at stage 2. gates; TSan added to the corpus harness at stage 2.
@ -204,7 +204,7 @@ transitively-traced check), corpus + TSan.
> primary before any worker serves; (3) statements are serialized by the > primary before any worker serves; (3) statements are serialized by the
> DB actor — replies are materialized copies, no torn reads under the > DB actor — replies are materialized copies, no torn reads under the
> concurrent multi-shard corpus (TSan). The five-property map lives in > concurrent multi-shard corpus (TSan). The five-property map lives in
> [story 8's guarantee contract](../../stories/language-runtime-database/done/08-shard-actor-runtime.md); disk > [story 8's guarantee contract](../../stories/language-runtime-database/08-shard-actor-runtime.md); disk
> space reclamation is story 32, not this stage. > space reclamation is story 32, not this stage.
### Task 7 — transparent DB RPC ### Task 7 — transparent DB RPC

View file

@ -30,7 +30,7 @@ stdlib-only (one stdlib-table row).
**Spec:** [`../specs/2026-08-21-db-bench-design.md`](../specs/2026-08-21-db-bench-design.md) **Spec:** [`../specs/2026-08-21-db-bench-design.md`](../specs/2026-08-21-db-bench-design.md)
(approved 2026-08-21, normative — the four forks + decisions 5/6 live (approved 2026-08-21, normative — the four forks + decisions 5/6 live
there). Story: there). Story:
[`22-durability-throughput-scale.md`](../../stories/language-runtime-database/done/22-durability-throughput-scale.md). [`22-durability-throughput-scale.md`](../../stories/language-runtime-database/22-durability-throughput-scale.md).
## Global Constraints ## Global Constraints

View file

@ -21,7 +21,7 @@ time), `not` at the unary level beside minus.
(`runtime/src/`), dune + just gates. (`runtime/src/`), dune + just gates.
**Spec:** the story IS the spec — **Spec:** the story IS the spec —
`docs/stories/language-runtime-database/refine/36-operator-parity.md` `docs/stories/language-runtime-database/36-operator-parity.md`
(story-19 deviation precedent: decisions recorded normatively there, (story-19 deviation precedent: decisions recorded normatively there,
reasoning-under-the-code lands in CODE-LOGIC.md as part of this plan). reasoning-under-the-code lands in CODE-LOGIC.md as part of this plan).
@ -240,7 +240,7 @@ frontmatter and folder in the same change), `docs/stories/00-status.md`
CODE-LOGIC.md on both sides for the decisions that live in code CODE-LOGIC.md on both sides for the decisions that live in code
(arithmetic SHR, trap-not-mask, `not` as EQ-zero, parse-time (arithmetic SHR, trap-not-mask, `not` as EQ-zero, parse-time
compound-assign desugar and its double-eval contract); story file compound-assign desugar and its double-eval contract); story file
moved to `done/` with `status: done` and a landing blockquote in the set to `status: done` with a landing blockquote in the
story-15/16 voice; standup entry in `00-status.md` answering the six story-15/16 voice; standup entry in `00-status.md` answering the six
questions (reference project: `.dev/reference/go`). questions (reference project: `.dev/reference/go`).
- [ ] Commit. - [ ] Commit.

View file

@ -84,7 +84,7 @@ pure-`.wo` framework code, bash + python3-stdlib gates.
fresh Bytes; C entry `wo_builtin_crypto(vm, R, ins, msg)` consumed by fresh Bytes; C entry `wo_builtin_crypto(vm, R, ins, msg)` consumed by
`builtin.c`'s dispatch. Task 6 consumes `crypto.sha1` from `.wo`. `builtin.c`'s dispatch. Task 6 consumes `crypto.sha1` from `.wo`.
- [ ] Create the board marker `docs/in-progress/2026-08-23-chat-ws-lifecycle.md` - [ ] Create the board marker `docs/active-slice-2026-08-23-chat-ws-lifecycle.md`
(slice active, links to spec+plan) and flip the board's In-progress (slice active, links to spec+plan) and flip the board's In-progress
Runtime row to this slice. Commit with the first code commit. Runtime row to this slice. Commit with the first code commit.
- [ ] Digest cores in `crypto.c`: SHA-1 and SHA-256 over one buffer - [ ] Digest cores in `crypto.c`: SHA-1 and SHA-256 over one buffer
@ -371,10 +371,10 @@ pure-`.wo` framework code, bash + python3-stdlib gates.
### Task 10 — docs, stories, board, graph ### Task 10 — docs, stories, board, graph
- [ ] Stories: 24 → `done/` with the landing banner (what landed, gate - [ ] Stories: 24 → `status: done` with the landing banner (what landed, gate
numbers, the monitor three-argument deviation, the reply-agreement numbers, the monitor three-argument deviation, the reply-agreement
rule); 31 → `done/` with a banner saying it landed INSIDE 24 (the rule); 31 → `status: done` with a banner saying it landed INSIDE 24 (the
four forks and their decisions, link to the spec); 34 → `done/` four forks and their decisions, link to the spec); 34 → `status: done`
(C-builtin resolution, ids, vectors). Frontmatter status + folder (C-builtin resolution, ids, vectors). Frontmatter status + folder
move together (house rule). move together (house rule).
- [ ] Board: In-progress row cleared (marker doc deleted), Landed - [ ] Board: In-progress row cleared (marker doc deleted), Landed

View file

@ -1,7 +1,7 @@
# Blue/Green VM deployment — design spec # Blue/Green VM deployment — design spec
**Date:** 2026-08-03 **Date:** 2026-08-03
**Status:** approved (2026-08-03); ⏸ on hold (2026-08-21, developer decision — story iteration 26 moved to `stories/language-runtime-database/hold/`); implementation plan deferred until plans 5 + 6 ship **Status:** approved (2026-08-03); ⏸ on hold (2026-08-21, developer decision — story iteration 26 carries `status: hold`); implementation plan deferred until plans 5 + 6 ship
**Scope:** the writeonce runtime's in-process deployment subsystem **Scope:** the writeonce runtime's in-process deployment subsystem
**Supersedes:** §5–§6 of [`docs/plan/exploration/blue-green-vm/00-vision.md`](../../plan/exploration/blue-green-vm/00-vision.md) **Supersedes:** §5–§6 of [`docs/plan/exploration/blue-green-vm/00-vision.md`](../../plan/exploration/blue-green-vm/00-vision.md)

View file

@ -1,7 +1,7 @@
# `@table`, Relations, and Language-Integrated Query — Design # `@table`, Relations, and Language-Integrated Query — Design
> **Status: proposed** (story iteration 9b). Settles the three forks recorded in > **Status: proposed** (story iteration 9b). Settles the three forks recorded in
> [`09b-table-relations-query.md`](../../stories/language-runtime-database/done/09b-table-relations-query.md). > [`09b-table-relations-query.md`](../../stories/language-runtime-database/09b-table-relations-query.md).
> Plan: [`docs/plan/compiler/2026-08-15-employee-relations-query.md`](../../plan/compiler/2026-08-15-employee-relations-query.md). > Plan: [`docs/plan/compiler/2026-08-15-employee-relations-query.md`](../../plan/compiler/2026-08-15-employee-relations-query.md).
> Depends on iteration 9's engine plan > Depends on iteration 9's engine plan
> ([`2026-08-01-db-engine-binding.md`](../plans/2026-08-01-db-engine-binding.md)) > ([`2026-08-01-db-engine-binding.md`](../plans/2026-08-01-db-engine-binding.md))

View file

@ -4,7 +4,7 @@
> analysis held (driver-only, VM/`.wob`/GC untouched). Approved, then parked > analysis held (driver-only, VM/`.wob`/GC untouched). Approved, then parked
> the same day by directive, then unparked and executed. Decisions were > the same day by directive, then unparked and executed. Decisions were
> settled in > settled in
> [the iteration](../../stories/language-runtime-database/done/17-library-projects-internal.md) > [the iteration](../../stories/language-runtime-database/17-library-projects-internal.md)
> (four forks + impact analysis); this spec makes them buildable. The plan > (four forks + impact analysis); this spec makes them buildable. The plan
> follows after review. Board: [docs/00-status.md](../../stories/00-status.md). > follows after review. Board: [docs/00-status.md](../../stories/00-status.md).
> >

View file

@ -6,8 +6,8 @@
> >
> **Status: APPROVED 2026-08-20** (developer review); ⏸ on hold > **Status: APPROVED 2026-08-20** (developer review); ⏸ on hold
> (2026-08-21, developer decision — story iteration 18 sits in > (2026-08-21, developer decision — story iteration 18 sits in
> `stories/language-runtime-database/hold/`). Decisions were settled in > its story carries `status: hold`). Decisions were settled in
> [the iteration](../../stories/language-runtime-database/hold/18-memory-db-features.md); > [the iteration](../../stories/language-runtime-database/18-memory-db-features.md);
> this spec makes them buildable. The plan is authored > this spec makes them buildable. The plan is authored
> ([framework v2 plan](../plans/2026-08-20-framework-v2-memory-features.md)) > ([framework v2 plan](../plans/2026-08-20-framework-v2-memory-features.md))
> and held with it. > and held with it.

View file

@ -1,7 +1,7 @@
# The 8+11 concurrency arc — shards, fibers, actors: design # The 8+11 concurrency arc — shards, fibers, actors: design
> **Status: spec, awaiting review (2026-08-20).** The arc's decisions were > **Status: spec, awaiting review (2026-08-20).** The arc's decisions were
> settled in [iteration 8](../../stories/language-runtime-database/done/08-shard-actor-runtime.md) > settled in [iteration 8](../../stories/language-runtime-database/08-shard-actor-runtime.md)
> (refined) and the brainstorm of 2026-08-20 (this document's Decisions). > (refined) and the brainstorm of 2026-08-20 (this document's Decisions).
> Covers iterations 8 AND 11 as one arc; iteration 24 (chat) is its > Covers iterations 8 AND 11 as one arc; iteration 24 (chat) is its
> acceptance workload and gets its own spec after this one. The plan > acceptance workload and gets its own spec after this one. The plan

View file

@ -8,9 +8,9 @@ Board: [docs/00-status.md](../../stories/00-status.md)
**Scope:** the measurement backbone — a benchmark workload in `.wo`, a **Scope:** the measurement backbone — a benchmark workload in `.wo`, a
campaign driver script, a tracked baseline contract, and the durability campaign driver script, a tracked baseline contract, and the durability
proofs (restart persistence + crash battery), single- AND multi-shard. proofs (restart persistence + crash battery), single- AND multi-shard.
**Relates to:** [story 22](../../stories/language-runtime-database/done/22-durability-throughput-scale.md) **Relates to:** [story 22](../../stories/language-runtime-database/22-durability-throughput-scale.md)
(the four forks settled below), the landed arc (the four forks settled below), the landed arc
([story 8's guarantee contract](../../stories/language-runtime-database/done/08-shard-actor-runtime.md) ([story 8's guarantee contract](../../stories/language-runtime-database/08-shard-actor-runtime.md)
— the stage-3 delta this iteration records), iteration 23 (the durable — the stage-3 delta this iteration records), iteration 23 (the durable
write number it exists to beat), iteration 32 (the aged-store replay write number it exists to beat), iteration 32 (the aged-store replay
number its policy wants), stage-2 deviation 4 (the mutex-inbox number). number its policy wants), stage-2 deviation 4 (the mutex-inbox number).

View file

@ -8,9 +8,9 @@
> death notices, and timers as it needs them; the recorded chain > death notices, and timers as it needs them; the recorded chain
> "31 → 24" collapses into "24". Story 34's fork is resolved here too > "31 → 24" collapses into "24". Story 34's fork is resolved here too
> (C builtins, full set). Stories: > (C builtins, full set). Stories:
> [24](../../stories/language-runtime-database/refine/24-chat-websocket-workload.md) · > [24](../../stories/language-runtime-database/24-chat-websocket-workload.md) ·
> [31](../../stories/language-runtime-database/refine/31-actor-lifecycle.md) · > [31](../../stories/language-runtime-database/31-actor-lifecycle.md) ·
> [34](../../stories/language-runtime-database/refine/34-crypto-builtins.md). > [34](../../stories/language-runtime-database/34-crypto-builtins.md).
> Substrate: the landed 8+11 arc > Substrate: the landed 8+11 arc
> ([spec](2026-08-20-shard-fiber-arc-design.md)); every decision below > ([spec](2026-08-20-shard-fiber-arc-design.md)); every decision below
> reuses its machinery rather than growing parallel machinery. > reuses its machinery rather than growing parallel machinery.

View file

@ -1,136 +1,218 @@
# `wo-rt-c` — the writeonce runtime environment, in C # `runtime/` — `wovm`, the writeonce bytecode VM
A single-file C implementation of the **runtime layer** the writeonce language runs on — now at **phase E: a durable RAM database**. Writes follow the dual-write order — RAM apply, framed WAL record (`len|crc32|payload|COMMIT`) to a per-shard `fallocate`'d log, one group-commit `fdatasync` per loop tick, **HTTP ack only after the fsync completion**. Boot performs the **first load, hard drive → RAM**: each shard replays its snapshot + WAL tail into its arena slice in parallel before any accept arms; clean shutdown snapshots each slice and truncates the WAL; a `meta` file pins the shard count so a mismatched `WO_THREADS` refuses to boot. `./wo-rt wal-check <file>` validates a log offline. `WO_THREADS` pinned threads (default = online cores), each owning its own raw io_uring ring (`io_uring_setup` + mmap'd SQ/CQ rings + `io_uring_enter` — **no liburing**), its own `SO_REUSEPORT` listener (multishot accept), its own keep-alive connections, and its own slice of the one mlock'd mmap arena — shared-nothing, no locks. Steady state is one `io_uring_enter` syscall per loop tick. Thread 0 owns the `signalfd`; shutdown broadcasts through per-thread `eventfd`s, both watched via `POLL_ADD` SQEs. **Zero dependencies beyond libc + kernel uapi headers.** The kernel is the runtime. The C11 register VM that loads and runs `.wob` images. Sibling of the OCaml
`woc` compiler ([`compiler/README.md`](../compiler/README.md)): `woc` emits the
image, `wovm` executes it, and `woc build` appends an image to a copy of this
binary to produce one self-contained executable. libc only, direct syscalls, no
libraries.
This is the runtime-layer sibling of [`prototypes/wo-db/`](../prototypes/wo-db/) (the C++ query-layer prototype): a reference card showing, with no abstraction in the way, exactly which kernel primitives the production Rust runtime (`crates/rt/`) drives through `libc`. Same role, different layer. The embedded database engine ([`database/`](../database/src/CODE-LOGIC.md)) is
statically linked into every `wovm` and every test binary — one binary, no
separate database process.
``` Reasoning under the code: [`src/CODE-LOGIC.md`](src/CODE-LOGIC.md). Normative
prototypes/wo-db/ C++ what the LANGUAGE executes (parser, engine, transactions) contracts: [`docs/plan/oop-vm/00-wob-format.md`](../docs/plan/oop-vm/00-wob-format.md)
runtime/ C what the RUNTIME stands on (epoll, signalfd, sockets) (format, opcodes, builtin ids — `src/wob.h` is its machine-readable twin) and
crates/rt/ Rust the product — both layers, libc only [`08-builtin-surface.md`](../docs/plan/oop-vm/08-builtin-surface.md) (what each
``` builtin means in source terms).
> **Moved from `prototypes/wo-rt-c/` to root-level `runtime/`** (monorepo layout, per the OOP compiler + VM design spec). `wo-rt.c` is untouched — it stays the event-loop reference described below. The **`wovm` bytecode VM now lives under `runtime/src/`** — see [The wovm bytecode VM](#the-wovm-bytecode-vm-runtimesrc) for what shipped and [Debugging](#debugging) for how to step through it. > `wo-rt.c` in this directory is **not** part of that toolchain. It is the
> retired io_uring event-loop reference prototype the runtime's design was read
> off, kept for reading. See the "Historical" section at the bottom of this page
> — nothing in the shipped build compiles it.
## Build, run, poke ## Build, test
```bash ```bash
make # cc -O2 -Wall -Wextra -std=c11 -pthread — no libraries just wovm-build # -> runtime/wovm (the release binary)
./wo-rt # 127.0.0.1:8085 (WO_PORT=9000 WO_THREADS=4 ./wo-rt to override) just wovm-test # unit suites, both dispatch flavors, + CLI smoke, ASan+UBSan
curl localhost:8085/ # {"runtime":"wo-rt-c","loop":"epoll-et","threads":4, # in runtime/ directly:
# "shard":2,"shard_requests":[68,36,44,53]} make wovm # the release binary
curl -X POST localhost:8085/api/notes -d '{"title":"hello"}' make test # unit suites, computed-goto dispatch
# {"id":2,"title":"hello","shard":1} ← ids interleave per shard make test-iso # the same suites under -DWO_ISO_C (plain switch)
curl localhost:8085/api/notes # the connection's shard only — shared-nothing make wovm-asan # a separate sanitized binary, for corpus fixtures needing a leak/UB proof
# ctrl-C → signalfd on shard 0 → eventfd broadcast → all shards join make wovm-tsan # thread-sanitized, for the fiber/actor demos
``` ```
Each connection hashes to one shard for life (`SO_REUSEPORT` 4-tuple): a list may land on a different shard than the create that preceded it. That is the architecture, not a bug — cross-shard reads are a later phase / design decision (see the [architecture doc's improvements](../docs/plan/exploration/c-runtime/01-architecture.md)). Across both halves of the toolchain: `just oop-e2e` (the `woc` + `wovm`
conformance corpus) and `just oop-accept` (the full milestone gate — compile-time
budget, corpus under ASan, single-binary smoke, both unit suites, one command).
Or from the repo root: `just rt-c-demo`. ## Shipped features
## Module map - **Register interpreter** — fixed 32-bit instructions, Lua-style window-overlap
calls (callee r0 = caller slot A), dual dispatch: computed goto under GNU C,
`switch` under `-DWO_ISO_C`. Both flavors are gated so neither rots.
- **Owned objects with a runtime borrow word** — shared-reader count /
exclusive sentinel in every 16-byte header; violations trap `T_BORROW`. The
compiler elides provable sites; the VM enforces the residual ones.
- **Inferred GC, incremental tri-color mark-sweep** — GC-ness is a compiler
inference, never an annotation (`@gc` is rejected outright, WO-E104). Per-shard
traced list, snapshot-at-beginning roots, Yuasa deletion barrier, budgeted mark
and sweep slices — no stop-the-world by construction. Reference counting and
the Bacon–Rajan trial-deletion collector that shipped in iteration 2 were both
**removed** by iteration 7b. `wovm` pumps the collector to quiescence after the
entry returns (`WO_GC_BUDGET` steps per call, default 64; `WO_GC_TRACE=1`
prints one stderr line per step).
- **Deterministic drops** — kind-directed drop plans (scalar/owned/gcref/text/
bytes/float/multi/map), recursive over class fields and container elements.
- **Trap unwinding that never leaks** — per-method drop tables (pc → owned/gc
register masks); a trap walks every frame and frees what was live; structured
error `{code, line, method, msg}` via line tables, catchable with `try`/`catch`.
- **Validating loader** — bounds-checked parse, aligned copies, const-string
interning, full static validation (opcodes, registers, indexes, jump targets,
terminators, builtin arity, call windows, sorted vtables). What the loader
accepts, the interpreter trusts — no UB on any input.
- **Structural interfaces** — `ICALL` binary-searches sorted (class, slot,
method) vtable triples by receiver class.
- **Fibers and shard actors** — reduction-budget preemption, pinned per-core
shards, ownership-move message sends, bounded mailboxes (`WO_MAILBOX`, default
cap 1024) with a catchable `WO_T_ACTOR` trap on overflow, `call` parking the
caller for a typed scalar reply, and actor death that traps callers rather than
hanging them. Blocking stdlib calls park the fiber; the shard runs someone else.
- **The systems stdlib and the engine** — six module namespaces (`fs`, `time`,
`env`, `net`, `proc`, `json`) plus the free builtins: text and containers, the
`Float`/`Bytes` bridges, base64, and the digests `sha1`/`sha256`/`hmac_sha256`.
`WO_B_MAX` is 95. Database builtins reach the linked engine directly; the
legacy `DB_STUB` opcode survives only for images emitted against no engine.
- **CLI contract** — `wovm app.wob`: exit 0 = ran; exit 1 = trap, one stderr
line `trap CODE in METHOD at line N: MESSAGE`; exit 2 = usage/load failure.
`wovm --version` prints `wovm <VERSION>`. `WO_HEAP_MB` overrides the 64 MiB
arena; `WO_DATA` opts into durability; `WO_SHARDS` sets the shard count. Run
with no `.wob` argument, `wovm` checks its own trailer for an appended image
(`woc build`'s single-binary output) — a recognized-but-corrupt trailer fails
clearly on exit 2, never a crash.
Every block in `wo-rt.c` corresponds one-to-one to a module of the Rust runtime, which in turn mirrors Go's netpoller — the same lineage the docs trace: Interpreter ceilings, all in `src/wob.h`: 64 registers per frame
(`WO_MAX_REGS`), a 4096-slot value stack (`WO_STACK_SLOTS`), 256 frames
(`WO_MAX_FRAMES`), 64 shards (`WO_MAX_SHARDS`). The image format is at
`WOB_VERSION 6` (iteration 36's bitwise opcodes 42–46 moved it last).
| `wo-rt.c` block | Rust (`crates/rt/src/`) | Go (`.dev/reference/go/src/runtime/`) | Kernel reference card | ## File map
| --- | --- | --- | --- |
| `main` event loop (`epoll_create1` / `epoll_wait`, `EPOLLET`) | `runtime/netpoll_epoll.rs` | `netpoll_epoll.go` | [`linux/01-epoll.md`](../docs/plan/exploration/linux/01-epoll.md) |
| `sig_setup` (`sigprocmask` + `signalfd`) | `runtime/signalfd.rs` | signal mask handling | [`linux/04-signalfd.md`](../docs/plan/exploration/linux/04-signalfd.md) |
| `listener_bind` (`SOCK_NONBLOCK`, `accept4`-to-EAGAIN) | `http/listener.rs` | `net.Listen` + accept loop | `socket(7)` |
| `conn_drive` (read-to-EAGAIN, one buffer per fd) | `http/connection.rs` | `conn.Read` loop | the edge-triggered contract |
| `notes[]` store | `engine.rs` (BTreeMaps) | — | [`03-inmemory-engine.md`](../docs/runtime/database/03-inmemory-engine.md) |
## What it demonstrates
- **One thread owns each shard outright.** Accept, parse, store, respond — no locks, no worker pool, no connection migration. Scaling past one core is more shards ([`09-concurrency-scaleout.md`](../docs/plan/09-concurrency-scaleout.md)), never shared mutable state. The single cross-thread touch is the relaxed-atomic stats counters on `/` — monotonic, never on the data path.
- **Edge-triggered discipline.** Every registration sets `EPOLLET`; every readiness event is drained to `EAGAIN` (the accept loop and the read loop both). Get this wrong and connections silently hang — the reason the Rust module documents the same contract at the top of `netpoll_epoll.rs`.
- **Signals as fd events.** `SIGINT`/`SIGTERM` are blocked, then read from a `signalfd` on the same epoll — no async-signal-unsafe handler, no self-pipe trick.
- **RAM is the read path.** `GET /api/notes` touches a C array. The production engine is the same idea with MVCC and a WAL behind it.
## Architecture and roadmap
Documentation lives under `docs/` (repo convention) — this README stays here as the directory's orientation page only:
- [`docs/plan/exploration/c-runtime/01-architecture.md`](../docs/plan/exploration/c-runtime/01-architecture.md) — the runtime defined by tracing **one memory address** through user space, kernel space, and hardware under a million concurrent connections, plus seven improvement proposals (seqlock reads, registered buffers, zero-copy send, SQPOLL, …).
- [`docs/plan/exploration/c-runtime/00-plan.md`](../docs/plan/exploration/c-runtime/00-plan.md) — the phase sequence: **A → B → C → D → E → F, all ✅ shipped.**
- [`docs/plan/exploration/blue-green-vm/00-vision.md`](../docs/plan/exploration/blue-green-vm/00-vision.md) — where the runtime goes next: port-free transports, fibers, source embedded in the binary, agent-managed source over MCP, and the Blue/Green two-VM hot-swap deployment model.
## Measured (phase F, 20-core Linux 6.14, tmpfs data dir, `just rt-c-bench`)
Same C bench client (`bench/bench.c`, keep-alive, only 2xx counted) against both servers:
| Benchmark | **wo-rt-c** (8 shards, durable WAL, io_uring + group commit) | **Go `net/http`** (go1.25.1, 20 cores, no durability) | **Rust `wo`** (release, 8 shards, durable WAL, io_uring group commit)¹ |
| --- | --- | --- | --- |
| `GET /healthz` | **859,033 req/s** · p50 71 µs · p99 159 µs | 336,444 req/s · p50 70 µs · p99 1,277 µs | 746,340 req/s · p50 73 µs · p99 186 µs |
| `GET /` (JSON) | **671,312 req/s** · p99 180 µs | — | 692,671 req/s · p99 167 µs |
| `POST` write (tmpfs) | **618,343 commits/s** — fsync-acked · p99 194 µs | 320,516 req/s — RAM only, no WAL · p99 1,581 µs | 330,285 commits/s — fsync-acked · p99 360 µs |
| `POST` write (real ext4/NVMe) | — | — | **27,014 commits/s group commit vs 5,765 per-commit (4.7×)** · p50 2.2 ms |
| 10,000 idle conns | 0 errors | 0 errors | 0 errors |
¹ All numbers measured on a clean box with the same client (earlier parasite-contaminated runs superseded). The Rust column's history is the architecture roadmap, measured: 09a global mutex (74.9k writes/s) → 09b sharded engine (+51%) → 09c per-shard WAL (~1% durability cost) → **keep-alive: reads ×3.4 to 770k/s, durable writes ×1.9 to 331k/s, p99 under 350 µs everywhere**. Rust now beats Go on both columns *while fsyncing every write*, and sits within ~10% of the C prototype on reads — converging exactly as the same-architecture argument predicted. The one remaining C advantage is **group commit on io_uring** (one batched fsync + one syscall per tick vs per-commit fsync over epoll), which is the next port. Bonus finding: with `/tmp` accidentally full, the C runtime **refused to ack non-durable writes under ENOSPC** — the durability guarantee holding in an unplanned failure mode.
wo-rt-c on 8 cores outpaces Go on 20 with ~8× tighter p99 (Go's GC shows there) — while fsyncing every write Go doesn't. Honest caveats: `net/http` does full general-purpose HTTP; our parser is minimal; .NET was not installed on the box. **ACID under load:** three crash rounds (`kill -9` mid-bench at ~2M commits) all showed WAL records ≥ acked; isolation probe: 300 concurrent commits → 300 distinct ids; torn-tail records drop whole by CRC.
The crash-under-load test **found and fixed two real bugs** the lighter phase-D test missed: an ack-before-fsync race (`conn_continue` armed the send in the same tick the commit was staged) and an fd-reuse ABA hazard in ack parking (fixed with per-connection generation stamps). That is what phase F is for.
- [`docs/plan/exploration/c-runtime/02-single-binary.md`](../docs/plan/exploration/c-runtime/02-single-binary.md) — the end goal: how the `wo build` **single binary** runs on this runtime environment — the runtime kernel is statically linked into every writeonce app (Go model, nothing to install), with the catalog/routes/bytecode payload consumed at boot.
## Deliberate simplifications
Single-shot RECV re-armed per request (multishot recv + buffer rings are a phase-F improvement), one outstanding SQE per connection, fixed-size buffers, naive `"title"` extraction instead of a JSON parser, no `timerfd`. Requires kernel ≥ 5.19 (multishot accept). This file is for reading; `crates/rt` is for running writeonce.
## The wovm bytecode VM (runtime/src/)
Milestone 1 of the OOP track (spec: [`docs/superpowers/specs/2026-08-01-oop-compiler-vm-design.md`](../docs/superpowers/specs/2026-08-01-oop-compiler-vm-design.md), plan 1: [`docs/superpowers/plans/2026-08-01-wob-format-and-vm-core.md`](../docs/superpowers/plans/2026-08-01-wob-format-and-vm-core.md)) — a register VM that executes `.wob` bytecode (format: [`docs/plan/oop-vm/00-wob-format.md`](../docs/plan/oop-vm/00-wob-format.md)) with the full milestone-1 memory model. C11, libc only, same doctrine as `wo-rt.c`. `wovm` doesn't produce `.wob` itself — that's the OCaml `woc` front end's job ([`compiler/README.md`](../compiler/README.md), plan 3); this directory is the VM, not the compiler.
**Shipped features:**
- **Register interpreter** — fixed 32-bit instructions, Lua-style window-overlap calls (callee r0 = caller slot A), dual dispatch: computed goto under GNU C, `switch` under `-DWO_ISO_C` (both flavors gated in CI so neither rots).
- **Owned objects with a runtime borrow word** — shared-reader count / exclusive sentinel in every 16-byte header; violations trap `T_BORROW`. The compiler elides provable sites; the VM enforces the residual ones (hybrid model, spec §4).
- **`@gc` reference counting + budgeted cycle collection** — rc at zero frees immediately; possible cycles buffer as candidates (Bacon–Rajan trial deletion), collected in budgeted epochs per shard — no stop-the-world by construction. The `wovm` CLI pumps the collector to quiescence after the entry method returns (`WO_GC_BUDGET` steps per call, default 64; `WO_GC_TRACE=1` prints one stderr line per step) — scheduler-paced stepping between requests is sub-project 2, not this milestone.
- **Deterministic drops** — kind-directed drop plans (scalar/owned/gcref/text/multi/map), recursive over class fields and container elements.
- **Trap unwinding that never leaks** — per-method drop tables (pc → owned/gc register masks); a trap walks every frame and frees what was live; structured error `{code, method, line, message}` via line tables.
- **Validating loader** — bounds-checked parse, aligned copies, const-string interning, full static validation (opcodes, registers, indexes, jump targets, terminators, builtin arity, call windows, sorted vtables); what the loader accepts, the interpreter trusts — no UB on any input.
- **Structural interfaces** — `ICALL` binary-searches sorted (class, slot, method) vtable triples by receiver class.
- **Native containers + builtins** — `multi`/`map` with element-kind tags; `now/print/print_int/words/multi_*/map_*`; `DB_STUB` traps "engine not linked" until the DB engine binds (plan 5).
- **CLI contract** — `wovm app.wob`: exit 0 = ran; exit 1 = trap, one stderr line `trap CODE in METHOD at line N: MESSAGE`; exit 2 = usage/load failure. `WO_HEAP_MB` overrides the 64 MiB arena. Run with no `.wob` argument, `wovm` also checks its own trailer for an appended image (`woc build`'s single-binary output, [`docs/plan/oop-vm/00-wob-format.md`](../docs/plan/oop-vm/00-wob-format.md)'s "single-binary trailer" section) — a recognized-but-corrupt trailer fails clearly on exit 2, never a crash.
**File map:**
| File | What it is | | File | What it is |
| --- | --- | | --- | --- |
| `src/wob.h` | the `.wob` v1 contract: opcodes, field kinds, trap codes, builtin ids, limits, the 16-byte object header (machine-readable twin of the format doc) | | `src/wob.h` | the `.wob` contract: opcodes, field kinds, trap codes, builtin ids, limits, the 16-byte object header |
| `src/obj.h/.c` | arena allocator (16-byte size-class free lists ≤ 1024 B, malloc above), `wo_rt` context, object creation, strings | | `src/obj.h/.c` | arena allocator (16-byte size-class free lists ≤ 1024 B, malloc above), `wo_rt` context, object creation, `wo_str` |
| `src/borrow.h/.c` | the borrow word: acquire shared/exclusive, unconditional releases | | `src/borrow.h/.c` | the borrow word: acquire shared/exclusive, unconditional releases |
| `src/cont.h/.c` | native containers: growable `multi`, linear-scan `map` (content-compared text keys) | | `src/cont.h/.c` | native containers: growable `multi`, **linear-scan** `map` (content-compared text keys) — deliberate KISS, and O(n) per lookup |
| `src/gc.h/.c` | kind-directed drop dispatcher, rc inc/dec, cycle-candidate buffer, budgeted Bacon–Rajan collector | | `src/gc.h/.c` | the kind-directed drop dispatcher plus the incremental tri-color mark-sweep for traced objects |
| `src/loader.h/.c` | `.wob` parse + full static validation + mmap file path | | `src/loader.h/.c` | `.wob` parse + full static validation + the mmap file path |
| `src/vm.h/.c` | the interpreter: dispatch, frames, traps, drop-map unwinding, `ICALL` | | `src/vm.h/.c` | the interpreter: dispatch, frames, traps, drop-map unwinding, `ICALL`, shard/engine startup |
| `src/builtin.h/.c` | builtin dispatcher | | `src/builtin.h/.c` | the builtin dispatcher — the single entry point the interpreter calls, forwarding to `sysio.c` and `json.c` |
| `src/main.c` | the `wovm` CLI: arg parsing, self-embedded-trailer detection, the post-exit gc pump | | `src/sysio.c` | the OS half: `fs`, `time`, `env`, `net`, `proc` |
| `src/json.c` | `json.encode` / `json.decode`, driven by class metadata |
| `src/crypto.c/.h` | SHA-1, SHA-256, HMAC-SHA256 (iteration 34), vector-verified |
| `src/park.c/.h` | the park plane: raw io_uring ABI (no liburing) and the epoll fallback, fiber parking, per-call deadlines |
| `src/main.c` | the `wovm` CLI: find an image (argument or embedded trailer), build argv, call the entry, map its result to an exit code, pump the collector |
| `test/t.h` | 20-line assert harness (no framework) | | `test/t.h` | 20-line assert harness (no framework) |
| `test/wob_build.h/.c` | in-memory `.wob` assembler — the second, independent encoding of the format; builder/loader disagreements fail tests | | `test/wob_build.h/.c` | in-memory `.wob` assembler — a second, independent encoding of the format, so builder/loader disagreements fail tests |
| `test/test_*.c` | 13 suites, one binary each, ASan+UBSan | | `test/test_*.c` | 18 suites, one binary each, ASan+UBSan: arena, borrow, builtin, cont, crypto, cycle, fiber, icall, loader, mailbox, obj, objops, rc, table, unwind, vm, wal, wobbuild |
| `test/mkwob.c` | fixture generator for the CLI smoke | | `test/mkwob.c` | fixture generator for the CLI smoke |
| `test/cli_smoke.sh` | end-to-end exit-code/stderr-shape check | | `test/cli_smoke.sh` | end-to-end exit-code/stderr-shape check |
**Gates:** `just wovm-build` · `just wovm-test` (unit suites + ISO flavor + CLI smoke, all ASan-clean) · in `runtime/`: `make test`, `make test-iso`, `make wovm`, `make wovm-asan` (sanitized binary for corpus fixtures that need a leak/UB proof, e.g. `tests/corpus/gc/`). Across both halves: `just oop-e2e` (the `woc` + `wovm` conformance corpus, [`compiler/README.md`](../compiler/README.md)) and `just oop-accept` (milestone 1's full acceptance gate — spec success criteria + both unit suites, one command). Note where io_uring is and is not: `src/park.c` drives it for the fiber and
network plane, while the WAL commit path in `database/src/wal.c` is still a plain
per-commit `fdatasync`. Moving the WAL onto the rings is iteration 23.
## Debugging ## Debugging
VS Code: `.vscode/launch.json` ships four configs (needs the *C/C++* extension, `ms-vscode.cpptools`): VS Code: `.vscode/launch.json` ships four configs (needs the *C/C++* extension,
`ms-vscode.cpptools`):
1. **wovm: debug hello.wob** — rebuilds `wovm` at `-O0 -g`, regenerates fixtures, breaks anywhere in the VM. 1. **wovm: debug hello.wob** — rebuilds `wovm` at `-O0 -g`, regenerates
fixtures, breaks anywhere in the VM.
2. **wovm: debug a .wob file** — same, prompts for the image path. 2. **wovm: debug a .wob file** — same, prompts for the image path.
3. **wovm: debug unit test** — pick one of the 13 ASan test binaries (already `-g`), step through it. 3. **wovm: debug unit test (ISO dispatch)** — pick one of the ASan test
4. **wo-rt: debug server** — the event-loop reference at `-O0 -g`, `WO_THREADS=1` so one shard owns everything. binaries (already `-g`), step through it.
4. **wo-rt: debug server (1 shard)** — the retired event-loop reference at
`-O0 -g`, `WO_THREADS=1` so one shard owns everything.
How to work on the VM under a debugger: How to work on the VM under a debugger:
- **Step the ISO flavor, not the computed-goto one.** The goto interpreter jumps label-to-label and single-stepping is disorienting. Test binaries have an ISO twin (`build/iso_test_*`, plain `switch`) where `next`/`step` behave normally. For `wovm` itself, build `make -B wovm CFLAGS='-O0 -g -std=c11 -DWO_ISO_C'`. - **Step the ISO flavor, not the computed-goto one.** The goto interpreter jumps
- **`break vm_trap`** — one breakpoint catches every trap at the moment of failure, with the trapping frame intact (`vm->frames[vm->depth-1]`, `pc` already rewound to the faulting instruction). `vm_unwind` is the next frame down if you're chasing a leak-on-trap. label-to-label and single-stepping is disorienting. Test binaries have an ISO
- **Other load-bearing breakpoints:** `wo_load_buf` (validation rejects), `recv_check` (residual field checks), `wo_builtin` (all builtins), `wo_gc_step` (cycle collection epochs). twin (`build/iso_test_*`, plain `switch`) where `next`/`step` behave normally.
- **ASan under gdb:** `ASAN_OPTIONS=abort_on_error=1` makes the first report SIGABRT so the debugger stops on it with the full stack; without gdb the report alone usually names the exact free you missed. For `wovm` itself, build
- **CLI knobs:** `WO_HEAP_MB=1 ./wovm app.wob` forces early `T_OOM` paths; exit codes 0/1/2 are stable for scripting. `make -B wovm CFLAGS='-O0 -g -std=c11 -DWO_ISO_C'`.
- **gdb without VS Code:** `gdb --args ./wovm build/hello.wob`, or `gdb ./build/iso_test_unwind`. - **`break vm_trap`** — one breakpoint catches every trap at the moment of
failure, with the trapping frame intact (`vm->frames[vm->depth-1]`, `pc`
already rewound to the faulting instruction). `vm_unwind` is the next frame
down if you're chasing a leak-on-trap.
- **Other load-bearing breakpoints:** `wo_load_buf` (validation rejects),
`recv_check` (residual field checks), `wo_builtin` (all builtins), `wo_gc_step`
(mark-sweep slices).
- **ASan under gdb:** `ASAN_OPTIONS=abort_on_error=1` makes the first report
SIGABRT so the debugger stops on it with the full stack; without gdb the report
alone usually names the exact free you missed.
- **CLI knobs:** `WO_HEAP_MB=1 ./wovm app.wob` forces early `T_OOM` paths; exit
codes 0/1/2 are stable for scripting.
- **gdb without VS Code:** `gdb --args ./wovm build/hello.wob`, or
`gdb ./build/iso_test_unwind`.
---
## Historical: `wo-rt.c`
A single-file C server that was the **runtime-layer reference prototype** —
built to find out which kernel primitives a writeonce runtime should stand on,
by writing them with no abstraction in the way. Its design conclusions are what
`src/park.c` and the shard model implement. It is not part of the toolchain, no
gate builds it, and it shares no code with `wovm`.
It reached phase F of its own plan
([`docs/plan/exploration/c-runtime/00-plan.md`](../docs/plan/exploration/c-runtime/00-plan.md),
phases A→F all shipped): `WO_THREADS` pinned threads, each owning a raw io_uring
ring (`io_uring_setup` + mmap'd SQ/CQ rings + `io_uring_enter`, no liburing), its
own `SO_REUSEPORT` listener with multishot accept, its own keep-alive
connections, and its own slice of one mlock'd mmap arena — shared-nothing, no
locks, one `io_uring_enter` per loop tick in steady state. Writes followed the
dual-write order: RAM apply, framed WAL record to a per-shard `fallocate`'d log,
one group-commit `fdatasync` per tick, **HTTP ack only after the fsync
completes**. Boot replayed each shard's snapshot + WAL tail in parallel before
any accept armed; `./wo-rt wal-check <file>` validated a log offline.
Build and poke it, if you want to read it running:
```bash
make -C runtime wo-rt # cc -O2 -Wall -Wextra -std=c11 -pthread, no libraries
./runtime/wo-rt # 127.0.0.1:8085 (WO_PORT=9000 WO_THREADS=4 to override)
```
Its measured numbers, kept as the historical record they are — bench client
`bench/bench.c` (keep-alive, only 2xx counted), Go reference in `bench/goref/`,
20-core Linux 6.14, tmpfs data dir. These are **not** `wovm` numbers; the
shipped VM's measurements live in `bench/baseline.json` and
[`docs/plan/perf-targets.md`](../docs/plan/perf-targets.md).
| Benchmark | **wo-rt-c** (8 shards, durable WAL, io_uring group commit) | **Go `net/http`** (go1.25.1, 20 cores, no durability) |
| --- | --- | --- |
| `GET /healthz` | **859,033 req/s** · p50 71 µs · p99 159 µs | 336,444 req/s · p50 70 µs · p99 1,277 µs |
| `GET /` (JSON) | **671,312 req/s** · p99 180 µs | — |
| `POST` write (tmpfs) | **618,343 commits/s** — fsync-acked · p99 194 µs | 320,516 req/s — RAM only, no WAL · p99 1,581 µs |
| 10,000 idle conns | 0 errors | 0 errors |
Honest caveats as recorded then: `net/http` does full general-purpose HTTP and
this parser was minimal; tmpfs makes fsync nearly free, so the durable column
flatters itself. ACID probes: three `kill -9` rounds mid-bench at ~2M commits all
showed WAL records ≥ acked; 300 concurrent commits → 300 distinct ids; torn-tail
records dropped whole by CRC. The crash-under-load test found two real bugs the
lighter phase-D test missed — an ack-before-fsync race and an fd-reuse ABA hazard
in ack parking — which is what the phase existed for.
Deliberate simplifications it never outgrew: single-shot RECV re-armed per
request, one outstanding SQE per connection, fixed-size buffers, naive `"title"`
extraction instead of a JSON parser, no `timerfd`. Requires kernel ≥ 5.19
(multishot accept).
Design docs it fed:
[`c-runtime/01-architecture.md`](../docs/plan/exploration/c-runtime/01-architecture.md)
(the runtime traced through one memory address, plus seven improvement
proposals),
[`c-runtime/02-single-binary.md`](../docs/plan/exploration/c-runtime/02-single-binary.md)
(how a single binary runs on this runtime),
[`blue-green-vm/00-vision.md`](../docs/plan/exploration/blue-green-vm/00-vision.md)
(port-free transports, fibers, embedded source, two-VM hot swap), and the
kernel-primitive cards under
[`exploration/linux/`](../docs/plan/exploration/linux/00-linux.md).

View file

@ -1,6 +1,8 @@
# `runtime/src` — how the VM is put together # `runtime/src` — how the VM is put together
Written 2026-08-14, when the runtime grew the systems stdlib and json. Read Written 2026-08-14, when the runtime grew the systems stdlib and json; the
file table was brought back in line with `src/` on 2026-08-26 (`park.c` and
`crypto.c` had arrived with iterations 11/35 and 34 and were missing). Read
this before changing a file here; the normative contracts are this before changing a file here; the normative contracts are
[`docs/plan/oop-vm/00-wob-format.md`](../../docs/plan/oop-vm/00-wob-format.md) [`docs/plan/oop-vm/00-wob-format.md`](../../docs/plan/oop-vm/00-wob-format.md)
(the `.wob` format, opcodes, builtin ids) and (the `.wob` format, opcodes, builtin ids) and
@ -22,6 +24,8 @@ the first: constants there and prose there must never disagree.
| `builtin.h/.c` | the pure builtins: print, containers, text | | `builtin.h/.c` | the pure builtins: print, containers, text |
| `sysio.c` | the OS half: `fs`, `time`, `env`, `net`, `proc` | | `sysio.c` | the OS half: `fs`, `time`, `env`, `net`, `proc` |
| `json.c` | `json.encode` / `json.decode`, driven by class metadata | | `json.c` | `json.encode` / `json.decode`, driven by class metadata |
| `park.c/.h` | the park plane (iteration 11 + 35, added after this doc was first written): the raw io_uring ABI mirrored from uapi with no liburing, the epoll fallback, fiber parking and unparking, and the per-call deadlines the `_dl` net members lower to. This is where a blocking builtin becomes "the shard runs someone else" |
| `crypto.c/.h` | SHA-1, SHA-256, HMAC-SHA256 (iteration 34, builtin ids 85–87): hand-rolled per the no-dependency doctrine, accepted against FIPS 180 / RFC 2202 / RFC 4231 vectors in `test/test_crypto.c` |
| `main.c` | the CLI: find an image (argument or embedded trailer), build argv, call the entry, map its result to an exit code; post-exit gc pump (a rootless cycle frees everything unreachable, in budgeted slices) | | `main.c` | the CLI: find an image (argument or embedded trailer), build argv, call the entry, map its result to an exit code; post-exit gc pump (a rootless cycle frees everything unreachable, in budgeted slices) |
`builtin.c`'s `wo_builtin` is the single entry point the interpreter calls; it `builtin.c`'s `wo_builtin` is the single entry point the interpreter calls; it

View file

@ -1,11 +1,19 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Scan every .md in repo, extract links, report broken local targets + bad anchors.""" """Scan every repo-authored .md, extract links, report broken local targets + bad anchors.
`.dev/` and `.superpowers/` are excluded: both are gitignored, per-developer
material this repo does not author -- vendored plugin-skill copies and cloned
reference projects (whose own docs use site-build-relative links that cannot
resolve on disk). Including them made the gate report the same ~23 failures
forever, which is a gate nobody reads. Everything the repo actually ships is
still scanned.
"""
import os, re, sys, urllib.parse import os, re, sys, urllib.parse
from collections import defaultdict from collections import defaultdict
ROOT = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 else ".") ROOT = os.path.abspath(sys.argv[1] if len(sys.argv) > 1 else ".")
SKIP_DIRS = {".git", "node_modules", "_build", "target", "dist"} SKIP_DIRS = {".git", "node_modules", "_build", "target", "dist", ".dev", ".superpowers"}
INLINE = re.compile(r'(?<!!)\[([^\]\n]*)\]\(\s*<?([^)\s>]+)>?(?:\s+"[^"]*")?\s*\)') INLINE = re.compile(r'(?<!!)\[([^\]\n]*)\]\(\s*<?([^)\s>]+)>?(?:\s+"[^"]*")?\s*\)')
REFDEF = re.compile(r'^\s{0,3}\[([^\]]+)\]:\s*<?(\S+)>?', re.M) REFDEF = re.compile(r'^\s{0,3}\[([^\]]+)\]:\s*<?(\S+)>?', re.M)