Web UI

A tour of the in-browser workflow editor and run viewer.

Roscoe ships with a React + Vite web app that covers the graph editor, live run animation, run history, and settings. Reach for it when you want to draft a workflow, eyeball a run as it executes, or skim recent history without dropping into the terminal.

In dev mode (bun run dev) the UI runs on http://localhost:3000 and talks to the API on :8080. The compiled roscoe serve binary serves both on a single port (default http://localhost:7777). See Environment variables for port overrides.

The sidebar switches projects. The active project decides which project store the library reads, and run history filters to it by default, so switching changes what those pages show. See Global vs Project.

Workflows (/workflows)#

The library. Lists every workflow visible to Roscoe, split into Project (<repo>/.roscoe/workflows/) and Global (~/.roscoe/workflows/) sections. Search by name or filter by source. The New Workflow action scaffolds a new YAML file.

Roscoe dims and badges any global workflow row a same-id project copy shadows, so you know which file actually runs. Delete lives in the workflow detail page's kebab menu. There is no rename in the web UI.

If the install is empty the page shows an onboarding card with a Create starter workflows button, which seeds the example workflows.

Workflow editor (/workflows/$id/edit)#

The authoring surface. The graph canvas is a React Flow view with one node per state: click a node to open its config panel, and add nodes from the canvas add menu. You author edges as transitions in that panel. The canvas auto-lays out as you edit.

Edits validate against the workflow schema and surface errors inline. Save writes the file on the server, and the watcher pushes the change to other open tabs.

The editor enforces the same schema and integrity rules as roscoe validate, so a workflow that saves cleanly is one that will start cleanly.

Edit with a prompt hands the workflow to Claude instead of the canvas. Describe the change you want ("add a step", "handle a failure") and it edits the file. Unsaved canvas edits are not included, and the editor says so before it starts.

Undo (⌘Z) and Redo (⌘⇧Z) sit in the toolbar. If the file changes on disk while you have unsaved edits, a notice offers you the new version rather than overwriting either copy silently.

A subworkflow node opens its child read-only in the same editor, tracked in the URL as ?drill=. The ?from= trail is how the back link finds its way out again.

Workflow detail (/workflows/$id)#

A read-mostly preview. Renders the graph, surfaces validation errors when the config fails to parse, and offers the primary Run action. A History button jumps to the run history list pre-filtered to this workflow. The kebab menu has Move to Project / Move to Global for promoting a workflow between sources, plus Delete workflow; the moves round-trip through POST /api/workflows/:id/move.

Run viewer (/runs/$id)#

The live run page. The header links its workflow title back to the workflow detail page (dimmed and un-clickable only when the source workflow no longer exists) and renders the run id and run metadata as click-to-copy chips. The graph animates as nodes execute: running nodes pulse, completed nodes fill in, and failed nodes go red. During a live run a Follow active node toggle (top-right of the canvas) keeps the camera spotlighted on the currently executing node, auto-panning as the run advances; click it to stop following and pan freely.

The right rail shows a per-node trace with input, output, duration, error (if any), and child sub-runs (e.g. one row per consensus agent with its individual verdict and reasoning). With no node selected, the rail follows the live front of the run. It opens on the node currently executing, falling back to the most-recently-finished node once nothing is active, so an in-progress run shows what's running, not the last completed step. Click any node to pin the rail to it.

