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:
parent
c72b3354ff
commit
d6025e131d
4 changed files with 58 additions and 23 deletions
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,17 @@
|
||||||
> 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.** 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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue