Dashboard

Component Invocation Graph View

Visual DAG rendering of workflow invocation with real-time job state, readonly editor canvas

Raw
Revised:
2026-05-13
Revision:
3
Related Notes:
Component - Invocation Report to Pages, Component - Workflow Editor Terminals, PR 17413 - Invocation Graph View, PR 21932 - History Graph API

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 view
  • stepsJobsSummary — per-step job summaries (provided by parent, not fetched internally)
  • workflowId, workflowVersion — for fetching the correct workflow version

Key exports

ExportTypeDescription
GraphStepinterfaceExtends Step with state, jobs, headerClass, headerIcon, headerIconSpin, nodeText
iconClassesRecordMaps states to FontAwesome icons (ok->check, error->triangle, running->spinner, etc.)
statePlaceholdersRecordHuman-readable state labels (“ok”->“successful”, “error”->“failed”)
getHeaderClassfunctionReturns CSS class object { "node-header-invocation": true, "header-{state}": true }
useInvocationGraph()composableReturns { storeId, steps, loadInvocationGraph, loading }

State derivation logic

The composable derives a single visual state per step from job states using priority:

  1. Single-instance states (error, running, paused, deleting): if any job has this state, the step gets it
  2. All-instances states (deleted, skipped, new, queued): all jobs must be in this state
  3. Falls back to populated_state from the invocation step summary, with mapping: scheduled/ready -> queued, resubmitted -> new, failed -> error, deleting -> deleted
  4. If no invocation step exists for a workflow step, defaults to queued

Workflow loading

