Skip to main content

Viewing workflows and runs

The local FX service keeps a project's plans and recorded runs available at one loopback address. Its bundled viewer displays each workflow on a read-only canvas. The service needs FX 0.2 or later; grida fx --version names yours.

Start the project service​

From your project directory:

grida fx init
grida fx start --background --open
grida fx status --json

init checks an existing fx.yaml, or starts a new project in an empty folder (Getting started). The dashboard is at http://127.0.0.1:8787/ by default, with one row per recorded workflow. Use start --port N to choose another exact port; an occupied port is an error, not a reason to silently pick a random port. FX remembers a successful selection. status --json reports the actual address and project/service identity.

When the service cannot start, start says why in one line. Another project's service often holds the default port, and start --background then prints:

grida fx: FX service could not start: port 8787 is in use; choose another with --port N

With --json, a failed background start also prints the stopped status with that sentence as cause. grida fx logs keeps the earlier attempts' lines.

Omit --background to keep the service in your terminal. Background startup returns only after readiness and reuses a compatible service for the same project. It survives the launching command and terminal, but does not install automatic crash restart or login/boot activation. No Docker or frontend server is needed.

grida fx logs --lines 50
grida fx stop

stop closes the selected project's service without deleting records or stopping workflow processes. Restarting it restores access to retained run URLs. Closing a browser tab has no effect on service or execution. Service commands accept --project DIRECTORY when you are outside the project.

Use the canvas​

The canvas fills the window. A bar in its corner names the workflow and shows the run's state and spend, or a plan's estimate, and the path back from an imported workflow. Scroll with two fingers or drag the background to pan. Pinch to zoom around the pointer, or fit the graph to the window. Ctrl+wheel also zooms; ordinary wheel scrolling pans. Nodes and connections cannot be edited. Cards take keyboard focus, so Tab and Enter select without a pointer. The list button in the bottom-left corner opens the steps as a list; choosing one selects its card and moves the camera to it. A press anywhere outside the list closes it.

Select a card to see its details over the canvas: its outputs, inputs and settings as values and previews, whether it was cached, how long it took, and any error. Structured values read as name/value rows, and deeper JSON stays folded until you open it. Under the title is the step's id (state['waving'].draw#1), the same one inspect --json and the run's events use; its copy button puts it on the clipboard, to ask an agent about that exact step. Select the workflow's name in the bar for the workflow's own inputs, outputs and notices, and the run's name. Esc, the background or × closes them; a second Esc closes the step list.

In a run's details, Download all (.zip) saves its output files in one zip named after the run, each under its name in the run folder's outputs/ (hero.png, looks/ada.png). Files the viewer cannot serve are left out, and the details say how many. A zip too large for the browser tab fails and says so; each output's own link still saves it. To put outputs in your project, use run --deliver. Identities, digests and raw records stay on the command line (grida fx inspect runs/example --json).

Running steps have a blue card outline and tint, an activity indicator, and a highlighted row in the step list. The highlight follows recorded state updates without changing your selection or camera. Reduced-motion settings disable the indicator pulse. Connections stay muted until you hover a wire or a node; hovering a node highlights all its incoming and outgoing connections.

Steps sit on a grid that reads left to right: each step goes in the column after the steps it depends on, a column with more than three steps wraps into columns of three, and unconnected chains sit side by side. The engine computes the grid from the recorded plan, so the picture is the same for a plan and its run, and a preview arriving never moves a card. Group iterations are cards with their steps inside.

Repeated instances of one step (for-each items, matrix combinations, takes) and the iterations of a repeated group are drawn as a deck: one stack of cards with a count badge such as 5 items, 3 takes or 2 items · 3 takes each, and how many failed and how many wait for their route. Hovering a card brings it to the front; moving away puts the deck back. The card in front at rest is the selected one, else the last that failed, else a running one, else a waiting one, else the take its consumers read, else the latest that finished. Click a deck (a card, its edge or its badge) to spread it into a grid in place; click a card inside to select it, and press Esc, click outside or use × to put the deck back. Dragging pans from anywhere, cards included. Spreading and closing are short slides, and none under the system's reduced-motion setting.

A running step counts up how long it has run (running · 1m 04s), and a running workflow card how long since its first step started. A count far past what the step usually takes points to a stalled or stopped run: the record cannot tell whether the process is still alive. Runs recorded before FX kept start times show running alone.

