Commit graph

766 commits

Author SHA1 Message Date
Bryan Helmkamp
b170a6dead
feat(web): render sub-second durations in milliseconds
Tool and command durations under 1s now render as e.g. 321ms instead
of 0.3s, matching how short steps actually feel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:46:38 -07:00
Bryan Helmkamp
1182025c4c
feat(web): refine run stage metric column
Render sub-second tool/command durations in milliseconds (e.g. 321ms)
instead of 0.3s. Round token counts to whole k/M. Wrap assistant token
metric in a tooltip showing the input/output breakdown. Allow Tooltip
labels to be arbitrary React nodes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:40:55 -07:00
Bryan Helmkamp
be908cd0d5
style(web): drop redundant when/tool labels in tool group accordion
The accordion row already shows elapsed/duration and the group header
already names the tool, so the When and Tool fields inside the expanded
event detail are redundant. Keep them for the standalone single-event
panel.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:39:21 -07:00
Bryan Helmkamp
927ed419f7
feat(web): wire steer bar to interrupt and steer APIs
The bottom steer bar now actually calls the run APIs: Send posts a
steering message and Interrupt fires immediately as a button (not a
toggle). The Actions menu drops the redundant "Steer" item and its
modal composer; "Send interrupt" still fires immediately and "Send
steering…" still focuses the bar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:38:18 -07:00
Bryan Helmkamp
7034266dc9
feat(web): always show workflow breadcrumb on run detail
The workflow breadcrumb was previously demo-mode-only. Render it in
all modes and point it at the runs list filtered by that workflow,
which is now honored via URL params.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:20:00 -07:00
Bryan Helmkamp
49c1263c8a
feat(web): sync runs page filters and view to URL params
Search query, repo, workflow, created-time, archived toggle, and
columns/list view are now read from and written to URL search params,
so the selected state survives reloads and is shareable. Defaults are
omitted from the URL to keep links clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:16:10 -07:00
Bryan Helmkamp
6290b5a3f8
style(web): drop redundant tool labels in tool group details panel
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 22:11:27 -07:00
Bryan Helmkamp
befb2e00ec
feat(runs): merge command output streams
Route command stderr into stdout at execution time and expose a single output log across events, projections, API clients, and the web UI. Keep replay compatibility for older command.completed events that still contain split stdout/stderr fields.
2026-05-07 22:07:13 -07:00
Bryan Helmkamp
23cb211cce
feat(runs): surface diff summary counts
Compute cheap diff stats on checkpoint and terminal events, roll them into run summaries, and use them for the Files Changed tab badge without fetching full file diffs.
2026-05-07 17:34:32 -07:00
Bryan Helmkamp
de26face0f
feat(web): collapse consecutive same-tool calls into one event row
Successful tool calls of the same tool that run back-to-back (e.g., five
Bash curls to the same endpoint) now collapse into a single Tool group row
labelled "Bash x5", summing durations and using the first call's start
time. Errored calls and tool-name boundaries break the run, so distinct
work is never hidden. Clicking the group opens a panel that lists each
child with its input preview and per-call duration; an inline accordion
reveals the full Tool use / Tool result for one child at a time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:05:13 -07:00
Bryan Helmkamp
ebf9bc7ed0
refactor(web): replace Graph tab with a Graph Source sidebar page
The Graph tab duplicated the Overview's diagram. Drop it and the
/runs/:id/graph route, and move the DOT source view to a dedicated
/runs/:id/source page reachable from the sidebar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:36:20 -07:00
Bryan Helmkamp
6e979ef85a
feat(web): add hover tooltip with absolute datetime on event timestamps
Replaces native title attribute with a portal-based Tooltip component on
the events-feed elapsed times and the run header's last-event timestamp,
showing the full datetime (e.g. "04/24/2026, 1:23:40 PM") on hover.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:30:19 -07:00
Bryan Helmkamp
c464b4cca2
style(web): suppress sandbox_gone degraded banner on run files page
The "Showing final patch only" banner fired for every finished run whose
sandbox had been reaped, which is the normal post-run state and just
adds noise. Other degraded reasons (provider_unsupported,
sandbox_unreachable) still show their banners.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:24:53 -07:00
Bryan Helmkamp
0aceebbd9e
feat(web): render command stages with a Logs tab showing real stdout/stderr
Command nodes now show a "Logs" tab (in place of "Transcript") that fetches
the actual stdout/stderr bytes via the stage log endpoint, rather than the
blob:// refs carried in command.completed events. Exit code and duration
sit on the right side of the toolbar alongside the tab toggle. Agent stages
are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:21:56 -07:00
Bryan Helmkamp
858caf7434
style(web): show errored tool calls with red Error label, not red pill
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:16:16 -07:00
Bryan Helmkamp
7f659df9ac
feat(web): add icons next to token and duration metrics in events feed
Tool/command rows show a clock icon next to the duration; agent rows
show a tokens icon next to the input/output token count. Adds left
padding between the metric column and the elapsed timestamp column so
the two values read as separate fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:14:39 -07:00
Bryan Helmkamp
089f6befe8
feat(web): show LLM model name in stage view toolbar
Extracts the model from `stage.prompt`, `agent.session.activated`, and
`agent.cli.started` events that the stage already loads, and renders it
on the right side of the events toolbar with a CpuChipIcon.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 16:11:09 -07:00
Bryan Helmkamp
094ebe164a
feat(web): add Send interrupt and Send steering actions to the run menu
Some checks are pending
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
TypeScript / Typecheck (push) Waiting to run
TypeScript / Test (push) Waiting to run
TypeScript / Build (push) Waiting to run
Adds two new items to the run-detail Actions dropdown, always
present and disabled when unavailable. Send interrupt is wired to
POST /api/v1/runs/{id}/interrupt and gated on running status. Send
steering… focuses the bottom-dock steer textarea via a new
forwardRef handle on SteerBar; gated on running status with no
pending questions. Bumps the separator after the new pair to render
whenever any later group exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:26:10 -07:00
Bryan Helmkamp
525233f94a
feat(web): unify steer and interview into a bottom-attached run dock
Replaces the two floating overlays with a single full-width bottom
panel that's always present on every run-detail tab and renders
either the InterviewDock (when there are pending questions) or the
SteerBar (otherwise). The dock has a top border and bg-page so it
sits flat against the page above instead of floating with a gradient.

