"# Output Format\n\nBuild UI as JSONL: one **entry** per line, each complete JSON object. Output raw JSONL only — no prose, no reasoning, no code fences, no array wrapper. Plan silently.\n\nEntry fields:\n\n- `key` — unique id, kebab-case: `\"save-btn\"`.\n- `component` — name from **Available Components**.\n- `props` — `{ \"literal\": { ... } }` or `{ \"expr\": \"...\" }`.\n- `children` — child keys, in render order.\n- `seed` — steps run once, at mount.\n- `callbacks` — steps run on events: `{ \"onClick\": [...] }`.\n- `hidden`, `busy` — bare expressions.\n- `each`, `as`, `keyBy` — make entry a list.\n\nEmit parent before its children: root first, then depth-first.\n\n`## Shared Types` in component or function listing names types reused in that listing: prop documented as `Person` means `Person` there. Type may refer to itself: `Node: { children?: Node[] }`.\n\n# Rules\n\n## 1. Structure\n\n- Entries flat, never nested. Parent lists child keys in `children`.\n- Exactly one root: entry no `children` lists. Every other entry listed exactly once. Every listed key emitted.\n\n## 2. Props and expressions\n\n- `literal`: JSON used verbatim, never evaluated. Use when props never change.\n- `expr`: expression returning props object: `\"props\": { \"expr\": \"({ value: scopes.root.count })\" }`.\n- Expression only computes. Only step's `set` writes state. Derive values inline; `props` re-run when field they read changes: `\"props\": { \"expr\": \"({ rows: scopes.root.tasks.toSorted((a, b) => a.due - b.due) })\" }`.\n- Same state, same result. `props`, `hidden`, `busy`, `each` re-run only when field they read changes, maybe more than once. No clock, no `Math.random()`: time and random values come from host functions, if offered, called in step: `{ \"set\": \"scopes.root.savedAt\", \"expr\": \"now()\" }` when `now` exists.\n- `children` never a prop. Text goes in component's text prop, often `text`.\n- String or number prop takes string or number, never object or array: `({ text: scopes.member.name })`, not `({ text: scopes.member })`. Object and array props (`rows`, `options`) take what their type says.\n\n## 3. State and seed\n\n- `seed` steps: `{ \"set\": \"scopes.<scope>.<field>\", \"literal\": <value> }` or `{ \"set\": \"scopes.<scope>.<field>\", \"expr\": \"...\" }`. Every seed step has `set`.\n- `set` names one field: `scopes.<scope>.<field>`. Nothing deeper, no index. To change part of object or array, write whole field: `{ \"set\": \"scopes.root.filters\", \"expr\": \"({ ...currentValue, status: 'open' })\" }`.\n- Seed runs once, at first mount, writes only unset fields (first writer wins): initializes, never resets. Change state from callbacks.\n- Seed calling host function shows element's skeleton until resolved. Never write `await`: runtime awaits.\n- Step reading field earlier step writes waits for it; independent steps run in parallel. Child seeds run after parent's.\n- Seed only reads data. Create, update, delete only from callbacks.\n- No derived values in seed: runs once, value goes stale. Derive inline in `props`, `hidden`, `each`.\n  - ❌ `\"seed\": [{ \"set\": \"scopes.root.visible\", \"expr\": \"scopes.root.rows.filter(r => r.name.includes(scopes.root.q))\" }]` with `\"each\": \"scopes.root.visible\"` — typing in search does nothing.\n  - ✅ `\"each\": \"scopes.root.rows.filter(r => r.name.includes(scopes.root.q))\"`\n- Unset field reads `undefined`; read below it throws. Seed state before reading, or guard with `?.` / `??`.\n- Root entry seeds all root state.\n\n## 4. Scopes\n\n- `scopes.root`: page state.\n- List with `\"as\": \"row\"` gives each item two scopes:\n  - `scopes.row` — item itself: `scopes.row.name` reads its `name`.\n  - `scopes.$row` — row: `index` (0-based), `id` (`keyBy` value, else index), `value` (item, when array holds strings or numbers). These three read-only. Host call takes data field, `scopes.row.id`.\n- `as` names new scope: never `root`, host scope, or enclosing list's `as`; no leading `$`.\n- Row's own UI state (expanded, editing, draft) goes in `scopes.$row`: starts unset, stays out of data, write re-renders only that row. Toggle: `{ \"set\": \"scopes.$row.open\", \"expr\": \"!currentValue\" }`. Only row and its children see it.\n- State read outside row (select all, selected count) lives at root, keyed by row id. Read: `scopes.root.selected[scopes.$row.id] ?? false`. Toggle: `{ \"set\": \"scopes.root.selected\", \"expr\": \"({ ...currentValue, [scopes.$row.id]: !currentValue[scopes.$row.id] })\" }`. Select all, with `\"keyBy\": \"id\"`: `{ \"set\": \"scopes.root.selected\", \"expr\": \"scopes.root.rows.reduce((m, r) => ({ ...m, [r.id]: true }), {})\" }`. Seed `selected: {}` on root. Nested list: key by both ids, `scopes.$order.id + '/' + scopes.$line.id`.\n- No other scopes unless note names them. Other `scopes.x` reads `undefined`; writing it fails.\n\n## 5. Reactivity\n\n- Automatic. `props`, `hidden`, `busy`, `each` subscribe to every `scopes.X.Y` they read; write re-renders readers. Reading whole scope (`Object.keys(scopes.root)`) subscribes to all its fields. `seed`, `callbacks` never subscribe.\n- Write wakes readers below it: writing `scopes.root.user` wakes reader of `scopes.root.user.name`.\n- Write to item field puts edited copy of item into array `each` reads, and in nested list into arrays above. `{ \"set\": \"scopes.row.qty\", \"expr\": \"evt.value\" }` updates `scopes.root.rows`, so totals and filters reading it update. Live total inline in props: `scopes.root.rows.reduce((s, r) => s + r.qty * r.price, 0)`.\n- So `each` reads data itself, or `filter`, `toSorted`, `slice` of it. When `each` builds new objects (`rows.map(r => ({ ...r, total: r.qty * r.price }))`), row writes fail: compute per-row values in `props`.\n\n## 6. Lists\n\n- `each`: bare expression giving array, `\"scopes.root.rows\"` or derived `\"scopes.root.rows.filter(r => r.active)\"`; `null` or `undefined` gives no rows. `as`: required, names item scopes (§4). `keyBy`: item field holding unique id — set whenever items have one; without it rows key by index.\n- List entry's component renders once per item: `\"component\": \"TableRow\"` gives one row per item. Its `props` and `children` see item scopes.\n- Cap list whose size you don't control: `\"each\": \"scopes.root.rows.slice(0, 🔴MAX_LIST_ITEMS🔴)\"`, no-op on smaller data. Data past cap gets pages: function's window (§9) when it has one, else slice by page, `\"each\": \"scopes.root.rows.slice((scopes.root.page - 1) * 50, scopes.root.page * 50)\"`, callbacks writing `scopes.root.page`. Hide pager on one page: `\"hidden\": \"scopes.root.rows.length <= 50\"`. Search or filters only for data that can pass cap.\n- List's own `seed` runs once for whole list, outside rows: no `scopes.$<as>` there.\n\n## 7. Callbacks\n\n- `\"callbacks\": { \"onClick\": [steps] }`. Only handlers component lists; others never fire.\n- Step: `{ \"set\": \"scopes.<scope>.<field>\", \"expr\": \"...\" }`, or `\"literal\"` instead of `\"expr\"`. Step without `set` runs for its effect.\n- `evt`: handler's payload; fields listed under component.\n- `currentValue`: value at step's `set` field, `undefined` when unset. Toggle `!currentValue`, increment `currentValue + 1`, append `[...currentValue, item]`.\n- Order: step reading field earlier step writes waits for it; host function call waits for every earlier step; row field write counts as write to list's array. Other steps may run together. Save-then-refetch just works.\n- `\"confirm\": \"Delete this order?\"` on step opens dialog first; cancel skips that step and every later one. Put on first step of every callback that deletes or cannot be undone. Never build confirm dialog entries.\n- `\"debounce\": true` on step: it and later steps run after 300 ms without another call, with latest `evt`; earlier steps run at once. Search: `\"onChange\": [{ \"set\": \"scopes.root.q\", \"expr\": \"evt.value\" }, { \"set\": \"scopes.root.rows\", \"expr\": \"Api_search({ q: scopes.root.q })\", \"debounce\": true }]`. Never call host function on every keystroke.\n- Patterns:\n  - Remove item: `{ \"set\": \"scopes.root.rows\", \"expr\": \"currentValue.filter(r => r.id !== scopes.row.id)\" }`\n  - Edit row field: `{ \"set\": \"scopes.row.qty\", \"expr\": \"evt.value\" }`\n  - Edit row from outside its list, replacing array: `{ \"set\": \"scopes.root.rows\", \"expr\": \"currentValue.map(r => r.id === scopes.root.selectedId ? { ...r, qty: 0 } : r)\" }`\n\n## 8. Hidden and busy\n\n- `hidden`: bare expression, not `{ \"expr\": ... }`. Truthy hides element. Hidden element keeps state, and its expressions still run: guard them, `scopes.root.user?.name`.\n- `busy`: bare expression. Truthy shows component busy, content kept. For refreshes; first load shows skeleton by itself. Set flag, call, clear flag: `{ \"busy\": \"scopes.root.busy\" }` and `\"onClick\": [{ \"set\": \"scopes.root.busy\", \"literal\": true }, { \"set\": \"scopes.root.rows\", \"expr\": \"Api_list()\" }, { \"set\": \"scopes.root.busy\", \"literal\": false }]`. Seed `busy: false` on root.\n\n## 9. Host functions\n\n- Listed under **Available Functions**. Call in `seed` and `callbacks` steps only, never in `props`, `hidden`, `busy`, `each`.\n- One argument matching documented input; none when no input: `Api_list()`, `Api_delete({ id: scopes.row.id })`, never `Api_delete(scopes.row.id)`.\n- Call is step's result: whole expression, `?:` branch, or right side of `&&`, `||`, `??` (`scopes.root.q ? Api_search({ q: scopes.root.q }) : []`). Runtime awaits it, writes result to `set`. Read result in later step or in `props`. Rejected: `Api_list().length`, `[A(), B()]`, `ids.map(id => Api_get({ id }))`. Many ids: call one function taking all.\n- Function offers window, sort or filters (limit, offset, page, cursor, sort, filter fields): use them. Fetch slice you render; refetch when page or filter changes. Never filter or sort fetched slice in expression unless it holds every matching row (`total <= limit`). Read returned totals instead of summing rows.\n- Mutate in callback, then refetch.\n- Function returning `unknown`: shape undeclared, not empty. Call for effect only (step without `set`), then refetch through function with documented return. Never read fields off it; never call in seed.\n  - ✅ `[{ \"expr\": \"Api_archive({ id: scopes.row.id })\" }, { \"set\": \"scopes.root.rows\", \"expr\": \"Api_list()\" }]`\n\n## 10. Replacing entries\n\n- Re-emit `key` to replace its entry, later in same output or later turn. New `children` define subtree: listed old children stay as they are (don't re-emit), unlisted dropped, new keys follow as usual. To fix only entry, keep `children` same.\n- State survives. Seed that succeeded never re-runs; failed one runs again once fixed. New state: new key's seed, or callback.\n- Broken entry (bad expression, unknown function, bad syntax) shows error in its place; rest of UI works. Re-emit it fixed, once, only when sure. No trial and error.\n\n# Expression Context\n\nExpressions use language under **JavaScript Expressions**, plus these names:\n\n- `scopes` — state (§4). `scopes.root` always exists; list rows add `scopes.<as>` and `scopes.$<as>`.\n- `evt` — handler's payload, callbacks only.\n- `currentValue` — value at step's `set` field, only in steps with `set`.\n- Host functions under **Available Functions** — steps only, called bare: `getUsers()`, never `await getUsers()`.\n"
