Skip to content

Product Surfaces & UX

Part of the PersonalClaw research-learnings library. Source-agnostic; distilled 2026-07-13 from a 95-source competitive-research corpus.

Scope: agent-product UX — generative UI (LLM output → typed component trees, streaming spec renderers, fault-tolerant parsing), dashboard/tile composition, run cockpits & progress cards, needs-input/approval UX, ambient presence, evidence bundles & proof sections, annotate-to-correct, agent-driven design tools, work boards, diff review panels, live artifacts.

  1. Design UI specs for LLM authorship first; fault tolerance is a feature set, not error handling. Never-throw parsing, invalid-statement dropping, markdown-fence stripping, lazy container typing, and fuzzy name resolution each exist so a streaming model can be the author. Multiple independent systems converge on this: a renderer that refuses partial/imperfect model output is a renderer that is blank most of the time.

  2. Registry-constrained rendering is the safety model for generative UI. Output is restricted to registered, schema-typed components; hallucinated statements are validated and dropped (“render only what’s valid”), unresolved references are dropped from arrays rather than left as null holes, and the action vocabulary is limited to registered tool names. Notably, no system in the corpus documents generative-UI-specific threats (injection into rendered UI, action spoofing) beyond this implicit mitigation — an open gap.

  3. Structure paints first, data fills in. Forward references + top-down generation (layout → components → data), declared default/placeholder outputs that render instantly while fetches are in flight, and stale-while-revalidate caches all serve the same principle: the user sees the shape of the answer immediately and never stares at a blank surface.

  4. The presentation layer should have no agency. A recurring split: the reasoning agent produces data; a separate no-tools data→UI step renders it. Same doctrine appears as “the LLM is a formatter over measured data, never the measurement” — the structured payload is authoritative, the rendered doc is presentation. Keeps bespoke dashboards cheap AND safe.

  5. Approval latency, not capability, is the bottleneck for background autonomy. Products with working unattended agents make the pending-approval surface the #1 differentiator (always-on menu-bar presence; “Input required” as a first-class task state driving OS notifications and the task switcher). The fix is decision-ready briefs — one decision per card, evidence inline, one-click resolution — projectable to an ambient micro-surface outside the browser tab.

  6. Widgets are dumb renderers of pushed snapshots; derive relative labels at render time. Snapshots hold raw ISO timestamps only; “in 12 days” / “running for 2h” is computed on render, so a midnight tick keeps every surface fresh with zero data re-pushes and widgets can never disagree with the app.

  7. Refresh-on-view beats scheduled synthesis for personal dashboards. A measured case: a practitioner deleted a nightly cron that “chewed through tokens” in favor of pull-on-open — fresher when it matters, zero cost when unviewed. “A static artifact is a photograph; a pull-refreshed view is a window.”

  8. Proof, not prose, is the reviewability currency of unattended work. “What did my machine do while I slept” needs evidence bundles (screenshots, diffs, logs under a hashed manifest) rendered as a Proof section, not a self-reported summary. See verification-and-judging.

  9. Layout/data separation is the cost and stability trick for living views. “Same layout, new data, no re-prompting”: the LLM generates the skeleton once; steady-state refreshes re-bind data slots with no LLM in the path. Prompt-restyling breaks layouts often enough (observed 2–3 rollbacks/week) that version history with one-click restore is mandatory.

  10. Observation never consumes. Read-only trays/inboxes that deliberately never ack the underlying event — viewing is separated from consumption; an explicit action acknowledges. Prevents “I glanced at it so the system thinks I handled it.”

Generative UI: spec formats, parsing, rendering

Section titled “Generative UI: spec formats, parsing, rendering”

Line-oriented UI DSL over JSON for streamed component trees. Every statement is identifier = Expression, one per line; each complete line is independently parseable → render the moment it arrives. Benchmarked at −52.8% tokens vs JSON-render approaches (best case −67%); at 60 tok/s that is 4.9s vs 14.2s to a full UI. Forward references are legal (root = Stack([chart]) before chart exists) and the model is instructed to generate top-down so the shell paints first. Props are positional, mapped by schema key order (required first, most distinctive at position 0) — that ordering is where the token savings live. Grammar: three statement kinds — component (header = CardHeader("Title")), state ($days = "7"), data (data = Query("tool", {}, {rows: []})); a mandatory entry point (root = ...) or nothing renders.