Strips the outer fixed/gradient/rounded wrappers from both
InterviewDock and SteerBar so they render as inline content inside
the new dock. SteerBar gets a max-w-4xl centered form, an outlined
textarea field, and a new Interrupt checkbox button (with a visible
amber-fill checkbox indicator) between the input and Send. Send and
Interrupt aren't wired to the steer API yet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:14:12 -07:00
Bryan Helmkamp
427a24d145
feat(web): add a sticky steer bar at the bottom of run views
Adds a single-row text input + Send button that pins to the viewport
bottom on every run-detail tab as a placeholder for the future
steering composer (will replace the modal). Renders mutually
exclusive with InterviewDock: the dock takes precedence on blocked
runs with pending questions, otherwise the steer bar shows. Reuses
the existing --fabro-interview-dock-clearance variable so consumers
that already pad for the dock pick up the steer bar's clearance too.
Send is intentionally a no-op for now until the steering API is
swapped over from the modal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:07:58 -07:00
Bryan Helmkamp
1b9920e9e5
feat(web): split repo, workflow, and created columns in runs list
Promote repo to its own column instead of sharing space with the
goal, add a workflow-slug column, and add a relative creation-time
column (with the ISO timestamp on hover) so the list view exposes
the same identifying info the board cards already show.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:03:20 -07:00
Bryan Helmkamp
5daa07152a
feat(web): make debug events clickable with a JSON detail panel
Promotes DebugRow to a button with the same padding and hover/selected
chrome as the transcript rows, and opens a side panel showing the
event name as the header and the full EventEnvelope JSON, pretty-
printed and syntax-highlighted via highlightJson. Refactors the panel
chrome into a shared DetailsPanel so EventDetailsPanel and the new
DebugEventDetailsPanel both reuse the slide-out animation, header,
and Esc-to-close behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:52:08 -07:00
Bryan Helmkamp
2ea8e20f51
feat(web): time stage transcript events from the stage's own start
The Transcript view is filtered to a single stage, so measure each
event's elapsed time from that stage's started_at instead of the
run's created_at. The previous behavior folded the run's
initialization wait (sandbox build, clone, devcontainer setup) into
every per-stage timestamp. Falls back to RunSummary.start_time and
then created_at for stages or runs that never ran.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:46:21 -07:00
Bryan Helmkamp
257d0de444
feat(web): populate Debug tab with category filter and event rows
Generalize the kind filter into a reusable multi-select and use it for
both Transcript (fixed kinds) and Debug (event-name-prefix categories).
Debug rows show a category pill, the full event name, and elapsed time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:40:12 -07:00
Bryan Helmkamp
bdd109597f
style(web): always format event row durations in seconds
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:30:02 -07:00
Bryan Helmkamp
2e3a71862c
style(web): increase stage event row padding and split token counts
Bumps the row's vertical padding from py-1.5 to py-2.5 so the hover
and selected highlight band is taller and the rows breathe more, and
shows Agent token counts as input / output rather than a single total.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:28:53 -07:00
Bryan Helmkamp
d078170553
style(web): match event row right padding to left for symmetric gutters
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:26:12 -07:00
Bryan Helmkamp
84c3cca3c8
style(web): extend event row hover highlight to full column width
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:24:54 -07:00
Bryan Helmkamp
657ed98feb
style(web): extend stage toolbar border and details panel to right viewport edge
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:22:08 -07:00
Bryan Helmkamp
337f52883a
feat(web): show duration and token counts on stage event rows
Adds a metric column on the run stages Transcript view: token totals
for Agent messages (from agent.message billing.input_tokens +
output_tokens), and elapsed time for Tool calls (computed from the
paired started/completed timestamps) and Command runs (from
command.completed.duration_ms). Also tightens the column top padding
so the toolbar reads with balanced breathing on each side, and lets
the toolbar underline extend across the row in line with the run-tab
underline pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:15:49 -07:00
Bryan Helmkamp
9020e2e058
style(web): extend run tab bar underline to viewport edges
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:05:26 -07:00
Bryan Helmkamp
b158185091
feat(web): add Transcript/Debug toggle to run stages toolbar
Adds a segmented toggle to the left of the type filter. Transcript
keeps the existing transcript view; Debug renders a blank panel for
now and hides the kind filter, search, count, and any open detail
panel so they don't suggest controls that aren't wired up yet.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:03:09 -07:00
Bryan Helmkamp
34dc99963a
feat(web): add search and event-type filter to run stages tab
Mirrors the toolbar pattern from the run logs view so users can narrow
down stage events by kind (System, Agent, Tool, Command) or by free-text
search across event content. Filter state persists across stage
selection; the open detail panel still resets per stage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:00:16 -07:00
Bryan Helmkamp
e6e6c0b5f8
feat(web): pretty-print and syntax-highlight tool input/result JSON
Replace the plain CodeBlock for tool input and result with a JsonBlock
that parses the value, re-stringifies with indent 2, and applies a
small regex-based syntax highlighter (keys, strings, numbers,
booleans, null get distinct theme colors). Non-JSON results — file
contents from Read, error strings — fall through to plain text when
JSON.parse fails. No new dependencies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:49:24 -07:00
Bryan Helmkamp
036ee0472f
feat(web): render markdown for system and agent event details
Bring back the Marked-based renderer for system prompt and assistant
message bodies in the event details panel so headings, lists, inline
code, and fenced blocks render as formatted prose instead of a single
preformatted block. Tool input/result and command scripts continue to
render as fixed-width code since they're JSON/shell. Same URL/HTML
sanitization policy as the prior markdown integration: protocol-
relative and non-http(s)/mailto links are rewritten to empty hrefs,
and raw HTML tokens are dropped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:46:13 -07:00
Bryan Helmkamp
de9656dfb7
fix(web): propagate definite height through full-height layout chain
The full-height shell used min-h-dvh, which only sets a minimum and
leaves height: auto. CSS percentage heights (h-full) don't resolve
against an auto parent, so every descendant that relied on h-full
collapsed to its content size — leaving the run stages column
separator, events list, and detail panel ending mid-page instead of
reaching the window bottom.

