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>
The billing rollup filtered out the synthetic exit handler but left
start visible, so the billing tab showed an asymmetric pair. Both are
no-op workflow boundary handlers and shouldn't appear as billable
stages.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Signal server cancellation before worker cleanup, wire long-lived SSE streams to the shutdown token, and backstop HTTP drain after five seconds so open browser streams cannot keep the listener alive indefinitely.
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>
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>
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>
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>
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>
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>
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>
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>
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>
Keep non-secret terminal color controls in the worker subprocess environment so inherited stdout logs use the same color decision as the foreground server.
Route async API failures through the body-preserving classifier and add run-create context so CLI output keeps server response details in the cause chain.
Keep daemon and hidden serve logs on the file destination by default, while making foreground server commands stream logs to the terminal unless the server config explicitly selects file logging.
Expose the CLI reference renderer through a hidden fabro subcommand so fabro-dev can refresh docs without linking fabro-cli. Gate the fabro-dev binary behind the dev feature and update the cargo dev alias to opt into it explicitly.
Scrub Cargo build-script environment from nested cargo commands so cargo dev does not poison fingerprints, and preserve unchanged SPA asset files while refreshing embedded assets.
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>
In --watch-web dev mode the server silently fell back to the embedded SPA
snapshot whenever the disk dist/ was missing or partial, so edits to the
web app appeared not to take effect with no error anywhere. This change
makes the dev loop visible and quick:
- static_files plumbs a dev_disk_only flag from RouterOptions.watch_web
into the fallback handler. When set, embedded fallback is skipped and
a miss returns 503 with a "build in progress" auto-refresh page.
- The web build script writes each rebuild into apps/fabro-web/.dist-builds/<id>/
and atomically replaces the dist symlink via rename(2), so requests
never observe a partially-populated dist tree.
- Tailwind is invoked through node_modules/.bin/tailwindcss directly
instead of bunx, removing a per-rebuild bun add @latest --force round-
trip and dropping rebuild time from ~10s to ~250ms.
- load_asset no longer falls through to the workspace dist/ when an
explicit asset_root is provided, restoring test isolation when a
real dev build is sitting next door.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Commit 258e46e95 wrapped angle-bracket placeholders in backticks
without updating the inline snapshots, breaking CI on main.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bare `<slug>` and `<run-id>:<path>` in clap help text generate doc
table cells where MDX parses the placeholders as JSX tags and fails
the Mintlify build. Wrap them in backticks so the generated table
cells route them through inline code spans where MDX leaves them
alone.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>