Commit graph

115 commits

Author SHA1 Message Date
jedarden
7d40c937fb feat(bf-2f5): add comprehensive watchdog timeout mechanism
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
2026-06-25 07:42:17 -04:00
jedarden
18dea17a4f docs(bf-2w7): verify cleanup implementation is complete
- Confirm all cleanup mechanisms are in place and working
- All 90 tests pass
- Orphan sweeping on startup, Drop guard for normal paths, global cleanup for process::exit()
- All exit paths covered: normal, error, watchdog timeout, signal interruption

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-25 07:16:15 -04:00
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
93ced10afd fix(bf-2u1): prevent global settings inheritance to avoid startup hang
Root cause: Child claude hangs at startup when global settings containing
hooks (SessionStart, SessionEnd, etc.) are inherited despite creating a
temp settings.json with only a Stop hook.

When --settings=<temp_path> is passed without --setting-sources=, Claude Code
merges temp settings with global settings. Global hooks fire and may hang,
causing the child to never produce output and the first-output timeout to fire.

Fix: Always pass --setting-sources= to child claude (src/session.rs:127-129)
to prevent global settings inheritance. This ensures ONLY the temp settings.json
is loaded, preventing any global hooks from causing hangs.

Evidence: Documented in notes/bf-2u1-findings.md and notes/bf-2u1-investigation.md

Related beads:
- bf-2w7: temp dir and FIFO cleanup
- bf-3ag: session implementation
2026-06-25 06:00:56 -04:00
jedarden
25a52402e9 test(bf-3eq): add cargo test to CI workflow
Add test execution step to claude-print-ci WorkflowTemplate.
This ensures watchdog regression tests (silent child timeout)
run before creating GitHub releases.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-25 01:40:53 -04:00
jedarden
6d3841e67f fix(bf-2w7): fix Session::run call sites after signature change
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
2026-06-25 00:59:49 -04:00
jedarden
7ddbf68e54 fix(bf-2w7): ensure temp dir and FIFO cleanup on all exit paths
- Add cleanup_orphans() to HookInstaller: sweeps stale claude-print-* dirs on startup
- Add cleanup() method to HookInstaller: explicitly removes FIFO and temp dir artifacts
- Add CleanupGuard struct in session.rs: ensures cleanup via Drop on all exit paths
- Call cleanup_orphans() in HookInstaller::new() on each invocation

This prevents orphaned temp directories from accumulating after crashes,
timeouts, or signal interruptions.

Co-Authored-By: Claude <noreply@anthropic.com>
Bead-Id: bf-2w7
2026-06-25 00:25:17 -04:00
jedarden
0165600806 docs: improve README for clarity and discoverability
Update billing description to present tense (Agent SDK split is now
active, not upcoming); tighten the tagline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 07:07:47 -04:00
jedarden
b76fa15479 feat: forward --dangerously-skip-permissions, --allowedTools, --disallowedTools to child; bump v0.2.0
These flags were parsed by the CLI but never added to claude_args,
so headless callers (e.g. marathon-coding) would hang on interactive
tool-use approval prompts. Also forward allowedTools/disallowedTools
for completeness.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 18:11:53 -04:00
jedarden
ef79f73ce3 docs(bf-4aw): verify main.rs execution path implementation
- Prompt resolution: --input-file, positional, stdin (exit 4 on errors)
- Session dispatch: session::run() with claude_args
- Result matching: Interrupted(130), Timeout(3), NoResponse(2), other(2)
- Stream-JSON output: replay transcript line-by-line
- AS-5: binary not found check with human-readable error (exit 2)

All 81 tests pass. No dead_code warnings for output format arms.
2026-06-14 01:01:38 -04:00
jedarden
53b582ef12 fix(deps): add missing atty and which to Cargo.toml
These crates were used in main.rs but absent from Cargo.toml, causing
CI build failures (exit 101) in fresh environments that lack the
local shared target/ dir where transitive deps were available.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 00:56:43 -04:00
jedarden
89b4b00b5e fix(install): use x86_64-linux asset names instead of musl target suffix
CI now ships glibc-linked binaries (cargo build --release on debian:bookworm,
same as forge-ci/needle-ci). Asset names: claude-print-x86_64-linux and
mock_claude-x86_64-linux. musl cross-compilation dropped — exceeded 3600s
workflow deadline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 00:10:05 -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
cf80cb2457 docs: expand README with usage examples, build-from-source, and limitations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 23:32:50 -04:00
jedarden
04f8ccb28f docs: update AGENTS.md — add session.rs to module map, document FIFO invariants and timeout-thread detach
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 23:32:50 -04:00
jedarden
d942572870 feat(main): wire prompt resolution, session dispatch, and emit
Replace the 'not yet implemented' stub with full execution path:

