writeonce/docs/00-code-review.md
shoney.arickathil ad1ad8361c docs(audit): fix stale docs against the code (TLS, net.connect, RNG, lang-41); jarvis deps
Code is the source of truth; these claims no longer matched runtime/src:

- "net.connect does not exist" — landed 2026-09-07 (id 110); net.connect_tls /
  read_tls / write_tls (115-117) + net.accept_tls (118), WO_B_MAX 118. Fixed
  in jarvis 00-story (problem statement + architecture + out-of-scope), porch
  00-story (proxy middleware row), rv2 7 (push-collector fork), 00-code-review
- "TLS: none / proxy-mandated forever" — retired by rv2 9 (in-process TLS both
  directions). Fixed in porch + web-app + site example READMEs (proxy is now a
  deployment choice; HSTS row), 00-code-review
- "no RNG anywhere in the runtime" — imprecise: the runtime has a getrandom(2)
  source since rv2 9 (TLS ephemerals), but nothing exposes it to .wo yet.
  Fixed in CODE-LOGIC (digests), lang 34, porch 2, status lang-39 row
- "porch 9 blocked on language 41" — lang 41 fixed 63065ff. Fixed in porch 1,
  jarvis 00-story, status NEXT PLAN, dependency graph (L41 done, P9 ready)
- dependency graph §7 rewritten: the runtime side is done; jarvis 1 waits only
  on porch (developer's porch-first order). Adds jarvis 1's dependency table +
  the build order that satisfies it
- 00-code-review: a dated 2026-09-09 re-verification appended (record kept)
- site README lives in the writeonce-site submodule: committed there, pointer
  bumped here

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit f1049dd9b7c7770da28bcabfc1cb1324621e7ee6)
2026-09-15 01:16:13 +02:00