A step whose paid call is queued for its route's slot is waiting, not running. A route with concurrency: 1 makes one call at a time, so of eight ready image steps on one such route one runs and seven wait; the Codex routes draw three at a time, so three run and five wait. A waiting card has a still clock instead of the activity indicator and no image shimmer, and counts how long it has waited for which route (waiting for imagegen@codex · 2m 10s). A running step's count leaves out the time it waited, so it shows how long its call has been working; its details say how long it waited in all. A workflow card is running while any of its steps runs, and waiting when none runs and one waits. Runs recorded before FX recorded waits show such steps as running.

Cards show the node's named, typed input and output ports. A list or keyed collection uses one port. Solid wires join the particular output and input referenced by the workflow; several wires can connect the same two steps through different ports. Parameters that read another node have a distinct parameter socket, and referenced node facts have fact sockets. Literal parameters remain settings. Ordering, judging, and other dependencies use dashed lines between whole cards.

The engine records this connection metadata. Expressions can combine several sources; their wires show those references, not an assertion that a parameter equals one upstream value. Unresolved choices retain dependency lines until a run resolves them. Older saved plans and runs remain viewable with dependency lines when they contain no port metadata; the viewer never guesses connections from matching artifacts.

Enter an imported workflow​

A step that imports another workflow appears as one card with its named inputs and outputs. Choose Open workflow, or double-click the card, to inspect its internal steps. Use a name in the bar, or Escape while the canvas is focused, to return to an enclosing workflow. Repeated imports have separate cards and navigation paths, even when they use the same workflow file. Returning restores that workflow occurrence's canvas position, zoom and selection. Refresh preserves your location in the workflow hierarchy.

Boundary ports keep the imported workflow's public names. Renamed outputs and two aliases of the same internal result remain distinct connections. Inline steps: groups stay on the current canvas inside labeled frames.

Enclosing cards show internal step and failure counts. A failed child remains visible through its enclosing workflow cards; selecting one exposes the recorded errors before you enter it. Older saved records without scope metadata keep their flat canvas instead of guessing workflow boundaries from step names. Recorded pending repeats remain unresolved inside their enclosing workflow. A declared alias without an observed source has no invented named wire; known step dependencies remain dashed connections.

Open a workflow plan​

With the project service running, name the same target you would execute:

grida fx plan workflows/gallery.yaml --inputs inputs.yaml --open
grida fx plan gallery --caption "A copper lantern" --open
grida fx plan assets.py:build --arg theme=dusk --open

FX plans once, saves the resulting fx-graph-v1, and opens its project page. It shows dependencies, parameters, routes, identities, estimates and planning problems. Conditional and absent steps remain visible; values that depend on execution remain unresolved. Planning state done may describe an existing or planning-time result and does not claim a new run succeeded.

Planning admits no FX paid calls or run-phase steps. It may import builders and node modules, Python or TypeScript, execute declared local at: plan steps, and cache their results. Builders are named as file.py:function or file.ts:function; arbitrary execution scripts are not loaded to infer workflows. YAML files written from JavaScript work like other files.

Plan pages are static materializations. They do not poll execution events or replan when source changes. Browser-serving flags cannot be mixed with --json.

Open a recorded run​

grida fx inspect runs/example --open
grida fx inspect example --open
grida fx inspect example/baseline --open

This registers and opens the selected record in the running project service. It reads recorded states, inputs, outputs, failures, costs and available artifacts; it never imports author code or calls a provider. The Python distribution uses exactly the same interface: python -m grida.fx inspect runs/example --open. For machine-readable inspection, use inspect runs/example --verify --json without browser flags.

Watch execution​

grida fx start --background
grida fx run workflows/example.yaml --run runs/example --open

After planning and run initialization, run reports its usable project run URL before run-phase steps execute. Report that actual URL to a person instead of constructing it from a folder name. --open requests one browser launch; omit it to visit the printed URL yourself. Planning-time code can execute before the URL exists. Browser or service failures do not change the workflow's result.

run stays foreground and exits normally after execution. The separate FX service keeps its page available afterward, including for failed and cancelled runs. The URL still identifies that record after a service restart or same-plan resume. The dashboard opens each workflow's newest-created run, including a failed run. Expand its history for earlier names, creation times and states, or a saved plan. Older unfinished runs stay visible even when a newer run exists. The arrow in the bar returns from a canvas to that index. Resume retains the original creation time and URL. Grouping uses the recorded workflow ID and source, so changed inputs or definitions at that path stay together; distinct sources sharing an ID remain separate. Old records without sufficient source metadata remain separate rather than guessed.

With an explicit project, run registers its record even when the service is stopped. Execution proceeds, and --open reports how to start the service. plan --open and inspect --open require that project's running service as well. None of these commands starts a background process implicitly.