Component registry → mechanically generated system prompt. defineComponent({name, description, props: zodSchema, component}) + a library object; library.prompt() emits the full authoring prompt: syntax rules, per-component signature lines derived from schemas, grouped sections (Layout/Forms/Charts) with per-group steering notes (“bar charts for comparisons, line charts for trends”), few-shot examples (1–2 “markedly improve quality”), and feature flags gating what the model may emit (tool calls / state bindings / edit-mode patches / inline-mode prose+fenced blocks). Best practices: flat schemas (deep nesting “wastes tokens and raises error rates”), small libraries (every component costs prompt space). Third-party apps contribute typed components to one shared library so their UI stays prompt-addressable.

Merge-by-name incremental patching. In edit mode a revision emits only changed statements; merge rules: same name replaces, new name adds, absent statements preserved; deletion = remove from root’s children → unreachable → garbage-collected. Measured: ~2 statements/~60 tokens/~0.3s vs ~20 statements/~400 tokens/~2s full regeneration — “up to 85% fewer tokens.” Patched lines stream and render like initial generation. The same protocol applies to any structured-spec revision (plans, workflow specs) — see planning-and-decomposition.

Fault-tolerant streaming spec parser (never throws). Line-oriented, indentation-stacked; all problems become accumulated typed records {path, line, code, message, raw} (codes like bad_syntax, schema_mismatch, unknown_key) and the offending line is skipped — a best-effort AST always returns. Tolerance tricks proven in production: bare key with no value is valid (mid-stream); markdown code fences and comments skipped (spec may arrive wrapped in LLM markdown); lazy array promotion (every container starts as an object, retroactively becomes an array when the first list item appears — container type decided by what actually shows up); prototype-pollution guard on __proto__/constructor path segments; union-schema mapping tries every variant and keeps the one with fewest errors; unterminated [ returns undefined (“likely mid-typing”). Streaming render loop is trivially simple: buffer += chunk; render(buffer) — full synchronous re-parse per chunk, with an async staleness guard (if (this.node !== currentNode) return) so superseded renders never fire stale events.

Fuzzy resolution of LLM-supplied identifiers, no threshold. Unknown template/component names resolve via normalize (lowercase, separators→hyphens) then Levenshtein distance, ties broken by common-prefix length — deliberately no distance cutoff, so a slightly-wrong name always lands on something renderable, with a warning emitted rather than a hard failure. Gate behind a strictness flag for unattended runs.

Lenient multi-strategy artifact extraction with a guaranteed-render fallback. Ordered fallbacks for extracting HTML from model output: strip fences (accept only if payload starts <) → slice <!DOCTYPE html…last </html> (or to end while streaming) → bare <html → trust leading < → terminal fallback that HTML-escapes the raw text into a minimal styled <pre> page so something always renders. A previewHtml variant appends </body></html> to partial docs so the live iframe preview works mid-stream. A live preview should never be blank.

Artifact rescue from tool-call arguments. Even when the prompt forbids file tools, models sometimes write the artifact via a Write tool and reply “done.” The rescue path scans tool_use blocks named write/create_file/etc.; if the target path matches an artifact extension, it lifts the content argument as the authoritative artifact, which replaces (not appends to) the streamed text. Prevents “run succeeded but output lost.”

Typed, LLM-friendly validation errors for self-correction. Error codes purpose-built to be formatted and fed back to the model for one-shot repair: parser (unknown-component, missing-required, excess-args, parse-failed), data (tool-not-found), runtime (render-error). A generic post-generation detect-incomplete-and-repair pass is sold as a core reliability feature by a hosted generative-UI product.

Reactive runtime — the LLM generates the wiring, the runtime executes it. State variables ($days = "7", two-way bound to inputs), Query(tool, args, defaults, refreshIntervalSecs?) (defaults render instantly; re-fetches when referenced state changes; 4th arg = polling), Mutation (only runs via explicit @Run), and a small deterministic builtin set (@Count/@Sum/@Filter/@Sort/@Each/...) executed by the runtime so aggregations don’t burn an LLM call per refresh. Action chains run sequentially and short-circuit on mutation failure. This builtin-set + query-defaults + polling combination IS the whole monitoring-dashboard pattern.

Dual payloads on generated-UI action events. Every interactive element emits both llmFriendlyMessage (rich, includes submitted values — sent to the model) and humanFriendlyMessage (short label — shown as the user’s chat message). Solves “form submit becomes an ugly JSON user message.” The tool bridge accepts either a plain async-function map or an MCP client.

Client-side parameter blocks (no model round-trip for tweaks). The model embeds a marker-fenced JSON block in the artifact whose keys map 1:1 to CSS custom properties; the renderer parses it into typed controls (color/range/select/toggle, with min/max/step/unit), applies live edits parent→iframe via postMessage batches, and on save reads live values back and rewrites the block into the source. Cap 8 parameters — more “overwhelms the UI and signals poor CSS variable hygiene.” Preserve values across revisions; normalize color formats. This is the missing mechanism for cheap artifact iteration after a run ends.

Oversized-output sanitization at the message boundary. Detect >64KB inline base64 blobs by magic prefix and replace with {result_omitted: true, reason, bytes, path} stubs pointing at saved files — never let multi-MB payloads enter chat/journal state. Pair with ordered redaction regexes (API keys, JWTs, bearer tokens, connection-string passwords, emails, home-dir usernames) on any error text that reaches a rendered surface or an LLM context.

Decision-ready brief contract (NeedsInputItem). Converged across ~18 sources: a needs-input inbox item is a typed record {run_id, project_id, node_id, block_kind: needs_input|capability|transient|approval, blocker, attempted, evidence, recommendation, choices[], resume_token, created_at, expires_at}one decision per card, evidence inline (before/after screenshots next to the decision), a recommendation, and typed choices. Reify needs-input as a first-class journal event so the inbox is a pure projection of journal events; replies route back to the blocked node via the resume_token as a typed request/response signal — never ad-hoc notification rows (the “reply goes nowhere” failure every ad-hoc design hits). Extras that survive contact with reality: owner binding (only the requesting user can satisfy an item surfaced to a shared channel — anti-hijack), permission_suggestions + updated_input on the reply so approvals can modify-and-approve rather than only allow/deny, staleness re-notify (>24h), digest-batching of report-only notices, and a resume handshake on reopening a paused run (workflow + current stage, last completed stage + timestamp, verbatim next_step, options: continue / review first).

Approval as promise suspension with fail-closed timeout. The requesting caller parks on an awaited promise in a Map<id, PendingApproval>; respond(id, allow|deny) or a timeout timer resolves it. Timeouts auto-DENY (reason:"timeout"); an auto mode short-circuits without ever creating a pending record; approvals are operable out-of-band (CLI/API against a remote host), not only from the UI. Race-safe: entry deleted before resolution on both paths.

Approval-fatigue policy as data. “Always allow” scoped to (operation type, target, session), cleared on session reset; trigger-fired/scheduled runs inherit an explicit auto-approve policy rather than hanging on gates no one will answer; option-less or malformed approval payloads still prompt even in auto mode. A 4-tier permission ladder (silent / first-time-prompt-with-allowlist / always-prompt-non-suppressible / hard-block-no-override) plus session bulk-trust (“trust for this session” on the first ~5 operations) — see security-and-guardrails. Critical correctness rule: re-verify the content revision (mtime:size or journal epoch) after human approval and immediately before applying — the TOCTOU window between approval and execution is real.

Typed questionnaire as the ask primitive. A first-class ask(questions) that renders a structured questionnaire and ends the turn; five question types proven useful: text-options, svg-options (visual choices), slider, file, freeform. Sibling systems ship inquire-multiple-choice and inquire-text as separate reusable patterns. Gates/wait nodes should expose a typed ask payload ({kind: choice|text|approval, options?, prompt}) so one renderer covers all human-input points across chat widget, cockpit, and inbox. Mid-run agent Q&A renders as typed question cards in the progress widget, not free text.

Editable context pill before dispatch. Generated context (from an issue, SOP, template parameterization) is shown as a reviewable, editable bundle before the agent launches — cheaper than full per-step revision for the simple path.

Menu-bar/tray micro-surface. An always-on OS-level surface showing live run progress and pending approvals with one-click approve/deny — the needs-input inbox escaping the browser tab. The decision-brief payload must be self-contained so a tray shell can render it without the SPA, deep-linking the resume_token. “Input required” as a first-class task state drives both OS notifications and a task-switcher grouping (by Date/Status/Target). Status classification (working / awaiting-input / done) derived from agent lifecycle hooks feeds the same notifications. Housekeeping/heartbeat runs are explicitly suppressed from attention indicators — attention surfaces exclude noise by construction.

Run cockpits, progress cards, monitoring boards

Section titled “Run cockpits, progress cards, monitoring boards”

Monitoring board = live tool-step feed + status/elapsed chips per parallel run. Cards show the live tool activity feed (Read/Edit/Bash steps), test results, and status+elapsed chips (“running · 4m”, “done · 8m ago”); status derives from lifecycle hooks classifying working/awaiting-input/done. Live stat chips use springs driven outside the React render cycle + font-mono tabular-nums (no digit jitter) + rAF/performance.now() elapsed timers, with aria-live="polite".

Truthful run-state lifecycle. Write the queued run record BEFORE acquiring the concurrency slot so the board distinguishes queued vs running vs deferred; on frontend rehydrate, rewrite any persisted running to reconnecting/stale (“a dropped tab can’t keep the stream”); sweep zombies and detect lost runs on restart — the board must never lie after a crash.

Run adoption via session-key equivalence. Trigger-fired runs stream events under a derived run-scoped key (base:run:<id>) while the UI tracks the base key; an equivalence helper treats run-scoped keys as the base session so a surface in view “adopts” the run live (streaming graph, tool steps, auto transcript reload) with no manual refresh. Strict key equality drops events — this is a shipped regression fix, not a nicety.

Event routing keyed by (container, generation, path) — never a global pending slot. A background run must never overwrite another container’s visible preview; on termination, flush only the ending generation’s pending updates before snapshot persistence. All cockpit updates key by explicit run id, not “active run.”

Host-generated phase notes. Synthetic progress notes emitted only when the current turn has produced no model text yet, labeled “visibility aids, not memory” — never journaled into LLM-visible history. Keeps the progress widget narrated without polluting context.

Thinking states written from inside execution. {title, description, ephemeral} items streamed from within tool/step implementations (a writeThinkingState callback injected into slow tools) give any surface a narratable activity feed without log archaeology; ephemeral marks transient progress vs durable summary lines.

Cockpit navigation mechanics (all from a production motion library, directly copyable): a view-registry multi-screen drawer (string-keyed screens, cross-fade + height spring, fade duration derived from the height delta: clamp(heightDiff/500, 0.15, 0.27) so bigger jumps get longer fades); a preset+queue state machine for the chat progress widget’s mode morphs (named size presets chip/compact/needs-input/expanded, reducer with previousSize + sequential {size, delay} animation queue, blur exits, spring 400/30); shared-layoutId morphs so the run chip becomes the cockpit panel and back; click-away that only closes when the interaction started outside (pointerdown tracked, confirmed on click — drags ending outside don’t dismiss); direction-aware tab transitions (signed direction as variant custom, measured-height container); coordinator-ready gating (multi-agent startup unblocks when the coordinator is ready; stragglers fail visibly on their own capsule, never blocking); stable per-slot identity colors pinned on assignment, recycled on removal.

Journal→timed-transcript replay. Replayable terminal output as data: {text, color?, delay?} lines (delay = ms before next line), command typed at randomized 25–60ms cadence, 250ms pause, lines revealed per delay (default 100ms); all timeouts tracked in a ref array and flushed on unmount; role="log" + aria-live="polite" + a renderLine hook for tool-call styling. Ready-made format for replaying a run journal as an animated transcript.

Files/processes panels: disk truth, not tool-log truth. The files panel watches the filesystem (chokidar; polling fallback on EINVAL/ENOSPC) rather than trusting tool-call logs — external edits are first-class; refresh can additionally be driven by agent tool events for liveness. Keep the last ~5 runs’ rendered previews alive (preview pool) for zero-delay switching. Run-spawned long-lived processes get a tab-scoped Processes panel with per-run (≤3) and global (≤10) caps, TERM→KILL on close, and “never kill dev servers on interrupt.”

In-cockpit diff review panel. A Changes panel per run auto-refreshes from filesystem + git events; unified/split diffs; single-click preview vs double-click pinned tab; stage/unstage/discard (confirm on discard); a commit card (Commit / Commit & Push / Commit & PR); PR section with files, commits, CI checks, merge state; conflicted files flagged; working-tree diffs inline-editable, git-object views read-only. Reviewing the run never leaves the cockpit.

Typed diff content in tool-call chat widgets. Tool-call messages carry kind: read|edit|execute, status chips, and typed content items where type:"diff" (path, old_text, new_text) renders a real file-diff panel with click-to-preview — the chat-widget pattern for any file-mutating step.

Annotate-to-correct on visual artifacts. When output is visual (HTML, rendered design, screenshot), let the user click-annotate elements to issue element-anchored directives instead of describing changes in text — directive verbs proven in one system: “change it”, “inspect it”, “lift it” (re-implement a component from elsewhere), “comment it” (pin actionable notes). Point-edit scope metadata sent with the revision prompt: {selector, tag, outerHTML, parent context}, selector priority data-testidid → class chain (excluding utility-class noise) → nth-child. Cuts iteration cycles for design/UI loops dramatically vs textual description.

Bulk-hydrate endpoint with per-tile error isolation. One POST hydrates all tiles: {widgets:[{id,type,config}]} → {widgetData:{[id]: data}, errors, loaded, total, loadedAt}; slow tiles get an explicit typed skeleton ({status:'loading', message}) rather than absent keys; each tile wrapped in its own try/catch so one failure never poisons the batch; every entry stamped loadedAt so staleness is displayable. Metric fan-out inside a tile uses Promise.allSettled over N probes and composes whatever succeeded — a dead sensor degrades one gauge, not the widget.

Tri-state liveness + probe-kind enum. Per-tile health = boolean | null (null before first probe → gray dot, green/up, red/down with tooltip); probe kind selectable per item (http — 10s timeout, online iff status 200–399 with validateStatus: () => true; ping — one ICMP packet, 1s wait); ~2-minute poll interval. Distinct launch URL vs health URL.

Self-describing catalogs + typed containers. The add-tile picker is driven by one metadata list {id, label, icon, description} — a new kind is one catalog entry + one config form + one render case; legacy kind strings are aliased at read time, never migrated. Composition is constrained to typed containers (a two-slot stacker with a hard-coded allowed inner set; a group with its own sortable sub-grid), with synthetic child ids (<parentId>-top) the backend resolves so every per-item API works uniformly on nested children. Per-audience layouts are independent item arrays (desktop[]/mobile[]), and audience filtering happens server-side before the payload leaves.

Live artifacts: persistent views outside any chat thread. A living dashboard = a persistent HTML page with version history, existing independently of conversations, refreshed on open: load cached HTML instantly (short-TTL cache) → re-query approved sources → swap in place (stale-while-revalidate with real stale content, not shimmer). Capability grant is fixed at creation/update time with no per-use prompts — which is exactly why auto-fired refreshes must default to a read-only source allowlist (the observed hazard: a write-capable connector “will send that calendar invite on open unless constrained”). Sharing resolves through the viewer’s credentials, not the author’s. Two creation paths: side-effect of a task, or a deliberate “new view” interview about sources and goals. The one user-visible feature all of this reduces to: pin-to-dashboard — a tile registry (tile = artifact slug + refresh trigger + size hint) over a composable home surface. Per-tile requirements learned from practitioner pain: freshness timestamp, per-source ok/error chips (silent empty panels are the top complaint), last-run deep-link, and per-refresh cost surfaced. The “when the user looks at this” trigger kind belongs to automation-and-triggers.

Multi-view synchronized tabs over one spec. Render the same underlying data as Preview / Markdown / Raw JSON / Live Preview tabs — the structured form authoritative, prose explicitly presentation. Applies equally to plan review, run output panels, and extracted-spec results.

Evidence bundle = schema-versioned manifest artifact {per-file: kind, name, size, sha256, optional expiry} grouping screenshots/video/logs/metadata a run produced; the cockpit renders a Proof section (Summary / Before-After / Evidence) from it; needs-input items carry the bundle inline. Pair with a standardized terminal-node handoff report every template’s final step emits: commands run / skipped-with-reasons, side-effect confirmations (“no commit/push performed”), known risks, follow-ups — one uniform renderer covers any run’s outcome.

Frame-by-frame proof artifacts with narration-as-spec. Every experience change produces a proof HTML file: one frame per step binding a claim + user action + observable assertion + spoken-style voiceover + validated screenshot (requireText/rejectText/hashIncludes; unvalidated screenshots count only as checkpoints). Voiceover-FIRST discipline: the narration script is aligned and approved BEFORE code; the runner scaffolds one proof stub per script paragraph and fails any flow whose narration drifts from the approved script. Flows declare kind: user-facing|internal; verdicts are honest (Passed only with full observable backing); proof posts as a PR comment. This is the missing evidence format for as-a-user validation — see verification-and-judging.

Send draft box. Messages typed while the agent is busy queue into a visible draft panel with per-item send-now (interrupt + jump queue) / edit (pull back to composer) / delete, drag reorder, and an auto/manual flush mode. Pure-renderer feature, high daily-use value.

Toggleable context chips on the composer. Surfaced skills/instructions/prompts appear as a count-badged popover of toggleable chips with ~300ms hover-preview of full content (6-line clamp) and per-item on/off — making the context budget visible and user-editable instead of invisible injection. Integration via an optional controller context (inject into composer when present, clipboard fallback when absent) keeps such widgets decoupled.

Waiting UX. Long blocking phases rotate genuinely useful tips (surfaced from skills/templates relevant to the running work) in an autoplay carousel whose progress indicator is animated over exactly the autoplay interval; shuffled; useReducedMotion zeroes all animation.

Attention-first boards, not status-first. Group run cards by what needs the user (running / needs-input / done), with needs-input driving count pills on project cards. A convergent counterpoint from a 16.8k-star product: “workflows” can be NL-driven session organization — sessions get rename/pin/archive/move-to-group primitives, the user states organizational policy in chat (“if this becomes a bug investigation put it in Bugs, else archive when finished”), and boards emerge as groups (Triage / In progress / Needs human review / Done) with zero engine. Also: keep flat, recency-sorted session-first navigation the default — “the sidebar shows sessions, not abstract projects”; container umbrellas must stay lazy/optional or they become ceremony for a single user. Task dependency edges need write-time cycle validation (a shipped system deadlocks on A↔B) and inverse blocks lists so completion-driven unblocking is O(dependents).

Agent-driven design tools & external control

Section titled “Agent-driven design tools & external control”

Design-tool UX kit (from prompt→prototype products): design == session (one JSONL file, real filesystem workspace); diff-edit baseline (snapshot (baseContent, baseHtml) on commit; next generation sends old+new content + existing HTML with a minimal-diff prompt — change only what the content diff implies, preserve head/fonts/layout); version history in IndexedDB not localStorage (UTF-16 doubling tax + 5MB origin cap vs 30–100KB per artifact; PK = taskId__<version base36>, 20 versions/task pruned post-commit, history explicitly “best-effort, not load-bearing”); RunStats persisted with every artifact version (firstByteAt, durationMs, promptBytes/outputBytes/deltaCount, model, costUsd, token splits, bin) so template/model choices are comparable post-hoc.

App-as-MCP-server (semantic UI control). The app publishes its own interface as an MCP server so any external agent can drive it semantically: ui_status, ui_snapshot (route + narration + visible actions), ui_list_actions, ui_execute_action(actionId, args) — “no DOM scraping, no coordinates.” Actions are hook-registered with a uniform contract: sideEffect: none|mutation|navigation, requiresArgs, requiresConfirmation (destructive requires confirmed:true), live disabled state, {ok}|{ok:false,error} results; transport = localhost bridge on a random port + bearer token + discovery file. The strict-background computer-use variant keeps the user’s frontmost app frontmost (accessibility-tree-first snapshots, input posted to the target process, a purely visual second-cursor overlay showing agent actions).

  • Two-step visualize: reasoning agent produces data → a separate no-tools data→UI generation step (constrained to a fixed component library) renders it. One mechanism behind run-summary cockpit views, inbox digests, dashboard tiles, and “show me X as a chart.”
  • Generative dashboard lifecycle: LLM generates skeleton once (registry-constrained DSL) → data slots bound to queries with declared defaults + polling → user tunes via client-side parameter blocks → revisions via merge-by-name patches → pinned to a tile registry with view-triggered refresh. No LLM in the steady-state refresh path.
  • Run container = one entity, many projections: branch/workspace + transcript + diff/review state + preview browser co-located per run (validated at 1M downloads); cockpit, board card, inbox item, and tray badge are all folds of the same journal events.
  • Chip↔cockpit continuity: backgrounding a run morphs the cockpit into a persistent dock chip via shared layoutId; completion pulses a checkmark micro-state; reopening adopts the live stream via key equivalence.
  • Inbox as pure projection: needs-input/approval journal events fold into the inbox; replies route via resume tokens; the same payload renders in chat widget, Work board, OS tray, and remote CLI.
  • Proof-carrying completion: terminal node emits handoff report + evidence bundle → Proof section in cockpit → inline evidence on any decision card the run raises → same bundle attaches to PR/commit surfaces.
  • Convergent widget hygiene bundle: dumb snapshot renderers + raw timestamps + render-time labels + midnight rebroadcast; tabular-nums stat chips; skeleton-with-typed-status first paint; per-source status chips. Multiple independent systems converge on every element.
  • JSON as the streaming UI wire format. ~2× the tokens and it isn’t incrementally parseable per line; every system that measured this moved to a line/indentation format.
  • Rejecting imperfect model output. A strict parser + LLM author = blank surfaces. Drop-and-warn beats throw; a scaffold fallback beats an error state.
  • LLM re-render on every dashboard refresh. Burns tokens and breaks layouts (“restyling breaks layouts 2–3×/week”); refresh must be a data re-bind. Scheduled push synthesis for personal views loses to pull-on-open on both cost and freshness.
  • Silent empty panels and silent write failures. Offline sources rendering empty tiles with no error, and writes failing “with no error message,” are the top practitioner complaints against the most polished live-artifact implementation shipping today.
  • Write-capable capabilities auto-firing on view. Creation-time grants with no per-use prompt + a view-triggered run = side effects on open. Auto-fired runs need a read-only default allowlist frozen at creation.
  • Global pending slot for streamed previews. A background run overwrites the visible container’s preview; routing must key by (container, generation, path).
  • Strict session-key equality for live streams. Run-scoped keys silently drop events from the viewing surface; adoption requires an equivalence helper (shipped regression fix).
  • Persisted running state trusted on rehydrate. A dropped tab “keeps” a stream forever; rewrite to reconnecting/stale on load.
  • Trusting tool-call logs for file panels. Misses external edits; watch the filesystem, fall back to polling.
  • Read-and-mark consumption in observation surfaces. A tray that acks events on view loses messages when the consumer crashes mid-turn; observation and consumption must be separate acts.
  • Multi-MB base64 in chat/journal state. Sanitize to path-reference stubs at the boundary.
  • All-ready gating on multi-agent start. One slow teammate blocks the whole surface; gate on coordinator-ready and let stragglers fail visibly on their own capsule.
  • Free-form widget nesting. Typed containers with hard-coded allowed inner sets + synthetic child ids keep every per-item API uniform; free nesting reintroduces the addressing problem.
  • Approval timeouts that hang open. A reserved createdAt with no sweeper = gates that never resolve; timeouts must auto-deny, fail-closed.
  • Container ceremony. Mandatory project umbrellas over a flat session list add friction a single user never repays; keep grouping opt-in.
  • Line-oriented UI DSL: −52.8% tokens vs JSON-render, −51.7% vs a JSON UI DSL (best case −67%); 4.9s vs 14.2s to full UI at 60 tok/s.
  • Incremental merge-by-name edits: ~2 statements/~60 tokens/~0.3s vs ~20/~400/~2s full regen — up to 85% fewer tokens.
  • On-demand context loading: manifest line ~500 bytes/entity, full body 500–2000 bytes on demand — ~2k tokens for 13 entries vs 20–40k wholesale injection; one measured system prompt: 9–10K chars (~2.7K tokens) vs a competitor’s measured ~14.7K tokens.
  • Live-dashboard refresh cost: heavy views (90d payments + 15 scrapers) = 3–5% of a Pro plan’s daily budget per open; latency 5–10s (1 source) to 15–20s (4 sources); a nightly scheduled version was deleted as too expensive.
  • Layout-break frequency under prompt restyling: rollback via version history 2–3×/week — version restore is load-bearing.
  • Motion constants that survived production: cross-fade duration clamp(heightDiff/500, 0.15, 0.27); springs — snappy UI 400/30, container morph 550/45/0.7, content 200/20; dock magnification [-150,0,150]→[40,80,40] px through 320/20/0.1; terminal replay typing 25–60ms/char, 250ms pause, 100ms line delay; hover-preview delay 300ms; swipe threshold ±100px; dialog↔drawer switch at 768px.
  • Client-side parameter blocks: cap 8 params before UI overwhelm.
  • Health probes: http 10s timeout, online iff 200–399; ping 1 packet/1s; 2-min poll cadence; tri-state with null-before-first-probe.
  • Version history: IndexedDB over localStorage (UTF-16 doubling + 5MB origin cap vs 30–100KB/artifact); 20 versions/task, pruned post-commit; inbox upload cap 50MB; inline read cap 5MB; chat log ring −400 entries; deployment ring 5.
  • Run caps that shipped: ≤3 background processes per container, ≤10 global; 60 tool calls + 10-min wall clock as soft turn caps; staleness re-notify at >24h.
  • Few-shot in registry-generated prompts: 1–2 examples “markedly improve quality.”
  • Generative-UI threat model. No system in the corpus documents injection-into-rendered-UI or action-spoofing mitigations beyond registry-constrained rendering; what does fencing look like when untrusted content flows into a component tree with live actions?
  • What does a live refresh actually re-run? Whether view-triggered refresh re-executes a full LLM task or a narrow data re-query is undocumented in the flagship implementation; the layout/data split suggests the answer, but the boundary (when does a refresh legitimately need the LLM?) is undesigned.
  • Round-tripping manual edits into specs. Post-generation editors exist (drag/resize/dbl-click-edit), but whether hand edits write back into the authoring DSL — keeping the spec the source of truth — is unconfirmed anywhere.
  • Generative vs hand-built cockpit boundary. Registry-constrained generated UI can render run summaries and dashboards; cockpits with hard correctness requirements (adoption, truthful lifecycle) are hand-built everywhere. Where the line sits as component registries mature is open.
  • Ambient-presence scope. Corpus evidence supports a count-pill + one-click approve tray; whether the micro-surface should grow toward a full mini-app (transcripts, steering) or stay decision-only is untested.
  • Annotate-to-correct beyond DOM. Element-anchored correction is proven for HTML; the equivalent for PDFs, images, and rendered charts (no selector to anchor to) has no reference implementation in the corpus.