Skip to content

feat: breadcrumb navigation, subworkflow isolation, and stop reliability#113

Open
PolyphonyRequiem wants to merge 10 commits intomicrosoft:mainfrom
PolyphonyRequiem:feat/web-breadcrumb-navigation
Open

feat: breadcrumb navigation, subworkflow isolation, and stop reliability#113
PolyphonyRequiem wants to merge 10 commits intomicrosoft:mainfrom
PolyphonyRequiem:feat/web-breadcrumb-navigation

Conversation

@PolyphonyRequiem
Copy link
Copy Markdown
Member

Summary

Adds full subworkflow awareness to the per-run web dashboard with breadcrumb navigation, fixes node status pollution from repeated subworkflow runs, and fixes the Stop button not working during subworkflows.

Changes

State Pollution Fix

  • Added wfDepth counter to the Zustand workflow store — only depth-0 workflow_started initializes root context
  • Each subworkflow invocation gets an isolated SubworkflowContext with its own nodes/routes/agents/groupProgress maps
  • Repeated runs of the same subworkflow no longer share state (no more green/blue node bleed)

Subworkflow Event Handling

  • Added TypeScript types for subworkflow_started, subworkflow_completed, subworkflow_failed
  • Event handlers create/update child contexts and route events via activeTarget() helper

Breadcrumb Navigation

  • BreadcrumbBar component shows the context stack above the graph
  • Click any breadcrumb to navigate to that context level
  • Double-click a workflow agent node to dive into its subworkflow graph
  • getViewedContext() + useViewed*() hooks provide stable, memoized context data

Subworkflow Node Visual

  • New WorkflowNode React Flow node type with dashed border and Layers icon
  • SubworkflowDetail panel lists subworkflow runs with status/cost, click to navigate

Stop Button Reliability (Engine Fix)

  • _check_interrupt(): in subworkflows (depth > 0), raises InterruptError instead of silently consuming the interrupt event — unwinds the child engine back to the parent
  • _handle_web_pause(): in subworkflows, also watches interrupt_event so Stop-while-paused works without requiring Resume first

Context-Aware Rendering

  • All graph node components read from useViewed*() hooks instead of root state
  • Detail panels resolve nodes from the viewed context

Testing

  • Zero new TypeScript errors (all tsc errors are pre-existing)
  • Verified on live dashboard instance at localhost:60108

Daniel Green and others added 4 commits April 22, 2026 13:54
Adds full subworkflow awareness to the per-run web dashboard:

State pollution fix:
- Added wf_depth counter to workflow store — only depth-0
  workflow_started initializes root context. Inner workflow events
  are routed to isolated SubworkflowContext objects.
- Each subworkflow invocation gets its own nodes/routes/agents maps,
  keyed by (parentAgent, iteration). Repeated runs of the same
  subworkflow no longer share state.

Subworkflow event handling:
- Added TypeScript types for subworkflow_started, subworkflow_completed,
  subworkflow_failed events (mirrors engine emit).
- Event handlers create/update child contexts and track the active
  context path for routing subsequent events.

Breadcrumb navigation:
- New BreadcrumbBar component shows the context stack above the graph
  (e.g., Root > twig-sdlc-planning > plan-issue).
- Click any breadcrumb to navigate to that context level.
- Double-click a workflow agent node in the graph to dive into its
  subworkflow context.
- Graph rebuilds automatically when context changes.

Context stack architecture:
- SubworkflowContext[] tree structure mirrors workflow nesting.
- activeContextPath tracks where live events are routed.
- viewContextPath tracks what the user is viewing (independent).
- getViewedContext() returns the correct nodes/routes for rendering.
- All event handlers use activeTarget() helper to route to the
  correct context's nodes/groupProgress.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Phase 4-5 of breadcrumb navigation feature:

WorkflowNode component:
- New node type for type:'workflow' agents with dashed border and
  Layers icon (visually distinct from regular agent nodes).
- Shows child workflow name, elapsed time, and a chevron indicator
  when a SubworkflowContext exists.
- Double-click to navigate into the subworkflow graph.

SubworkflowDetail panel:
- New detail component shown when a workflow agent is selected.
- Lists all subworkflow runs for that agent with status, agent
  count, and cost summary.
- Click any run to navigate into its context.

Context-aware rendering:
- All graph node components (AgentNode, ScriptNode, GateNode,
  GroupNode, AnimatedEdge) now read from getViewedContext().nodes
  instead of root state.nodes — ensures correct status display
  when viewing child contexts.