- Prompt resolution (precedence: --input-file, positional, stdin)
- Build claude_args to forward flags to child process
- Call session::run() and match results
- Emit success/error outputs per format (text/json/stream-json)
- Handle AS-5 (binary not found) with human-readable error
- Exit codes: 0=success, 2=setup/child errors, 3=timeout, 4=input errors, 130=interrupted

Completes bead bf-4aw.

Co-Authored-By: Claude <noreply@anthropic.com>
Bead-Id: bf-4aw
2026-06-13 23:32:50 -04:00
jedarden
5110f0bf57 docs(bf-3ag): verify session implementation complete - all tests pass 2026-06-13 15:56:07 -04:00
jedarden
bbe18cbfb1 docs(bf-3ag): verify session implementation complete - all tests pass 2026-06-13 14:52:20 -04:00
jedarden
4db1b7fb15 docs(bf-3ag): document completed Session struct and version resolution implementation 2026-06-13 14:20:58 -04:00
jedarden
508ba576d9 test(session): fix version resolution test and add struct validation test
- Fix test_version_resolution_with_mock_binary to properly mock claude --version
- Add test_session_result_struct_has_required_fields to verify SessionResult struct
- Remove unused std::env import
- Ensure all TranscriptResult fields are properly initialized

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-13 14:20:58 -04:00
jedarden
d3b7281ed5 feat(session): implement Session struct and version resolution
- Add SessionResult struct with transcript, claude_version, duration_ms
- Implement Session::run() with full signature and PTY session orchestration
- Add resolve_claude_version() using Command::new(claude_bin).arg("--version").output()
- Add pub mod session to lib.rs
- Include unit tests for version resolution with mock binaries

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-13 13:41:32 -04:00
jedarden
b852efc9d8 docs(bf-2pw): verify emitter implementation is complete
All 13 emitter tests pass. Implementation completed in commit bfb50da.
Verified output formatting for text, json, and stream-json formats.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-11 09:33:17 -04:00
jedarden
96524de012 docs(bf-gqf): verify PTY spawn and signal forwarding implementation is complete 2026-06-11 09:16:00 -04:00
jedarden
ee59f07c99 docs(bf-1en): verify transcript.rs implementation is complete
All tests pass:
- cargo test --lib transcript: 3 passed
- cargo test --test transcript: 18 passed

Implementation was already complete in commit c6241e3.
No code changes needed - verification only.
2026-06-11 09:13:42 -04:00
jedarden
6b29283141 feat(error): implement comprehensive Error enum and Result alias
Complete implementation of error.rs with:
- Full Error enum covering all error types (PTY spawn failures, hook
  setup, parse failures, timeout, interruption, binary resolution,
  version checks, terminal errors, and child process errors)
- Proper error propagation with user-friendly messages
- ClaudePrintError for user-facing errors with exit codes and JSON
  subtypes
- 18 unit tests covering all error variants and conversions

Fixes bead bf-46v
2026-06-11 09:11:49 -04:00
jedarden
949c741a47 feat(pty): implement SIGINT forwarding to child process
- Add SIGINT_RECEIVED static flag and sigint_handler signal handler
- Install SIGINT handler in relay() to catch Ctrl-C presses
- Forward SIGINT to child process in relay loop using kill()
- Restore default SIGINT handler on exit
- Update error types to use specific PTY errors (OpenptyFailed, ForkFailed, SignalHandlerFailed, WaitpidFailed)

This implements key invariant #3 from AGENTS.md: pressing Ctrl-C must reach
the claude child process, not just terminate claude-print.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-11 08:49:51 -04:00
jedarden
ef1ae96925 docs(bf-1en): verify transcript.rs implementation is complete
- Verified src/transcript.rs was implemented in commit c6241e3
- All 18 integration tests pass
- Module provides full JSONL parsing with:
  - Token counting (input, output, cache)
  - Streaming deduplication
  - Retry loop with fallback
  - Session tracking
- Implementation complete and verified
2026-06-11 08:44:11 -04:00
jedarden
868380e9ad chore: sync beads database after weave strand generation 2026-06-11 08:36:33 -04:00
jedarden
af5c24718d docs(bf-2pw): record emitter verification - already complete in bfb50da 2026-06-11 08:36:33 -04:00
jedarden
d751a8baea feat(config): implement config file loading with model resolution
Add comprehensive config loading for ~/.claude/claude-print.toml:
- default_path() returns the config file path
- load_or_default() gracefully handles missing/invalid files
- resolve_model() prioritizes CLI flag > config > hardcoded default
- Add 11 tests covering all scenarios

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-11 08:29:05 -04:00
jedarden
49cf72bf57 docs(adr): add ADR-004 — no silent agent escalation in NEEDLE workers
Documents the root cause of the bf-40i loss (claude-sonnet PTY fallback
in resolve_adapter), the consequences, and the mitigations (atomic label,
NEEDLE fixes bf-14w/bf-2wi).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 07:31:56 -04:00
jedarden
2cdbb97cab docs(agents): add src/lib.rs to module map in AGENTS.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 20:47:17 -04:00
jedarden
210b234a92 docs(agents): add AGENTS.md for Phase 9
Documents build commands, test structure, module map, key invariants,
and bead workflow for AI coding agents working in this repo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 20:46:02 -04:00
jedarden
b8d7037de7 docs(notes): record bf-1vd as already-complete (plan.md updated in 4b2161c)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 20:43:06 -04:00
jedarden
16bf418396 bf-4eb: Remove overly-conservative dependency blocking bf-4r6
bf-4r6 (Write AGENTS.md) was blocked on bf-40i (Wire main()), but
documentation writing does not require main() to be functional.
Removing this dependency makes bf-4r6 ready for a worker to claim.

