fabro/apps/fabro-web/app/components
Bryan Helmkamp 178adf15ea
feat(web): add Context tab to the stage detail view (#340)
## What

Adds a **Context** tab to the stage detail view
(`/runs/:id/stages/:stageId`), beside the existing primary tab (Thread /
Logs / …) and Debug tab.

It surfaces a stage's *deliberate per-visit outputs* — the data the
workflow author makes a stage write into shared context, plus the
routing hints it emitted:

- **Routing** — `preferred_label` and `suggested_next_ids`
- **Context writes** — author-set `context_updates` keys

This data flow was previously invisible in the UI, which made it hard to
debug "why did the next stage get the wrong input / take the wrong
edge".

## Why no backend change

The per-visit `stage.completed` event already carries `context_updates`,
`preferred_label`, and `suggested_next_ids`, and the web UI already
fetches it via `useRunStageEvents`. The checkpoint's `node_outcomes` map
was rejected as a source: it is keyed by `node_id` only, so it is lossy
across visits (`implement@2` would overwrite `implement@1`).

## How

- `extractStageContext` (in `stage-renderers/helpers.ts`) reads the
`stage.completed` event and filters `context_updates` through an
engine-key denylist: `last_stage`, `last_response`, `response.*`,
`internal.*`, `current.*`, `command.output`, `human.gate.*`,
`parallel.*`. Those are bookkeeping or already shown in the stage's
primary tab.
- It returns `null` when nothing is left, so the tab stays
**conditional** — same pattern as Thread/Logs. It only appears when a
stage actually wrote something deliberate.
- New `stage-context.tsx` renders the result, reusing `CodeBlock` /
`JsonBlock`.
- `run-stages.tsx` gains a dynamic `availableTabs` list; `effectiveTab`
falls back to `primary` gracefully when the Context tab is absent.

## Verification

- `bun run typecheck` clean, `bun test` — 412 pass / 0 fail (4 new tests
for the denylist + routing extraction).
- Live run `01KS5WBZAE7K8321NHR7KFAHF9` (`context-demo` workflow): the
`emit@1` stage emitted `demo.greeting` / `demo.answer` / `demo.payload`
plus `preferred_label: "Done"` and `suggested_next_ids: ["exit"]`, and
the Context tab rendered them correctly.

## Notes

- The second commit adds a small `context-demo` workflow used for that
verification — kept separate so it can be dropped independently.
- Known limitations (acceptable for v1): data is read from
`stage.completed` only, so a stage ending in `stage.failed` shows no
tab; parallel stages write `parallel.*` directly to context
(denylisted), so they show no tab.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 20:19:55 -04:00
..
chats feat(web): add Ask Fabro sidebar and settings panels (#346) 2026-05-21 19:36:19 -04:00
stage-renderers feat(web): add Context tab to the stage detail view (#340) 2026-05-21 20:19:55 -04:00
auth-layout.tsx chore(web): namespace static images under /images and skip in HTTP logs 2026-04-30 08:57:14 -04:00
collapsible-file.tsx refactor(web): drop light mode, go dark-only 2026-04-20 09:27:08 -04:00
editable-run-title.test.tsx feat(web): inline-edit run titles in the run header 2026-05-09 11:54:41 -04:00
editable-run-title.tsx feat(web): inline-edit run titles in the run header 2026-05-09 11:54:41 -04:00
event-debug.tsx refactor(core): simplify reviewed run cleanup 2026-05-10 22:53:20 -04:00
graph-toolbar.tsx style(runs): tighten stages, graph toolbar, billing, and stage sidebar 2026-04-19 16:51:55 -04:00
icons.tsx feat(runs): link stored pull requests 2026-05-08 13:17:08 -07:00
inline-markdown.test.tsx feat(web): render inline Markdown in run titles 2026-05-08 18:30:51 -07:00
inline-markdown.tsx refactor: simplify reviewed run cleanup 2026-05-08 23:29:08 -04:00
interview-dock.test.tsx fix(api): type interview answer submissions 2026-05-08 09:18:35 -07:00
interview-dock.tsx fix(api): type interview answer submissions 2026-05-08 09:18:35 -07:00
pull-request-chip.test.tsx feat(pr): support GitHub pull request associations (#270) 2026-05-16 12:47:27 -04:00
pull-request-chip.tsx feat(pr): support GitHub pull request associations (#270) 2026-05-16 12:47:27 -04:00
run-summary-panel.test.tsx feat(web): show sandbox status dot on the run overview panel 2026-05-21 11:28:39 -04:00
run-summary-panel.tsx feat(web): show sandbox status dot on the run overview panel 2026-05-21 11:28:39 -04:00
settings-panel.tsx feat(web): add secrets management settings page (#327) 2026-05-20 17:50:09 -04:00
stage-context.tsx feat(web): add Context tab to the stage detail view (#340) 2026-05-21 20:19:55 -04:00
stage-sidebar.test.tsx fix(web): measure active stage duration from startedAt 2026-05-21 18:10:31 -04:00
stage-sidebar.tsx fix(web): measure active stage duration from startedAt 2026-05-21 18:10:31 -04:00
state.tsx feat(web): unify empty/error/loading states in one primitive 2026-04-19 17:22:57 -04:00
steer-bar.tsx [codex] Add ACP steering support (#329) 2026-05-20 17:21:56 -04:00
terminal-view.test.ts refactor(core): simplify reviewed run cleanup 2026-05-10 22:53:20 -04:00
terminal-view.tsx refactor(core): simplify reviewed run cleanup 2026-05-10 22:53:20 -04:00
toast.test.tsx feat(web): add run lifecycle actions 2026-04-20 08:43:30 -04:00
toast.tsx feat(web): add run lifecycle actions 2026-04-20 08:43:30 -04:00
tool-use.tsx style(run-stages): copy affordance, collapsed long output, sticky header 2026-04-19 17:27:25 -04:00
ui.tsx feat(web): add detail popovers to the run header 2026-05-21 14:10:39 -04:00