Skip to content

fix: resolve stale output race in session send --wait and session output#448

Open
Abeansits wants to merge 1 commit intoasheshgoplani:mainfrom
Abeansits:feature/stale-output-investigation
Open

fix: resolve stale output race in session send --wait and session output#448
Abeansits wants to merge 1 commit intoasheshgoplani:mainfrom
Abeansits:feature/stale-output-investigation

Conversation

@Abeansits
Copy link
Copy Markdown
Contributor

Summary

  • session output -q and session send --wait -q return the previous assistant response instead of the current one
  • Root cause: the CLI detects agent completion by watching the terminal UI (spinner stops, prompt reappears), but reads the response from the JSONL file on disk — which may not be flushed yet at the moment the prompt reappears
  • This was discovered in production use with conductor bridge sessions, where stale responses were being relayed to Telegram/Slack users

Fix

waitForFreshOutput() — after status-based completion detection, polls the JSONL until a response with a timestamp within 2 seconds of send time appears (handles clock skew and second-precision rounding). 5-second timeout with 250ms poll interval; gracefully falls back to the best available response on timeout.

Session ID refresh in handleSessionOutput — adds the same GetSessionIDFromTmux() refresh that handleSessionSend already has, closing a stale-ID gap where /clear or PostStartSync timeout could cause reads from an old JSONL file.

Test plan

  • 11 subtests across 3 test functions, all passing
  • Delayed JSONL flush simulation (goroutine writes after 200ms delay)
  • Timeout graceful fallback returns stale response rather than error
  • Already-fresh response returns immediately (no polling overhead)
  • Same-second timestamp accepted via 2s skew tolerance
  • Behind-clock timestamp (1s before sentAt) accepted via skew tolerance
  • Unparseable timestamp gracefully falls through to timeout
  • Stale session ID → refresh → successful read
  • Graceful empty response on unrecoverable state (no crash)
  • Full cmd/agent-deck test suite passes (38s)
  • Reviewed by Codex via /midflight — timestamp comparison, test timing, and edge cases addressed

🤖 Generated with Claude Code

The CLI detected agent completion via the terminal UI (spinner/prompt)
but read responses from the JSONL file, which may not have been flushed
yet — returning the previous response instead of the current one.

Two fixes:
- waitForFreshOutput: after status-based completion detection, poll the
  JSONL until a response with a timestamp within 2s of send time appears
  (5s timeout, 250ms poll, graceful fallback to stale on timeout).
- Refresh ClaudeSessionID from tmux env in handleSessionOutput before
  reading, matching the existing refresh in handleSessionSend.

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