Script (script) output streams live: a running node's stdout/stderr appear in the rail as they are produced (coalesced server-side so the poll stays cheap), rather than only landing once the node finishes. Terminal streams (stdout, stderr, error) render in a monospace block with ANSI colors preserved: the SGR (Select Graphic Rendition) escapes that test runners and build CLIs emit (vitest's green checks, red failures, dim notes) show as themed color instead of dropping to plain text or leaking as literal [0m noise. Long lines and box-drawing rules wrap rather than overflow.

When the run pauses at a human node, an Awaiting input panel above the canvas surfaces transition buttons; click one to call POST /api/runs/:id/resume. Consensus nodes get a dedicated panel showing each agent's vote, the quorum threshold, and the final outcome.

While a run is running or paused, a Cancel run button (with a confirmation modal) stops it. Cancelling marks the node the run was sitting on as cancelled, and Roscoe kills the running script worker or claude CLI/SDK call rather than leaving it to finish in the background. A cancelled node is rendered neutral gray, distinct from the red of a failed node, since a deliberate stop is not an error. You can cancel any run, including runs driven over MCP (Model Context Protocol).

Runs started via MCP show an MCP badge; Claude Code controls those transitions and no approval panel renders. When such a run is paused on an AI handback node (ai_agent / ai_judge / consensus / round_robin, i.e. the calling agent is actively generating a response), the status reads Processing in blue with a pulsing dot, rather than the amber Paused used for a human wait. A genuine human node still shows amber Paused, because that one really is blocked on a person. The run page, the run history list, and the Workflows "last run" column all read the same run field (pauseKind: 'agent' vs 'human'), so the same run reads consistently everywhere.

On a finished run the header also has a Record button (or Edit recording once you have directed it). It opens the recording editor at /runs/$id/record, where the run plays back fast on the real canvas and you choose which steps the camera stops on before exporting a 1080p MP4. See Record a run.

A run carries its own cost and token totals, split between metered API spend and the subscription-equivalent of work that ran on your Claude plan. A run you capped that hit the cap stops as cancelled with a banner naming the cap it hit. A run can carry both a dollar and a token cap and stops at whichever comes first.

Run history (/runs)#

Every run, with branch and workflow filters and a Clear all runs action. Use this to clear test debris or audit recent activity.

The page is workspace-scoped. A scope selector toggles between this workspace (the default), which shows only runs for the workspace the nav project switcher currently has active, and all workspaces, which shows every run across Global and all projects with a per-row tag (a globe for Global, a folder + project name otherwise) so mixed rows stay distinguishable. The active workspace can be a project or Global, where cwd-less runs from the Claude desktop app land (see Global vs Project).

When the active project has no runs, the empty state points you to where your runs actually live: a View Global runs shortcut for desktop-app runs that landed in Global rather than the project you're sitting in. Opening a run (/runs/$id) carries the run's scope in the URL, and the ?project=… links the MCP returns auto-select the matching workspace.

Those MCP links point at /open/runs/$id, which hands the run to the desktop app when it is installed and falls back to this browser UI when it is not — see Run links open in the app.

Settings (/settings)#

Edit roscoe.yaml (the active merged config, global plus project overrides) directly in the browser. Adjust the model list and default model, pick the AI backend, set the CLI path, override ports, and tweak retry/timeout, analytics, and preference defaults. Changes hit disk immediately and the file watcher fires server-sent events (SSE) so other open tabs reload without a refresh. See roscoe.yaml for the schema and worked example.

The Updates card at the bottom shows the running version next to the latest release on the channel: a blue "Update available" badge appears when a newer build exists, flipping to a filled "Update ready" once the background pre-download finishes. Click Download & install to swap the binary, then stop the server (Ctrl-C or q) and run roscoe serve again. The sidebar chip mirrors this state machine in a single icon so you can tell at a glance from any page.

The icon at the bottom of the left sidebar reflects the update state for the running binary:

  • Neutral check → the current version matches the latest manifest.
  • Blue up-arrow with a static dot → a newer release exists and Roscoe is pre-downloading it in the background.
  • Blue up-arrow with a pulsing dot → the new binary is on disk; one click takes you to Settings to apply.
  • Teal reload icon with a teal dot → the new binary is installed; restart to run it.

Docs (/docs)#

These docs, served in-app. Useful when you're already in the UI and don't want to context-switch to a browser tab.

When the app cannot start normally#

A fresh install opens an onboarding flow instead of the library: it checks the prerequisites, offers to seed the example workflows, and hands you to the Workflows page when it is done.

If the database cannot be opened, the app drops into a recovery screen rather than failing page by page. The server reports the problem honestly instead of serving errors from a server still claiming to be healthy. See Database recovery.

Default URLs#

Dev (bun run dev) serves the UI at http://localhost:3000; roscoe serve serves it at http://localhost:7777. Override either with ROSCOE_WEB_PORT (dev) or --port / ROSCOE_PORT (serve).

See also#

View this page as Markdown

Predictable workflows from unpredictable AI