Component Architecture: Invocation Graph View
Overview
The invocation graph view renders a visual DAG (directed acyclic graph) of a workflow invocation, reusing the workflow editor canvas in readonly mode. Each node displays real-time job state information (running, ok, error, paused, queued, skipped, etc.) with color-coded headers and state icons. Clicking a node expands step details in a card below the graph.
The feature spans ~25 files across components, composables, stores, styles, and tests.
Component Hierarchy
WorkflowInvocationState (top-level, route-mounted)
+-- WorkflowNavigationTitle (header bar w/ workflow name, actions)
+-- WorkflowAnnotation (annotation, progress bars)
+-- BNav pills (tab navigation via router: Overview|Steps|Inputs|Outputs|Report|Export|Metrics|Debug)
|
+-- [Overview tab] WorkflowInvocationOverview
| +-- SubworkflowAlert (if subworkflow)
| +-- WorkflowInvocationError (per invocation message)
| +-- InvocationGraph
| +-- WorkflowGraph (readonly editor canvas)
| | +-- Node (per step, with isInvocation=true)
| | +-- NodeInvocationText (job state counts / input preview)
| | +-- NodeInput (blank=true, invisible labels)
| | +-- NodeOutput (blank=true, invisible labels)
| +-- [step detail card below graph]
| +-- WorkflowInvocationStepHeader
| +-- WorkflowInvocationStep (expanded, inGraphView=true)
|
+-- [Steps tab] WorkflowInvocationSteps
| +-- WorkflowInvocationStep (per step, collapsible)
| +-- WorkflowInvocationStepHeader
| +-- JobStep / ParameterStep / GenericHistoryItem / SubworkflowAlert
|
+-- [Inputs/Outputs tab] WorkflowInvocationInputOutputTabs
+-- [Report tab] InvocationReport
+-- [Export tab] WorkflowInvocationExportOptions
+-- [Metrics tab] WorkflowInvocationMetrics (Vega charts)
+-- [Debug tab] WorkflowInvocationFeedback
Routing
Single consolidated route in client/src/entry/analysis/router.js:
path: "workflows/invocations/:invocationId/:tab?"
props: { invocationId, tab, isFullPage: true, success }
component: WorkflowInvocationState
Tab navigation uses BNav pills with <router-link> (:to) — each tab is a sub-path (e.g., /workflows/invocations/{id}/steps). The Overview tab is the default (when :tab is absent).
Core Composable: useInvocationGraph
File: client/src/composables/useInvocationGraph.ts (366 lines)
Central logic for creating a readonly invocation graph. Takes four Ref params:
invocation— the full invocation element viewstepsJobsSummary— per-step job summaries (provided by parent, not fetched internally)workflowId,workflowVersion— for fetching the correct workflow version
Key exports
| Export | Type | Description |
|---|---|---|
GraphStep | interface | Extends Step with state, jobs, headerClass, headerIcon, headerIconSpin, nodeText |
iconClasses | Record | Maps states to FontAwesome icons (ok->check, error->triangle, running->spinner, etc.) |
statePlaceholders | Record | Human-readable state labels (“ok”->“successful”, “error”->“failed”) |
getHeaderClass | function | Returns CSS class object { "node-header-invocation": true, "header-{state}": true } |
useInvocationGraph() | composable | Returns { storeId, steps, loadInvocationGraph, loading } |
State derivation logic
The composable derives a single visual state per step from job states using priority:
- Single-instance states (
error,running,paused,deleting): if any job has this state, the step gets it - All-instances states (
deleted,skipped,new,queued): all jobs must be in this state - Falls back to
populated_statefrom the invocation step summary, with mapping:scheduled/ready->queued,resubmitted->new,failed->error,deleting->deleted - If no invocation step exists for a workflow step, defaults to
queued
Workflow loading
On first call to loadInvocationGraph():
- Fetches the specific workflow version via
workflowStore.getFullWorkflowCached(workflowId, version) - Creates an
invocationGraphref (workflow structure withid = "invocation-{invocationId}") - Initializes
GraphStepobjects from workflow steps + invocation step data - Calls
fromSimple(storeId, invocationGraph)to load into scoped editor stores - On subsequent calls, only updates step states (doesn’t reload the editor)
Input step handling
Input steps (data_input, data_collection_input, parameter_input) are handled separately:
- Dataset inputs: fetches HDA/HDCA details, displays
{hid}: {name}asnodeText - Parameter inputs: displays the parameter value directly
- Boolean inputs: displayed as checkbox icon
- Color inputs: displayed with a color swatch
Scoped Workflow Stores
File: client/src/composables/workflowStores.ts
Each invocation graph gets isolated Pinia stores via provideScopedWorkflowStores(storeId) where storeId = "invocation-{invocationId}". This creates scoped instances of:
useConnectionStore— graph connections/edgesuseWorkflowStateStore— active node, scale, dragging stateuseWorkflowStepStore— step datauseWorkflowCommentStore— (unused in invocation mode)useWorkflowEditorToolbarStore— (unused in invocation mode)useUndoRedoStore— (unused in invocation mode)useWorkflowSearchStore— (unused in invocation mode)
All stores are auto-disposed via useTimeoutStoreDispose on scope disposal. This scoping prevents conflicts between the invocation graph view and any concurrently open workflow editor.
The activeNodeId from the state store drives which step’s detail card is displayed below the graph.
Data Flow & Polling Architecture
invocationStore (Pinia)
|
|-- fetchInvocationById() --> invocation data
|-- fetchInvocationJobsSummaryForId() --> aggregate job summary
|-- fetchInvocationStepJobsSummaryForId() --> per-step job summaries
|
v
WorkflowInvocationState (orchestrator)
|-- polls invocation until scheduling terminal (3s interval)
|-- polls job summaries until all jobs terminal (3s interval)
|-- provides: invocation, stepsJobsSummary, invocationAndJobTerminal
|
v
WorkflowInvocationOverview
|-- useWorkflowInstance() --> fetches StoredWorkflowDetailed
|
v
InvocationGraph
|-- useInvocationGraph(invocation, stepsJobsSummary, workflowId, workflowVersion)
|-- polls loadInvocationGraph() every 3s until terminal (redundant w/ parent)
|-- renders WorkflowGraph with GraphStep objects
Three levels of polling
- WorkflowInvocationState polls
invocationStore.fetchInvocationByIduntilinvocationSchedulingTerminal(state is scheduled/cancelled/failed/completed) - WorkflowInvocationState polls
invocationStore.fetchInvocationJobsSummaryForId+fetchInvocationStepJobsSummaryForIduntil all jobs are terminal - InvocationGraph polls
loadInvocationGraph()every 3s untilisTerminalprop is true — this re-processes thestepsJobsSummary(already being polled by parent) to update step visual states
All polling uses setTimeout chains (not setInterval) and cleans up via onUnmounted / clearTimeout.
Editor Canvas Reuse
The invocation graph reuses WorkflowGraph.vue in readonly mode with isInvocation=true:
Props passed to WorkflowGraph
steps: GraphStep objects (with state/jobs/headerClass/nodeText)readonly: trueisInvocation: truefixedHeight: 60 (vh units)showMinimap: depends onisFullPageshowZoomControls: trueinitialPosition:{ x: -40 * zoom, y: -40 * zoom }
How Node.vue adapts for invocation mode
When isInvocation=true:
- Header: uses
invocationStep.headerClass(state-colored) instead of defaultnode-header; cursor ispointernotmove - Header icon: shows state icon (spinner for running, check for ok, etc.)
- Body: shows
NodeInvocationTextinstead of normal step content - Inputs: rendered with
position-absoluteandblank=true(invisible labels, but connection points preserved for edge rendering) - Outputs: rendered with
blank=true, positioned absolutely to overlay - Rule divider: hidden
- Click behavior:
onActivateemits to setactiveNodeIdin state store
NodeInvocationText rendering
For tool/subworkflow steps with jobs:
- Renders
InvocationStepStateDisplayper job state (e.g., “2 jobs successful”, “1 job failed”)
For input steps:
- Boolean: checkbox icon + value
- Color: hex string + color swatch input
- Other: sanitized HTML (e.g.,
{hid}: <b>{name}</b>)
Step Detail Card (below graph)
When a node is clicked (activeNodeId !== null):
- A
BCardappears below the graph with:- Header:
WorkflowInvocationStepHeader(step title, state badge, icon) + navigation (Prev/Next/Close buttons) - Body:
WorkflowInvocationStepwithexpanded=trueandinGraphView=true
- Header:
- Navigation: Prev/Next buttons increment/decrement
activeNodeId - Scroll-to-view: clicking a step scrolls the card header into view; a “scroll to step” button also exists
WorkflowInvocationStep handles different step types:
- Tool steps: Shows
JobStep(job list with state filtering, pagination) + Outputs tab - Subworkflow steps: Shows
SubworkflowAlertwith link to subworkflow invocation - Data input steps: Shows
GenericHistoryItem - Parameter input steps: Shows
ParameterStep
Steps Tab (separate from graph)
WorkflowInvocationSteps.vue (in Workflow/Invocation/Graph/) renders all steps in a list view:
- Uses
useInvocationGraphwithloadOntoEditor=false(just computes GraphStep objects, doesn’t load onto canvas) - Groups input steps in a collapsible “Workflow Inputs” section
- Each step is a
WorkflowInvocationStepwith externally-managed expansion state - Uses
useWorkflowInstanceto fetch the workflow
Sibling: Workflow Run Graph
useWorkflowRunGraph.ts (223 lines) is a sibling composable that reuses the same graph infrastructure for the workflow run form (before invocation). It:
- Imports
getHeaderClassfromuseInvocationGraph - Shows input state on the graph as users fill in the run form (populated/unpopulated/error)
- Uses the same
fromSimplepattern to load onto scoped editor stores - Does NOT show job states (no invocation exists yet)
This establishes a pattern where the editor canvas is a reusable visualization layer, with composables providing different data overlays (invocation states vs. run-form states).
Styling
State-colored headers (base.scss:195)
.node-header-invocation {
color: $text-color;
@each $state in map-keys($galaxy-state-bg) {
&.header-#{$state} {
background-color: map-get($galaxy-state-bg, $state) !important;
}
}
&.header-paused { background-color: $state-info-bg !important; }
&.header-skipped { background-color: map-get($galaxy-state-bg, "hidden") !important; }
}
Uses the $galaxy-state-bg SCSS map (defined in theme) to derive colors for each state. The getHeaderClass function returns { "node-header-invocation": true, "header-{state}": true }.
Component-scoped styles
- InvocationGraph.vue:
.graph-scroll-overlay(semi-transparent edge overlays when step selected),.invocation-graph(z-index for minimap/zoom),.invocation-step-card(min-height 500px) - Node.vue:
.invocation-node-output(absolutely positioned outputs),.node-headervariants - NodeInvocationText.vue:
.truncate(overflow handling),.color-input(color swatch sizing)
API Dependencies
Invocation Store endpoints
| Method | Endpoint | Used for |
|---|---|---|
fetchInvocationById | GET /api/invocations/{invocation_id} | Full invocation data |
fetchInvocationJobsSummaryForId | GET /api/invocations/{invocation_id}/jobs_summary | Aggregate job states |
fetchInvocationStepJobsSummaryForId | GET /api/invocations/{invocation_id}/step_jobs_summary | Per-step job summaries |
fetchInvocationStepById | GET /api/invocations/steps/{step_id} | Individual step details |
cancelWorkflowScheduling | DELETE /api/invocations/{invocation_id} | Cancel invocation |
Key API Types (client/src/api/invocations.ts)
WorkflowInvocationElementView— full invocation with steps, inputs, outputsStepJobSummary— union ofInvocationStepJobsResponseStepModel | ...JobModel | ...CollectionJobsModelInvocationJobsSummary— aggregate job state countsInvocationStep— individual step withjob_id,implicit_collection_jobs_id,state
Workflow Store
getFullWorkflowCached(workflowId, version)— fetches and caches the specific workflow versiongetStoredWorkflowByInstanceId(workflowId)— gets stored workflow metadatafetchWorkflowForInstanceId(workflowId)— fetches workflow if not cached
Test Coverage
Unit Tests
| File | What it tests |
|---|---|
WorkflowInvocationState.test.ts | Tab rendering, terminal/non-terminal states, fetch call counts |
WorkflowInvocationOverview.test.js | Overview rendering with invocation graph |
JobStep.test.ts | Job listing, state filtering |
JobStepJobs.test.ts | Job pagination, sorting |
WorkflowInvocationInputOutputTabs.test.ts | Input/output tab rendering |
WorkflowInvocationShare.test.ts | Share functionality |
Selenium Test
lib/galaxy_test/selenium/test_workflow_invocation_details.py (143 lines, 2 tests):
test_job_details— Verifies progress bars, inputs tab, steps tab, job details, outputs tabtest_invocation_step_jobs_with_failed_jobs— Verifies mixed ok/error job state counters, error filtering, Debug tab
File Inventory (27 files)
Core graph components
| File | Lines | Role |
|---|---|---|
composables/useInvocationGraph.ts | 366 | Core graph state logic |
composables/useWorkflowRunGraph.ts | 223 | Sibling: run-form graph |
composables/useWorkflowInstance.ts | 31 | Workflow fetch helper |
composables/workflowStores.ts | 103 | Scoped store provisioning |
Workflow/Invocation/Graph/InvocationGraph.vue | 310 | Graph + step detail card |
Workflow/Invocation/Graph/WorkflowInvocationSteps.vue | 101 | Steps list view |
Editor components (adapted for invocation)
| File | Role |
|---|---|
Workflow/Editor/WorkflowGraph.vue | Canvas container |
Workflow/Editor/Node.vue | Node rendering w/ isInvocation prop |
Workflow/Editor/NodeInvocationText.vue | In-node job state display |
Workflow/Editor/NodeInput.vue | Input terminals w/ blank prop |
Workflow/Editor/NodeOutput.vue | Output terminals w/ blank prop |
Invocation state components
| File | Role |
|---|---|
WorkflowInvocationState/WorkflowInvocationState.vue | Top-level page, polling, tabs |
WorkflowInvocationState/WorkflowInvocationOverview.vue | Overview tab, hosts InvocationGraph |
WorkflowInvocationState/WorkflowInvocationStep.vue | Expandable step detail |
WorkflowInvocationState/WorkflowInvocationStepHeader.vue | Step header w/ state badge |
WorkflowInvocationState/WorkflowInvocationError.vue | Error cards w/ step navigation |
WorkflowInvocationState/InvocationStepStateDisplay.vue | Job state counter (icon + count) |
WorkflowInvocationState/JobStep.vue | Job list w/ state filtering |
WorkflowInvocationState/JobStepJobs.vue | Paginated job table |
WorkflowInvocationState/WorkflowInvocationInputOutputTabs.vue | Inputs/Outputs tabs |
WorkflowInvocationState/WorkflowInvocationMetrics.vue | Vega metric charts |
WorkflowInvocationState/WorkflowInvocationFeedback.vue | Debug tab |
WorkflowInvocationState/SubworkflowAlert.vue | Subworkflow link |
WorkflowInvocationState/WorkflowStepIcon.vue | Step type icon |
WorkflowInvocationState/WorkflowStepTitle.vue | Step title display |
WorkflowInvocationState/util.ts | Job state counting helpers |
Stores
| File | Role |
|---|---|
stores/invocationStore.ts | Invocation data caching + API calls |
stores/workflowStore.ts | Workflow data + version-aware caching |
stores/workflowEditorStateStore.ts | Per-graph state (activeNodeId, scale) |
Known TODOs / Limitations
5 TODOs remain in useInvocationGraph.ts:
- L172: “What if the state of something not in the stepsJobsSummary has changed? (e.g.: subworkflows…)”
- L227: Subworkflow steps are often
scheduledregardless of actual output success — could derive state from subworkflow outputs - L250: “There is no summary for this step’s
job_id; what does this mean?” — falls back towaiting - L323: Type mismatch between HDA state and
GraphStep["state"] - L328: Same type mismatch for HDCA state
Additional architectural concerns:
- Redundant polling: InvocationGraph polls independently at 3s even though WorkflowInvocationState already polls the same data. The
stepsJobsSummaryis passed down as a prop, so the InvocationGraph poll just reprocesses already-updated data. - Multiple
useInvocationGraphinstances: The composable is called independently by InvocationGraph (Overview tab), WorkflowInvocationSteps (Steps tab), and WorkflowInvocationFeedback (Debug tab). Each creates its own scoped stores and step state. A TODO in WorkflowInvocationOverview notes: “Refactor so thatstoreIdis only defined here, and then used in all children components/composables.” - Subworkflow state is not fully represented: The graph shows subworkflow steps as “scheduled” even when their child invocation has completed or failed. The only way to see subworkflow details is via the SubworkflowAlert link.
fromSimpleside effect: Loading graph data into scoped stores usesfromSimple, which is the same function used by the editor to deserialize workflows. In invocation mode most of the store data (connections, comments, undo/redo) goes unused.