Switch the shell to h-dvh, make the run-detail outlet wrapper a flex
column, and replace h-full with flex-1 on the run-stages and
run-files roots so they grow via flex sizing within the column. The
height chain is now: shell h-dvh → main flex-1 → layout div h-full →
run-detail h-full → outlet wrapper flex-1 flex-col → page root flex-1
→ children fill via flex stretch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:43:03 -07:00
Bryan Helmkamp
f7fd30f36c
refactor(web): inline event details panel so events list contracts
Replaces the fixed-position overlay panel with an in-flow flex sibling.
The panel now starts under the tab bar (not over the navbar) and the
events list contracts via flex-1 to make room for it instead of being
covered. Uses self-stretch on the panel wrapper so its height
propagates reliably; an inner absolute container right-anchored at
w-[28rem] gives the slide-in-from-right reveal as the wrapper width
animates from 0 to 28rem.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 11:31:08 -07:00
Bryan Helmkamp
9d2c83d904
refactor(web): redesign run stages tab as a row list with detail panel
Replaces the per-event SystemBlock / AssistantBlock / ToolBlock /
CommandBlock layout with a flat list of three-column rows (label pill,
truncated summary, elapsed time from run start) and a slide-out detail
panel that opens on row click. Tool names are humanized (read_file →
"Read", shell → "Bash", etc.). The vertical column separator now
extends to the actual window bottom via an absolutely positioned line
that bleeds 1.5rem past its flex parent's bottom edge into the layout's
bottom padding, sidestepping a calc(100% + 3rem) approach that wasn't
resolving reliably on a flex-1 ancestor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:58:11 -07:00
Bryan Helmkamp
59e8f2d30b
style(web): remove stage label/duration header from run stages tab
The stage label and ticking duration were a redundant repeat of the
sidebar's selected entry. Drop the sticky header (and the now-unused
RunningStageDuration helper) so the right column focuses on the stage
activity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:30:52 -07:00
Bryan Helmkamp
fd66797635
style(web): add full-height vertical separator on run stages tab
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:29:41 -07:00
Bryan Helmkamp
ac0492a617
feat: track last_event_at on runs and show it in the run header
Add a `last_event_at` timestamp to RunProjection (set in apply_event so
every event ticks the field) and surface it through RunSummary and the
RunListItem board response. Backed by an OpenAPI extension so both the
Rust and TypeScript clients pick up the new optional field.

