Appearance
Running flows and reading results
Pressing ▶ RUN (or Ctrl+Enter in the editor) is the only way a flow leaves the page. This page documents what happens in code on the way through.
The five-step run loop
FlowpadApp.onRunFromCode (projects/matrix-3/packages/flowpad/src/FlowpadApp.ts:342-371) is the canonical entry. Pre-built examples take a slightly different path through onRunFlow (FlowpadApp.ts:373-412), which uses FlowBuilder to construct the same kind of plan from coded stage descriptors.
1. compile compileSourceToCanonicalIR({ source, defaultTargetMount, modeHint, sourceFormatHint })
2. plan emit executionPlanned { flowId, language, irKind, adapterPath }
3. execute executeCanonicalIR(executionContext, ir)
4. complete emit flowCompleted { flowId, result }
5. display ResultsPanel adds a result child of the right viewer kindErrors break out at step 3: flowError { flowId, error } is emitted, then re-thrown.
Step 1 — Compile to IR
compileSourceToCanonicalIR (runtime/queryPipeline.ts) detects the language and produces one of two IR shapes:
ts
type CanonicalIR = IQueryExecutionIR | IComponentSchemaIR;IQueryExecutionIR carries the steps array; each step is remote-call, local-map, or local-filter. IComponentSchemaIR covers the five-representation parity case where the source is a component schema (HTML / Fluent / S-expr / Topics / JSON).
The detection precedence is:
- Explicit
sourceFormatHint— matchesflow/flow-dsl/flow-json/lisp/scheme/json/etc. modeHint(the editor's auto-detected mode).- JSON parse — distinguishes schemas (
accepts/emits/children), commands (op/operation), or payloads. - Fall back to
flow-dsl.
Step 2 — Status pane reflects the plan
ExecutionStatus (components/ExecutionStatus.ts) listens for executionPlanned and statusChanged. Its toolbar carries:
- A status word (
Idle,Running,OK,Error). - A topology tag —
runtime <root> · authority <root> · target <kind>resolved byresolveExecutionTopologyreadingdata-runtime-root,data-authority-root, anddata-target-sourceoff the host element (ExecutionStatus.ts:31-76). - A recovery-actions panel populated by
resolveHostedRuntimeRecoveryPlanwhen the requested root is unavailable. - A mount tag (
flowpad-page.status) so this status bar can itself be addressed over the bus.
Step 3 — Execute against actors
executeCanonicalIR walks the steps, sending request/reply calls to actor mounts and routing local stages through pure-function transforms. The execution context is a derive('_runner') of FlowpadApp's own context, so all calls inherit the same authority root and transport.
Per-stage timeout policy (FlowpadApp.ts:336-340):
ts
const isCrossRoot = mount.includes('/');
const timeoutMs = isCrossRoot
? 30000 // cross-root federated calls
: mount.includes('vlm')
? 10000 // VLM Lisp evaluation
: 5000; // defaultStep 4 — Result becomes a child actor
ResultsPanel (components/ResultsPanel.ts) is the most interesting part of the pipeline. Each successful run becomes an addressable child of flowpad-page.results.result-N:
ts
// components/ResultsPanel.ts:55-66
export class ResultsPanel extends MatrixActor {
static accepts = {
addResult: { data: 'unknown', type: 'string?', label: 'string?', query: 'string?' },
removeResult: { id: 'string' },
clearResults: {},
};
static emits = {
resultAdded: { id: 'string', mount: 'string', type: 'string', label: 'string?' },
resultRemoved: { id: 'string' },
resultsCleared: { count: 'number' },
};
}The viewer kind is auto-selected from the result shape:
| Shape | Viewer | Mount class |
|---|---|---|
| Array of records | <table-viewer> | TableResultActor (TableViewer accepts/emits) |
Tree-shaped object (with children/accepts) | <tree-viewer> | TreeViewer |
| Error | <error-viewer> | ErrorResultActor |
| Anything else | <data-viewer> | DataResultActor |
Because each result is a real Matrix child, you can address it like any other actor: ./results.result-0 accepts expandAll, collapseAll, sortBy, introspect, etc. The DSL examples 'Expand Result', 'Collapse Result', 'Sort Table', 'Introspect Result' (dsl/examples.ts) demonstrate this — a flow can talk to a previously rendered result the same way it talks to ./db.
Step 5 — Transport log captures the bus traffic
TransportLog (components/TransportLog.ts) lives in the dock. It subscribes to bus traffic and renders entries timestamped by direction (accepts / emits / lifecycle). Each entry shows:
- topic
- payload preview
- colour-coded by category (blue for accepts, green for emits, amber for lifecycle).
This is your post-mortem when a flow misbehaves — every send, every reply, every $lifecycle event is captured here.
Reading the four viewers
| Viewer | Use it for | Notable ops |
|---|---|---|
| DataViewer | Any unstructured value (numbers, strings, generic objects) | setData |
| TableViewer | Arrays of records (database query results) | setRows, sortBy, setColumns |
| TreeViewer | Hierarchical actor schemas (output of introspect) | setTree, expandAll, collapseAll |
| ErrorViewer | Step failures and timeouts | setError |
Their schemas live in @open-matrix/core/flow/viewers. ResultsPanel re-exports their accepts/emits onto its synthesised *ResultActor classes so that the result children declare exactly the same contract as the standalone viewers.
Clearing and replaying
The toolbar's Clear button calls FlowpadApp's clearResults — it both wipes the per-stage data map and tells ResultsPanel to drop every result child. Re-running an example refills the panel.
See also
Source:
projects/matrix-3/packages/flowpad/src/FlowpadApp.ts:342-412andprojects/matrix-3/packages/flowpad/src/components/ResultsPanel.ts.