chore(bf-3wya): close bead after verification

All acceptance criteria met:
- Identified exact point where bracketed-paste write completes (PromptInjected phase)
- Captured file size using std::fs::metadata().map(|m| m.len()).unwrap_or(0)
- Stored offset in start_offset variable for reader thread
- Handles missing transcript case with .unwrap_or(0)

Implementation verified at src/session.rs:363-375
This commit is contained in:
jedarden 2026-07-02 09:51:59 -04:00
parent 50f3fdd982
commit 4d09ce8a9a

View file

@ -1,7 +1,7 @@
{"id":"bf-1en","title":"Implement src/transcript.rs: JSONL transcript parsing","description":"AGENTS.md documents transcript.rs as reading .jsonl transcript files and extracting the last assistant message plus token usage. Implement transcript locating, JSONL parsing, message extraction, and token counting.\n\n---\nPriority: P1\nCreated by: weave strand","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-06-11T12:23:37.728293462Z","updated_at":"2026-06-11T13:14:48.106571747Z","closed_at":"2026-06-11T13:14:48.106571747Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["weave-generated"]}
{"id":"bf-1fq","title":"Add timeout handling and cleanup","description":"In src/session.rs run():\n- Wrap event loop with timeout_secs handling (thread or SIGALRM)\n- On timeout expiry: SIGTERM child, Err(Error::Timeout)\n- PtySpawner::waitpid() after loop\n- HookInstaller drops automatically\n\nAcceptance:\n- Timeout implemented (thread or SIGALRM approach)\n- Child process properly waited for\n- Resources cleaned up\n- Unit test test_session_timeout verifies timeout behavior\n- Full test test_session_runs_mock_claude passes\n- cargo test passes","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-06-13T17:34:42.664294130Z","updated_at":"2026-06-14T02:05:40.219114899Z","closed_at":"2026-06-14T02:05:40.219114899Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-1fq","depends_on_id":"bf-1yt","type":"blocks","created_at":"2026-06-13T17:35:09.488849455Z","created_by":"cli","thread_id":""}]}
{"id":"bf-1n6","title":"Add scripts/check-billing.sh (AS-4 billing conformance script referenced by plan)","description":"Plan sections 'Conformance Harness' and 'Operations / Monitoring and Alerting' (docs/plan/plan.md) both reference scripts/check-billing.sh: it inspects the most recent transcript JSONL under the user's ~/.claude/projects/ tree and asserts the session has entrypoint 'cli' (subscription pool), not 'sdk-cli'. It is the AS-4 pre-release gate.\n\nACTUAL: scripts/ contains test_exact_claude_print_scenario.sh, test_sessionstart_hook.sh, test_startup_wedge.sh, verify_fix.sh, verify-startup-wedge-fix.sh — check-billing.sh does not exist anywhere in the repo.\n\nWORK: implement scripts/check-billing.sh (POSIX sh or bash): find the newest *.jsonl under $HOME/.claude/projects/, scan for the entrypoint field, print it, exit 0 iff it equals cli, exit 1 otherwise (with a clear message naming the file inspected). Handle the no-transcripts case with a distinct error. Reference it from README troubleshooting or release checklist as appropriate.\n\nACCEPTANCE: bash -n passes; running it right after any claude-print invocation on a machine with credentials prints 'entrypoint: cli' and exits 0; running when latest transcript is sdk-cli exits 1. Script is executable (mode 755) and committed.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":3,"issue_type":"task","created_at":"2026-07-02T11:28:08.800269939Z","updated_at":"2026-07-02T13:20:37.493262849Z","source_repo":".","compaction_level":0}
{"id":"bf-1v8","title":"Fix README exit-code table (wrong: 3/4 vs actual 124/130) and sync flags table + docs/notes stubs","description":"Plan 'Documentation / README.md' section (docs/plan/plan.md) requires the README flags table to match claude-print --help output and the exit-code table to be: 0 success, 1 assistant error, 2 internal error, 124 timeout, 130 interrupted.\n\nACTUAL BUGS in README.md:\n1. Exit-code table is factually WRONG: it lists 3 = 'Timeout exceeded' and 4 = 'Bad input'. The implementation (src/error.rs exit_code(): Setup=2, Timeout=124, Interrupted=130, AssistantError=1) and the plan both use 124 and 130; codes 3 and 4 exist nowhere in the code. Any caller scripting off the README will mishandle timeouts.\n2. Flags table is missing three real flags present in src/cli.rs: --first-output-timeout (default 90), --stream-json-timeout (default 90), --stop-hook-timeout (default 120). Defaults in README must match src/cli.rs.\n3. Plan 'Docs Organization' says docs/notes/ should contain hook-design.md (relay hook mechanics, FIFO protocol, keeper fd pattern) and terminal-probes.md (Ink probe table + response bytes) — only billing-context.md exists. Write the two missing notes; content can be distilled from plan sections 'Hook Installer', 'FIFO Poller States', and 'Components #5 Terminal Emulator'.\n\nACCEPTANCE: README exit codes exactly match src/error.rs mapping; README flags table matches --help output field-for-field (verify by reading src/cli.rs, do not guess); docs/notes/hook-design.md and docs/notes/terminal-probes.md exist and are accurate to the current implementation (src/hook.rs, src/poller.rs, src/terminal.rs).","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"bug","created_at":"2026-07-02T11:28:42.048190215Z","updated_at":"2026-07-02T13:20:37.493262849Z","source_repo":".","compaction_level":0}
{"id":"bf-1n6","title":"Add scripts/check-billing.sh (AS-4 billing conformance script referenced by plan)","description":"Plan sections 'Conformance Harness' and 'Operations / Monitoring and Alerting' (docs/plan/plan.md) both reference scripts/check-billing.sh: it inspects the most recent transcript JSONL under the user's ~/.claude/projects/ tree and asserts the session has entrypoint 'cli' (subscription pool), not 'sdk-cli'. It is the AS-4 pre-release gate.\n\nACTUAL: scripts/ contains test_exact_claude_print_scenario.sh, test_sessionstart_hook.sh, test_startup_wedge.sh, verify_fix.sh, verify-startup-wedge-fix.sh — check-billing.sh does not exist anywhere in the repo.\n\nWORK: implement scripts/check-billing.sh (POSIX sh or bash): find the newest *.jsonl under $HOME/.claude/projects/, scan for the entrypoint field, print it, exit 0 iff it equals cli, exit 1 otherwise (with a clear message naming the file inspected). Handle the no-transcripts case with a distinct error. Reference it from README troubleshooting or release checklist as appropriate.\n\nACCEPTANCE: bash -n passes; running it right after any claude-print invocation on a machine with credentials prints 'entrypoint: cli' and exits 0; running when latest transcript is sdk-cli exits 1. Script is executable (mode 755) and committed.","design":"","acceptance_criteria":"","notes":"","status":"in_progress","priority":3,"issue_type":"task","assignee":"echo-test-test-worker","created_at":"2026-07-02T11:28:08.800269939Z","updated_at":"2026-07-02T13:50:44.367952862Z","source_repo":".","compaction_level":0}
{"id":"bf-1v8","title":"Fix README exit-code table (wrong: 3/4 vs actual 124/130) and sync flags table + docs/notes stubs","description":"Plan 'Documentation / README.md' section (docs/plan/plan.md) requires the README flags table to match claude-print --help output and the exit-code table to be: 0 success, 1 assistant error, 2 internal error, 124 timeout, 130 interrupted.\n\nACTUAL BUGS in README.md:\n1. Exit-code table is factually WRONG: it lists 3 = 'Timeout exceeded' and 4 = 'Bad input'. The implementation (src/error.rs exit_code(): Setup=2, Timeout=124, Interrupted=130, AssistantError=1) and the plan both use 124 and 130; codes 3 and 4 exist nowhere in the code. Any caller scripting off the README will mishandle timeouts.\n2. Flags table is missing three real flags present in src/cli.rs: --first-output-timeout (default 90), --stream-json-timeout (default 90), --stop-hook-timeout (default 120). Defaults in README must match src/cli.rs.\n3. Plan 'Docs Organization' says docs/notes/ should contain hook-design.md (relay hook mechanics, FIFO protocol, keeper fd pattern) and terminal-probes.md (Ink probe table + response bytes) — only billing-context.md exists. Write the two missing notes; content can be distilled from plan sections 'Hook Installer', 'FIFO Poller States', and 'Components #5 Terminal Emulator'.\n\nACCEPTANCE: README exit codes exactly match src/error.rs mapping; README flags table matches --help output field-for-field (verify by reading src/cli.rs, do not guess); docs/notes/hook-design.md and docs/notes/terminal-probes.md exist and are accurate to the current implementation (src/hook.rs, src/poller.rs, src/terminal.rs).","design":"","acceptance_criteria":"","notes":"","status":"in_progress","priority":2,"issue_type":"bug","assignee":"echo-test-test-worker","created_at":"2026-07-02T11:28:42.048190215Z","updated_at":"2026-07-02T13:50:43.895778729Z","source_repo":".","compaction_level":0}
{"id":"bf-1yt","title":"Implement main event loop and ExitReason handling","description":"In src/session.rs run():\n- Loop via event_loop.run(|chunk| { ... })\n- Feed chunk to TerminalEmu; write probe responses to master_fd\n- Feed chunk to StartupSeq.feed(); handle Write(bytes) and HardTimeout\n- Match ExitReason:\n - FifoPayload(bytes): parse_stop_payload → resolve_stop_info → read_transcript()\n - ChildExited: Err(Error::NoResponse)\n - Interrupted: SIGTERM child, Err(Error::Interrupted)\n\nAcceptance:\n- Event loop feeds chunks to TerminalEmu and StartupSeq\n- ExitReason handling covers all three cases\n- Unit test mocks each exit path\n- cargo test passes","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-06-13T17:34:38.809442755Z","updated_at":"2026-06-14T02:05:40.219062100Z","closed_at":"2026-06-14T02:05:40.219062100Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-1yt","depends_on_id":"bf-44e","type":"blocks","created_at":"2026-06-13T17:35:09.483856323Z","created_by":"cli","thread_id":""}]}
{"id":"bf-2f5","title":"Add watchdog: no-output + max-turn timeout that kills child and exits non-zero (never poll stop.fifo forever)","description":"Core hardening, independent of root cause. claude-print currently polls stop.fifo with no timeout. Add: (1) startup/first-output timeout — if the child emits no stream-json within N seconds (configurable, e.g. 90s), declare hung; (2) overall max-turn timeout; (3) on timeout: SIGTERM then SIGKILL the child and its descendants, write a clear diagnostic to stderr (and a stream-json error event), tear down temp resources, and exit NON-ZERO so the caller (marathon loop / NEEDLE) retries cleanly instead of hanging. This alone prevents the indefinite hang regardless of why the child wedged.","design":"","acceptance_criteria":"","notes":"","status":"completed","priority":0,"issue_type":"task","assignee":"claude-code-glm47-lima","created_at":"2026-06-21T14:14:56.281531209Z","updated_at":"2026-06-25T19:35:48.632949465Z","source_repo":".","compaction_level":0,"labels":["deferred","hardening"]}
{"id":"bf-2mm","title":"Add src/session.rs: Session::run() orchestration","description":"Create src/session.rs with a Session::run() function that orchestrates the full PTY pipeline. main.rs calls this; it owns nothing about CLI parsing or output formatting.\n\n## What to implement\n\npub struct SessionResult {\n pub transcript: TranscriptResult, // from transcript.rs\n pub claude_version: String,\n pub duration_ms: u64,\n}\n\npub fn run(\n claude_bin: &Path,\n claude_args: &[OsString], // flags to forward: --model, --max-turns, --settings <path>, optionally --setting-sources=\n prompt: Vec<u8>,\n timeout_secs: Option<u64>,\n) -> Result<SessionResult>\n\nInternally:\n1. HookInstaller::new() — creates temp dir, hook.sh, stop.fifo\n2. Resolve claude version: Command::new(claude_bin).arg(\"--version\").output()\n3. Build child argv: [claude_bin, --dangerously-skip-permissions, --settings <hook.settings_path>, ...claude_args]\n4. Self-pipe for SIGINT: pipe2(O_CLOEXEC); install signal handler writing one byte to write-end\n5. PtySpawner::spawn(cmd, args)\n6. EventLoop::new(pty.master_fd, self_pipe_read)\n7. TerminalEmu::new() — check terminal.rs for its API\n8. StartupSeq::new(prompt) (or with_idle_gap if DEFAULT_POST_DISMISS_IDLE_MS needed)\n9. Loop via event_loop.run(|chunk| { ... }):\n - Feed chunk to TerminalEmu; write any probe responses to master_fd\n - Feed chunk to StartupSeq.feed(); on Write(bytes) write to master_fd; on HardTimeout return Err\n - Call StartupSeq.poll_timers() each iteration; handle Write/HardTimeout\n - When StartupSeq.phase() == PromptInjected and FIFO not yet added: open_fifo_nonblock() and event_loop.add_fifo_fd()\n10. Match ExitReason from event_loop.run():\n - FifoPayload(bytes): parse_stop_payload → resolve_stop_info → read_transcript()\n - ChildExited: Err(Error::NoResponse)\n - Interrupted: send SIGTERM to child, return Err(Error::Interrupted)\n11. Handle timeout_secs: wrap the event loop in a thread or use SIGALRM; on expiry SIGTERM child and return Err(Error::Timeout)\n12. PtySpawner::waitpid(); HookInstaller drops automatically\n\n## Complete when\n- src/session.rs compiles as part of the workspace (add pub mod session to lib.rs)\n- Unit test test_session_runs_mock_claude: sets CLAUDE_BINARY=path/to/mock_claude, calls session::run() with a test prompt, asserts SessionResult.transcript.text is non-empty\n- cargo test passes","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":0,"issue_type":"task","created_at":"2026-06-11T11:28:29.360032072Z","updated_at":"2026-06-14T02:05:45.146078060Z","closed_at":"2026-06-14T02:05:45.146078060Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["atomic","deferred","umbrella"],"dependencies":[{"issue_id":"bf-2mm","depends_on_id":"bf-1fq","type":"blocks","created_at":"2026-06-13T17:35:12.598168667Z","created_by":"cli","thread_id":""}]}
@ -12,22 +12,22 @@
{"id":"bf-3ag","title":"Create Session struct and version resolution","description":"Create src/session.rs with:\n- SessionResult struct with transcript, claude_version, duration_ms\n- run() function signature with all parameters\n- Version resolution: Command::new(claude_bin).arg(\"--version\").output()\n- Add pub mod session to lib.rs\n\nAcceptance:\n- session.rs compiles\n- Unit test test_version_resolution mocks claude --version output\n- cargo test passes","design":"","acceptance_criteria":"","notes":"","status":"completed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-06-13T17:34:26.004990180Z","updated_at":"2026-06-13T19:56:48.451326259Z","source_repo":".","compaction_level":0,"labels":["deferred","split-child"]}
{"id":"bf-3eq","title":"Regression test: a child that never outputs / never fires Stop must time out, not hang","description":"Add an integration test with a stub child that (a) produces no output and (b) never fires the Stop hook. Assert claude-print exits non-zero within the configured watchdog window, kills the stub, and leaves no orphaned temp dir/FIFO. Wire into the existing claude-print CI workflow.","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm47-lima","created_at":"2026-06-21T14:14:56.325053907Z","updated_at":"2026-06-25T23:26:01.550249721Z","closed_at":"2026-06-25T23:28:21Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["deferred","hardening"]}
{"id":"bf-3j4","title":"Implement src/config.rs: load ~/.claude/claude-print.toml","description":"AGENTS.md documents config.rs as loading model defaults from ~/.claude/claude-print.toml. Implement the config file loading with proper error handling, default values, and toml parsing. The --model CLI flag should override config defaults.\n\n---\nPriority: P1\nCreated by: weave strand","design":"","acceptance_criteria":"","notes":"","status":"completed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-06-11T12:23:37.660620030Z","updated_at":"2026-06-11T12:29:45.643595768Z","source_repo":".","compaction_level":0,"labels":["weave-generated"],"comments":[{"id":1,"issue_id":"bf-3j4","author":"cli","text":"Completed: Implemented config file loading with:\n- default_path() returns ~/.claude/claude-print.toml\n- load_or_default() handles missing/invalid files gracefully\n- resolve_model() prioritizes CLI flag > config > hardcoded default (claude-sonnet-4-6)\n- Added 11 comprehensive tests, all passing\n- Committed and pushed to main","created_at":"2026-06-11T12:30:07.023948718Z"}]}
{"id":"bf-3k2","title":"Repo hygiene: remove committed cruft (escaped ~/ dir, target artifact, db backup, scratch notes)","description":"The plan 'Module Layout' (docs/plan/plan.md) defines the repo contents; several tracked files fall outside it and are accidental commits from NEEDLE worker runs:\n\n1. A literal directory named '~' at repo root ('~/.needle/state/...': workers.json, heartbeats/claude-code-glm-4.7-alpha.json, claude-code-glm-4.7-alpha-idle-completed-1939771.txt) — an escaped-home-path bug; dangerous cruft (a careless 'rm -rf ~' cleanup attempt would be catastrophic — remove with git rm and quote the path literally).\n2. target/last-claude-version.txt — build artifact tracked in git; target/ should be untracked entirely (add to .gitignore if not present).\n3. .beads/beads.db.backup.20260625110230 — a binary sqlite backup committed to git; .beads/.gitignore should exclude db backups. Do NOT touch .beads/issues.jsonl or other live beads files.\n4. test-cleanup-verification.md at repo root — worker scratch note; move under notes/ or delete.\n5. notes/*.md (30+ per-bead scratch files) and .needle-predispatch-sha are worker-run artifacts at root; decide: keep notes/ (document it in AGENTS.md) or relocate/ignore. Prefer keeping history simple — no force-push, no history rewrite; plain git rm commits only.\n\nACCEPTANCE: no literal '~' path tracked; no target/ files tracked; no beads.db backups tracked; root contains only files named in the plan Module Layout plus AGENTS.md, build.rs, CI YAMLs, docs/, scripts/, notes/ (if deliberately kept and documented). All removals via git rm in a normal commit.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":3,"issue_type":"task","created_at":"2026-07-02T11:28:42.073445464Z","updated_at":"2026-07-02T13:20:37.493262849Z","source_repo":".","compaction_level":0}
{"id":"bf-3k2","title":"Repo hygiene: remove committed cruft (escaped ~/ dir, target artifact, db backup, scratch notes)","description":"The plan 'Module Layout' (docs/plan/plan.md) defines the repo contents; several tracked files fall outside it and are accidental commits from NEEDLE worker runs:\n\n1. A literal directory named '~' at repo root ('~/.needle/state/...': workers.json, heartbeats/claude-code-glm-4.7-alpha.json, claude-code-glm-4.7-alpha-idle-completed-1939771.txt) — an escaped-home-path bug; dangerous cruft (a careless 'rm -rf ~' cleanup attempt would be catastrophic — remove with git rm and quote the path literally).\n2. target/last-claude-version.txt — build artifact tracked in git; target/ should be untracked entirely (add to .gitignore if not present).\n3. .beads/beads.db.backup.20260625110230 — a binary sqlite backup committed to git; .beads/.gitignore should exclude db backups. Do NOT touch .beads/issues.jsonl or other live beads files.\n4. test-cleanup-verification.md at repo root — worker scratch note; move under notes/ or delete.\n5. notes/*.md (30+ per-bead scratch files) and .needle-predispatch-sha are worker-run artifacts at root; decide: keep notes/ (document it in AGENTS.md) or relocate/ignore. Prefer keeping history simple — no force-push, no history rewrite; plain git rm commits only.\n\nACCEPTANCE: no literal '~' path tracked; no target/ files tracked; no beads.db backups tracked; root contains only files named in the plan Module Layout plus AGENTS.md, build.rs, CI YAMLs, docs/, scripts/, notes/ (if deliberately kept and documented). All removals via git rm in a normal commit.","design":"","acceptance_criteria":"","notes":"","status":"in_progress","priority":3,"issue_type":"task","assignee":"echo-test-test-worker","created_at":"2026-07-02T11:28:42.073445464Z","updated_at":"2026-07-02T13:50:45.259519551Z","source_repo":".","compaction_level":0}
{"id":"bf-3p8h","title":"Verify spawn_stream_json_reader signature and retry logic","description":"Examine the existing spawn_stream_json_reader function in emitter.rs to understand its signature and verify the retry logic for when the transcript file doesn't exist yet. The function should accept a transcript path and start offset, and internally handle file-not-found with 50ms retries up to 5 seconds.\n\nACCEPTANCE:\n- Document the function signature of spawn_stream_json_reader\n- Verify the 50ms/5s retry logic is implemented in the reader loop\n- Confirm the function drains to mpsc channel correctly\n- No code changes required, just verification and documentation","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","assignee":"claude-code-glm47-delta","created_at":"2026-07-02T13:33:35.553714219Z","updated_at":"2026-07-02T13:39:37.422342676Z","closed_at":"2026-07-02T13:39:37.422342676Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"]}
{"id":"bf-3wya","title":"Capture transcript byte offset at bracketed-paste write","description":"Identify the exact location in the session flow where the bracketed-paste write occurs and capture the transcript file length at that moment. This byte offset will be used as the start_offset for the stream-json reader.\n\nThe transcript file is located at <temp_dir>/transcript.jsonl. We need to capture the file size immediately after the prompt is written but before Claude starts responding.\n\nACCEPTANCE:\n- Identify the exact point in session.rs where bracketed-paste write completes\n- Add code to capture std::fs::metadata(&transcript_path).map(|m| m.len()).unwrap_or(0)\n- Store this offset in a variable for use by the reader thread\n- Handle the case where transcript doesn't exist yet (return 0)","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-02T13:33:40.968807345Z","updated_at":"2026-07-02T13:33:40.968807345Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-3wya","depends_on_id":"bf-3p8h","type":"blocks","created_at":"2026-07-02T13:34:11.097519673Z","created_by":"cli","thread_id":""}]}
{"id":"bf-3wya","title":"Capture transcript byte offset at bracketed-paste write","description":"Identify the exact location in the session flow where the bracketed-paste write occurs and capture the transcript file length at that moment. This byte offset will be used as the start_offset for the stream-json reader.\n\nThe transcript file is located at <temp_dir>/transcript.jsonl. We need to capture the file size immediately after the prompt is written but before Claude starts responding.\n\nACCEPTANCE:\n- Identify the exact point in session.rs where bracketed-paste write completes\n- Add code to capture std::fs::metadata(&transcript_path).map(|m| m.len()).unwrap_or(0)\n- Store this offset in a variable for use by the reader thread\n- Handle the case where transcript doesn't exist yet (return 0)","design":"","acceptance_criteria":"","notes":"","status":"in_progress","priority":2,"issue_type":"task","assignee":"claude-code-glm47-delta","created_at":"2026-07-02T13:33:40.968807345Z","updated_at":"2026-07-02T13:50:03.573124308Z","source_repo":".","compaction_level":0,"labels":["deferred","failure-count:1","split-child"],"dependencies":[{"issue_id":"bf-3wya","depends_on_id":"bf-3p8h","type":"blocks","created_at":"2026-07-02T13:34:11.097519673Z","created_by":"cli","thread_id":""}]}
{"id":"bf-44e","title":"Setup EventLoop, TerminalEmu, and StartupSeq","description":"In src/session.rs run():\n- EventLoop::new(pty.master_fd, self_pipe_read)\n- TerminalEmu::new() - use terminal.rs API\n- StartupSeq::new(prompt) (or with_idle_gap if needed)\n- FIFO handling: when StartupSeq.phase() == PromptInjected, open_fifo_nonblock() and event_loop.add_fifo_fd()\n- Call StartupSeq.poll_timers() each iteration\n\nAcceptance:\n- EventLoop, TerminalEmu, StartupSeq all initialized\n- FIFO added at correct phase\n- Unit test verifies initialization order\n- cargo test passes","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-06-13T17:34:35.107621691Z","updated_at":"2026-06-14T02:05:40.219004012Z","closed_at":"2026-06-14T02:05:40.219004012Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-44e","depends_on_id":"bf-4ai","type":"blocks","created_at":"2026-06-13T17:35:09.478343903Z","created_by":"cli","thread_id":""}]}
{"id":"bf-46v","title":"Implement src/error.rs: Error enum and Result alias","description":"AGENTS.md documents error.rs as defining an Error enum and Result alias. Implement comprehensive error types covering PTY spawn failures, hook setup errors, parse failures, timeout, and interruption. Ensure proper error propagation and user-friendly error messages.\n\n---\nPriority: P1\nCreated by: weave strand","design":"","acceptance_criteria":"","notes":"","status":"completed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-06-11T12:23:37.705300992Z","updated_at":"2026-06-11T13:12:05.294894225Z","source_repo":".","compaction_level":0,"labels":["weave-generated"]}
{"id":"bf-46x","title":"Binary-level E2E tests: AS-1/AS-2/AS-5 via mock_claude","description":"Write tests that invoke the compiled claude-print binary as a subprocess using mock_claude as the claude backend. Tests live in tests/binary_e2e.rs (new file). All tests set --claude-binary to mock_claude path — no real credentials needed.\n\nRead test-fixtures/mock-claude/src/main.rs first to understand the env vars mock_claude responds to (MOCK_RESPONSE, MOCK_EXIT_CODE, MOCK_DELAY_STOP, etc.).\n\nTests to add:\n- AS-1 simulation: claude-print --claude-binary <mock_claude> 'test prompt' → exit 0, non-empty text on stdout, no JSON syntax\n- AS-2 simulation: claude-print --claude-binary <mock_claude> --output-format json 'test prompt' → exit 0, single-line valid JSON with type='result', subtype='success', is_error=false, result non-empty, claude_version present, usage object present\n- AS-5: claude-print --claude-binary /nonexistent 'hello' → exit 2, stderr contains human-readable message naming the missing binary; with --output-format json stdout has is_error=true\n- stream-json: claude-print --claude-binary <mock_claude> --output-format stream-json 'test prompt' → exit 0, each stdout line is valid JSON\n- No prompt: claude-print --claude-binary <mock_claude> with stdin=/dev/null and no positional arg → exit 4\n- --version: claude-print --claude-binary <mock_claude> --version → exit 0, stdout contains 'claude-print' and 'wrapping'\n\nComplete when: cargo test --test binary_e2e passes with all scenarios, no #[ignore]","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"task","created_at":"2026-06-11T11:29:15.578899366Z","updated_at":"2026-06-11T11:29:15.578899366Z","source_repo":".","compaction_level":0,"labels":["atomic"],"dependencies":[{"issue_id":"bf-46x","depends_on_id":"bf-4aw","type":"blocks","created_at":"2026-06-11T11:29:25.303249619Z","created_by":"cli","thread_id":""}]}
{"id":"bf-4ai","title":"Integrate HookInstaller and PtySpawner","description":"In src/session.rs run():\n- HookInstaller::new() - creates temp dir, hook.sh, stop.fifo\n- Build child argv: [claude_bin, --dangerously-skip-permissions, --settings <hook.settings_path>, ...claude_args]\n- Self-pipe for SIGINT: pipe2(O_CLOEXEC); install signal handler\n- PtySpawner::spawn(cmd, args)\n\nAcceptance:\n- HookInstaller and PtySpawner are called in sequence\n- Unit test verifies child argv contains expected flags\n- cargo test passes","design":"","acceptance_criteria":"","notes":"","status":"closed","priority":2,"issue_type":"task","created_at":"2026-06-13T17:34:30.462502961Z","updated_at":"2026-06-14T02:05:40.218903570Z","closed_at":"2026-06-14T02:05:40.218903570Z","close_reason":"Completed","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-4ai","depends_on_id":"bf-3ag","type":"blocks","created_at":"2026-06-13T17:35:09.473102143Z","created_by":"cli","thread_id":""}]}
{"id":"bf-4aw","title":"Wire main.rs: prompt resolution, session dispatch, emit","description":"Replace the 'not yet implemented' stub in main.rs with the full execution path. session::run() (bf-2mm) owns the PTY pipeline; main.rs owns CLI parsing, prompt resolution, output formatting, and exit codes.\n\n## What to implement in main.rs\n\nAfter --version and --check handling, add:\n\n1. **Prompt resolution** (in order of precedence):\n - --input-file <path>: read file bytes; Err → exit 4 with human-readable message\n - positional <prompt>: encode as UTF-8 bytes\n - stdin (when !stdin.is_terminal()): read_to_end; empty → exit 4\n - None found → exit 4, stderr: 'claude-print: no prompt provided (pass as argument, --input-file, or stdin)'\n\n2. **Build claude_args**: collect flags to forward to child:\n - always: [\"--dangerously-skip-permissions\", \"--settings\", hook_path] — NOTE: hook_path comes from session.rs, so just pass model/max-turns here; session.rs handles --settings internally\n - if cli.model: push [\"--model\", model]\n - if cli.max_turns: push [\"--max-turns\", n.to_string()]\n - if cli.no_inherit_hooks: push [\"--setting-sources=\"] (empty string arg)\n\n3. **Call session::run()**:\n let t0 = Instant::now();\n let result = session::run(\n claude_bin,\n &claude_args,\n prompt_bytes,\n cli.timeout,\n );\n\n4. **Match result**:\n - Ok(session_result): call emitter::emit_success(stdout, &session_result.transcript, &cli.output_format, &session_result.claude_version, session_result.duration_ms); exit 0\n - Err(Error::Interrupted): exit 130\n - Err(Error::Timeout): emit_error(..., 'timeout after Ns', &cli.output_format, PostInject); exit 3\n - Err(Error::NoResponse): emit_error(..., 'claude exited before Stop hook fired', ...); exit 2\n - Err(Error::InputError(msg)): emit_error(..., msg, ...); exit 4\n - Err(e): emit_error(..., e.to_string(), ...); exit 2\n\n5. **stream-json**: if output_format == StreamJson, after session::run() succeeds, open the transcript path and emit each line to stdout (replay). The transcript path is available from session_result (add transcript_path: PathBuf to SessionResult if not already there).\n\n6. **AS-5 (binary not found)**: when session::run() returns Err because claude_bin is not found, stderr must contain a human-readable message naming the missing binary. Handle this before calling session::run() by checking claude_bin existence: if !which::which(claude_bin).is_ok() { eprintln!(\"claude-print: '{}' not found in PATH\", ...); exit 2 }\n\n## Complete when\n- echo 'hello' | ./target/debug/claude-print --claude-binary /nonexistent exits 2 with human-readable error (AS-5 passes without credentials)\n- PATH= ./target/debug/claude-print 'hello' exits 2 with human-readable error naming 'claude'\n- All output format arms (text/json/stream-json) compile without dead_code warnings\n- cargo test passes","design":"","acceptance_criteria":"","notes":"","status":"completed","priority":0,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-06-11T11:28:49.217745491Z","updated_at":"2026-06-14T05:02:18.095080955Z","source_repo":".","compaction_level":0,"labels":["atomic","deferred"],"dependencies":[{"issue_id":"bf-4aw","depends_on_id":"bf-2mm","type":"blocks","created_at":"2026-06-11T11:28:54.989889755Z","created_by":"cli","thread_id":""}]}
{"id":"bf-4j7","title":"Write AGENTS.md (Phase 9 deliverable)","description":"AGENTS.md was written in a previous session but may be stale now that session.rs is being added (bf-2mm). Review and update AGENTS.md to reflect the final module map including session.rs.\n\nCheck current AGENTS.md and update if needed:\n- Add session.rs to the module map with a one-line description\n- Verify all build/test commands are still accurate\n- Verify the key invariants section still matches actual code\n- Verify bead workflow section is accurate\n\nComplete when: AGENTS.md exists, includes session.rs in module map, cargo test passes","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-06-11T11:29:21.239910202Z","updated_at":"2026-06-11T11:29:21.239910202Z","source_repo":".","compaction_level":0,"labels":["atomic"],"dependencies":[{"issue_id":"bf-4j7","depends_on_id":"bf-4aw","type":"blocks","created_at":"2026-06-11T11:29:25.308812268Z","created_by":"cli","thread_id":""}]}
{"id":"bf-4q2","title":"Update plan.md: stale Status table, module layout, and undocumented watchdog/flags","description":"docs/plan/plan.md has drifted from the implemented v0.2.0 codebase. Single consolidated plan-refresh bead — do NOT restructure the plan, only correct stale facts:\n\n1. 'Implementation Phases / Status' table: says main() session orchestration IN PROGRESS (bf-40i) and binary E2E tests IN PROGRESS (bf-52c) — src/main.rs and src/session.rs orchestration are complete and shipped as v0.2.0 (Cargo.toml version 0.2.0; it is the production NEEDLE adapter). Bead ids bf-40i/bf-52c no longer exist in .beads (E2E tests are now tracked as bf-46x). Update states and bead references; CI-release row stays pending until a tag is cut (tracked separately).\n2. 'Module Layout': missing files that now exist — src/check.rs, src/lib.rs, src/poller.rs, src/session.rs, src/watchdog.rs; tests/ actually has hooks.rs (not hook.rs), plus stop_poller.rs, pty_integration.rs, integration.rs, watchdog.rs; also build.rs, scripts/, and the CI YAMLs at repo root (claude-print-ci-workflowtemplate.yml, claude-print-ci-sensor.yml, claude-print-eventsource-stanza.yml).\n3. Undocumented component: src/watchdog.rs (no-output timeout, max-turn/overall timeout, stream-json first-output monitoring) and the CLI flags --first-output-timeout, --stream-json-timeout, --stop-hook-timeout (src/cli.rs) appear nowhere in the plan. Add a short component subsection + rows in the CLI table.\n4. 'Components #9 Emitter' / stream-json: note current implementation is post-session replay (src/main.rs replay_stream_json) with live tailing tracked as an open work item (bead bf-5vm), so the plan reflects reality until that lands.\n5. Phase 1 --version text says 0.1.0 — make version-agnostic.\n\nACCEPTANCE: every file in src/ and tests/ appears in the Module Layout; Status table names only live bead ids; watchdog + new flags documented; no other sections rewritten.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":3,"issue_type":"task","created_at":"2026-07-02T11:28:42.061173121Z","updated_at":"2026-07-02T13:20:37.493262849Z","source_repo":".","compaction_level":0}
{"id":"bf-4q2","title":"Update plan.md: stale Status table, module layout, and undocumented watchdog/flags","description":"docs/plan/plan.md has drifted from the implemented v0.2.0 codebase. Single consolidated plan-refresh bead — do NOT restructure the plan, only correct stale facts:\n\n1. 'Implementation Phases / Status' table: says main() session orchestration IN PROGRESS (bf-40i) and binary E2E tests IN PROGRESS (bf-52c) — src/main.rs and src/session.rs orchestration are complete and shipped as v0.2.0 (Cargo.toml version 0.2.0; it is the production NEEDLE adapter). Bead ids bf-40i/bf-52c no longer exist in .beads (E2E tests are now tracked as bf-46x). Update states and bead references; CI-release row stays pending until a tag is cut (tracked separately).\n2. 'Module Layout': missing files that now exist — src/check.rs, src/lib.rs, src/poller.rs, src/session.rs, src/watchdog.rs; tests/ actually has hooks.rs (not hook.rs), plus stop_poller.rs, pty_integration.rs, integration.rs, watchdog.rs; also build.rs, scripts/, and the CI YAMLs at repo root (claude-print-ci-workflowtemplate.yml, claude-print-ci-sensor.yml, claude-print-eventsource-stanza.yml).\n3. Undocumented component: src/watchdog.rs (no-output timeout, max-turn/overall timeout, stream-json first-output monitoring) and the CLI flags --first-output-timeout, --stream-json-timeout, --stop-hook-timeout (src/cli.rs) appear nowhere in the plan. Add a short component subsection + rows in the CLI table.\n4. 'Components #9 Emitter' / stream-json: note current implementation is post-session replay (src/main.rs replay_stream_json) with live tailing tracked as an open work item (bead bf-5vm), so the plan reflects reality until that lands.\n5. Phase 1 --version text says 0.1.0 — make version-agnostic.\n\nACCEPTANCE: every file in src/ and tests/ appears in the Module Layout; Status table names only live bead ids; watchdog + new flags documented; no other sections rewritten.","design":"","acceptance_criteria":"","notes":"","status":"in_progress","priority":3,"issue_type":"task","assignee":"echo-test-test-worker","created_at":"2026-07-02T11:28:42.061173121Z","updated_at":"2026-07-02T13:50:44.851268254Z","source_repo":".","compaction_level":0}
{"id":"bf-549b","title":"Wire stream-json reader spawn at PROMPT_INJECTED transition","description":"Add the call to emitter::spawn_stream_json_reader at the PROMPT_INJECTED transition in the session flow. This should happen in the event loop callback when the phase changes to PromptInjected.\n\nThe reader should be spawned with:\n- transcript_path: the path to <temp_dir>/transcript.jsonl\n- start_offset: the captured byte offset from the previous step\n\nStore the returned StreamJsonHandle in the stream_json_handle variable for later joining.\n\nACCEPTANCE:\n- Add spawn_stream_json_reader call in event loop at PROMPT_INJECTED\n- Only spawn when output_format is StreamJson\n- Pass transcript_path and start_offset correctly\n- Store handle for later use\n- Ensure the code compiles","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-02T13:33:47.026801648Z","updated_at":"2026-07-02T13:33:47.026801648Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-549b","depends_on_id":"bf-3wya","type":"blocks","created_at":"2026-07-02T13:34:10.950604916Z","created_by":"cli","thread_id":""}]}
{"id":"bf-5pb","title":"Verify existing stream-json tests still pass","description":"After implementing live streaming, ensure all existing stream-json tests in tests/emitter.rs and tests/integration/scenarios.rs still pass. These tests currently exercise spawn_stream_json_reader / spawn_stream_json_reader_to through the test harness but not through the production session path. Verify that the changes to session flow don't break the existing test infrastructure.\n\nRun cargo test to verify.\n\nACCEPTANCE:\n- All existing tests in tests/emitter.rs pass\n- All existing tests in tests/integration/scenarios.rs pass\n- No regressions in stream-json functionality\n- cargo test passes","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-02T12:59:09.749090050Z","updated_at":"2026-07-02T12:59:09.749090050Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5pb","depends_on_id":"bf-5xw","type":"blocks","created_at":"2026-07-02T12:59:34.901532069Z","created_by":"cli","thread_id":""}]}
{"id":"bf-5vm","title":"Wire live stream-json streaming into session flow (currently buffered replay)","description":"Plan sections 'Concurrency Model / stream-json mode' and 'Components #9 Emitter' (docs/plan/plan.md) specify that in --output-format stream-json a reader thread is spawned at the PROMPT_INJECTED transition, tails the transcript JSONL from the injection byte offset, and forwards each new line to stdout in real time while the session runs.\n\nACTUAL: src/main.rs (see replay_stream_json, ~line 164-166 and 247+) replays the transcript line-by-line only AFTER the session completes. The live reader thread already exists as emitter::spawn_stream_json_reader / spawn_stream_json_reader_to in src/emitter.rs (with mpsc drain channel) but is only exercised by tests (tests/emitter.rs, tests/integration/scenarios.rs) — it is never invoked from the production session path in src/main.rs / src/session.rs. This is the known v0.2.0 limitation: NEEDLE and other stream-json consumers see nothing until iteration end.\n\nWORK: wire spawn_stream_json_reader into the session flow — spawn at prompt injection with the captured byte offset (per plan, offset = transcript file length at bracketed-paste write; handle transcript-not-yet-created with the 50ms/5s retry described in the plan), send drain signal on Stop, drop sender on SIGINT/timeout paths, and join the handle on ALL exit paths (plan invariant INV-8). Remove or keep replay_stream_json only as fallback. Session state lives in src/session.rs; watchdog interaction (--stream-json-timeout flag, src/watchdog.rs) must keep working.\n\nACCEPTANCE:\n- Integration test with mock_claude using MOCK_DELAY_JSONL / multi-turn output asserting stream-json lines appear on stdout BEFORE the session completes (incremental output, not one post-hoc burst).\n- Reader thread joined on success, timeout, and SIGINT paths (INV-8 test).\n- Existing stream-json tests in tests/emitter.rs and tests/integration/scenarios.rs still pass.\n- cargo fmt/clippy/test clean per plan Definition of Done.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"feature","created_at":"2026-07-02T11:27:47.754345606Z","updated_at":"2026-07-02T13:30:38.051184945Z","source_repo":".","compaction_level":0,"labels":["deferred","umbrella"],"dependencies":[{"issue_id":"bf-5vm","depends_on_id":"bf-q5i","type":"blocks","created_at":"2026-07-02T12:59:39.622717216Z","created_by":"cli","thread_id":""}]}
{"id":"bf-5xw","title":"Write integration test for incremental stream-json output","description":"Write an integration test using mock_claude that asserts stream-json lines appear on stdout BEFORE the session completes (incremental output, not one post-hoc burst). Use MOCK_DELAY_JSONL environment variable and multi-turn output scenario. The test should verify that lines are streamed in real-time during the session, not all at once after completion.\n\nTest should be in tests/integration/scenarios.rs or a new test file.\n\nACCEPTANCE:\n- Integration test using mock_claude with MOCK_DELAY_JSONL\n- Multi-turn output scenario\n- Assertion that stream-json lines appear on stdout BEFORE session completion\n- Test passes after bead 1 and 2 are implemented","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-02T12:59:03.452794050Z","updated_at":"2026-07-02T12:59:03.452794050Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-5xw","depends_on_id":"bf-8le","type":"blocks","created_at":"2026-07-02T12:59:34.895200188Z","created_by":"cli","thread_id":""}]}
{"id":"bf-68nl","title":"Add stream-json reader join on all exit paths","description":"Ensure the stream-json reader thread is properly joined on all exit paths (success, timeout, interrupted, error). The reader must be joined before the SessionResult is returned to ensure all output is drained.\n\nOn success: send drain signal () and join\nOn timeout/error: drop handle without sending (exit immediately)\nOn all paths: call handle.join_handle.join() to wait for thread exit\n\nACCEPTANCE:\n- Add drain signal and join on success path (FifoPayload)\n- Add drop-and-join on timeout path\n- Add drop-and-join on interrupted path\n- Add drop-and-join on child exit path\n- Ensure all paths join the thread before returning\n- Code compiles and tests pass","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-02T13:33:53.715394460Z","updated_at":"2026-07-02T13:33:53.715394460Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-68nl","depends_on_id":"bf-549b","type":"blocks","created_at":"2026-07-02T13:34:10.804845804Z","created_by":"cli","thread_id":""}]}
{"id":"bf-6d4","title":"Cut first release tag and exercise claude-print-ci release path + install.sh e2e","description":"Plan 'Implementation Phases / Status' and 'CI/CD' sections (docs/plan/plan.md): CI release binary is PENDING — 'no release tag cut yet'. Phase 11 has a deferred item: install.sh end-to-end download test blocked on a release binary existing.\n\nACTUAL: git tag list is EMPTY; no GitHub release exists at jedarden/claude-print, so the primary install path documented in README.md (install.sh downloads claude-print-linux-amd64 + mock_claude from GitHub Releases) is dead. Cargo.toml is at version 0.2.0 and the binary works locally (it is the live NEEDLE default adapter), so the code is releasable. CI YAMLs exist at repo root (claude-print-ci-workflowtemplate.yml, claude-print-ci-sensor.yml, claude-print-eventsource-stanza.yml) — verify the WorkflowTemplate copy in jedarden/declarative-config k8s/iad-ci/argo-workflows/ matches and is Synced in ArgoCD.\n\nWORK:\n1. Run the pre-release gate: full test suite green, AS-4 billing check (transcript entrypoint=cli — needs scripts/check-billing.sh, separate bead) run manually.\n2. Tag v0.2.0 (semver per plan 'Release Tag Convention') and push the tag to origin (Forgejo; GitHub receives via server-side mirror). NEVER force-push.\n3. Submit claude-print-ci with tag parameter set (manual submission command is in plan 'Submitting CI Manually') and confirm it produces a GitHub release with claude-print-linux-amd64 and mock-claude-linux-amd64, both statically linked.\n4. Run install.sh end-to-end on a clean prefix: downloads release asset, claude-print --check exits 0.\n\nACCEPTANCE: GitHub release for the tag exists with both artifacts; install.sh e2e passes; plan Phase 11 deferred checklist item satisfiable.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-02T11:28:08.785282318Z","updated_at":"2026-07-02T12:58:32.618026121Z","source_repo":".","compaction_level":0}
{"id":"bf-6d4","title":"Cut first release tag and exercise claude-print-ci release path + install.sh e2e","description":"Plan 'Implementation Phases / Status' and 'CI/CD' sections (docs/plan/plan.md): CI release binary is PENDING — 'no release tag cut yet'. Phase 11 has a deferred item: install.sh end-to-end download test blocked on a release binary existing.\n\nACTUAL: git tag list is EMPTY; no GitHub release exists at jedarden/claude-print, so the primary install path documented in README.md (install.sh downloads claude-print-linux-amd64 + mock_claude from GitHub Releases) is dead. Cargo.toml is at version 0.2.0 and the binary works locally (it is the live NEEDLE default adapter), so the code is releasable. CI YAMLs exist at repo root (claude-print-ci-workflowtemplate.yml, claude-print-ci-sensor.yml, claude-print-eventsource-stanza.yml) — verify the WorkflowTemplate copy in jedarden/declarative-config k8s/iad-ci/argo-workflows/ matches and is Synced in ArgoCD.\n\nWORK:\n1. Run the pre-release gate: full test suite green, AS-4 billing check (transcript entrypoint=cli — needs scripts/check-billing.sh, separate bead) run manually.\n2. Tag v0.2.0 (semver per plan 'Release Tag Convention') and push the tag to origin (Forgejo; GitHub receives via server-side mirror). NEVER force-push.\n3. Submit claude-print-ci with tag parameter set (manual submission command is in plan 'Submitting CI Manually') and confirm it produces a GitHub release with claude-print-linux-amd64 and mock-claude-linux-amd64, both statically linked.\n4. Run install.sh end-to-end on a clean prefix: downloads release asset, claude-print --check exits 0.\n\nACCEPTANCE: GitHub release for the tag exists with both artifacts; install.sh e2e passes; plan Phase 11 deferred checklist item satisfiable.","design":"","acceptance_criteria":"","notes":"","status":"in_progress","priority":2,"issue_type":"task","assignee":"echo-test-test-worker","created_at":"2026-07-02T11:28:08.785282318Z","updated_at":"2026-07-02T13:50:43.409914396Z","source_repo":".","compaction_level":0}
{"id":"bf-8le","title":"Implement reader thread cleanup on all session exit paths","description":"Ensure the stream-json reader thread is properly cleaned up on ALL exit paths (success, timeout, SIGINT). Send drain signal on Stop transition, drop sender on SIGINT/timeout paths, and join the handle in all cases. This enforces plan invariant INV-8: reader thread must be joined before session exit.\n\nHandle exit paths in:\n- Normal completion (Stop → transition)\n- Timeout (watchdog timeout in src/watchdog.rs)\n- SIGINT (interrupt handling)\n\nACCEPTANCE:\n- Drain signal sent on Stop transition\n- Sender dropped on SIGINT/timeout paths\n- Handle joined on ALL exit paths (INV-8)\n- No orphaned reader threads after any exit scenario","design":"","acceptance_criteria":"","notes":"","status":"open","priority":2,"issue_type":"task","created_at":"2026-07-02T12:58:57.969477163Z","updated_at":"2026-07-02T12:58:57.969477163Z","source_repo":".","compaction_level":0,"labels":["split-child"],"dependencies":[{"issue_id":"bf-8le","depends_on_id":"bf-30e","type":"blocks","created_at":"2026-07-02T12:59:29.800204842Z","created_by":"cli","thread_id":""}]}
{"id":"bf-b5q","title":"Harden headless claude-print: never hang indefinitely on a wedged child","description":"Headless claude-print can block forever. Confirmed 2026-06-21: a marathon run wedged 33+ min on iteration 1 — child claude hung at startup (S state, 17s CPU/33min, never emitted stream-json), so its Stop hook never fired; claude-print sat in do_sys_poll reading stop.fifo with NO timeout. Same binary worked fine in a sibling repo simultaneously, so the trigger is session/folder-specific but the FATAL gap is that claude-print has no watchdog. Umbrella for investigation + hardening fixes so a wedged/slow/missing child can never hang the wrapper.","design":"","acceptance_criteria":"","notes":"","status":"open","priority":1,"issue_type":"feature","created_at":"2026-06-21T14:14:56.261559677Z","updated_at":"2026-06-21T14:14:56.261559677Z","source_repo":".","compaction_level":0,"labels":["hardening"],"dependencies":[{"issue_id":"bf-b5q","depends_on_id":"bf-2u1","type":"blocks","created_at":"2026-06-21T14:14:56.340488705Z","created_by":"cli","thread_id":""},{"issue_id":"bf-b5q","depends_on_id":"bf-2f5","type":"blocks","created_at":"2026-06-21T14:14:56.347857590Z","created_by":"cli","thread_id":""},{"issue_id":"bf-b5q","depends_on_id":"bf-uj0","type":"blocks","created_at":"2026-06-21T14:14:56.354414570Z","created_by":"cli","thread_id":""},{"issue_id":"bf-b5q","depends_on_id":"bf-2w7","type":"blocks","created_at":"2026-06-21T14:14:56.362176295Z","created_by":"cli","thread_id":""},{"issue_id":"bf-b5q","depends_on_id":"bf-3eq","type":"blocks","created_at":"2026-06-21T14:14:56.370071568Z","created_by":"cli","thread_id":""}]}
{"id":"bf-gqf","title":"Implement src/pty.rs: PTY spawn and signal forwarding","description":"AGENTS.md documents pty.rs as forking the child, opening PTY pairs, and forwarding SIGWINCH/SIGINT to the child. This is core functionality mentioned in key invariants (must forward SIGINT properly).\n\n---\nPriority: P1\nCreated by: weave strand","design":"","acceptance_criteria":"","notes":"","status":"completed","priority":2,"issue_type":"task","assignee":"claude-code-glm-4.7-alpha","created_at":"2026-06-11T12:23:37.744760557Z","updated_at":"2026-06-11T13:16:22.356835848Z","source_repo":".","compaction_level":0,"labels":["weave-generated"]}