In the web UI, the run-detail header gains a "Last activity Xm ago"
badge next to the elapsed-time chip, driven by a 30-second ticker so the
relative time stays current between event refreshes.

The fabro-server tests.rs hunk is incidental rustfmt drift surfaced by
running `cargo fmt --all` over the workspace; including it keeps CI's
fmt-check green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 10:15:04 -07:00
Bryan Helmkamp
4be52897ec
feat(web): filter runs list by workflow
Adds an "All workflows" dropdown to the /runs toolbar between the
repo filter and the "Show archived" toggle, applied client-side.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 09:06:57 -07:00
Bryan Helmkamp
08bac56388
feat(web): filter runs list by created time
Adds an "All time / Today / Last hour / Last day / Last 7 days /
Last 30 days" dropdown to the /runs toolbar, applied client-side
alongside the existing search and repo filters in both Board and
List views.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 09:05:27 -07:00
Bryan Helmkamp
7ae9cceb71
style(web): loosen spacing on run header metadata row
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 09:02:02 -07:00
Bryan Helmkamp
e4fbe4482a
feat(web): icon + workflow in run header metadata
Prefix each run header chip with its icon (folder for repo, stack for
workflow, clock for elapsed) and surface the workflow name alongside
the repo so it's discoverable from the detail header.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:57:49 -07:00
Bryan Helmkamp
c8336a250e
feat: show archived runs on the runs page
Extend GET /api/v1/boards/runs with include_archived=true (matching the
existing flag on listRuns), add an Archived BoardColumn that the server
appends only when the flag is set, and surface a "Show archived" toggle
on /runs that flips between request shapes. Default behavior is unchanged
— archived runs stay hidden.

