- Install a SIGWINCH handler that sets a static AtomicBool; the relay loop
re-issues TIOCSWINSZ on the master fd each time it fires.
- relay() poll-loops over the master fd and stdin, copying master output to
stdout and stdin input to the master fd, with 100 ms timeout so SIGWINCH
is handled promptly on EINTR.
- Loop exits on EIO/EOF/POLLHUP from the master fd (slave side closed).
- waitpid() at the end of relay() surfaces the child exit code (or
128+signum for signal termination).
- New tests: master_fd_carries_child_stdout (acceptance criterion),
relay_echo_exits_zero_and_produces_output, relay_surfaces_nonzero_exit_code.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Change spawn() to spawn(cmd, args): on the parent side set the PTY
window size via TIOCSWINSZ mirroring stdin or defaulting to 80x24; on
the child side call execvp with the given command after login_tty.
Update the test to exercise /bin/true through the full exec path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds PtySpawner struct that calls openpty() for master/slave fds, forks,
and runs login_tty on the child side before it exits. Fixes nix feature
flag (pty module is gated by `term`, not a `pty` feature in nix 0.29).
Adds mock-claude workspace stub so the workspace resolves cleanly.
Unit test: fork_and_login_tty_does_not_panic passes (child exits 0).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All completion criteria met: musl build succeeds, --version prints
expected format, cargo test --lib passes (5 tests), CI stub exists
in declarative-config.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
12 beads (genesis + 11 phases) flushed from SQLite to issues.jsonl.
JSONL is the authoritative, corruption-immune data source.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Genesis bead bf-5zr tracks the full 11-phase implementation.
Phase beads bf-2z4 through bf-5na are wired sequentially (each blocks
the next) and all block the genesis bead. Phase 1 is the only ready bead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
G-1 (HIGH): Argo param syntax fixed in build-musl — {{}} not $()
G-2 (HIGH): SIGTERM row in signal table now includes 'SIGTERM child (per HR-8 mirror)'
G-3 (HIGH): Phase 11 now includes deferred install.sh end-to-end download test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
G-1: claude_version claim narrowed — appears only in json/stream-json error, not text
G-2: claude_version runtime source specified — run binary once pre-fork, cache result
G-3: TRUST_DISMISSED idle-wait upper bound documented — wall-clock timeout is the only exit
G-4: keeper write-end fd close-before-waitpid added to all non-Stop exit paths
G-5: hook ordering claim softened to "likely first (unverified per OQ-1)"; OQ-1 expanded
G-6: ANSI stripping in last_assistant_message fallback extended to stream-json mode
G-7: parent fd 0 close after prompt read specified in PTY Spawner §3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Critical:
- G-1: HR-8 vs Signal Handling contradiction resolved — SIGINT handler now sends SIGINT (not SIGTERM) to child
High:
- G-2: Event Loop timer is not a timerfd; Instant::elapsed() with poll() timeout parameter instead
- G-3: Signal handler safety specified: AtomicBool, kill(2), self-pipe to wake blocked poll()
- G-4: NEEDLE invoke_template now includes --no-inherit-hooks; AS-3 pass criterion now achievable
- G-5: Error output per format: text→stderr only; json→stdout JSON; stream-json→final error line
Medium:
- G-6: Child resets SIGINT+SIGTERM to SIG_DFL before execvp (signal handler inheritance)
- G-7: "weekly schedule" claim removed; version-compat tests run on every push (CI already covers it)
- G-8: needle-transform-claude defined as NEEDLE's built-in transform for claude JSON output
- G-9: Integration tests use --claude-binary <path-to-mock_claude> via CARGO_MANIFEST_DIR
- G-10: XTVERSION probe accepts both ESC[>q and ESC[>0q (parameterized form)
- G-11: Install Script renumbered 1-8 with clean integers (removed decimal 2.5/3.5 steps)
- G-12: T-6 threat (PATH hijack) added to threat model
Low:
- G-13: Config path respects $XDG_CONFIG_HOME when set
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Addresses all MISSING/PARTIAL items from the plan review:
- Glossary, Non-Goals, Hard Requirements, What It Is Not, Scope Lock
- Acceptance scenarios (AS-1 through AS-6) with pass/fail criteria
- Module layout (src/ file tree), state machine diagrams, concurrency model
- Cross-cutting concerns: error propagation, signals, temp dir, log boundary
- Tech stack rationale column on crate table
- Edge case catalog (EC-1..12), anti-patterns, invariants (INV-1..8)
- Proof obligations (PO-1..6) with named recovery per assumption
- Phase entry/exit criteria and LOC estimates for all 11 phases
- Conformance harness + definition of done + all-gates policy in Testing
- Security section: threat model (T-1..5), untrusted input policy, supply chain
- Performance section: budgets, benchmark contract, CI size gate
- Operations section: migration plan, semver stance, rollout/rollback, doctor command
- Risk register (R-1..7) with likelihood/impact/mitigation
- ADRs (ADR-001..003) for the three churn-magnet decisions
- Open Questions updated with phase dependencies and resolution deadlines
- Phase 6 renamed from duplicate "Hook installer" to "Stop poller"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>