Commit graph

3 commits

Author SHA1 Message Date
jedarden
54834e5070 feat(bf-2f5): add comprehensive watchdog timeout mechanism
- Add Watchdog module with 4 timeout types:
  * PTY first-output timeout (90s default)
  * Stream-json first-output timeout (90s default)
  * Overall session timeout (3600s default)
  * Stop hook watchdog timeout (120s default)
- Timeout thread monitors child and sends SIGTERM on deadline
- Main thread detects timeout, kills child (SIGTERM→SIGKILL), exits non-zero (code 3)
- Clear diagnostics to stderr with specific timeout descriptions
- CleanupGuard ensures temp dir/FIFO removal on all exit paths
- Add CLI flags: --timeout, --first-output-timeout, --stream-json-timeout, --stop-hook-timeout
- Integration tests verify timeout fires and cleanup succeeds

This prevents indefinite hangs regardless of why child wedges.

Bead-Id: bf-2f5
2026-06-25 06:59:23 -04:00
jedarden
d344e9553c fix: five PTY→FIFO pipeline bugs that prevented end-to-end operation
Bug 1 (event_loop.rs): poll(-1) blocked forever when TUI went silent;
startup timer never fired so prompt was never injected. Fixed: poll(50ms)
+ empty-slice tick so poll_timers() runs on every iteration.

Bug 2 (session.rs): read_fd from open_fifo_nonblock() was dropped
immediately after as_raw_fd(), closing the fd the event loop was polling.
Fixed: store both (_fifo_read, _fifo_keeper) to keep both alive.

Bug 3 (pty.rs): child inherited CLAUDE_CODE_SESSION_ID from parent, so
it wrote events into the parent transcript and skipped Stop hook dispatch.
Fixed: unsetenv(CLAUDE_CODE_SESSION_ID) in child after fork; preserve
CLAUDECODE=1 and CLAUDE_CODE_ENTRYPOINT=cli.

Bug 4 (session.rs): empty-slice timer ticks were fed to startup.feed()
which reset last_output_at, preventing idle timer from ever firing.
Fixed: guard startup.feed() and terminal.feed() from empty slices.

Bug 5 (session.rs): handle.join() blocked main thread for up to
cli.timeout (default 3600s) on any early exit, because the timeout thread
sleeps for the full duration. Also, waitpid blocked forever if child
ignored SIGTERM. Fixed: drop(timeout_thread) to detach; add kill_child()
helper (SIGTERM → 2s wait → SIGKILL) used on all cleanup paths.

All five confirmed fixed: claude-print "what is 2+2?" → "4", exit 0,
cc_entrypoint=cli in session JSONL (subscription billing verified).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 23:32:50 -04:00
jedarden
6f67cd5b00 Phase 1: crate scaffold with CLI, config, and error types
Adds Cargo.toml with pinned deps (clap, anyhow, serde, thiserror,
toml), src/{main,lib,cli,config,error}.rs. --version prints
"claude-print <ver> (wrapping claude <ver>)". All lib tests pass.
Builds clean for x86_64-unknown-linux-musl.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 16:00:19 -04:00