On first call to loadInvocationGraph():

  1. Fetches the specific workflow version via workflowStore.getFullWorkflowCached(workflowId, version)
  2. Creates an invocationGraph ref (workflow structure with id = "invocation-{invocationId}")
  3. Initializes GraphStep objects from workflow steps + invocation step data
  4. Calls fromSimple(storeId, invocationGraph) to load into scoped editor stores
  5. 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} as nodeText
  • 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/edges
  • useWorkflowStateStore — active node, scale, dragging state
  • useWorkflowStepStore — step data
  • useWorkflowCommentStore — (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

  1. WorkflowInvocationState polls invocationStore.fetchInvocationById until invocationSchedulingTerminal (state is scheduled/cancelled/failed/completed)
  2. WorkflowInvocationState polls invocationStore.fetchInvocationJobsSummaryForId + fetchInvocationStepJobsSummaryForId until all jobs are terminal
  3. InvocationGraph polls loadInvocationGraph() every 3s until isTerminal prop is true — this re-processes the stepsJobsSummary (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: true
  • isInvocation: true
  • fixedHeight: 60 (vh units)
  • showMinimap: depends on isFullPage
  • showZoomControls: true
  • initialPosition: { x: -40 * zoom, y: -40 * zoom }

How Node.vue adapts for invocation mode

When isInvocation=true:

  • Header: uses invocationStep.headerClass (state-colored) instead of default node-header; cursor is pointer not move
  • Header icon: shows state icon (spinner for running, check for ok, etc.)
  • Body: shows NodeInvocationText instead of normal step content
  • Inputs: rendered with position-absolute and blank=true (invisible labels, but connection points preserved for edge rendering)
  • Outputs: rendered with blank=true, positioned absolutely to overlay
  • Rule divider: hidden
  • Click behavior: onActivate emits to set activeNodeId in state store

NodeInvocationText rendering

For tool/subworkflow steps with jobs:

  • Renders InvocationStepStateDisplay per 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):

  1. A BCard appears below the graph with:
    • Header: WorkflowInvocationStepHeader (step title, state badge, icon) + navigation (Prev/Next/Close buttons)
    • Body: WorkflowInvocationStep with expanded=true and inGraphView=true
  2. Navigation: Prev/Next buttons increment/decrement activeNodeId
  3. 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 SubworkflowAlert with 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 useInvocationGraph with loadOntoEditor=false (just computes GraphStep objects, doesn’t load onto canvas)
  • Groups input steps in a collapsible “Workflow Inputs” section
  • Each step is a WorkflowInvocationStep with externally-managed expansion state
  • Uses useWorkflowInstance to 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 getHeaderClass from useInvocationGraph
  • Shows input state on the graph as users fill in the run form (populated/unpopulated/error)
  • Uses the same fromSimple pattern 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-header variants
  • NodeInvocationText.vue: .truncate (overflow handling), .color-input (color swatch sizing)

API Dependencies

Invocation Store endpoints

MethodEndpointUsed for
fetchInvocationByIdGET /api/invocations/{invocation_id}Full invocation data
fetchInvocationJobsSummaryForIdGET /api/invocations/{invocation_id}/jobs_summaryAggregate job states
fetchInvocationStepJobsSummaryForIdGET /api/invocations/{invocation_id}/step_jobs_summaryPer-step job summaries
fetchInvocationStepByIdGET /api/invocations/steps/{step_id}Individual step details
cancelWorkflowSchedulingDELETE /api/invocations/{invocation_id}Cancel invocation

Key API Types (client/src/api/invocations.ts)

  • WorkflowInvocationElementView — full invocation with steps, inputs, outputs
  • StepJobSummary — union of InvocationStepJobsResponseStepModel | ...JobModel | ...CollectionJobsModel
  • InvocationJobsSummary — aggregate job state counts
  • InvocationStep — individual step with job_id, implicit_collection_jobs_id, state

Workflow Store

  • getFullWorkflowCached(workflowId, version) — fetches and caches the specific workflow version
  • getStoredWorkflowByInstanceId(workflowId) — gets stored workflow metadata
  • fetchWorkflowForInstanceId(workflowId) — fetches workflow if not cached

Test Coverage

Unit Tests

FileWhat it tests
WorkflowInvocationState.test.tsTab rendering, terminal/non-terminal states, fetch call counts
WorkflowInvocationOverview.test.jsOverview rendering with invocation graph
JobStep.test.tsJob listing, state filtering
JobStepJobs.test.tsJob pagination, sorting
WorkflowInvocationInputOutputTabs.test.tsInput/output tab rendering
WorkflowInvocationShare.test.tsShare functionality

Selenium Test

lib/galaxy_test/selenium/test_workflow_invocation_details.py (143 lines, 2 tests):

  1. test_job_details — Verifies progress bars, inputs tab, steps tab, job details, outputs tab
  2. test_invocation_step_jobs_with_failed_jobs — Verifies mixed ok/error job state counters, error filtering, Debug tab

File Inventory (27 files)

Core graph components

FileLinesRole
composables/useInvocationGraph.ts366Core graph state logic
composables/useWorkflowRunGraph.ts223Sibling: run-form graph
composables/useWorkflowInstance.ts31Workflow fetch helper
composables/workflowStores.ts103Scoped store provisioning
Workflow/Invocation/Graph/InvocationGraph.vue310Graph + step detail card
Workflow/Invocation/Graph/WorkflowInvocationSteps.vue101Steps list view

Editor components (adapted for invocation)

FileRole
Workflow/Editor/WorkflowGraph.vueCanvas container
Workflow/Editor/Node.vueNode rendering w/ isInvocation prop
Workflow/Editor/NodeInvocationText.vueIn-node job state display
Workflow/Editor/NodeInput.vueInput terminals w/ blank prop
Workflow/Editor/NodeOutput.vueOutput terminals w/ blank prop

Invocation state components

FileRole
WorkflowInvocationState/WorkflowInvocationState.vueTop-level page, polling, tabs
WorkflowInvocationState/WorkflowInvocationOverview.vueOverview tab, hosts InvocationGraph
WorkflowInvocationState/WorkflowInvocationStep.vueExpandable step detail
WorkflowInvocationState/WorkflowInvocationStepHeader.vueStep header w/ state badge
WorkflowInvocationState/WorkflowInvocationError.vueError cards w/ step navigation
WorkflowInvocationState/InvocationStepStateDisplay.vueJob state counter (icon + count)
WorkflowInvocationState/JobStep.vueJob list w/ state filtering
WorkflowInvocationState/JobStepJobs.vuePaginated job table
WorkflowInvocationState/WorkflowInvocationInputOutputTabs.vueInputs/Outputs tabs
WorkflowInvocationState/WorkflowInvocationMetrics.vueVega metric charts
WorkflowInvocationState/WorkflowInvocationFeedback.vueDebug tab
WorkflowInvocationState/SubworkflowAlert.vueSubworkflow link
WorkflowInvocationState/WorkflowStepIcon.vueStep type icon
WorkflowInvocationState/WorkflowStepTitle.vueStep title display
WorkflowInvocationState/util.tsJob state counting helpers

Stores

FileRole
stores/invocationStore.tsInvocation data caching + API calls
stores/workflowStore.tsWorkflow data + version-aware caching
stores/workflowEditorStateStore.tsPer-graph state (activeNodeId, scale)

Known TODOs / Limitations

5 TODOs remain in useInvocationGraph.ts:

  1. L172: “What if the state of something not in the stepsJobsSummary has changed? (e.g.: subworkflows…)”
  2. L227: Subworkflow steps are often scheduled regardless of actual output success — could derive state from subworkflow outputs
  3. L250: “There is no summary for this step’s job_id; what does this mean?” — falls back to waiting
  4. L323: Type mismatch between HDA state and GraphStep["state"]
  5. 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 stepsJobsSummary is passed down as a prop, so the InvocationGraph poll just reprocesses already-updated data.
  • Multiple useInvocationGraph instances: 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 that storeId is 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.
  • fromSimple side effect: Loading graph data into scoped stores uses fromSimple, 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.

Incoming References (4)