Run views follow the public observation contract:

grida fx observe runs/example --snapshot
grida fx observe runs/example --after CURSOR --limit 256

Apply a batch before saving its returned opaque cursor. A terminal event ends one invocation; a resumed invocation can append more events to the same folder. Unfinished recorded state does not prove the execution process is alive. Python/JavaScript SDK runs suppress viewer integration and return results after execution; they do not provide a live browser or observation handle.

Standalone inspection​

For tests, probes and one-off work, retain an independent viewer:

grida fx run workflows/example.yaml --standalone --open
grida fx plan workflows/example.yaml --standalone --open
grida fx inspect runs/example --standalone --open

No init or start is needed. This explicit mode works inside an existing project too. It changes viewer ownership and lifetime, not workflow resolution, budgets, record formats or cache reuse. Without an fx.yaml, run uses standalone viewing by default; projectless plan --open and inspect --open do likewise.

The independent server uses an available loopback port. A standalone run's server ends when execution returns, fails or is cancelled. Standalone plan and saved-run inspection stays foreground until Ctrl-C. Omit --open to print the URL without launching a browser. run --no-view skips viewer integration and conflicts with --open; SDK run methods use it.

The old view command remains hidden compatibility syntax for independent source, saved-plan and run viewing. It still supports --open, an exact --port N, and --no-open for older scripts. To inspect an already materialized graph without loading author code:

grida fx expand workflows/gallery.yaml --inputs inputs.yaml > graph.json
grida fx view --plan graph.json --open

Run records and artifacts​

The selected directory supplies plan.json, events.jsonl, and placed output files. Runs of YAML files and of builders, in Python or TypeScript, use these same recorded contracts; the viewer does not need to import their workflow modules. A stand-in run is visibly marked.

Select a step to see its recorded values and artifacts. The viewer follows the record automatically, polling about once a second. Reloading the page also reattaches to the latest consistent snapshot. Images, text/JSON, audio, and video use generic previews; other file types can be opened in a new tab.

Run canvas cards also show the first available recorded image output. A step without one shows the first frame of its first MP4 or WebM video. Resting the pointer on it plays it muted and looping; click it to keep it playing with sound, or use its play button. The video plays over the card, follows it as you pan and zoom, and stops when it ends, on Escape, or when another card covers it; played again, it continues where it stopped. Its controls keep their size at any zoom (play, seek, time, mute and full screen, fewer on a small card); with the player focused, Space or K plays and pauses, the arrow keys seek, M mutes and F goes full screen. A count indicates multiple distinct available images (or videos); select the card to inspect all outputs. The canvas uses the verified artifact inventory and never evaluates author code to produce thumbnails; a video's first frame is read in the browser once its card comes into view. Plan cards have no artifact previews, including steps marked done by the planner. Updates preserve the canvas camera, selection and workflow navigation.

These automatic thumbnails use structured run data. The existing authoring view declarations are booleans or HTML template paths; custom templates and structured preview declarations or callbacks are not yet rendered by this viewer.

Files are matched to recorded content digests. A missing, changed, or store-only file is unavailable; this bootstrap does not discover the originating project's cache. Consequently, a copied run may show its placed outputs while some inputs are absent. HTML and other active documents are not executed as viewer pages.

Recorded spend is what the engine booked. It is distinct from a plan estimate and does not imply that detailed provider usage was retained. Absent cost evidence is shown as unknown. The viewer does not change the source run or recover missing files by rerunning anything.

Distribution and later work​

The viewer's production JavaScript and CSS are embedded in the native engine. Installed users need no frontend dev server, frontend build, Docker, or internet connection to view a local run. The npm and Python launchers retain their normal runtime requirements. Contributor builds are described in CONTRIBUTING.md.

Contributors can use the canonical viewer fixtures to generate and check real provider-free plans/runs covering branches, matrices, repeats, conditions, media, cache, failure, and takes. Generated data stays ignored; the fixture's expectations are the regression baseline.

To share a recorded run as a portable package, see Share a recorded run. Custom node views, machine-wide project aggregation and OS-managed startup/recovery remain later work. The service contract owns current lifecycle guarantees; TODO records open ideas.

Cancellation intent​

The bar shows cancelling after a recorded cancel_requested event and returns to the terminal state when that invocation ends. A resume resets the intent. This label does not prove the runner is still alive or that cleanup finished. Use inspect --control or cancel --wait for verified local control. The viewer has no cancellation button or write endpoint in this version.