Skip to content

feat: Phase 1b — SSE step events, event rotation, lane animation - #102

Merged
nutt-adam merged 5 commits into
mainfrom
feat/phase-1b-dashboard-flow
Mar 21, 2026
Merged

feat: Phase 1b — SSE step events, event rotation, lane animation#102
nutt-adam merged 5 commits into
mainfrom
feat/phase-1b-dashboard-flow

Conversation

@nutt-adam

@nutt-adam nutt-adam commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Rust event emission: workflow.step.started, workflow.step.completed, workflow.step.failed events emitted from WorkflowExecutor::execute with step index, type, agent, and workflow metadata
  • Event rotation: events.jsonl auto-rotates at 5k events — archives oldest, retains 2.5k in active file
  • Dashboard run tracking: appState.runs tracks workflow runs by correlation_id; animated green dots on stages, red dots for failures, marching-dash flow connectors between active stages
  • HUD: active run counter in top bar
  • JS tests: 20+ assertions covering run lifecycle, failure paths, concurrent runs, edge cases

Test plan

  • cargo test — 327 tests pass (326 unit + 1 integration)
  • cargo clippy -- -D warnings — clean
  • cargo fmt -- --check — clean
  • Visual QA: trigger a workflow run via tt run, verify dots appear and animate through stages
  • Visual QA: trigger a failing workflow, verify red dot stops at failed stage
  • Open dashboard/test.html in browser, verify all JS tests pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Dashboard shows per-stage run dots with tooltips and running/failure states; HUD now shows active workflow run count.
    • Real-time pipeline updates via events and a dispatch panel to select and trigger workflows (optional issue) with status feedback.
  • Style

    • Added animated flow connectors, pulsing run-dot animations, failure highlighting, and responsive connector animations for mobile.

…tion

Emit workflow.step.started/completed/failed events from the workflow
executor so the dashboard can track work items flowing through the
pipeline. Add event file rotation (archive at 5k, retain 2.5k) to
prevent unbounded growth. Dashboard now renders animated run dots on
stages, marching-dash flow connectors, and a HUD run counter. 20+ new
JS tests cover run lifecycle, failure paths, and concurrent runs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Mar 21, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@nutt-adam has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 25 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 487d8af3-772b-4f95-bc00-c9e58f369ab0

📥 Commits

Reviewing files that changed from the base of the PR and between 03a0b8e and 1281338.

📒 Files selected for processing (3)
  • dashboard/app.js
  • src/automation/mod.rs
  • src/state/mod.rs
📝 Walkthrough

Walkthrough

Added per-run workflow state and UI visualizations, emitted step lifecycle control events from the executor, introduced event-log rotation for control events, and wired a dispatch panel to trigger workflows from the dashboard.

Changes

Cohort / File(s) Summary
Dashboard Frontend (app + UI + styles)
dashboard/app.js, dashboard/index.html, dashboard/style.css
Track per-correlation_id workflow runs in appState.runs; process workflow.* SSE events to update run stage/status/step counters; render per-stage run-dots and flowing connectors; add HUD #hud-runs; add dispatch panel UI and POST /v1/actions/run wiring.
Dashboard Tests
dashboard/test.html
Added test-only functions runStageFromEvent, processWorkflowEvent, runsAtStage and suites verifying lifecycle handling, failures, stage filtering, and ignoring events without correlation_id.
Backend Workflow Execution
src/automation/mod.rs
Added step_agent_name() helper; emit workflow.step.started when a step is attempted and emit workflow.step.completed / workflow.step.failed after step execution (including agent, indices, duration, message).
Event Log Management
src/state/mod.rs
Add rotation constants and maybe_rotate_events() to archive old events.jsonl when >5000 lines and retain newest 2500; append_control_event now triggers best-effort rotation; add unit test control_events_rotation.

Sequence Diagram(s)

sequenceDiagram
    participant WE as WorkflowExecutor
    participant STATE as State/Events
    participant SSE as SSE Server
    participant DASH as Dashboard(app.js)
    participant UI as Dashboard UI

    WE->>STATE: append_control_event(workflow.step.started...)
    WE->>WE: execute step
    WE->>STATE: append_control_event(workflow.step.completed|failed...)
    STATE->>STATE: maybe_rotate_events() (archive if >5000)
    STATE->>SSE: events available for streaming
    SSE->>DASH: SSE: workflow.* events
    DASH->>DASH: processWorkflowEvent() (update appState.runs)
    DASH->>UI: renderPipeline() -> update run-dots, connectors, hud-runs
    UI-->>DASH: DOM updated
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

Poem

🐇 I nibble logs and count each run,

