From e0185279dbd75dfeae8f67f35e56cd1eacfd8c64 Mon Sep 17 00:00:00 2001 From: Ryan Carniato Date: Tue, 29 Sep 2026 08:58:28 -0700 Subject: [PATCH 1/5] =?UTF-8?q?docs:=20design=20fit=20(=C2=A710),=20live-m?= =?UTF-8?q?utation=20seed=20(=C2=A79.6),=20and=20the=20examples=20grid=20p?= =?UTF-8?q?lan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recorded from the fit conversation after the Stage 7 gate, parked while #3704 was in review; lands now that it has merged. Nothing here touches code. Principles §9.6 seeds Stage 9 — mutations through a live connection: three homes for a rejection in the live quadrant (client, a bounded sibling's single-flight response, server memory) and which one the design recommends. §10 answers where server components fit: three axes (content weight, origin of change, client-owned position density), five quadrants with each example placed, where a rejection lives per shape, the advice as rules of thumb, and the candidates to evaluate against the map with the evidence each needs. `documentation/plans/examples-grid-plan.md` turns that into the example set as a whole: one idiomatic app per grid coordinate, the bar every example meets, per-example dispositions (hackernews' Toggle becomes an attribute slot; todos-server reshaped; chat rebuilt and board added as the two flagships; room retired), the verification items that gate the reshapes, and the order — revised today to least work first, one example per PR, exact code reviewed before it is written. todos-server's target is decided: Q5. Co-authored-by: Claude via Cursor Co-authored-by: Cursor --- documentation/plans/examples-grid-plan.md | 249 +++++++++++++++ .../server-components-principles.md | 294 ++++++++++++++++++ 2 files changed, 543 insertions(+) create mode 100644 documentation/plans/examples-grid-plan.md diff --git a/documentation/plans/examples-grid-plan.md b/documentation/plans/examples-grid-plan.md new file mode 100644 index 000000000..8546c6905 --- /dev/null +++ b/documentation/plans/examples-grid-plan.md @@ -0,0 +1,249 @@ +# Examples Plan — one idiomatic app per grid coordinate + +_Drafted 2026-09-28, from the fit conversation that produced principles §10 +and §9.6, with Stages 1–8 built and #3704 (attribute slots, second form) +the last heavy feature. Status: DIRECTION AGREED in conversation; nothing +here is built. This page is the checklist for the example set as a whole; +the two flagships get their own plans (linked below) and this page does not +design them. Design record: `documentation/server-components/ +server-components-principles.md` §10 (fit) and §9.6 (live mutations seed). +The grid is the one in "The Grand Unifying Architecture of Frontend" +(dev.to, 2026-09-22): response window on the horizontal axis +(request/response → persistent), affordance weight on the vertical +(server-owned markup → client-owned state), and three responsibilities every +app separates — navigation (client), content (server), affordances +(client). Owner: Ryan._ + +## Objective + +The examples were built as test beds, one per feature, and that was the +right way to build the features. Now that the feature set is complete they +should become **good examples**: one idiomatic app per coordinate on the +grid, each the app it would be if the framework didn't need proving, with +`rendering` remaining the one deliberate kitchen sink. Two of them are +flagships — realistic products, not demos — because the right half of the +grid is where no one else has a story and an AI chat and a board are the +two apps people are actually building there. + +## The bar (every example) + +Priority order; a lower item never overrides a higher one. + +1. **One coordinate.** The README's first paragraph names it and names the + example's twin (the same app at another coordinate) where one exists. +2. **The app's own affordances and nothing else.** Every feature present is + one the app would have if the competitors didn't exist. Features with no + idiomatic home live in specs, not examples — that absence is evidence + for principles §10.5, not a gap to fill. +3. **The three responsibilities visible.** Which elements the compiler + claims (navigation), which functions are content (`"use server"`), and + what the overlay is (affordances) should be readable from the source + without a tour. +4. **Lead with the affordance that is hardest elsewhere.** Among the app's + natural affordances, the README and the polish go to the _layering + moment_ — the place where client behaviour on server markup is one line + here and a DSL, a controller, or a round trip in Datastar, Turbo + + Stimulus, or LiveView. By demonstration only: READMEs stay neutral and + name no competitor; the head-to-head is an article. + +The trivial case must stay trivial: one client position with no per-item +data is one line each side (`codeBlock={() => ({ onCopy })}` on the client, +`onClick={block.onCopy}` on the server). No example may need a fill to be +more than an object literal; where one does, that is a §9.2.3 ergonomics +finding to raise, not something example code hides. + +## The map + +```text + request/response persistent + ┌──────────────────────────────┬──────────────────────────────┐ + client-heavy │ hackernews-spa SPA + JSON │ board FLAGSHIP │ + (affordances)│ todos SPA + optim │ live data tier, optimistic │ + │ │ store, until, drag, │ + │ │ presence │ + ├──────────────────────────────┼──────────────────────────────┤ + (islands) │ notes RSC coord. │ │ + │ islands + single-flight │ │ + ├──────────────────────────────┼──────────────────────────────┤ + server-heavy │ hackernews reads │ chat FLAGSHIP │ + (markup) │ todos-server writes │ live server components, │ + │ │ durable generation, threads│ + └──────────────────────────────┴──────────────────────────────┘ + off-grid: rendering (SSR-mode kitchen sink) · effect (data tier × Effect) + · sierpinski (renderer perf) · migrating-element (conditional, §V5) + retired: room (both pages absorbed by the flagships) +``` + +Twins: `hackernews` ↔ `hackernews-spa`; `todos-server` ↔ `todos`. The +realistic pair on the right (`board`, `chat`) mirrors the pedagogical pair +on the left (`hackernews`, `notes`), and is its own comparison: the same +primitives with the client owning the markup (`board`) and with the server +owning it (`chat`). + +## Per-example disposition + +### `hackernews` — bottom-left, reads. KEEP; one binding change + README + +The front door: the simplest server component, navigation over server +markup, a single stateful client concern. Its layering moment is comment +collapse — client state on server-rendered elements deep in a tree, surviving +navigation. Today that is a `Toggle` client component wrapping a markup slot +(`toggle={p => {p.children}}`); under §9.2.3's placement +principle a thread exists because the server has comments, so it is server +markup and the collapse is an attribute slot (`class` and `onClick` bound on +the server's own elements). Convert it; it is the idiomatic form and the +example gets smaller. README repositioned to the coordinate and twin. + +### `hackernews-spa` — top-left. KEEP as is + +The twin; exists only as the comparison. README names the coordinate. + +### `notes` — middle-left, the RSC coordinate. KEEP; README only + +React's own server-components demo ported: client islands whose state +survives server updates around them, single-flight mutations by redirect, +the search field as the idiomatic attribute slot. Its layering moment is the +editor keeping its draft while the sidebar list refreshes around it — the +"shared client state preserved" line the HTML-partial tools cannot cross. +Code unchanged; README repositioned. The overlap with `chat` (both are +sidebar + viewer + mutations) is intentional: opposite sides of the grid, +different audience. + +### `todos` — top-left. KEEP as is + +The SPA control: optimistic store over `refresh`, client-held API mock. +Twin of `todos-server`; also the pedagogical control for `board`. + +### `todos-server` — bottom-left, writes. RESHAPE + +Today: the §9.2.3 acceptance gate — seven client-owned positions per row, an +intent record, a client error map, multi-flight `refresh`. Principles §10 +places it as a widget app wearing collaborative-list clothes; it is not the +example anyone should learn from and the §9.2.3 record stays as its +history. + +Target: the write side of the HTMX corner, made enviable rather than +mimicked. Server markup throughout; every mutation a compiler-claimed +`
` that works without JS; **one** attribute slot +with **one** position (`done`) fed by an optimistic store the action writes +before it yields; single-flight responses that morph the row back. About +ten lines of client code, no component beyond the root, instant toggles. +Failures server-rendered: a rejected action's response carries the row with +its error and a retry form. Layering moment: the optimistic toggle. + +Scope line: toggle, remove, and add are optimistic. If any of them needs +more than a store write inside its action, it is not slick and it is out +(the pending-row-for-add markup slot is the first candidate to fall; the +README may describe it as the increment). Bulk actions stay plain forms. +Pending feedback, if the router marks a submitting claimed form +(`aria-busy` / `data-pending`), is CSS only — see V4. + +### `chat` — bottom-right FLAGSHIP. REBUILD (own plan) + +Plan: `documentation/plans/chat-flagship.md` (to write first). Realistic +AI chat: threads durable and addressable; generation as a job that outlives +the request, with the thread a `live` server component projecting durable +state (close the tab, come back, caught up in one morph; two tabs agree); +real model with the fake as no-key fallback; structured message parts; +stop / regenerate / rename / delete as forms; the optimistic user bubble as +a client element in a markup slot cleared by `until` on the echo; copy +button as the attribute slot; native `
` for collapse. Failed +generations are facts about the thread — durable, server-rendered with a +retry. Absorbs `room`'s `/` page. The `usage` projection goes (token usage +is a number on the finished message), which removes the container tier's +only example — flagged, consistent with principles §10.5. + +### `board` — top-right FLAGSHIP. NEW (own plan) + +Plan: `documentation/plans/board-flagship.md`. Pure data tier, no frames: +one board of lists and cards as a `live` source, reconciled by id into an +optimistic nested store; moves and reorders as optimistic writes with +`until`; two tabs converging; rejected moves reverting; presence. Drag +within and between lists with pointer events, fractional ordering. Scope: +create / rename / archive, titles only, no card detail. Absorbs `room`'s +`/live` page. Acceptance bar must name the two identity moments explicitly +or they will not get built: a card sliding into its new column (same +element on both sides), and a card being edited inline while another tab +moves it — focus, caret, and draft arriving in the new column intact +(one element per card at board level, referenced from whichever list holds +it). `board` is also the top-right form of `chat`: a live thread read by +client components into an optimistic store is this architecture with a +transcript instead of lists, which is why no separate `chat-spa` exists. + +### `room` — RETIRE + +Stage 8's test bed. `/` becomes `chat`'s thread; `/live` becomes `board`. +The chaos switch and status pills were apparatus; connection state stays +visible in both flagships through `onstatus`, as an affordance the apps +would have anyway. Delete once both flagships exist. + +### `migrating-element` — CONDITIONAL on V5 + +Today a canvas, because the README records that `