writeonce/docs/stories/porch/00-story.md
shoney.arickathil 01df75245f docs(porch): give the framework its own story track, iterations 1-8
- docs/stories/porch/ — a TRACK folder, not a status folder: status still
  lives only in frontmatter. Adds `track: porch` so a query over
  docs/stories/ can tell a porch 3 from a language 3
- 00-story.md carries the sequence, the dependency graph, and a table of
  what the track explicitly does NOT own (binding -> 29, cache -> 18,
  proxy -> 38, metrics -> 30, TLS/templates -> doctrine)
- eight iterations, each with phases, per-phase tasks, Given/When/Then
  criteria, out-of-scope and the forks a spec must settle:
  1 store-backed middleware (limiter + idempotency — needs nothing new,
    first on purpose so the store pattern is proven cheaply)
  2 randomness + cookies (phase A is language-track: a CSPRNG builtin;
    `Resp.headers` being a map cannot emit two Set-Cookie lines)
  3 sessions   4 CSRF   5 routing/response ergonomics (independent)
  6 streaming core (the seam 7 and 8 wait on; chunked-request refusal
    must survive)   7 SSE + compression   8 static + lifecycle hooks
- language iteration 39 -> status: hold, retitled superseded, with a row
  mapping each of its goals to the porch iteration that took it. Kept, not
  deleted: the Fiber study cites it and its randomness argument is what
  this track is built on
- board gains a porch section; board-views gains porch and both-track
  Dataview queries; porch README and the Fiber study §7 point at the track
- no code blocks in any story (plans carry concept and actions in words);
  linkcheck 0 broken / 0 anchors

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-26 20:17:33 +02:00

4.8 KiB
Raw Blame History

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:

  1. Different substrate, different gates. porch is .wo source. Its iterations are proven by just web-app and just site, never by the conformance corpus or oop-accept. Mixing them into the language track's sequence made both harder to read.
  2. Different cadence. A porch slice is days; a language slice that touches wob.h and the VM is longer and riskier. Interleaving them in one numbering forced false ordering decisions.
  3. The framework is now the product surface. writeonce.de is 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 nothing new — starts today
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 language iteration 30 (no story file yet)
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.