@osolmaz/pi-workflows 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +36 -21
  2. package/dist/extension/executor.d.ts +14 -1
  3. package/dist/extension/executor.js +11 -1
  4. package/dist/extension/executor.js.map +1 -1
  5. package/dist/extension/index.js +79 -7
  6. package/dist/extension/index.js.map +1 -1
  7. package/dist/extension/recorder.d.ts +85 -0
  8. package/dist/extension/recorder.js +525 -0
  9. package/dist/extension/recorder.js.map +1 -0
  10. package/dist/extension/session-events.d.ts +134 -0
  11. package/dist/extension/session-events.js +60 -0
  12. package/dist/extension/session-events.js.map +1 -0
  13. package/dist/extension/widget.js +25 -24
  14. package/dist/extension/widget.js.map +1 -1
  15. package/dist/render/canvas.d.ts +1 -1
  16. package/dist/render/canvas.js +5 -0
  17. package/dist/render/canvas.js.map +1 -1
  18. package/dist/render/graph-render.d.ts +5 -0
  19. package/dist/render/graph-render.js +211 -48
  20. package/dist/render/graph-render.js.map +1 -1
  21. package/dist/viewer/render.js +19 -3
  22. package/dist/viewer/render.js.map +1 -1
  23. package/dist/viewer/session-reducer.d.ts +45 -0
  24. package/dist/viewer/session-reducer.js +266 -0
  25. package/dist/viewer/session-reducer.js.map +1 -0
  26. package/dist/workflows/artifacts.d.ts +40 -0
  27. package/dist/workflows/artifacts.js +155 -0
  28. package/dist/workflows/artifacts.js.map +1 -0
  29. package/dist/workflows/engine.d.ts +2 -0
  30. package/dist/workflows/engine.js +38 -7
  31. package/dist/workflows/engine.js.map +1 -1
  32. package/dist/workflows/index.d.ts +3 -2
  33. package/dist/workflows/index.js +2 -1
  34. package/dist/workflows/index.js.map +1 -1
  35. package/dist/workflows/store.d.ts +53 -9
  36. package/dist/workflows/store.js +523 -43
  37. package/dist/workflows/store.js.map +1 -1
  38. package/dist/workflows/types.d.ts +126 -3
  39. package/docs/development.md +43 -19
  40. package/docs/live-replay-protocol.md +155 -0
  41. package/docs/plans/piw-viewer-experience-implementation-plan.md +674 -0
  42. package/docs/plans/replayable-run-bundles-implementation-plan.md +65 -0
  43. package/docs/plans/session-event-replay-implementation-plan.md +494 -0
  44. package/docs/plans/tui-viewer-implementation-plan.md +64 -0
  45. package/docs/run-bundles.md +320 -55
  46. package/docs/session-event-journal.md +470 -0
  47. package/docs/tui-viewer.md +218 -0
  48. package/package.json +2 -1
  49. package/src/extension/executor.ts +28 -1
  50. package/src/extension/index.ts +87 -7
  51. package/src/extension/recorder.ts +633 -0
  52. package/src/extension/session-events.ts +119 -0
  53. package/src/extension/widget.ts +26 -24
  54. package/src/render/canvas.ts +19 -1
  55. package/src/render/graph-render.ts +277 -44
  56. package/src/viewer/render.ts +21 -3
  57. package/src/viewer/session-reducer.ts +347 -0
  58. package/src/workflows/artifacts.ts +188 -0
  59. package/src/workflows/engine.ts +39 -7
  60. package/src/workflows/index.ts +15 -0
  61. package/src/workflows/store.ts +649 -49
  62. package/src/workflows/types.ts +141 -3
