Skip to content

fix: dashboard submits to wrong endpoint and doesn't handle completion events#175

Closed
josephfung wants to merge 2 commits into
ryaker:mainfrom
josephfung:fix/dashboard-submission-and-completion
Closed

fix: dashboard submits to wrong endpoint and doesn't handle completion events#175
josephfung wants to merge 2 commits into
ryaker:mainfrom
josephfung:fix/dashboard-submission-and-completion

Conversation

@josephfung

@josephfung josephfung commented Apr 23, 2026

Copy link
Copy Markdown

Summary

Fixes #167.

Two bugs in the dashboard frontend that prevent task submission and completion from working end-to-end:

  1. Wrong submission endpoint — chat messages were sent to POST /api/steer (mid-flight steering for already-running tasks) instead of POST /api/task (submits a new task to the orchestrator). Since no task was running with the default jobId, messages were written to a file on disk that nothing read.

  2. SSE completion events never handled — the event handler only cleared the "Working..." state on job_update events, which the backend never emits during normal task completion. Added handlers for done, task.end, and job_failed — the actual event types the orchestrator emits — so the spinner clears and responses appear correctly.

Also suppresses task.start, turn.start, and turn.end lifecycle events from creating noise as system messages in the chat.

Changes

  • Replace POST /api/steer with POST /api/task for new task submission
  • Add SSE handlers for done, task.end, job_failed, task.start, turn.start, turn.end

Testing

  • Manual: submit a task from the dashboard chat input — response appears, spinner clears after completion
  • Manual: verify job_failed shows an error message in the chat

Summary by CodeRabbit

  • Refactor

    • Simplified task submission to send only the prompt for cleaner handling.
    • Improved real-time status processing to be more reliable and responsive.
  • Bug Fixes

    • Prevents duplicate assistant messages at task completion.
    • Ensures task completion/failure reliably stops progress indicators and surfaces a clear "Task failed" system message when appropriate.
    • Suppresses internal start/turn events from appearing to users.

…n events

Two bugs in the dashboard frontend:

1. Chat messages were sent to POST /api/steer (mid-flight steering for
   running tasks) instead of POST /api/task (submits to orchestrator).
   Since no task was running with the default jobId, messages went to
   a file on disk that nothing read.

2. The SSE handler only cleared "Working..." on job_update events,
   which the backend never sends. Added handlers for done, task.end,
   and job_failed — the event types the orchestrator actually emits.

Also suppress task.start, turn.start, turn.end lifecycle events from
creating noise in the chat as system messages.
@codeant-ai

codeant-ai Bot commented Apr 23, 2026

Copy link
Copy Markdown

User does not have a PR Review subscription.

Go to Team management and add this email to the PR Review subscription.

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Fixes dashboard chat submission and SSE handling: client now posts prompts to /api/task and the SSE handler processes orchestrator lifecycle events (done, task.end, job_failed, plus suppressions for task.start/turn.*) to correctly clear the "working" state and append agent output.

Changes

Cohort / File(s) Summary
Task Submission & SSE Lifecycle Handling
src/dashboard/frontend/src/App.tsx
Replaced POST to /api/steer with POST /api/task using { prompt }. Expanded SSE onmessage handling to explicitly handle done (append agent bubble if last message not agent, stop task), task.end (stop task), job_failed (stop task and emit system error), and to early-return for task.start/turn.start/turn.end. job_update remains as fallback.

Sequence Diagram(s)