bf-52c's dependency on bf-40i remains correct — binary E2E tests
genuinely require a working binary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 20:43:00 -04:00
jedarden
4b2161c2ad docs(plan): mark phases 1-11 complete, add Status section
All module-level deliverables for phases 1-11 are committed. Checked all
[ ] boxes to [x], added a Status section summarizing what is done vs.
pending (main() orchestration, E2E tests, billing verification, CI release),
and noted that the Phase 11 install.sh end-to-end download test is blocked
on a release binary (which requires main() completion first).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 20:42:07 -04:00
jedarden
4d9a970176 Add bf-4km sixth verification: claude-print-ci WorkflowTemplate confirmed Synced in ArgoCD
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 02:31:08 -04:00
jedarden
d0fe872ff4 Add bf-4km fifth verification: claude-print-ci WorkflowTemplate confirmed Synced in ArgoCD
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 02:29:16 -04:00
jedarden
5da2055ae8 Add bf-4km fourth verification: claude-print-ci WorkflowTemplate confirmed Synced in ArgoCD
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 02:27:14 -04:00
jedarden
60649e2d8b Add bf-4km re-verification notes (third): claude-print-ci WorkflowTemplate confirmed Synced in ArgoCD
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 02:20:54 -04:00
jedarden
d0e9220720 Add bf-4km re-verification notes: claude-print-ci WorkflowTemplate confirmed Synced in ArgoCD
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 02:18:46 -04:00
jedarden
f14b2c1c11 Add bf-4km notes: claude-print-ci WorkflowTemplate synced in ArgoCD
Verified claude-print-ci WorkflowTemplate is present in iad-ci and
shows Synced status in ArgoCD app argo-workflows-ns-iad-ci. Overall
app is OutOfSync/Degraded due to pre-existing unrelated pdftract and
ExternalSecret issues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 02:16:49 -04:00
jedarden
0c87ec4fdf Add bf-4km notes: claude-print-ci WorkflowTemplate synced in ArgoCD
WorkflowTemplate/claude-print-ci shows Sync: Synced in ArgoCD app
argo-workflows-ns-iad-ci, and is present in iad-ci cluster (created
2026-06-10T06:09:14Z). Overall app is OutOfSync due to pre-existing
unrelated pdftract/ExternalSecret issues outside this bead's scope.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 02:14:20 -04:00
jedarden
e50cfba234 Close bead bf-5nr: claude-print-ci WorkflowTemplate YAML validated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 02:12:56 -04:00
jedarden
7176ef2939 Add bf-5nr validation notes: claude-print-ci WorkflowTemplate YAML is valid
YAML parses cleanly and kubectl dry-run returns no errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 02:11:37 -04:00
jedarden
e16680820e Add bf-168 notes: claude-print-ci WorkflowTemplate added to declarative-config
WorkflowTemplate claude-print-ci committed to jedarden/declarative-config at
k8s/iad-ci/argo-workflows/claude-print-ci-workflowtemplate.yml. Phase 9 scope:
verify step only, delegating to rust-verify. Build+release steps deferred to Phase 11.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 01:53:23 -04:00
jedarden
f81e54d894 Close bead bf-3n3: claude-print.yaml NEEDLE adapter complete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 01:52:15 -04:00
jedarden
8ef28b339b Add bf-3n3 notes: claude-print.yaml NEEDLE adapter installed and validated
Copied claude-print.yaml to ~/.config/needle/adapters/ and confirmed
needle test-agent returns Status: READY with stdin input and
needle-transform-claude transform.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 01:50:08 -04:00
jedarden
f5d6a0c381 Close bead bf-3vj: install.sh verified complete
install.sh already existed (Phase 9, commit 50b2132) and passes all
acceptance criteria: bash -n passes, detects platform, installs with
-m 755 to ~/.local/bin, runs --check post-install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 01:47:51 -04:00
jedarden
86c51495a9 Add bf-3vj verification notes: install.sh already complete
install.sh was created in Phase 9 (commit 50b2132). Verified it passes
bash -n, covers x86_64/aarch64 musl targets, installs with -m 755 to
~/.local/bin, and runs claude-print --check post-install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 01:46:35 -04:00