Workflow definition graph: static DAG with live run overlay#13
Merged
Conversation
Prism-based DefinitionAnalyzer in core produces a rendering-agnostic conditional DAG; dashboard overlays a run's execution_logs and renders via Mermaid on a new per-run definition page. Reference: durable_flow DefinitionAnalyzer. Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq
9 TDD tasks: Definition value objects + Prism dep; analyzer (linear, conditionals, fan-out/merge, repeat/helper-tracing/loop-warnings); dashboard overlay, Mermaid renderer, and per-run definition page. Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq
Model if/unless/case around durable calls as :conditional edges carrying a guard label; thread multiple exit ids so the post-conditional step is reachable from both the body exit and the skip path. continue_if's false path emits a :terminal edge to a virtual halt sink (mirrors the virtual start id). Also fixes latent perform-resolution bug: bind to the DefNode on the method's source line so multiple workflow classes in one file no longer collide. Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq
…r tracing, dynamic-merge join, guard sanitization Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq
…d-positional); merge status from own log; drop dead controller branch Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq
…odes by prefix (no double-report)
… of Mermaid - Structured elements (JSON) consumed directly — no text-DSL escaping (kills the guard-label bug class); pan/zoom + tap-to-inspect; ~670KB vs 3.5MB vendored. - Fix: analyze the workflow's OWN perform, not the prepended ChronoForge::Executor #perform (every real workflow was being analyzed as the executor).
…xi routing, spacing
…tchenSinkWorkflow a real analyzable perform
thedumbtechguy
added a commit
that referenced
this pull request
Jul 2, 2026
…un overlay (#13) * docs: design spec for static workflow definition DAG (future timeline) Prism-based DefinitionAnalyzer in core produces a rendering-agnostic conditional DAG; dashboard overlays a run's execution_logs and renders via Mermaid on a new per-run definition page. Reference: durable_flow DefinitionAnalyzer. Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq * docs: implementation plan for workflow definition DAG 9 TDD tasks: Definition value objects + Prism dep; analyzer (linear, conditionals, fan-out/merge, repeat/helper-tracing/loop-warnings); dashboard overlay, Mermaid renderer, and per-run definition page. Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq * feat(definition): graph value objects + prism dependency Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq * feat(analyzer): linear durable-step extraction via Prism Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq * feat(analyzer): guarded conditional edges and continue_if terminal Model if/unless/case around durable calls as :conditional edges carrying a guard label; thread multiple exit ids so the post-conditional step is reachable from both the body exit and the skip path. continue_if's false path emits a :terminal edge to a virtual halt sink (mirrors the virtual start id). Also fixes latent perform-resolution bug: bind to the DefNode on the method's source line so multiple workflow classes in one file no longer collide. Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq * feat(analyzer): branch fan-out nodes and merge join edges Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq * feat(analyzer): repeat node, same-class helper tracing, loop warnings Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq * feat(dashboard): overlay execution_logs onto the definition graph Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq * feat(dashboard): render statused definition graph to Mermaid * fix(analyzer): correct unless/nested/elsif guards, class-scoped helper tracing, dynamic-merge join, guard sanitization Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq * feat(dashboard): per-run workflow definition DAG page with Mermaid * chore: lint + silence -w warnings; document definition graph page * fix(analyzer): resolve step names per real DSL arg positions (wait 2nd-positional); merge status from own log; drop dead controller branch Claude-Session: https://claude.ai/code/session_01DPLkBrXrXdjXdpoDyfdyqq * fix(dashboard): quote Mermaid edge labels so guards with () and < render * feat(analyzer): model early returns as terminal edges; bind dynamic nodes by prefix (no double-report) * feat(dashboard): render definition graph with Cytoscape+dagre instead of Mermaid - Structured elements (JSON) consumed directly — no text-DSL escaping (kills the guard-label bug class); pan/zoom + tap-to-inspect; ~670KB vs 3.5MB vendored. - Fix: analyze the workflow's OWN perform, not the prepended ChronoForge::Executor #perform (every real workflow was being analyzed as the executor). * style(dashboard): polish definition graph — aligned cards, legend, taxi routing, spacing * docs: drop stale Mermaid mention in guard-truncation comment * docs: document definition graph in READMEs + screenshot; give demo KitchenSinkWorkflow a real analyzable perform * style: lint-clean the definition test files (scope deliberate-syntax fixtures) * docs: add real-world scheduled-payment definition graph screenshot
thedumbtechguy
added a commit
that referenced
this pull request
Jul 2, 2026
…UX polish Follow-up fixes and polish for the definition graph (#13). Analyzer (lib/chrono_forge): - follow durable calls through assignments, &&/||, and case/in so a step one expression deep is no longer silently dropped - treat merge_branches with a non-literal branch name as a dynamic node instead of a merge with a nil step_name that never binds - warn when a perform has no durable steps (empty/unanalyzable) so the page shows a readable note rather than a bare unmapped node Overlay: - a dynamic prefix node no longer rebinds a log a static node already owns - count repetitions by exact string prefix, not SQL LIKE (an underscore in a step name was treated as a wildcard and over-counted) - map pending logs to :active (the enum is only pending/completed/failed) Rendering: - HTML-escape labels, step names, and guards before writing them into the tap-to-inspect detail panel (DOM XSS) - surface the repeat count (xN) and fan-out child tallies the overlay computes Dashboard: - the definition page opts out of the auto-refresh region swap (which wiped the Cytoscape canvas) and hides the now-inert refresh control Docs/fixtures: - README updates for the above - give OrderProcessingWorkflow a real branch/merge body and add a ScheduledPaymentRecurrenceWorkflow demo fixture with distinct per-branch step names; regenerate the scheduled-payment screenshot so one branch reads as taken and the others dimmed
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 a per-run definition graph to the ChronoForge dashboard: a static DAG of the durable steps a workflow will run — parsed from
performwith Prism, never executed, never touching the DB — with the run's live status overlaid on each node.Core (
chrono_forge)ChronoForge::Definition/Node/Edge— rendering-agnostic graph value objects (to_h→ JSON-safe).ChronoForge::DefinitionAnalyzer— Prism static analysis of a workflow's ownperforminto a conservative conditional DAG:durably_execute/wait/wait_until/continue_if/branch/merge_branches/durably_repeat, withif/unless/case→ guarded edges, earlyreturn→ dashed terminal exit, same-class helper tracing, and loops/computed names → adynamicnode + warning rather than a confident-but-wrong graph.Dashboard (
chrono_forge-dashboard)DefinitionOverlaypaints each node with the run's status fromexecution_logs(done / in-progress / failed / not-reached, plus fan-out and repeat aggregates and unmapped steps).CytoscapeGraphemits structured elements; the page renders them client-side with Cytoscape + dagre (vendored, ~670 KB) — pan/zoom, tap a node/edge to inspect its step name / guard. No text-DSL, so labels/guards need no escaping.GET workflows/:id/definitionpage, linked from the workflow detail page; degrades to a warning (never a 500) for unknown/unanalyzable classes.KitchenSinkWorkflowgiven a real analyzableperformso the preview renders a mixed-status graph; screenshot + README docs added.Built TDD across a 9-task plan; then hardened against the real Achieve
ScheduledPaymentRecurrenceWorkflow, which surfaced several defects the fixtures missed (all fixed with regression tests):wait's name is the 2nd positional; merge status must come from its own log; unquoted guard labels; and — critically — analyzing the workflow's ownperformrather than the prependedChronoForge::Executor#perform.Test Plan
bundle exec rake test) — 288 examplesstandardrb)main(fix(branch): converge merges promptly via drain-ETA cadence and never-started-count rekick #12)chrono_forge-dashboard/README.md), confirm status overlay + pan/zoom/tap🤖 Generated with Claude Code