Implement a complete watchdog timeout system that ensures hung child
processes are terminated cleanly with proper diagnostics and cleanup.
Features:
- PTY first-output timeout (default 90s): detects if child produces no PTY output
- Stream-json first-output timeout (default 90s): detects if child produces no stream-json events
- Overall session timeout (default 3600s): prevents indefinite hangs
- Stop hook watchdog timeout (default 120s): detects if Stop hook doesn't fire after prompt injection
Timeout handling:
- Sends SIGTERM to child process when timeout fires
- kill_child() ensures SIGTERM → SIGKILL sequence (2s grace period)
- Writes clear diagnostic to stderr indicating timeout type
- Emits stream-json error event for downstream consumers
- CleanupGuard ensures temp dir/FIFO cleanup on all exit paths
- Returns Error::Timeout and exits non-zero (code 3) for retry loop
Fixes:
- Pass temp_dir_path to Watchdog so stream-json monitoring works correctly
- Remove unused constants (duplicates of watchdog module defaults)
- Improve mock-claude binary path resolution for workspace builds
This prevents the indefinite hang that occurs when Claude Code wedges
during session initialization or tool use, ensuring marathon loops and
NEEDLE can retry cleanly instead of blocking forever.
Bead-Id: bf-2f5
Update all call sites to include the new first_output_timeout_secs parameter:
- src/main.rs: pass None for default first-output timeout
- tests/watchdog.rs: pass None in both watchdog tests
The prior commit added the 5th parameter but missed updating the callers,
causing compilation errors.
Co-Authored-By: Claude <noreply@anthropic.com>
Bead-Id: bf-2w7
Adds emitter.rs with three output format handlers and stream-json reader thread,
ClaudePrintError enum with exit codes and JSON subtypes to error.rs,
and 13 unit tests in tests/emitter.rs covering all plan requirements.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements src/transcript.rs: lenient JSONL parsing, message.id dedup
with usage-fingerprint fallback, text extraction from ContentBlock arrays,
40×50ms retry loop for Stop-before-JSONL races (PO-5), and last_assistant_message
fallback. All 18 tests in tests/transcript.rs pass; AS-6 verified with
MOCK_DELAY_JSONL=100.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add src/poller.rs with FIFO O_NONBLOCK open (read-end + keeper write-end),
Stop hook JSON payload parsing, transcript path derivation via cwd slug,
and StopInfo resolution. Wire poller into EventLoop via add_fifo_fd() which
was already present in event_loop.rs from Phase 3.
Update mock-claude to emit proper JSON Stop payloads (with and without
transcript_path via MOCK_OMIT_TRANSCRIPT_PATH=1) and update the pty_integration
assertion to match.
Tests test_stop_hook_fires and test_missing_transcript_path_derived both pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements StartupSeq with scan_line() that detects 2+ trust keywords
("trust", "Allow", "continue", "folder", "permission", "proceed") on a
PTY output line and returns CR to dismiss the dialog. Includes idle
fallback (0.8 s after 200+ bytes) and hard timeout (45 s / <200 bytes →
HardTimeout). Phase 2 injects the prompt via bracketed paste after a
2 s post-dismiss idle.
11 test_trust_dialog_* integration tests cover keyword match, threshold,
case sensitivity, chunk-boundary assembly, one-shot dismiss, and CR- vs
LF-terminated lines. 12 unit tests in startup::tests cover scan_line
and feed() in isolation.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>