Dots pulse and flow beneath the sun,
Steps sing started, completed, failed,
Old lines tucked where archives hailed,
A rabbit cheers — the workflow's fun!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is missing critical versioning information required by the template, specifically SemVer choice selection and version number. Complete the Versioning section: select a SemVer level (PATCH/MINOR/MAJOR), update Cargo.toml version, add CHANGELOG.md entry, and specify the version number (e.g., v0.2.0).
Docstring Coverage ⚠️ Warning Docstring coverage is 58.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: SSE step events, event rotation, and lane animation features are all present in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/phase-1b-dashboard-flow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Adds a "+ run" button in the dashboard that opens a dispatch form with
workflow selector, optional issue number input, and a dispatch button.
Calls POST /v1/actions/run to trigger the workflow. Workflows are
fetched from GET /v1/workflows on panel open.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
dashboard/style.css (1)

232-259: Honor prefers-reduced-motion for the new animations.

The pulsing dot and marching connectors are always animated. Please add a reduced-motion override so motion-sensitive users can keep the dashboard usable.

💡 Minimal fix
+@media (prefers-reduced-motion: reduce) {
+  .run-dot-active,
+  .flow-connector.flowing {
+    animation: none;
+  }
+}

Also applies to: 395-397

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/style.css` around lines 232 - 259, Add a prefers-reduced-motion
media query that disables the animations for motion-sensitive users: inside a
`@media` (prefers-reduced-motion: reduce) block set animation: none and remove
transform/opacity animations for .run-dot-active (which uses `@keyframes`
dot-pulse) and .flow-connector.flowing (which uses `@keyframes` flow-march), and
also ensure any shadow/visual-only motion for .run-dot-failed is non-animated;
reference .run-dot-active, .run-dot-failed, .flow-connector.flowing, `@keyframes`
dot-pulse and `@keyframes` flow-march when applying these overrides.
dashboard/test.html (1)

285-289: Consider documenting or explicitly handling workflow.step.completed.

This test validates that step.completed doesn't change the run status, which is correct. However, processWorkflowEvent has no explicit handler for workflow.step.completed — it works only because the else-if chain falls through. Since the backend emits this event (per PR objectives), adding an explicit case (even as a no-op comment) would clarify intent for future maintainers:

} else if (evt.event === "workflow.step.completed") {
  // No state change needed; run continues
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dashboard/test.html` around lines 285 - 289, The test shows
workflow.step.completed currently leaves run status as "running" because
processWorkflowEvent falls through; add an explicit handler for this event in
processWorkflowEvent (e.g., an else if branch checking evt.event ===
"workflow.step.completed") and make it a no-op with a clarifying comment so
intent is clear to future maintainers while preserving current behavior (also
reference testRuns["run-1"].status and the existing test using
processWorkflowEvent to verify no state change).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dashboard/app.js`:
- Around line 68-72: The renderer is treating staged/terminal runs as active
because UI animation logic doesn't distinguish run states; update rendering to
separate active runs from terminal runs by checking each run's status (e.g.,
run.status or run.state) before applying pulsing/connector animations so only
runs with status "running" animate, and ensure terminal runs remain static until
they're removed by the existing setTimeout that deletes from appState.runs and
calls scheduleRender(); adjust any rendering functions (where runs are
iterated—references: appState.runs, scheduleRender(), and the setTimeout removal
block) to use this status check so completed/failed runs do not pulse or keep
connectors marching.

In `@src/automation/mod.rs`:
- Around line 789-804: The started-event emission (append_control_event with
event "workflow.step.started") is only in the sequential branch, so when
explicit_dep_mode uses execute_control_dag() ensure_running/review/land steps
never publish it; update execute_control_dag (and any helper that begins step
execution) to call the same append_control_event snippet when a step transitions
to running — specifically add the workflow.step.started append_control_event
call inside execute_control_dag before executing a step (or inside the function
that dispatches/marks a step as started), using the same fields: workspace from
self.config.workspace.name, agent from step_agent_name(step), timestamp
Utc::now(), correlation_id run_id, and data including workflow.name, step_index,
step_type_name(step), and total_steps (workflow.steps.len()) so the dashboard
receives started events for ensure_running, review, land and other control-DAG
steps.
- Around line 563-570: The match arm in step_agent_name for ResolvedStep::Review
returns the source agent, which misroutes review events; change the
ResolvedStep::Review arm inside function step_agent_name to return the reviewer
session identifier instead (e.g., Some("reviewer") or the existing
REVIEWER/REVIEW_SESSION constant if one exists) so review steps are attributed
to the reviewer lane rather than the source agent.
- Around line 2095-2116: The code currently collapses StepStatus::Warning into
failure; update the branch that sets event_name (where sr.status is checked) to
handle StepStatus::Warning separately (e.g., set event_name =
"workflow.step.warning" when sr.status == StepStatus::Warning) before falling
back to "workflow.step.failed" for actual failures, leaving append_control_event
and ControlEvent payload unchanged so the dashboard receives a distinct warning
event rather than a failed event; modify the conditional around sr.status in the
same block that builds event_name and calls append_control_event.

In `@src/state/mod.rs`:
- Around line 929-944: The rotation routine maybe_rotate_events currently does
an unlocked read/modify/write which can drop concurrent events from
append_control_event() and may clobber another rotation with the same-second
archive name; fix by taking an interprocess lock (e.g., an advisory file lock)
around the entire rotation snapshot-and-rotate in maybe_rotate_events and any
writer paths (append_control_event) so reads and appends serialize, use the
snapshot (read the entire file while holding the lock) to compute split, write
the archive and the reduced active file to temporary paths and then atomically
rename into place (avoid rewriting the live file), and make archive names unique
(include subsecond timestamp or a UUID) to prevent same-second collisions.

---

Nitpick comments:
In `@dashboard/style.css`:
- Around line 232-259: Add a prefers-reduced-motion media query that disables
the animations for motion-sensitive users: inside a `@media`
(prefers-reduced-motion: reduce) block set animation: none and remove
transform/opacity animations for .run-dot-active (which uses `@keyframes`
dot-pulse) and .flow-connector.flowing (which uses `@keyframes` flow-march), and
also ensure any shadow/visual-only motion for .run-dot-failed is non-animated;
reference .run-dot-active, .run-dot-failed, .flow-connector.flowing, `@keyframes`
dot-pulse and `@keyframes` flow-march when applying these overrides.

In `@dashboard/test.html`:
- Around line 285-289: The test shows workflow.step.completed currently leaves
run status as "running" because processWorkflowEvent falls through; add an
explicit handler for this event in processWorkflowEvent (e.g., an else if branch
checking evt.event === "workflow.step.completed") and make it a no-op with a
clarifying comment so intent is clear to future maintainers while preserving
current behavior (also reference testRuns["run-1"].status and the existing test
using processWorkflowEvent to verify no state change).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fab3fd66-8fc3-42e5-afe1-21ddee84c4d1

📥 Commits

Reviewing files that changed from the base of the PR and between 444f3df and 5501550.

📒 Files selected for processing (6)
  • dashboard/app.js
  • dashboard/index.html
  • dashboard/style.css
  • dashboard/test.html
  • src/automation/mod.rs
  • src/state/mod.rs

Comment thread dashboard/app.js
Comment thread src/automation/mod.rs Outdated
Comment thread src/automation/mod.rs
Comment thread src/automation/mod.rs Outdated
Comment thread src/state/mod.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dashboard/app.js`:
- Around line 40-50: The handler currently only seeds appState.runs on
"workflow.started" so late-joined clients ignore "workflow.step.*" events;
update the event handling (the code around the evt.event checks for
"workflow.started" and other step events like
"workflow.step.started"/"workflow.step.completed") to create a minimal run
object when a step event arrives and appState.runs[id] is missing: populate id,
status: "running", stepIndex from evt.data.step_index (or 0), totalSteps from
evt.data.total_steps, workflowName from evt.data.workflow_name (or ""), and
startedAt from evt.timestamp (or null); ensure this same seeding logic is
applied to the other step-event branches referenced around lines 53-54 so runs
appear after refresh/SSE reconnect.

