bug(cursor-cli-adapter): silently marks task "done" when docker exec fails before stream-json starts #1036
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks#1036
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
As a dispatch operator,
I want the cursor-cli-adapter to surface in-container exec failures as a
cursor_cli_errorsystem event,so that the runner flags the task instead of marking it "done" with empty output.
Context
PR #1035 fixed a Dockerfile bug where
cursor-agentcouldn't run inside the container (/usr/local/bin/node: No such file or directory). The diagnosis was made harder than it should have been because every dispatch silently loggeddone — task completedwhile the in-container CLI was actually exiting before any output. No SSE error, no failure marker on the task row, no operator-visible signal — just a string of empty completions.Repro
CLAUDE_HOOKS_CONTAINER_BINto a non-existent path inside the container (or removecursor-agentfrom the image).provider: cursortask.cursor_cli_errorsystem event reaches the SSE bus; task is marked completed with empty result text.Root cause
apps/server/src/infrastructure/agent/cursor-cli-adapter.ts(aroundrunTask):cursor_cli_errorsystem event is only emitted whenproc.exitedis non-zero AND the abort signal hasn't fired AND the read loop has finished — but the docker-exec exec-fail path causesproc.exitedto resolve to non-zero (e.g. 126/127) with no stream-json events at all. The current branch: should fire here. The bug is that noresultevent is ever yielded before this point, sorunWithSessionResume(or whatever wraps the iterable) doesn't see a finalresultand treats the iterator-end as success, not failure.cursor_cli_erroris asystemevent, not aresultevent — the consumer's "did this run end ok?" check only looks at the result event, so the system error is logged but doesn't influence the task outcome.Acceptance criteria
Adapter
resultevent has been emitted yet, the adapter synthesises a finalresultevent withok: false,subtype: "exec_error", anderrors: [<stderr tail>](in addition to the existingcursor_cli_errorsystem event for log breadcrumb).resultevent was emitted (cursor crashed mid-stream without emitting one), same synthetic failure result — never let "no result" mean "success".Tests
resultevent when noresultevent preceded it. Mock the spawn boundary to emit onlysystem{init}then exit 127.resultevent from cursor passes through unchanged (no double-emit).Operator-visible
taskstable hassuccess: false+ the exec error string in its result column.Out of scope
cursor_cli_errorsystem event for parity with the claude adapter.References
apps/server/src/infrastructure/agent/cursor-cli-adapter.tsrunTaskexit branch🦵 @charles kicked the queue — re-running implement on @code-lead.