docs: iteration 16 closeout (Task 6)

Board row 16 -> landed (web-app 14/0), pending row removed; story header
records the landing + the two as-built discoveries (idle-keep-alive
starvation policy; the two compiler gaps the chain exposed and fixed);
README gains the framework+web-app sample entry; plan checkboxes ticked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
shoney.arickathil 2026-08-19 19:57:52 +02:00
parent 41ed0614fa
commit c1b73a3e6b
4 changed files with 58 additions and 23 deletions

View file

@ -303,6 +303,15 @@ acceptance tests:
just employee # compile + run every mode against a durable database just employee # compile + run every mode against a durable database
``` ```
- **`docs/examples/writeonce-framework/` + `docs/examples/web-app/`** — a web
framework written in writeonce (HTTP/1.1 behind a TLS-terminating proxy,
router with `:param` captures, interface-based handlers) and a storefront
consuming it **as a `[deps]` dependency**, with `@table` persistence. Run:
```bash
just web-app
```
- **`docs/examples/log-watcher/`** — a long-running daemon that watches log - **`docs/examples/log-watcher/`** — a long-running daemon that watches log
files for silent death, using the `fs`/`time`/`net`/`proc` stdlib. Run it: files for silent death, using the `fs`/`time`/`net`/`proc` stdlib. Run it:

View file