In `@dashboard/index.html`:
- Around line 42-45: The dispatch controls lack accessible names and the status
span won't be announced; add explicit accessible labels for the select and input
(use <label for="dispatch-workflow"> and <label for="dispatch-issue"> or
aria-label attributes on elements with ids dispatch-workflow and
dispatch-issue), ensure the dispatch button (id dispatch-go) has an accessible
name (visible text or aria-label), and make the status element
(`#dispatch-status`) live to assistive tech by adding a role or aria-live (e.g.,
role="status" or aria-live="polite") so updates are announced.

In `@dashboard/style.css`:
- Around line 329-395: The dispatch row overflows on small screens because
desktop widths (min-width: 140px, input width: 100px) aren't overridden; add a
mobile media query targeting `#dispatch-panel/.dispatch-form` that enables
flex-wrap (or switches .dispatch-form to column), set .dispatch-form select and
.dispatch-form input to use max-width: 100% / min-width: auto (or width: 100%)
so they shrink to the container, and make `#dispatch-go` and .dispatch-toggle
adapt (full-width or auto with flex-shrink) so the action/status don't get
pushed off-screen; update rules for .dispatch-form.open, .dispatch-form select,
.dispatch-form input, `#dispatch-go`, .dispatch-toggle and `#dispatch-panel` within
that media query.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2b0f3386-d009-4a50-8cb5-1e2879dbf2ad

📥 Commits

Reviewing files that changed from the base of the PR and between 5501550 and 03a0b8e.

📒 Files selected for processing (3)
  • dashboard/app.js
  • dashboard/index.html
  • dashboard/style.css

Comment thread dashboard/app.js
Comment thread dashboard/index.html
Comment thread dashboard/style.css
nutt-adam and others added 2 commits March 21, 2026 16:42
- Dashboard: filter terminal runs from stage dot rendering
- Route review step events to reviewer agent instead of source
- Emit workflow.step.started in control-DAG execution path
- Distinguish Warning from Failed in step completion events
- Protect event log rotation with atomic rename

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.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.

1 participant