@@ -0,0 +1,674 @@
1
+ # PIW viewer experience implementation plan
2
+
3
+ ## Objective
4
+
5
+ Make `piw` a state-rich, terminal-native workflow viewer with Catppuccin as
6
+ its complete default visual system, a Herdr-style theme picker, clearer replay
7
+ and run states, a useful inspector, a real timeline, and resilient remote
8
+ viewing.
9
+
10
+ The result is verified by styled ratatui buffer tests, graph parity fixtures,
11
+ remote reconnect tests, real-run end-to-end tests, and manual PTY checks across
12
+ dark, light, narrow, and wide terminal layouts.
13
+
14
+ ## Boundaries
15
+
16
+ - Keep `piw` a standalone Rust TUI. Do not imitate browser-only effects such
17
+ as shadows, smooth camera animation, or fractional text animation.
18
+ - Do not modify Pi internals or undocumented Pi APIs.
19
+ - Do not write or rewrite Pi session history.
20
+ - Do not change the workflow run-bundle schemas or persisted run documents.
21
+ - Persist only viewer preferences under the user's normal config directory.
22
+ Do not store replay positions, run data, or session data there.
23
+ - Preserve the existing TypeScript/Rust graph layout contract. Any textual or
24
+ geometric graph change must be made in both renderers and reflected in the
25
+ shared fixtures.
26
+ - Keep compact line nodes as an optional density mode; bordered nodes remain
27
+ the default.
28
+ - Keep all remote access loopback-only unless the existing SSH-tunnel workflow
29
+ is used.
30
+
31
+ ## Product decisions
32
+
33
+ ### Terminal-native ACPX parity
34
+
35
+ Adopt ACPX ideas that improve inspection and state comprehension:
36
+
37
+ - semantic, surface-backed graph nodes;
38
+ - separate running and replay-focus presentation;
39
+ - a visible timeline and playback speed controls;
40
+ - detailed attempt and event inspection;
41
+ - graph-node selection;
42
+ - a collapsible run browser;
43
+ - structured tool-call and tool-result rendering; and
44
+ - reliable live reconnection.
45
+
46
+ Do not adopt ELK, React Flow, browser card decoration, smooth zoom, animated
47
+ edges, multiple ACP-session concepts, or character-by-character replay.
48
+
49
+ ### Stable full node cards
50
+
51
+ Box mode is the canonical graph presentation. Each node is a full card with a
52
+ border, an interior surface, and enough padding to keep its outer dimensions
53
+ stable. Compact line mode remains an explicit density option and does not set
54
+ the box-mode contract.
55
+
56
+ The graph computes one card width and one card height before it runs layout.
57
+ Every real node uses those dimensions. The calculation uses the largest card
58
+ required by the immutable definition and reserves fixed rows for every runtime
59
+ field. Shorter cards receive blank padding. A status change, timer tick, new
60
+ attempt, replay seek, selection change, or live event may replace text inside a
61
+ reserved slot, but it must never resize a node or move the graph. Terminal
62
+ resize changes the viewport only. A small viewport pans over the graph instead
63
+ of shrinking or clipping cards.
64
+
65
+ A full card contains the same classes of information as an ACPX workflow card:
66
+
67
+ - the exact node id and full node type;
68
+ - a status symbol with its text label;
69
+ - semantic markers for start, branch count, and terminal position;
70
+ - every branch label associated with the node;
71
+ - not-visited or attempt-count metadata;
72
+ - live elapsed time or final duration; and
73
+ - the complete node `statusDetail` or short terminal outcome when present.
74
+
75
+ Use the established symbols consistently:
76
+
77
+ | Meaning | Symbol |
78
+ | ------------- | ------ |
79
+ | queued | `·` |
80
+ | running | `◐` |
81
+ | replay focus | `◆` |
82
+ | completed | `✓` |
83
+ | failed | `✗` |
84
+ | timed out | `×` |
85
+ | waiting | `⏸` |
86
+ | cancelled | `~` |
87
+ | start node | `▶` |
88
+ | branch node | `◇N` |
89
+ | terminal node | `■` |
90
+
91
+ Node-card fields must not use ellipses or disappear because another state needs
92
+ the space. Long fixed text wraps during the initial card measurement, and all
93
+ nodes are padded to the resulting graph-wide dimensions. Prompts, outputs,
94
+ errors, tool payloads, and other unbounded attempt details remain in the
95
+ inspector; they are not node-card fields.
96
+
97
+ ### Theme model
98
+
99
+ Use a purpose-built `piw` theme contract rather than Pi's 51-token theme JSON
100
+ format. `piw` is a standalone viewer and needs graph, timeline, run-state, and
101
+ node-surface tokens that Pi themes do not define.
102
+
103
+ Use the same operating model as Herdr:
104
+
105
+ - Catppuccin Mocha is the default;
106
+ - built-in dark and light themes are selectable;
107
+ - moving through the picker previews immediately;
108
+ - Apply persists the choice;
109
+ - Cancel restores the exact original palette;
110
+ - config and CLI selection are supported; and
111
+ - optional per-token overrides layer on top of a built-in palette.
112
+
113
+ The initial built-in list should match Herdr's user-facing list and names:
114
+
115
+ - `catppuccin`
116
+ - `catppuccin-latte`
117
+ - `terminal`
118
+ - `tokyo-night`
119
+ - `tokyo-night-day`
120
+ - `dracula`
121
+ - `nord`
122
+ - `gruvbox`
123
+ - `gruvbox-light`
124
+ - `one-dark`
125
+ - `one-light`
126
+ - `solarized`
127
+ - `solarized-light`
128
+ - `kanagawa`
129
+ - `kanagawa-lotus`
130
+ - `rose-pine`
131
+ - `rose-pine-dawn`
132
+ - `vesper`
133
+
134
+ Theme names are normalized by lowercasing and replacing spaces and underscores
135
+ with hyphens. Unknown names produce a visible diagnostic and fall back to
136
+ Catppuccin instead of panicking.
137
+
138
+ ### Surface hierarchy
139
+
140
+ Every frame must be painted from theme tokens. Do not rely on the terminal's
141
+ default background except in the explicit `terminal` theme.
142
+
143
+ For Catppuccin Mocha, use a clear hierarchy:
144
+
145
+ - application background: Crust (`#11111b`);
146
+ - panel background: Mantle (`#181825`);
147
+ - graph canvas: Base (`#1e1e2e`);
148
+ - ordinary node surface: Surface 0 (`#313244`);
149
+ - selected/running node surface: Surface 1 (`#45475a`);
150
+ - main text: Text (`#cdd6f4`);
151
+ - secondary text: Subtext 0 (`#a6adc8`);
152
+ - muted lines: Overlay 0 (`#6c7086`);
153
+ - accent/running: Blue (`#89b4fa`);
154
+ - replay focus: Mauve (`#cba6f7`);
155
+ - completed: Green (`#a6e3a1`);
156
+ - waiting: Yellow (`#f9e2af`);
157
+ - failed: Red (`#f38ba8`);
158
+ - timed out/interrupted: Peach (`#fab387`);
159
+ - branch/tool accent: Teal (`#94e2d5`).
160
+
161
+ A node box must always have a background distinct from its graph canvas. State
162
+ is carried primarily by the border, glyph, and accent text rather than by
163
+ painting every state a different card color.
164
+
165
+ ## Theme architecture
166
+
167
+ ### Semantic palette
168
+
169
+ Add `tui/src/theme/` with a `Theme` or `Palette` value containing semantic
170
+ colors rather than component-specific ad hoc styles. At minimum, define:
171
+
172
+ - surfaces: `appBg`, `panelBg`, `canvasBg`, `nodeBg`, `nodeFocusBg`,
173
+ `selectionBg`, `surfaceDim`;
174
+ - structure: `border`, `borderFocused`, `edge`, `edgeBack`, `edgeTaken`;
175
+ - text: `text`, `subtext`, `muted`;
176
+ - interaction: `accent`, `replayFocus`;
177
+ - state: `running`, `success`, `warning`, `error`, `timedOut`, `cancelled`;
178
+ - content: `branch`, `user`, `assistant`, `tool`; and
179
+ - timeline: `timelineTrack`, `timelineFill`, `timelineThumb`.
180
+
181
+ Components must request semantic styles from the active theme. Remove direct
182
+ `Color::Cyan`, `Color::Green`, and similar choices from `tui/src/ui/` and
183
+ `tui/src/ui/conversation.rs`. Literal colors should remain only in built-in
184
+ palette definitions, color parsing, and theme tests.
185
+
186
+ ### Canvas roles and node backgrounds
187
+
188
+ The current `CanvasStyle` combines node and edge meanings, and sparse spaces
189
+ cannot carry a background. Replace it with semantic canvas roles that
190
+ separate:
191
+
192
+ - ordinary text;
193
+ - muted text;
194
+ - queued, taken, active, and back edges; and
195
+ - queued, running, replay-focused, completed, failed, timed-out, waiting, and
196
+ cancelled nodes.
197
+
198
+ Add an intentional-space/fill operation to `CharCanvas` so every cell inside a
199
+ bordered node carries `nodeBg` or `nodeFocusBg`. Draw the node surface before
200
+ its border and content, and ensure graph edges cannot bleed through node
201
+ interiors.
202
+
203
+ Node content should use separate roles for the status glyph, node id, node
204
+ type, and metadata instead of applying one color to the whole line. Plain-text
205
+ fixture output must remain deterministic.
206
+
207
+ ### Configuration
208
+
209
+ Add viewer configuration at:
210
+
211
+ 1. `PIW_CONFIG_PATH`, when set;
212
+ 2. `$XDG_CONFIG_HOME/piw/config.toml`; or
213
+ 3. `~/.config/piw/config.toml`.
214
+
215
+ Use this shape:
216
+
217
+ ```toml
218
+ [theme]
219
+ name = "catppuccin"
220
+ auto_switch = false
221
+ dark_name = "catppuccin"
222
+ light_name = "catppuccin-latte"
223
+
224
+ [theme.custom]
225
+ # canvas_bg = "#1e1e2e"
226
+ # node_bg = "#313244"
227
+ # accent = "#89b4fa"
228
+ ```
229
+
230
+ Color values accept `#rrggbb`, `#rgb`, `rgb(r,g,b)`, named terminal colors,
231
+ and `reset` aliases. Invalid overrides are reported and ignored individually;
232
+ they do not invalidate the entire theme.
233
+
234
+ Configuration precedence is:
235
+
236
+ 1. an explicit picker selection made during the current session;
237
+ 2. `--theme <name>`;
238
+ 3. `PIW_THEME`;
239
+ 4. config file; and
240
+ 5. `catppuccin`.
241
+
242
+ Add `--list-themes`. A picker Apply writes only the theme selection, preserves
243
+ unknown config keys and comments, and uses a same-directory temporary file plus
244
+ atomic rename. Applying a manual theme disables `auto_switch`, matching Herdr.
245
+
246
+ Host light/dark detection and automatic sibling switching are a final theme
247
+ phase, not a prerequisite for manual theme selection. When added, query the
248
+ host terminal background only through standard terminal escape sequences,
249
+ restore terminal modes on exit, and retain the manually configured theme when
250
+ the terminal does not answer.
251
+
252
+ ### Theme picker
253
+
254
+ Add a modal opened with `,`:
255
+
256
+ - list every built-in theme;
257
+ - show compact color swatches;
258
+ - preview on Up/Down, `j`/`k`, or mouse selection;
259
+ - Apply with Enter;
260
+ - Cancel with Escape and restore the original palette exactly;
261
+ - mark the persisted/current theme with a checkmark; and
262
+ - show config write errors inside the modal without closing it.
263
+
264
+ The modal must be usable at 80x24, clip or scroll at smaller sizes, and paint
265
+ its own panel background. Update the footer help to advertise the picker
266
+ without displacing essential replay controls.
267
+
268
+ ## State presentation
269
+
270
+ Replace the current overloaded active state with these distinct graph states:
271
+
272
+ - queued;
273
+ - running;
274
+ - replay focus;
275
+ - completed;
276
+ - failed;
277
+ - timed out;
278
+ - waiting;
279
+ - cancelled.
280
+
281
+ Requirements:
282
+
283
+ - Running uses the active glyph, active color, live elapsed timer, and heavy
284
+ border.
285
+ - Replay focus uses a distinct glyph/color and heavy border, but never says or
286
+ looks like it is currently running.
287
+ - Timed out has a width-stable glyph and the `timedOut` color instead of using
288
+ the failed presentation.
289
+ - Waiting remains distinct.
290
+ - `paused: true` is visible in the graph title and transport even if the run's
291
+ persisted status is `running`.
292
+ - `statusDetail`, `waitingOn`, and run errors are shown consistently without
293
+ overflowing node cells.
294
+ - `(live)`, `(latest)`, and `(replay)` remain separate from the persisted run
295
+ outcome.
296
+
297
+ Where glyph or node text changes affect graph fixture output, update both
298
+ `src/render/` and `tui/src/render.rs`, regenerate the fixtures, and retain
299
+ byte-for-byte plain rendering parity.
300
+
301
+ ## Inspector
302
+
303
+ Refactor the inspector out of `tui/src/ui/mod.rs` into focused modules and make
304
+ it useful for debugging without changing bundle data.
305
+
306
+ ### Steps
307
+
308
+ - Keep the visible attempt list.
309
+ - Provide summary and expanded detail modes.
310
+ - Render full wrapped prompt, output, and error content in expanded mode.
311
+ - Render complete action receipts: action type, command, arguments, working
312
+ directory, exit code, signal, and duration.
313
+ - Show the selected attempt id and timestamps.
314
+ - Resolve local artifact-backed fields within existing containment and size
315
+ limits.
316
+ - Use a side-by-side attempt-list/detail layout when wide and a stacked layout
317
+ when narrow.
318
+
319
+ ### Trace
320
+
321
+ Make trace rows selectable and add three explicit scopes:
322
+
323
+ - selected attempt;
324
+ - replay-visible trace; and
325
+ - full run.
326
+
327
+ For replay-visible trace, derive the cutoff from events associated with the
328
+ visible attempt ids and include run-level events only up to that sequence. The
329
+ full-run mode must be labeled because it can reveal events after the current
330
+ replay position.
331
+
332
+ Enter expands the selected event's sanitized JSON payload. Preserve trace tail
333
+ following only while the user is at the bottom and the scope includes live
334
+ events.
335
+
336
+ ### Info
337
+
338
+ Keep and theme the existing run Info tab. Continue showing source, status,
339
+ status detail, error, counts, interruption warning, and final output.
340
+
341
+ Store selection and scroll state per inspector tab so switching tabs does not
342
+ lose the user's place.
343
+
344
+ ## Replay timeline
345
+
346
+ Replace the text-only footer with a responsive terminal timeline:
347
+
348
+ - a Unicode track, completed segment, and playhead;
349
+ - `step n/m`, before-first, latest, and LIVE labels;
350
+ - mouse click and drag seeking;
351
+ - previous, play/pause, next, start, and latest hit areas;
352
+ - 1x, 2x, 5x, and 10x playback speeds;
353
+ - the existing `[`, `]`, Space, Home, End, `g`, `G`, and `L` controls; and
354
+ - `{` and `}` for slower/faster playback.
355
+
356
+ Playback remains discrete. At 1x, retain the current 700 ms step interval;
357
+ other speeds divide that interval. Do not synthesize fractional attempt state.
358
+
359
+ Use two footer rows when the terminal has enough height and a compact one-row
360
+ fallback in short terminals. Seeking backward detaches from live; selecting
361
+ latest rejoins live. Appended steps advance automatically only when the viewer
362
+ was already following latest.
363
+
364
+ ## Remote reliability and artifacts
365
+
366
+ Refactor `RemoteRuns` into a reconnecting client:
367
+
368
+ - bounded exponential backoff with jitter;
369
+ - explicit connecting, reconnecting, connected, and disconnected states;
370
+ - automatic `watch_runs` and selected-run resubscription after reconnect;
371
+ - existing revision-gap resnapshot behavior;
372
+ - cached data remains visible with a prominent stale/disconnected label; and
373
+ - dropping the client stops and joins the background worker cleanly.
374
+
375
+ Do not treat commands sent while offline as delivered. Keep desired
376
+ subscriptions in shared state and reconcile them after each successful hello.
377
+
378
+ Wire the existing artifact protocol into the inspector. Cache bounded artifact
379
+ content by `(runId, path)`, serialize requests so the current protocol's errors
380
+ can be associated with the outstanding request, and retain path containment
381
+ and maximum-size checks. Do not change the replay protocol schema for this
382
+ feature.
383
+
384
+ ## Graph and run-browser interaction
385
+
386
+ ### Graph semantics
387
+
388
+ Replace the current one-line box interior with the stable full-card contract.
389
+ Every boxed node shows identity, type, current status, semantic markers, and
390
+ branch labels. It also shows attempt metadata and timing plus the available
391
+ short detail or outcome. The exact node id remains visible.
392
+
393
+ Compute canonical graph-wide card dimensions before placing ranks and routing
394
+ edges. Pass those fixed bounds into layout, drawing, camera targeting, mouse hit
395
+ testing, and edge attachment. Fill unused rows and columns with intentional
396
+ node-surface padding. Live and replay updates redraw card cells without
397
+ re-running layout.
398
+
399
+ Keep branch labels on edges as routing labels and repeat them inside branch
400
+ cards as node metadata, matching ACPX. Show all labels instead of taking a
401
+ fixed prefix.
402
+
403
+ Implement card measurement and rendering in both graph renderers, regenerate
404
+ parity fixtures, and verify that every replay position has identical
405
+ `NodeBounds` for each node.
406
+
407
+ ### Graph selection
408
+
409
+ Return node bounds with the rendered canvas. Use those bounds for:
410
+
411
+ - precise active/replay camera targeting instead of searching styled text;
412
+ - mouse hit testing; and
413
+ - selecting the latest visible attempt for a clicked node.
414
+
415
+ A queued node click may focus the definition node but must not invent an
416
+ attempt. Panning and clicking must remain distinguishable by a drag threshold.
417
+
418
+ ### Run browser
419
+
420
+ Add expanded and collapsed browser modes:
421
+
422
+ - expanded retains title, status, elapsed time, and interrupted marker;
423
+ - collapsed is a narrow status rail;
424
+ - `b` toggles the mode;
425
+ - a single directly opened run continues to hide the browser; and
426
+ - narrow terminals start collapsed without overwriting an explicit user
427
+ toggle during that session.
428
+
429
+ Theme selected rows with `selectionBg` and preserve a visible status color in
430
+ both modes.
431
+
432
+ ## Conversation rendering
433
+
434
+ Keep Pi's recorded session entries and explicit conversation ranges as the
435
+ authoritative source.
436
+
437
+ - Preserve replay-safe progressive reveal; do not show future messages while
438
+ rewound.
439
+ - Preserve the selected attempt's visible gutter.
440
+ - Render tool calls with tool name and a compact argument summary.
441
+ - Render tool results with running/success/error state and a short preview.
442
+ - Let Enter expand the selected tool payload as sanitized JSON.
443
+ - Add sticky live auto-follow: remain at the bottom while new entries arrive,
444
+ detach when the user scrolls upward, and reattach with End.
445
+ - Keep thinking text distinguishable but readable under every built-in theme.
446
+
447
+ Do not add a multiple-session selector; the current bundle contract records one
448
+ Pi conversation per run.
449
+
450
+ ## Responsive layout
451
+
452
+ Introduce explicit layout tiers rather than relying only on percentages:
453
+
454
+ - **wide:** run browser, graph, and split attempt detail use available width;
455
+ - **standard:** current graph-over-inspector layout;
456
+ - **narrow:** collapsed browser, compact timeline, and stacked inspector; and
457
+ - **very short:** one-row transport and minimum viable pane titles.
458
+
459
+ Every tier must preserve access to run status, replay position, graph content,
460
+ and inspector tabs. Avoid silently dropping information solely because the
461
+ terminal resized.
462
+
463
+ ## Implementation sequence
464
+
465
+ ### 1. Theme foundation
466
+
467
+ - Add semantic palette, built-ins, parser, config loader, CLI override, and
468
+ Catppuccin default.
469
+ - Paint the full frame and every panel from palette surfaces.
470
+ - Replace all hard-coded UI colors.
471
+ - Add node-surface canvas roles and filled node rectangles.
472
+ - Add styled buffer tests for Catppuccin and one light theme.
473
+
474
+ Exit criterion: Catppuccin is visibly applied to the whole viewer, and every
475
+ boxed node has a surface color distinct from the graph background.
476
+
477
+ ### 2. Theme picker and persistence
478
+
479
+ - Add the `,` modal with preview/apply/cancel and mouse support.
480
+ - Persist atomically while preserving unrelated config.
481
+ - Add all Herdr-aligned built-ins and custom overrides.
482
+ - Add invalid-config diagnostics and `--list-themes`.
483
+
484
+ Exit criterion: a user can preview, cancel, apply, restart, and retain any
485
+ built-in theme.
486
+
487
+ ### 3. State correctness
488
+
489
+ - Split running from replay focus.
490
+ - Add timed-out and paused presentation.
491
+ - Apply semantic state colors consistently to graph, run rows, inspector, and
492
+ timeline.
493
+ - Update both renderers and fixtures where plain graph output changes.
494
+
495
+ Exit criterion: no historical replay position appears live, and every persisted
496
+ run/node state has a distinct tested presentation.
497
+
498
+ ### 4. Inspector depth
499
+
500
+ - Refactor inspector modules.
501
+ - Add full attempt details and complete action receipts.
502
+ - Add selectable trace scopes and expandable payloads.
503
+ - Preserve per-tab selection and scroll state.
504
+
505
+ Exit criterion: all existing step and trace fields can be inspected without
506
+ opening bundle files manually.
507
+
508
+ ### 5. Replay timeline
509
+
510
+ - Add the responsive timeline, hit areas, seeking, and speeds.
511
+ - Keep discrete deterministic playback and live-follow semantics.
512
+
513
+ Exit criterion: keyboard and mouse can seek any recorded step and reliably
514
+ return to live.
515
+
516
+ ### 6. Remote resilience
517
+
518
+ - Add reconnect state machine, resubscription, cancellation, and server-restart
519
+ tests.
520
+ - Connect remote artifact fetching to expanded inspector fields.
521
+
522
+ Exit criterion: restarting `piw serve` does not require restarting the viewer,
523
+ and cached data is never presented as current while disconnected.
524
+
525
+ ### 7. Graph and browser interaction
526
+
527
+ - Replace one-line boxed interiors with stable full node cards.
528
+ - Measure one canonical card size from all node-card fields before layout.
529
+ - Add node bounds and click selection.
530
+ - Add start/end semantics and branch metadata in both renderers. Include status
531
+ and attempt counts. Timing and the short detail use their own slots.
532
+ - Add collapsed run browser and responsive layout tiers.
533
+
534
+ Exit criterion: every node-card field is visible, node bounds stay unchanged
535
+ across every live and replay state, graph nodes are selectable, and an
536
+ 80-column terminal remains usable through panning.
537
+
538
+ ### 8. Conversation polish and host-theme switching
539
+
540
+ - Add structured tool rows, expansion, and sticky auto-follow.
541
+ - Add optional host appearance detection and dark/light sibling switching.
542
+ - Verify terminal state restoration after normal exit, errors, and Ctrl-C.
543
+
544
+ Exit criterion: live tool activity remains readable and auto-switching never
545
+ leaves terminal modes or colors altered after exit.
546
+
547
+ ## Expected file changes
548
+
549
+ Likely additions:
550
+
551
+ ```text
552
+ tui/src/theme/mod.rs
553
+ tui/src/theme/builtins.rs
554
+ tui/src/theme/config.rs
555
+ tui/src/ui/theme_picker.rs
556
+ tui/src/ui/timeline.rs
557
+ tui/src/ui/inspector.rs
558
+ tui/src/ui/runs.rs
559
+ ```
560
+
561
+ Likely modifications:
562
+
563
+ ```text
564
+ tui/Cargo.toml
565
+ tui/src/main.rs
566
+ tui/src/lib.rs
567
+ tui/src/canvas.rs
568
+ tui/src/render.rs
569
+ tui/src/client.rs
570
+ tui/src/ui/mod.rs
571
+ tui/src/ui/graph.rs
572
+ tui/src/ui/conversation.rs
573
+ src/render/* # only for shared graph text/geometry changes
574
+ fixtures/layout/* # regenerated only when shared output changes
575
+ docs/tui-viewer.md
576
+ docs/development.md
577
+ README.md
578
+ ```
579
+
580
+ Avoid leaving theme logic split between components. Palette definitions,
581
+ parsing, resolution, and style derivation belong under `tui/src/theme/`.
582
+
583
+ ## Verification
584
+
585
+ ### Unit tests
586
+
587
+ - Every built-in theme resolves by canonical name and documented aliases.
588
+ - Catppuccin is the default.
589
+ - Invalid theme names and colors fall back predictably with diagnostics.
590
+ - Config precedence and atomic save behavior are deterministic.
591
+ - Picker preview, Cancel, and Apply preserve the correct palette.
592
+ - Every non-terminal built-in has distinct `canvasBg` and `nodeBg` values.
593
+ - Styled ratatui buffers contain the expected frame, panel, canvas, node, and
594
+ focused-node backgrounds.
595
+ - No state shares the wrong semantic role, especially failed/timed-out and
596
+ running/replay-focus.
597
+ - Timeline coordinate mapping is correct at step `-1`, first, middle, latest,
598
+ and after terminal resize.
599
+ - Trace replay cutoffs cannot reveal later attempt events.
600
+ - Node hit testing accounts for viewport offsets.
601
+ - Every boxed node in one graph has the same outer dimensions.
602
+ - Node bounds remain identical across every runtime state. Selection and replay
603
+ focus also leave the bounds unchanged.
604
+ - Long node ids, status details, and branch labels wrap without truncation and
605
+ without changing the graph-wide card size after layout.
606
+ - Every documented status and semantic symbol appears with the matching text
607
+ label and color role.
608
+
609
+ ### Integration tests
610
+
611
+ - Existing plain graph parity fixtures are regenerated for the full-card
612
+ geometry and continue to match between TypeScript and Rust.
613
+ - Shared fixtures assert identical node bounds at every replay position.
614
+ - Theme changes do not alter plain fixture output unless an intentional shared
615
+ graph change updates both renderers.
616
+ - A remote client reconnects after a server restart and receives a fresh
617
+ snapshot.
618
+ - Subscription changes made while disconnected are reconciled after reconnect.
619
+ - Local and remote expanded artifact views enforce the same size and path
620
+ limits.
621
+ - Narrow and wide TestBackend layouts retain all required controls.
622
+
623
+ ### Manual PTY checks
624
+
625
+ Exercise at least:
626
+
627
+ - Catppuccin Mocha at 80x24 and 140x40;
628
+ - Catppuccin Latte on a light terminal;
629
+ - `terminal` theme with a custom terminal palette;
630
+ - a completed run, a live run, a waiting run, a timed-out run, and a cancelled
631
+ run;
632
+ - replay before the first step, detached replay, and return to live;
633
+ - theme preview followed by Cancel and Apply;
634
+ - a stopped and restarted remote server; and
635
+ - tool calls, action receipts, long output, and artifact-backed values;
636
+ - a graph containing every node state and semantic symbol; and
637
+ - long ids, status details, and branch labels in an 80-column viewport.
638
+
639
+ ### Repository checks
640
+
641
+ Before finishing each coherent implementation slice, run the relevant focused
642
+ Rust tests. Before finishing the complete work, run:
643
+
644
+ ```bash
645
+ cargo fmt --check --manifest-path tui/Cargo.toml
646
+ cargo clippy --manifest-path tui/Cargo.toml --all-targets -- -D warnings
647
+ cargo test --manifest-path tui/Cargo.toml
648
+ npm run check
649
+ npm run test:e2e
650
+ npx slophammer-ts@latest dry .
651
+ npx slophammer-ts@latest check . --only ts.dependency-boundaries-required
652
+ ```
653
+
654
+ ## Completion criteria
655
+
656
+ The work is complete when:
657
+
658
+ 1. Catppuccin Mocha is the default and colors every visible viewer surface.
659
+ 2. Boxed nodes are full cards with one stable graph-wide size, intentional
660
+ padding, and every documented card field visible without truncation.
661
+ 3. Users can preview, apply, persist, and cancel built-in theme choices from
662
+ the TUI.
663
+ 4. No component chooses ad hoc colors outside the theme layer.
664
+ 5. Running, replay focus, completed, failed, timed out, waiting, cancelled,
665
+ paused, latest, replay, and live states are unambiguous.
666
+ 6. Full attempt, action, trace, run, and conversation details are inspectable.
667
+ 7. The timeline supports keyboard and mouse seeking plus playback speeds.
668
+ 8. Remote viewing reconnects safely and labels stale cached data.
669
+ 9. Graph selection and the collapsed browser work in narrow terminals.
670
+ 10. Node status and semantic markers use the documented ACPX-style slots and
671
+ symbols. Attempt counts and timing use reserved slots, as do short details,
672
+ without changing node bounds.
673
+ 11. Graph parity, bundle schemas, Pi session state, and Pi internals remain
674
+ unchanged except for intentional shared renderer fixture updates.