- jarvis (00-story): 6th track, 2nd software built with writeonce — an AI assistant; direct-HTTPS design; blockers named (net.connect + TLS) - runtime-v2 7 observability + 8 symmetric cipher: moved from the language track (were 30/43); 9 in-process TLS: created from the gap jarvis surfaces, RETIRES the "TLS is the proxy's job" doctrine (both directions) - language 41 (arena hang): fix design to ready — marshal cross-shard messages (root), align the shard_id % nshards route/compare + assert bound; poison-on-free + minimal fixture as follow-ups - fiber scope-gap analysis (plan/exploration/fiber/01): porch vs fiber, what porch lacks, would developers prefer porch - board + dependency-graph synced (porch 2-8 ready; rv2 table; §5/§5a graphs) (cherry picked from commit 203470ceb2a151fe3584931cd4237af3f96a9f29)
5 KiB
Story — porch, the writeonce web framework
The second track. Where
language-runtime-database/ grows
the language, this track grows the one library written in it:
porch, consumed by every serving sample
through wo.toml [deps].
Numbering restarts at 1 and is local to this track. Frontmatter carries
track: porch so a query over docs/stories/ can tell a porch iteration 3 from
a language iteration 3. Status rules are the repo's, unchanged: status: in
frontmatter is the only place state lives, no directory encodes it.
Why a separate track
Three reasons, all practical:
- Different substrate, different gates. porch is
.wosource. Its iterations are proven byjust web-appandjust site, never by the conformance corpus oroop-accept. Mixing them into the language track's sequence made both harder to read. - Different cadence. A porch slice is days; a language slice that touches
wob.hand the VM is longer and riskier. Interleaving them in one numbering forced false ordering decisions. - The framework is now the product surface.
writeonce.deis served by porch. Its gaps are what a visitor hits first, so they deserve a roadmap that is not buried behind runtime work.
The language track stays upstream: when a porch iteration needs a new builtin, that half is called out explicitly and the language track owns it.
Where the sequence came from
The Fiber v3.5.0 parity study
— gofiber/fiber read end to end against porch's actual .wo source: its routing
surface, Req/Res API, binder, lifecycle hooks and the Config of all 32 of
its middleware/ packages. Nine of those 32 already have a working porch
counterpart, so this is a breadth roadmap, not a rescue.
That study replaced language-track iteration 39, which is now a pointer here.
The sequence
Ordered by dependency, not by importance — and the first slice is deliberately the cheapest, so the store pattern and the gate shape are proven before the risky work starts.
| # | Iteration | Delivers | Needs |
|---|---|---|---|
| 1 | Store-backed middleware | rate limiting + idempotency over a @table store, serialized through a sharded actor pool |
🔄 in progress; needs no new primitive (call/send/monitor/time.after all landed) |
| 2 | Randomness and cookies | a random_bytes runtime builtin, repeated response headers, Cookie: parsing, signed cookies |
a language-track builtin (phase A) |
| 3 | Sessions | server-side sessions, idle + absolute timeout, revocation | 2 |
| 4 | CSRF | token mint/verify, trusted origins, single-use tokens | 2, 3 |
| 5 | Routing and response ergonomics | the remaining method helpers, named routes, per-route body limit, request ids, the missing response helpers | nothing — parallel to 2–4 |
| 6 | Streaming core | incremental response writes and chunked framing — the seam three iterations wait on | nothing new, but it changes Resp |
| 7 | SSE and compression | server-sent events, gzip/deflate | 6 |
| 8 | Static files and lifecycle | byte ranges, cache headers, directory listing, lifecycle hooks, the small middleware everyone ships | 6 |
1 ─ independent, start here
5 ─ independent, any time
2 ──▶ 3 ──▶ 4
6 ──▶ 7
└──▶ 8
What this track does NOT own
| Not porch's | Owner |
|---|---|
| typed binding of query/params/form into a class | language: @derive — reflection is forbidden by principle 13 |
TTL cache, transaction { }, durable job queue |
language: iteration 18 |
a proxy middleware |
language: iteration 38 — needs net.connect, which does not exist |
| metrics, profiling, per-change CI, fuzzing | runtime-v2 7 — observability (was language iteration 30; metrics/profiling/trace-on-trap; CI + fuzz are tooling, split out) |
| TLS, HTTP/2 | nobody — proxy-terminated by doctrine |
| a runtime template engine | nobody — rejected; markup is a compile-time literal (writeonce-view) |
| a radix-tree router | nobody yet — waiting on a measurement, not a decision |
Review protocol
Same as the language track: the developer reads one iteration, approves or
amends; the next starts only after approval. Each iteration is an unsplittable
value slice with phases, per-phase tasks, Given/When/Then acceptance criteria,
and an out-of-scope list. Every phase ends with both serving gates green —
just web-app and just site — because porch has two consumers and a change
that only satisfies one is not done.