feat: breadcrumb navigation, subworkflow isolation, and stop reliability#113
Open
PolyphonyRequiem wants to merge 10 commits intomicrosoft:mainfrom
Open
feat: breadcrumb navigation, subworkflow isolation, and stop reliability#113PolyphonyRequiem wants to merge 10 commits intomicrosoft:mainfrom
PolyphonyRequiem wants to merge 10 commits intomicrosoft:mainfrom
Conversation
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 Report❌ Patch coverage is
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. 🚀 New features to boost your workflow:
|
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
wfDepthcounter to the Zustand workflow store — only depth-0workflow_startedinitializes root contextSubworkflowContextwith its own nodes/routes/agents/groupProgress mapsSubworkflow Event Handling
subworkflow_started,subworkflow_completed,subworkflow_failedactiveTarget()helperBreadcrumb Navigation
BreadcrumbBarcomponent shows the context stack above the graphgetViewedContext()+useViewed*()hooks provide stable, memoized context dataSubworkflow Node Visual
WorkflowNodeReact Flow node type with dashed border and Layers iconSubworkflowDetailpanel lists subworkflow runs with status/cost, click to navigateStop Button Reliability (Engine Fix)
_check_interrupt(): in subworkflows (depth > 0), raisesInterruptErrorinstead of silently consuming the interrupt event — unwinds the child engine back to the parent_handle_web_pause(): in subworkflows, also watchesinterrupt_eventso Stop-while-paused works without requiring Resume firstContext-Aware Rendering
useViewed*()hooks instead of root stateTesting