- five stories status: done; 00-story records the one-run landing - spec History: three implementation amendments (Signal record not scalar, caller-owned stdio fds, handler-latch instead of signalfd) - board NEXT PLAN entry with measured findings (zero transport code added; the tty-across-the-socket handover proven; the double-raw refusal restoring the terminal — the "bug" that was the design working); section rows flipped; graph nodes green - CODE-LOGIC.md: the runtime-v2 section - full belt quoted on the board: suites 0 fail both flavors (test_proc 193/0, test_term 60/0), woc 557/0, subprocess 12/0, site 23/0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit bc1b4f070693eb755ad6a9fd0c853fb3e2bda347)
3.5 KiB
| track | iteration | status | readiness |
|---|---|---|---|
| runtime-v2 | 1 | done | ready |
runtime-v2 1 — streaming subprocess: a long-lived child as a peer
Part of Story — runtime-v2: the runtime beyond sockets. Spec:
2026-09-01-runtime-v2-design.md(track-wide brainstorm, approved 2026-09-01 — the forks below are settled). Iteration 42's named follow-up, promoted to this track's opening slice. 42 built the machinery a streaming form reuses whole: thewo_childregistry, the pidfd wait, the epoll bundle, the cap/refusal doctrine, the ownership sweeps (fib_reap/wo_vm_destroy/stop).The problem.
proc.run/proc.run_dlare one-shot: nothing can talk to a child while it runs, and a child that legitimately runs for hours (a shell under wmux, a browser under the CDP driver, a script under skillhost) has no shape at all. Output must reach the owning actor as it happens, stdin must reach the child, and exit must arrive as an event — all without violating a single 42 guarantee.
Info — the forks, settled (brainstorm 2026-09-01)
- Verb shape:
proc.spawn(cmd, args) -> ?Child— a new verb, and the handle is a RECORD, not an actor:Child {id, in, out, err}, all Int-shaped, joining the predeclared records. - Transport: PULL, and it already exists. The child's fds are
ordinary conn-like values;
net.read_dl/net.write_dl/net.closedrive them unchanged through the park plane. This iteration adds NO transport code at all — only acquisition, ownership and exit. - The mailbox-cap collision never starts — nothing is pushed. The kernel pipe is the backpressure: an owner that stops reading makes the child block on write, exactly tmux under a slow client.
- stdin mirror settled by the same move:
net.write_dlonchild.inparks with a deadline; torn-write semantics as on sockets. - Bounds: the idle deadline is
read_dl's per-call ms; output caps are the caller's read sizes (no runtime buffer exists); exit isproc.wait_dl(id, ms) -> ?Intparking on the slot's pidfd — one waiter per id, a second refuses by name.proc.signal(id, sig)completes the surface. A death-notice verb is refused: a two-line fiber composeswait_dlinto an event.
Ownership (stated, not forked): the spawning ACTOR owns the child (the
program, when spawned outside one). Actor death, engine stop and
wo_vm_destroy kill, reap and close — 42's doctrine, streaming edition.
Ceiling stays 32 per shard, failing closed by name.
Acceptance sketch (firmed at brainstorm)
- A child that emits a line a second: each line reaches the owning actor while the child lives — not after it exits.
- stdin round trip: feed a
cat-like child, read the echo back. - The chatty child against whatever fork 3 picked: the declared behavior happens, by name, and the shard never stalls.
- Exit: the owner learns the code as an event; the registry slot is released; fd count flat (the 42 measurement legs, streaming edition).
- Stop/unwind: identical guarantees to 42, proven with a LIVE stream.
- Gate legs land in
runtime/test/test_proc.cbeside 42's.
Consumers
wmux 1 (the shell under every pane), skillhost 28 (stdin/stdout transport was half its named gap), the zen study's CDP driver (spawn the browser, then talk to it). The alacritty stage-A "headless PTY runner" is this plus runtime-v2 2.