158 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Code Review: log-watcher Compilation Requirements
This document was a gap analysis of what the `woc` front end needs before the
log-watcher sample compiles. Its findings were extracted on 2026-08-10 into
[`superpowers/specs/2026-08-10-logwatcher-gap-closure-design.md`](superpowers/specs/2026-08-10-logwatcher-gap-closure-design.md)
and the plans it amends; its Phase 1–4 roadmap is retired in favour of the
approved story iterations. See [`00-status.md`](stories/00-status.md) for current
status.
## Standing critique (undated, author unrecorded)
Native speed — the big one. Everything is interpreted: ~40× behind Go on raw compute, no JIT, no AOT-to-native. The scheduling primitives win benchmarks; a compute-bound handler loses them all back. There is also no Float type at all (the storefront prices in cents for a reason), no SIMD story, and fixed interpreter ceilings (4096 register slots, 256 frames, ~42 KiB per fiber until growable contexts land).
- Language expressiveness. No generics — the cache stores Text and tells you to json.encode; no function values or closures (doctrine, but it's why every handler is a class with one method); byte-based strings with no Unicode awareness; no Result-style error values (traps + try only); pattern matching is a switch, not destructuring. Some of this is deliberate rejection, but "deliberate" doesn't make the expressiveness appear.
- Concurrency holes the arc hasn't closed. send is one-way — no reply/request-response primitive (my own benchmarks couldn't await the actor and had to sleep); no supervision, links, or actor death (actors live until process end); unbounded mailboxes with zero backpressure; no timers beyond sleep; round-robin placement with no work stealing; multi-shard DB access still traps (stage 3 unbuilt); accept lives on one shard.
- Production plumbing. No TLS anywhere (proxy-mandated forever), no HTTP/2 or WebSockets yet, no crypto primitives (blocked on the bit-ops-vs-builtins fork), observability is print/stderr — no metrics, tracing, or profiler; no debugger, no LSP (discussed, never built); deps are git-rev-only with no registry, no transitive resolution, no semver; blue-green deploy and schema migrations are recorded futures, not features.
- Proof maturity. 22's benchmark battery has never run — every number so far is a scratch measurement on one machine; TSan covers one demo; no fuzzing, no CI beyond local just, and the whole ecosystem is one framework, five samples, and one committed consumer. The honest summary: the architecture is ahead of the product — the doctrine bets (ownership+inference, actors, one binary, io_uring) are landing and measurable, while the surface a developer touches daily (types, tooling, ecosystem) is years behind the languages it benchmarks against.
## 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
does not, and two problems are worse than stated.**
### Rejected: "~40× behind Go on raw compute"
Unsourced. Nothing in the repo measures compute against Go. The only Go
comparison on record runs the other way and on a different workload:
[`plan/exploration/c-runtime/00-plan.md`](plan/exploration/c-runtime/00-plan.md)
records the C prototype at **908,916 reads/s and 643,250 fsync-acked
commits/s on 8 shards vs Go `net/http` at 495k/355k with ~8× worse p99** on a
20-core box — I/O-bound serving, not compute. `runtime/bench/goref/` holds a
Go reference program, but no compute-bound result from it is written down
anywhere.
The figure also contradicts this document's own closing sentence: the
doctrine bets cannot be "measurable" while iteration 22 has never run. Drop
the number or produce the benchmark.
### Understated
- **`map<K,V>` lookup is a linear scan.** `runtime/src/cont.h`: parallel
key/value arrays, "linear scan lookup — deliberate milestone-1 KISS". Every
`get`/`has`/`set` is O(n). For a language whose framework routes requests
and whose planned cache is keyed, this outranks the missing `Float` as a
compute problem — and the compute paragraph never mentions it.
- **The multi-shard DB gap is structural, not a missing flag.**
`wo_engine_start` (`runtime/src/vm.c`) `memset`s each worker VM to zero, so
`rt.db` and `rt.wal` are NULL by construction off the primary;
`wo_builtin_db` then returns `WO_T_DB "database engine not initialized"`. It
is a clean trap rather than a crash — but any multi-shard program that
touches the database is broken today.
### Confirmed, with corrections to the numbers
| Claim | Evidence |
| --- | --- |
| interpreted only, no JIT, no AOT | no `jit` anywhere; `specs/2026-08-01-oop-compiler-vm-design.md` records AOT-to-C as rejected |
| no `Float`, no `Bytes` | absent from `compiler/src/types.ml`; no float builtin; `net.read` returns `Text` |
| no SIMD | nothing in `runtime/src` or `compiler/src` |
| fixed interpreter ceilings | **mislabeled**: 4096 is the value **stack** (`WO_STACK_SLOTS`), registers are 64 per frame (`WO_MAX_REGS`), frames 256 (`WO_MAX_FRAMES`) — all `runtime/src/wob.h`. Unlisted: `WO_MAX_SHARDS 64`, `WO_ARENA_MAX_CLASS 1024`, `WO_MAX_CATCH 64`. ~42 KiB/fiber matches the arc spec |
| no generics | `multi T` / `map<K,V>` are runtime-provided native classes by design |
| no function values or closures | no such form in the lexer keyword set or typechecker |
| byte-based strings, no Unicode | `WO_B_BYTE_AT`, ASCII `WO_B_TO_LOWER`; `json.c` decodes BMP only |
| no Result-style errors | traps + `try`/`catch` only |
| pattern matching is a switch | `KwSwitch`/`KwCase`; no destructuring form |
| `send` is one-way | `WO_B_SEND=69` is the last builtin (`WO_B_MAX 69u`) — no ask/reply opcode |
| no supervision, links, actor death | nothing in the runtime |
| unbounded mailboxes, no backpressure | `vm.h`: `msgs` is a "FIFO ring, growable"; `mcap` only grows |
| no timers beyond sleep | `time.sleep` is the only one; no `timerfd` in the runtime |
| round-robin placement, no work stealing | `eng_rr` cursor, `vm.c` |
| accept on one shard | one listener, `SO_REUSEADDR` only — no `SO_REUSEPORT` |
| no TLS | the only `tls` in the runtime is thread-local storage (`wo_tls_vm`) |
| no HTTP/2 or WebSockets | framework `http/` is parse/serve/types/auth/multipart; h2c parked per `00-status.md` |
| no crypto primitives | none |
| observability is print/stderr | `WO_B_PRINT`, `PRINT_INT`, `PRINT_ERR`; no counters, tracing, or profiler |
| no debugger, no LSP | neither exists |
| deps git-rev only | no semver, registry, or transitive resolution in the compiler |
| blue-green + migrations are futures | iteration 26 still pending |
| 22's battery never run | 22 is ⬜ "needs a spec first"; no `bench/baseline.json`, no `just db-bench`; `runtime/bench/` is the retired C prototype's harness |
| TSan covers one demo | only `scripts/fibers-accept.sh` builds and runs `wovm_tsan` |
| no fuzzing, no CI | no `.github/`, no fuzz target |
| one framework, five samples, one consumer | exact: `writeonce-serve`; employee, employee-list, fibers, gc-cycle, log-watcher; `web-app` |
### Consequence
The iteration order in
[`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 `#`
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.
## Re-verification 2026-09-09
Code is the source of truth; the standing critique's production-plumbing row and
the 2026-08-26 re-verification have been overtaken by shipped work. Kept as
written above; corrected here:
- **"No TLS anywhere (proxy-mandated forever)"** — false since 2026-09-09.
Runtime-v2 9 landed hand-rolled TLS 1.3 in-process, both directions:
`net.connect_tls`/`net.read_tls`/`net.write_tls` (ids 115–117) and
`net.accept_tls` (118), live-gated (`just tls`, `just tls-server`). The
proxy-termination doctrine is retired.
- **"no crypto primitives"** — false. `crypto.c` holds SHA-1/SHA-256/HMAC (iteration
34), ChaCha20-Poly1305, AES-GCM, HKDF, X25519, RSA-PSS/PKCS1 + ECDSA-P256 verify
*and* constant-time sign (RFC 6979), and an X.509 layer — all RFC/NIST-vector
gated.
- **"there is no `net.connect` anywhere in `runtime/src/`"** — false since
2026-09-07 (`WO_B_NET_CONNECT` = 110; the id ceiling is now `WO_B_MAX` 118).
- **"send is one-way — no reply/request-response"** — overtaken by iteration 24's
`call`; **"no supervision, links, or actor death"** — overtaken by iteration 24's
monitors/death notices; the cross-shard message double free that shadowed the
actor path (language 41) is fixed (`63065ff`, marshal on the crossing).
- Still true: no HTTP/2, no debugger/LSP, git-rev-only deps, and — precisely —
**no RNG exposed to `.wo`** (the runtime has a `getrandom` source since rv2 9,
unsurfaced until porch 2's `random_bytes`).