@ -131,7 +131,7 @@ that sequences its tasks. Read one, approve, then the next starts.
| 13 | [Compile-time metaprogramming](stories/language-runtime-database/13-compile-time-metaprogramming.md) | ⬜ needs a spec first | | 13 | [Compile-time metaprogramming](stories/language-runtime-database/13-compile-time-metaprogramming.md) | ⬜ needs a spec first |
| 14 | [skillhost host workload](stories/language-runtime-database/14-skillhost-host-workload.md) | ⬜ gaps recorded (branch query-grammar found skillhost needs no new query grammar); each gap a candidate iteration | | 14 | [skillhost host workload](stories/language-runtime-database/14-skillhost-host-workload.md) | ⬜ gaps recorded (branch query-grammar found skillhost needs no new query grammar); each gap a candidate iteration |
| 15 | [deps: `wo.toml [deps]`](stories/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 | | 15 | [deps: `wo.toml [deps]`](stories/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](stories/language-runtime-database/16-web-framework.md) | ⬜ spec'd 2026-08-18 (§B; h2c parked §C behind 8/9f/11); TLS proxy-terminated by decision | | 16 | [web framework](stories/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]; `just web-app` 14/0; h2c parked (§C) behind 8/9f/11 |
--- ---
@ -320,7 +320,6 @@ The C proving-ground work (`exploration/c-runtime/`, phases A–F: 859k reads/s,
| 9f | io_uring group-commit write path — batched durability overlapped on shard threads, fsync fallback | **no spec yet** — brainstorm after iterations 8 + 9e | | 9f | io_uring group-commit write path — batched durability overlapped on shard threads, fsync fallback | **no spec yet** — brainstorm after iterations 8 + 9e |
| 9g | Query grammar from real embedded-DB corpora — whole-query count + correlated exists, driven by the skillhost SQL catalogue; add only what a corpus uses | **no spec yet** — three forks; may collapse to "confirm len(query) + add exists" | | 9g | Query grammar from real embedded-DB corpora — whole-query count + correlated exists, driven by the skillhost SQL catalogue; add only what a corpus uses | **no spec yet** — three forks; may collapse to "confirm len(query) + add exists" |
| 14 | skillhost host workload — port skillhost (MCP host + confined script runner) to writeonce; drives the missing host capabilities into the open (bounded subprocess, stdin/stdout transport, fs metadata, FFI-vs-out-of-process) | **no spec yet** — gaps recorded in the iteration; each gap brainstormed on demand, bounded-subprocess first | | 14 | skillhost host workload — port skillhost (MCP host + confined script runner) to writeonce; drives the missing host capabilities into the open (bounded subprocess, stdin/stdout transport, fs metadata, FFI-vs-out-of-process) | **no spec yet** — gaps recorded in the iteration; each gap brainstormed on demand, bounded-subprocess first |
| 16 | web framework — `.wo` library (HTTP/1.1 keep-alive behind a TLS-terminating proxy), Handler/Middleware interfaces, @table data layer; web-app sample consumes via [deps] | [spec §B](superpowers/specs/2026-08-18-web-framework-design.md) · [plan](superpowers/plans/2026-08-19-web-framework.md) — 15 landed; h2c parked (§C) behind 8/9f/11 |
| 10 | HTTP service layer | [plan 6](superpowers/plans/2026-08-01-http-service-layer.md) | | 10 | HTTP service layer | [plan 6](superpowers/plans/2026-08-01-http-service-layer.md) |
| 11 | Fibers | vision §3, [blue-green exploration](plan/exploration/blue-green-vm/00-vision.md) | | 11 | Fibers | vision §3, [blue-green exploration](plan/exploration/blue-green-vm/00-vision.md) |
| 12 | Blue-green deploy | [spec](superpowers/specs/2026-08-03-blue-green-vm-design.md) — plan authored after iterations 9–10 | | 12 | Blue-green deploy | [spec](superpowers/specs/2026-08-03-blue-green-vm-design.md) — plan authored after iterations 9–10 |

View file

@ -3,7 +3,17 @@
> Format: fiberloom `product/story-iteration-template`. Part of > Format: fiberloom `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.** The framework is written IN writeonce and imported > **Inserted 2026-08-18. LANDED 2026-08-19** (branch `web-framework`):
> `just web-app` 14/0 — deps chain, auth middleware, CRUD with @unique 409 /
> FK-restrict 409 / checked-decode 400, :name captures, 404, pipelined
> keep-alive, SIGTERM, WAL restart persistence. Two notable as-built facts:
> (1) connection policy is pipelined-keep-alive/close-when-idle — a probe
> showed a parked keep-alive connection starves accept on a single-threaded
> server; (2) the chain exposed and fixed two compiler gaps — owned values
> stored in containers now MOVE (`run/container-owned-move`), and a dep's
> internal `use` paths resolve dep-relatively.
>
> The framework is written IN writeonce and imported
> like any dependency (iteration 15 is the prerequisite). TLS terminates at a > like any dependency (iteration 15 is the prerequisite). TLS terminates at a
> reverse proxy — browsers get TLS+ALPN+h2 from nginx/caddy while the > reverse proxy — browsers get TLS+ALPN+h2 from nginx/caddy while the
> framework speaks HTTP/1.1 keep-alive behind it, so no TLS exists anywhere > framework speaks HTTP/1.1 keep-alive behind it, so no TLS exists anywhere

View file

@ -1,5 +1,22 @@
# Iteration 16 — the web framework + web-app: implementation plan # Iteration 16 — the web framework + web-app: implementation plan
> **Status: COMPLETE (2026-08-19)** — all tasks landed on branch
> `web-framework`; `just web-app` 14/0. Deviations from the plan as written,
> recorded honestly: (1) registration is `app.add(Route { method, pattern,
> h })` — the get/post/put helper fns were dropped because the ctor-literal-
> plus-`take` shape is the ownership pattern the corpus pins
> (`run/container-owned-move`); a helper taking an interface-typed parameter
> is unproven ground, deferred; (2) show/delete key on `:name` (the @unique
> index probe, the employee-proven pattern), not `:id` — row-id lookup is not
> in the query surface; (3) the `[deps]` key is `framework` (hyphens are not
> identifier characters in `use` paths); (4) connection policy became
> pipelined-keep-alive/close-when-idle after a probe showed an idle parked
> connection starves accept on a single-threaded server; (5) two compiler
> gaps surfaced and were fixed en route: owned-values-in-containers now MOVE,
> and a dep's internal `use` paths resolve dep-relatively; (6) Dispatcher
> takes `mut req` so :param captures land on the borrowed request — the
> borrow checker correctly refused rebuilding a Req from borrowed maps.
> **For agentic workers:** use superpowers:executing-plans (inline) or > **For agentic workers:** use superpowers:executing-plans (inline) or
> subagent-driven-development. Steps are checkboxes. Per repo rule, this plan > subagent-driven-development. Steps are checkboxes. Per repo rule, this plan
> carries **actions in words + verification commands, no code blocks** — the > carries **actions in words + verification commands, no code blocks** — the
@ -50,35 +67,35 @@ section B (normative; §C's h2c stays parked). Story:
**Files:** create `docs/examples/writeonce-framework/{wo.toml,README.md, **Files:** create `docs/examples/writeonce-framework/{wo.toml,README.md,
http/types.wo}`. http/types.wo}`.
- [ ] Project manifest (`name = "writeonce-framework"`); README states what - [x] Project manifest (`name = "writeonce-framework"`); README states what
it is, the single-thread/proxy deployment story, and the disclosed limits it is, the single-thread/proxy deployment story, and the disclosed limits
up front. up front.
- [ ] `Req` record: method, path, params (`:param` captures), query, headers - [x] `Req` record: method, path, params (`:param` captures), query, headers
(all `map<Text, Text>`), body Text. `Resp` record: status, headers, body. (all `map<Text, Text>`), body Text. `Resp` record: status, headers, body.
Builder free fns: ok_text, ok_json, not_found, bad_request, server_error, Builder free fns: ok_text, ok_json, not_found, bad_request, server_error,
redirect — each returns a fully-formed `Resp` (content-type set). redirect — each returns a fully-formed `Resp` (content-type set).
- [ ] Verify: the framework directory typechecks standalone (`woc` on it — - [x] Verify: the framework directory typechecks standalone (`woc` on it —
no `fn main`, so check-only) with zero diagnostics. Commit. no `fn main`, so check-only) with zero diagnostics. Commit.
## Task 2 — HTTP/1.1: parse, serialize, keep-alive serve loop ## Task 2 — HTTP/1.1: parse, serialize, keep-alive serve loop
**Files:** create `docs/examples/writeonce-framework/http/{parse.wo,serve.wo}`. **Files:** create `docs/examples/writeonce-framework/http/{parse.wo,serve.wo}`.
- [ ] Request parsing from a `net` connection: request line (method, - [x] Request parsing from a `net` connection: request line (method,
target — split path from query string, decode `%`-escapes in both), header target — split path from query string, decode `%`-escapes in both), header
lines to the blank line, then exactly `Content-Length` bytes of body lines to the blank line, then exactly `Content-Length` bytes of body
(missing length = empty body; a non-integer length or an oversized one is (missing length = empty body; a non-integer length or an oversized one is
a 400). Header names lowercase on read so lookups are predictable. a 400). Header names lowercase on read so lookups are predictable.
Anything malformed: respond 400, close, continue serving. Anything malformed: respond 400, close, continue serving.
- [ ] Response serialization: status line with reason text, headers, - [x] Response serialization: status line with reason text, headers,
`Content-Length` always computed from the body, `Connection: keep-alive` `Content-Length` always computed from the body, `Connection: keep-alive`
unless the request asked to close. unless the request asked to close.
- [ ] The serve loop: `net.listen`, accept, then per connection read - [x] The serve loop: `net.listen`, accept, then per connection read
requests until EOF/close/stop — the keep-alive inner loop; the dispatch requests until EOF/close/stop — the keep-alive inner loop; the dispatch
callback boundary is a structural interface the router provides (Task 3), callback boundary is a structural interface the router provides (Task 3),
wrapped in `try` so a trapping handler answers 500 and the loop lives. wrapped in `try` so a trapping handler answers 500 and the loop lives.
Close the connection fd on every exit path and the listener on stop. Close the connection fd on every exit path and the listener on stop.
- [ ] Verify with a throwaway `.wo` main beside the framework (not - [x] Verify with a throwaway `.wo` main beside the framework (not
committed): serve one echo handler, curl matrix — GET with query, committed): serve one echo handler, curl matrix — GET with query,
keep-alive reuse (two requests, one connection), 400 on garbage, SIGTERM keep-alive reuse (two requests, one connection), 400 on garbage, SIGTERM
stops cleanly. Commit. stops cleanly. Commit.
@ -87,17 +104,17 @@ http/types.wo}`.
**Files:** create `docs/examples/writeonce-framework/{router/router.wo,app.wo}`. **Files:** create `docs/examples/writeonce-framework/{router/router.wo,app.wo}`.
- [ ] `Handler` interface (`handle(req) -> Resp`) and `Middleware` interface - [x] `Handler` interface (`handle(req) -> Resp`) and `Middleware` interface
(`before(req) -> ?Resp`, nil = continue) — the spec's shapes verbatim. (`before(req) -> ?Resp`, nil = continue) — the spec's shapes verbatim.
- [ ] Route table: `App` holds `multi` of route records (method, the pattern - [x] Route table: `App` holds `multi` of route records (method, the pattern
split into segments, the handler value). Matching walks segments; a split into segments, the handler value). Matching walks segments; a
`:name` segment captures into `req.params`. First match wins; no match is `:name` segment captures into `req.params`. First match wins; no match is
the framework's 404. Registration helpers: get/post/put/delete_ plus a the framework's 404. Registration helpers: get/post/put/delete_ plus a
generic route(method, pattern, handler). generic route(method, pattern, handler).
- [ ] `App.serve(port)`: run middleware in order (a `Resp` short-circuits — - [x] `App.serve(port)`: run middleware in order (a `Resp` short-circuits —
`?Resp` narrowing), then route, then the matched handler, all inside the `?Resp` narrowing), then route, then the matched handler, all inside the
Task-2 loop's try boundary. Task-2 loop's try boundary.
- [ ] Verify with the throwaway main: two routes incl. `/things/:id` - [x] Verify with the throwaway main: two routes incl. `/things/:id`
echoing the capture, a header-checking middleware that short-circuits 401, echoing the capture, a header-checking middleware that short-circuits 401,
404 for unknown paths, 500 for a deliberately trapping handler with the 404 for unknown paths, 500 for a deliberately trapping handler with the
server surviving. Commit. server surviving. Commit.
@ -107,22 +124,22 @@ http/types.wo}`.
**Files:** create `docs/examples/web-app/{wo.toml,README.md,types.wo,main.wo}` **Files:** create `docs/examples/web-app/{wo.toml,README.md,types.wo,main.wo}`
(+ a module justfile mirroring the other samples). (+ a module justfile mirroring the other samples).
- [ ] `wo.toml`: `[deps] writeonce-framework = { git = <future GitHub URL>, - [x] `wo.toml`: `[deps] writeonce-framework = { git = <future GitHub URL>,
rev = "v0.1.0" }` (documentation value; the gate substitutes a `file://` rev = "v0.1.0" }` (documentation value; the gate substitutes a `file://`
remote), `[build]` runtime unpinned (portable, per the log-watcher remote), `[build]` runtime unpinned (portable, per the log-watcher
precedent). precedent).
- [ ] Data model: `@table Product` (name @unique, price, stock) and - [x] Data model: `@table Product` (name @unique, price, stock) and
`@table Order` (`ref Product`, qty) — small, honest, exercising `@unique` `@table Order` (`ref Product`, qty) — small, honest, exercising `@unique`
and FK restrict through web routes. and FK restrict through web routes.
- [ ] Routes: list products (query + json encode), show by `:id`, create - [x] Routes: list products (query + json encode), show by `:id`, create
product (json decode body — the checked decode's nil path is a 400), product (json decode body — the checked decode's nil path is a 400),
create order (FK), delete product (FK restrict surfaces as a 409-style create order (FK), delete product (FK restrict surfaces as a 409-style
error body, caught via try). One auth middleware (a shared-token header, error body, caught via try). One auth middleware (a shared-token header,
401 otherwise) registered before the routes. 401 otherwise) registered before the routes.
- [ ] `fn main`: build the App, register middleware + routes, `serve(port)` - [x] `fn main`: build the App, register middleware + routes, `serve(port)`
with the port from args. README documents the curl matrix and the nginx with the port from args. README documents the curl matrix and the nginx
h2-in-front config sketch. h2-in-front config sketch.
- [ ] Verify by hand end to end once (fetch via a local file:// remote, - [x] Verify by hand end to end once (fetch via a local file:// remote,
serve, curl, restart, drop). Commit. serve, curl, restart, drop). Commit.
## Task 5 — the acceptance gate ## Task 5 — the acceptance gate
@ -130,7 +147,7 @@ http/types.wo}`.
**Files:** create `scripts/web-app-accept.sh`; modify `justfile` **Files:** create `scripts/web-app-accept.sh`; modify `justfile`
(`web-app` recipe). (`web-app` recipe).
- [ ] The gate builds the whole chain at run time: git-init a temp remote - [x] The gate builds the whole chain at run time: git-init a temp remote
from `docs/examples/writeonce-framework/` (tag `v0.1.0`), copy from `docs/examples/writeonce-framework/` (tag `v0.1.0`), copy
`docs/examples/web-app/` to a temp dir, substitute the `file://` URL into `docs/examples/web-app/` to a temp dir, substitute the `file://` URL into
its manifest, then: fetch+build (lock written); serve on a scratch port its manifest, then: fetch+build (lock written); serve on a scratch port
@ -140,10 +157,10 @@ http/types.wo}`.
body while the server keeps serving; keep-alive reuse; kill -TERM stops body while the server keeps serving; keep-alive reuse; kill -TERM stops
cleanly; restart and the product list still answers (WAL persistence); cleanly; restart and the product list still answers (WAL persistence);
fd/resident flatness via an opt-in `WA_SOAK` mirroring log-watcher's. fd/resident flatness via an opt-in `WA_SOAK` mirroring log-watcher's.
- [ ] Wire `just web-app`; run it plus the standing gates - [x] Wire `just web-app`; run it plus the standing gates
(`just woc-test`, `just oop-e2e`, `just deps-accept`, `just log-watcher`, (`just woc-test`, `just oop-e2e`, `just deps-accept`, `just log-watcher`,
`just employee`) — all green. `just employee`) — all green.
- [ ] Commit. - [x] Commit.
## Task 6 — docs closeout ## Task 6 — docs closeout
@ -152,7 +169,7 @@ landed), story `16-web-framework.md` (landing note), `README.md` (one
paragraph + pointer under the samples list), `docs/08-project-structure.md` paragraph + pointer under the samples list), `docs/08-project-structure.md`
(the two new sample entries). (the two new sample entries).
- [ ] Apply; `just web-app` still green; commit. - [x] Apply; `just web-app` still green; commit.
## Success criteria (spec §Success criteria, restated) ## Success criteria (spec §Success criteria, restated)