Server: list_board_runs now takes ListRunsParams; board_column maps
RunStatus::Archived to BoardColumn::Archived; board_columns(include_archived)
appends the column conditionally. Two new handler tests cover the default
and flag-on paths.

Web: useBoardsRuns(includeArchived) keys requests so SWR refetches on
toggle; columnStatuses + columnStatusDisplay + columnStyles get an
"archived" entry; buildSkeletonColumns filters by the flag so the loading
state matches the eventual response. Two new buildBoardColumns tests cover
both column shapes.

Touched generated TS client files include unrelated whitespace drift from
openapi-generator-cli; including them keeps the working tree consistent
with what `bun run generate` produces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:36:04 -07:00
Bryan Helmkamp
9b10f7d4fb
feat(web): collapse run header actions into a dropdown menu
Replace the row of contextual buttons (Steer / Cancel / Archive / Unarchive
/ Preview) on the run detail page with a single Actions dropdown. Each
action's pending label ("Archiving…", "Cancelling…", etc.) now appears on
its menu item; the trigger shows a spinner while any mutation is in flight
and disables itself to prevent stacked calls. The menu is hidden entirely
when no actions apply.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:35:46 -07:00
Bryan Helmkamp
0d23d885a1
feat(web): filter and search run logs
Add a toolbar with a multi-select level filter (TRACE/DEBUG/INFO/WARN/ERROR)
and a typeahead search to /runs/{id}/logs. Filtering is record-aware so
multi-line entries (stack traces, indented continuations) stay together with
their parent log line. Combine the panel header into a single row with
filters on the left and size + copy on the right; round byte sizes to whole
units.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:35:39 -07:00
Bryan Helmkamp
dd28dc7a1f
feat(web): add column-level "Archive all" action to runs page
Adds a three-dot kebab menu to each runs board column header (in both
column and list view). The menu exposes a single "Archive all" action
that fans out individual archive POSTs for every archivable run in the
column via Promise.allSettled, then revalidates the board. A toast
summarizes full success, partial failure, or total failure. The menu
is hidden when a column has no archivable runs.

Tracked for a future single-POST API in #226.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 08:29:46 -07:00
Bryan Helmkamp
67ad1f520f
fix(web): debounce dev watcher and ignore non-source events
macOS recursive fs.watch fires multiple events per logical save and emits
spurious "bubble" events for sibling directories. With the previous slow
~10s tailwind step those re-fires were absorbed between rebuilds; with
~60ms rebuilds the watcher entered a continuous-rebuild loop instead.

- Coalesce events with a 75ms debounce window so one save fires one
  rebuild even when the editor produces several FS events.
- Filter to source-relevant extensions (ts/tsx/css/html/images/fonts);
  ignore .DS_Store, .tsbuildinfo, swap files, and the extensionless
  bubble events (e.g. "rename images") that were the dominant source
  of the loop.
- Optional FABRO_BUILD_DEBUG=1 logs which path queued or skipped each
  rebuild for future diagnosis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:01:06 -04:00