(none — changes are bug fixes to existing event handling and do not introduce a new multi-component feature flow requiring visualization.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested labels

size:M

Poem

🐰 I hopped to fix a chat ask,
Sent prompts the right way — no more mask.
SSE whispers now end the race,
"Working..." fades, the answer takes place.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and concisely describes the two main bug fixes: switching from wrong endpoint (/api/steer) and adding missing completion event handlers.
Linked Issues check ✅ Passed The changes fully address all three coding requirements from issue #167: correct endpoint change, SSE event handlers (done/task.end/job_failed), and suppression of lifecycle events.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the two bugs identified in #167 with no extraneous modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates the task submission endpoint from /api/steer to /api/task and expands the event handling logic to include specific handlers for done, task.end, and job_failed events. A review comment identifies an issue with side effects (incrementing a counter and creating a new date) inside a React state updater function, which should be kept pure to ensure predictable behavior.

Comment on lines +384 to +388
setMessages(prev => {
const last = prev[prev.length - 1];
if (last?.type === 'agent') return prev; // already shown via text streaming
return [...prev, { id: ++messageIdCounter, type: 'agent' as const, content: text, timestamp: new Date() }].slice(-MAX_MESSAGES);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

React state updaters should be pure functions. Performing side effects like incrementing a global counter ('++messageIdCounter') or creating a 'new Date()' inside the 'setMessages' updater can lead to unpredictable behavior, especially in React's Strict Mode or with concurrent rendering where the updater might be called multiple times. It is better to generate these values before calling the state setter.

            const nextId = ++messageIdCounter;
            const now = new Date();
            setMessages(prev => {
              const last = prev[prev.length - 1];
              if (last?.type === 'agent') return prev; // already shown via text streaming
              return [...prev, { id: nextId, type: 'agent' as const, content: text, timestamp: now }].slice(-MAX_MESSAGES);
            });

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch — fixed in b02cb86. Hoisted nextId and now out of the updater so the setMessages callback is a pure function. Thanks!

@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.

🧹 Nitpick comments (1)
src/dashboard/frontend/src/App.tsx (1)

379-406: Consider de-duplicating the event dispatch via a small map/switch.

The if/else-if ladder is now 8 branches deep and mixes "return early" vs. "fall through to generic push" control flow, which is easy to break when adding new event types. A lookup table or switch would make the control flow more explicit and keep early-return handlers (done, task.end, task.start, turn.*, text*) visually separate from fallthrough ones (job_failed, job_update, tool_call, etc.).

Non-blocking — current logic is correct for the documented event set.

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

In `@src/dashboard/frontend/src/App.tsx` around lines 379 - 406, Refactor the deep
if/else-if ladder that inspects data.type in App.tsx into a clearer dispatch:
replace the current chain (handling data.type values like 'done', 'task.end',
'job_failed', 'task.start', 'turn.start'/'turn.end', 'job_update', etc.) with a
switch or a lookup map of handlers keyed by data.type; keep early-return
lifecycle handlers (the 'done' branch that uses setMessages, messageIdCounter
and setTaskRunning, the 'task.end' and 'task.start' and 'turn.start'/'turn.end'
branches) as explicit handlers that return immediately, and move
fall-through/visible-message cases (e.g. 'job_failed' which sets msgType/content
and calls setTaskRunning, plus 'job_update'/'tool_call' style cases) into
dedicated handler functions so control flow is explicit and easier to extend
while reusing setMessages, setTaskRunning, messageIdCounter and MAX_MESSAGES.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/dashboard/frontend/src/App.tsx`:
- Around line 379-406: Refactor the deep if/else-if ladder that inspects
data.type in App.tsx into a clearer dispatch: replace the current chain
(handling data.type values like 'done', 'task.end', 'job_failed', 'task.start',
'turn.start'/'turn.end', 'job_update', etc.) with a switch or a lookup map of
handlers keyed by data.type; keep early-return lifecycle handlers (the 'done'
branch that uses setMessages, messageIdCounter and setTaskRunning, the
'task.end' and 'task.start' and 'turn.start'/'turn.end' branches) as explicit
handlers that return immediately, and move fall-through/visible-message cases
(e.g. 'job_failed' which sets msgType/content and calls setTaskRunning, plus
'job_update'/'tool_call' style cases) into dedicated handler functions so
control flow is explicit and easier to extend while reusing setMessages,
setTaskRunning, messageIdCounter and MAX_MESSAGES.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 07f4a8f6-be8d-4cf7-9fd4-a38388ca27e4

📥 Commits

Reviewing files that changed from the base of the PR and between 825cd4a and 92f306b.

📒 Files selected for processing (1)
  • src/dashboard/frontend/src/App.tsx

@josephfung

Copy link
Copy Markdown
Author

Thanks @coderabbitai — all pre-merge checks passing, appreciated the walkthrough. Note the PR also includes a follow-up commit (b02cb86) addressing a React purity issue flagged by Gemini: ++messageIdCounter and new Date() are now hoisted out of the setMessages updater.

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@josephfung — glad the checks are all green! The React purity fix in b02cb86 is exactly the right call. Updater functions passed to setMessages must be pure because React (especially in Strict Mode) can invoke them multiple times; having ++messageIdCounter inside the updater would silently skip IDs on those extra invocations, and new Date() would produce a different timestamp each time. Hoisting both out before the setMessages call eliminates those hazards cleanly. Nice catch by Gemini, and good follow-through. 🐇✨

@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.

🧹 Nitpick comments (1)
src/dashboard/frontend/src/App.tsx (1)

396-399: Nit: comment framing is slightly off vs. actual event ordering.

Per src/providers/claude-provider.ts (context snippet 3), task.end is yielded before done, so in the normal happy path task.end actually fires first and done is the redundant one. Behavior is still correct (both clear taskRunning idempotently), but consider rewording — e.g. "Lifecycle marker — also clears progress; done typically follows with the final text".

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

In `@src/dashboard/frontend/src/App.tsx` around lines 396 - 399, Update the
comment on the 'task.end' event handling to reflect actual event order: note
that 'task.end' is emitted before 'done' (so 'done' is usually redundant) and
that this branch idempotently clears progress via setTaskRunning(false); locate
the block handling data.type === 'task.end' in App.tsx and replace the comment
with something like "Lifecycle marker — also clears progress; 'done' typically
follows with the final text."
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/dashboard/frontend/src/App.tsx`:
- Around line 396-399: Update the comment on the 'task.end' event handling to
reflect actual event order: note that 'task.end' is emitted before 'done' (so
'done' is usually redundant) and that this branch idempotently clears progress
via setTaskRunning(false); locate the block handling data.type === 'task.end' in
App.tsx and replace the comment with something like "Lifecycle marker — also
clears progress; 'done' typically follows with the final text."

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5359164e-f542-433f-80d4-e730ad1d3e6c

📥 Commits

Reviewing files that changed from the base of the PR and between 92f306b and b02cb86.

📒 Files selected for processing (1)
  • src/dashboard/frontend/src/App.tsx

@josephfung

Copy link
Copy Markdown
Author

Hi there, I'm going to close this PR for now, as it hasn't received a response or review from the maintainers. I completely understand that open source projects can get busy, and there’s no pressure at all.

If there's ever interest in revisiting this, id be happy to reopen or resubmit.

@josephfung josephfung closed this Jun 27, 2026
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.

Dashboard chat submits to /api/steer instead of /api/task, and SSE handler doesn't clear "Working..." state

1 participant