- DetailPanel reads from viewed context for node lookup.
- GroupDetail reads groupProgress from viewed context.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
getViewedContext() creates a new object on every call, causing infinite
re-render loops (React error #185) when used inside Zustand selectors.

New hooks in use-viewed-context.ts use useMemo with stable state
references:
- useViewedNodes() — nodes map for current context
- useViewedGroupProgress() — group progress for current context
- useViewedHighlightedEdges() — edge highlights
- useViewedSubworkflowContexts() — child contexts
- useViewedGraphData() — full graph data for WorkflowGraph

All graph components and detail panels updated to use these hooks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When a subworkflow agent is running, the shared interrupt_event was
being silently consumed by _check_interrupt() in web mode (line 857:
'if self._web_dashboard is not None: return None'). This meant Stop
would pause the current agent but then resume and continue — the
interrupt never propagated to the parent engine.

Two fixes:

1. _check_interrupt(): when _subworkflow_depth > 0, raise
   InterruptError instead of silently consuming the interrupt.
   This unwinds the child engine back to the parent's
   _execute_subworkflow try/except, stopping the workflow.

2. _handle_web_pause(): in subworkflows, also watch interrupt_event
   alongside resume/kill/disconnect events. A second Stop click
   while an agent is paused now raises InterruptError immediately,
   without requiring Resume first.

Root-level (depth 0) behavior is unchanged — Stop still pauses the
current agent with Resume/Kill options in the dashboard.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 22, 2026

Codecov Report

❌ Patch coverage is 66.66667% with 16 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (main@873c72b). Learn more about missing BASE report.

Files with missing lines Patch % Lines
src/conductor/engine/workflow.py 0.00% 11 Missing ⚠️
src/conductor/web/server.py 86.48% 5 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #113   +/-   ##
=======================================
  Coverage        ?   84.73%           
=======================================
  Files           ?       53           
  Lines           ?     7212           
  Branches        ?        0           
=======================================
  Hits            ?     6111           
  Misses          ?     1101           
  Partials        ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Daniel Green and others added 6 commits April 23, 2026 17:04
On Windows with Python 3.14+, the proactor event loop's accept callback
can fire after Server.close() sets _sockets = None during shutdown,
causing an AssertionError in base_events.py:_attach that crashes the
workflow process.

Fix:
- Add _guarded_serve() wrapper that catches AssertionError when the
  uvicorn server is in shutdown state (should_exit = True)
- Install a custom event-loop exception handler during server lifetime
  that suppresses the same race when it surfaces through callbacks
- _is_proactor_shutdown_race() validates: AssertionError type, server
  shutdown state, and asyncio-originating traceback frames
- Restore original exception handler in stop()

The guard is narrowly scoped: only AssertionError during server shutdown
is suppressed. All other exceptions delegate to the original handler.

Tests: 9 new tests covering the race detection, exception handler
delegation, guarded serve behavior, and edge cases.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…yers

When navigating between subworkflow layers via breadcrumbs or double-click,
old React Flow edges from the previous layer persisted as floating links
disconnected from any visible nodes.

Two fixes:
- WorkflowGraph: explicitly clear nodes and edges when switching to an
  empty context (subworkflow data not yet populated)
- graph-layout: filter edges against the actual node ID set to prevent
  orphan edges from routes referencing non-existent nodes

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… nodes

Parse ?agent={name} and ?subworkflow={name} query params on initial load
to auto-select and center the matching node in the workflow graph. This
enables the meta-dashboard (conductor-dashboard) to generate clickable
breadcrumb links that open the conductor UI focused on a specific node.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update useDeepLink hook to:
- Parse slash-separated subworkflow paths (e.g., ?subworkflow=planning/design)
  for navigating multiple levels deep into nested subworkflows
- Support combined ?subworkflow=X&agent=Y to select an agent within
  a subworkflow context
- Remove dependency on subworkflowContexts selector (array mutation
  doesn't trigger re-renders); rely on late-joiner replay instead

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rewrote useDeepLink to fix timing issues that prevented navigation:

- Use zustand.subscribe() instead of useEffect + selector reactivity.
  The old approach relied on subworkflowContexts selector changes,
  but the store mutates the array in-place during processEvent,
  so the selector never detected changes.
- Resolve the full subworkflow path in one shot via index walking
  instead of calling navigateIntoSubworkflow() in a loop.
- Set viewContextPath directly via setState instead of relying on
  action functions that might see stale state.
- Add error banner when deep-link target is invalid: shows the error
  message and a link back to the root dashboard.
- Validate agent exists in the target context's agent list.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants