Iteration 24 closes, absorbing 31 and 34. No code in this commit. - stories 24, 31, 34 -> `status: done`, each with a landing banner. 24's records the gate numbers and BOTH disclosed deviations: monitor takes three arguments (the caller may be `main`, which has no mailbox) and a v1 `call` reply is a typed scalar (which is what let the agreement be checked at compile time, WO-E226). 31's notes it landed INSIDE 24 and that a fifth mechanism it never anticipated came out of proving the gate — the drain guarantee (40). 34's names the gap it did NOT close: still no RNG, so CSRF/sessions stay blocked - board: in-progress row cleared, marker doc deleted (convention), the standup entry in the six-question shape, chain note — next link is databasev2 4 (io_uring group-commit, chain 5) - graph: PUBSUB2 (pub/sub + WebSockets, "rejected until here") -> done - porch ledger: a WebSocket/pub-sub row added; the cancellation row now says what it actually waits on rather than repeating "the arc"; the README's "no WebSockets/SSE" limitation was stale — WebSockets are supported, SSE and chunked encoding are not - CODE-LOGIC: runtime/src gains the actor-lifecycle section (call, death, the cap counter's sender/home-thread split, the monitor walk, the timer list), the drain guarantee, and the digest section; docs/examples/chat gains its own — actor topology, WHY two actors per connection, fd ownership, and the shutdown choreography Battery after the doc edits: wovm-test 36 suites 0 fail, woc-test exit 0, oop-e2e 119/0, chat 11/0, web-app 46/0, linkcheck clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.7 KiB
| iteration | status | chain |
|---|---|---|
| 24 | done | 4 |
Iteration 24 — chat: the WebSocket pub/sub driving workload
Format:
product/story-iteration-template. Part of Story — one language, one runtime, one database, one binary.Inserted 2026-08-20 (concurrency-chain refinement): the 8+11 arc's driving workload, the role log-watcher played for iterations 3–7. Needs its spec AFTER the arc's — it lands at the arc's end and proves it.
RE-SEQUENCED 2026-08-21: fourth in the chain, stage 3 → 22 → 31 → 24 → 23 → 32 — chat cannot be written honestly before iteration 31 (request/response, bounded mailboxes, actor death, timers). Iteration 19 LANDED 2026-08-20, so Bytes is available for frame parse/serialize.
✅ LANDED 2026-08-27 (branch
chat-ws-lifecycle, merged to mastered5334d). Ten tasks: crypto (T1), bounded mailboxes (T2),call/reply and actor death (T3),monitor(T4),time.after(T5), the WS upgrade seam (T6), the pure-.woframe codec (T7), the chat sample (T8), the gate (T9), this closeout (T10). It absorbed 31 and 34, which land with it.Gate —
just chat, 11 checks, 0 failures at the full 1000-client soak: handshake with an independently recomputed accept-key, the functional matrix (presence, broadcast, room isolation, leave) on bothWO_IObackends and on a single shard, the 1k hot-room soak, the fd invariant, the SIGTERM drain,WO_MAILBOX=8backpressure, and an ASan run with zero leaks. Battery alongside: runtime 36 suites 0 fail, compiler 556 checks, corpus 119 checks. The sample logs to/tmp/chat.log.Two disclosed deviations from the spec.
monitortakes three arguments (watched, observer, msg) rather than two, because the caller may bemain, which has no mailbox and cannot be an implicit observer. And acallreply is a typed scalar in v1 — which is what let the agreement be checked at compile time (WO-E226) instead of carried as a tagged value.What finishing the gate found. Making every leg start its own server exposed a real runtime bug the warmed soak server had been hiding: on a fresh server, 5 of 16 SIGTERM drains left a client at EOF with no close frame. It was not this sample's fault — the fix is an engine guarantee, split out as 40. Design notes:
docs/examples/chat/CODE-LOGIC.md.
Why this iteration exists
Everything the framework ledger parks behind concurrency — WebSockets, pub/sub, streaming, per-request cancellation, the keep-alive parking retirement — needs a workload that actually exercises long-lived connections and cross-shard broadcast, or the arc ships mechanism without proof. Chat is the smallest honest such workload: rooms, N concurrent clients, fan-out on every message, presence on connect/disconnect — one binary, no broker.
Goals
docs/examples/chat: rooms + broadcast + presence over WebSocket, served by the framework through[deps]exactly as the web-app is.- Framework grows the WS mechanism in pure
.wo: the HTTP/1.1 upgrade handshake (Sec-WebSocket-Accept needs SHA-1/base64 — the crypto-builtin fork's first real consumer), frame parse/serialize (text, close, ping), fiber-per-connection serving (iteration 11), and in-memory channels whose delivery is cross-shard message send (iteration 8). - The arc's acceptance teeth: 1k concurrent clients across shards, broadcast latency measured, starvation-free under one hot room, SIGTERM drains every connection cleanly.
Acceptance Criteria (draft — the spec after the arc refines)
- Given two clients in one room on DIFFERENT shards, when one sends, then the other receives the frame (cross-shard ownership-move delivery), and a third client in another room receives nothing.
- Given 1k connected clients with one hot sender, when the reduction budget preempts, then every room keeps making progress (no starvation) on one OS thread per core, verified by TID.
- Given SIGTERM with clients connected, when the server drains, then every connection gets a close frame, every fiber unwinds its drop maps (ASan zero leaks), and the process exits 0.
- Given the web-app running beside chat features, when the standing gates run, then nothing regresses — HTTP and WS share the serve loop honestly.
Out Of Scope
Message persistence/history (a @table an app adds if it wants — not the
workload's point); auth beyond the existing bearer mechanism; permessage
compression; binary frames beyond echo coverage; wss (TLS stays at the
proxy — the proxy story extends to WS pass-through, documented).
Info
- Dependencies: the 8+11 arc (fibers + cross-shard send — stages 1+2 landed 2026-08-20, stage 3 pending), iteration 31 (request/response, backpressure, death, timers — the mechanisms rooms and presence are made of), iteration 19 (LANDED 2026-08-20 — Bytes carries the frames), the crypto builtins fork (SHA-1 for the upgrade handshake — note: the ledger's crypto slice lists SHA-256/512; the WS handshake specifically needs SHA-1, so the builtin set must include it), and the framework's parse seam (upgrade is an HTTP request until it isn't).
- Unparks on landing: the framework ledger's WebSocket/pub-sub rows and the iteration-18 rejection note ("pub/sub REJECTED until 8/11").
Proposed Solution
Brainstorm → spec → plan after the arc's spec exists; the arc's plan and this iteration's are written against each other (the arc names chat as its acceptance, chat names the arc as its substrate).