feat: headless exit-status contract for claude-box - #9
Merged
Merged
Conversation
Make `claude-box -p …` compose in scripts, CI, and one-shots: - Propagate the harness's own exit code verbatim (0, 1–124, 128+ signal deaths) and map docker's container-lifecycle band to distinguishable launcher faults: 125 image-missing, 126 engine-start-failed, 127 harness-not-executable. Each fault also prints a machine-readable `claude-box: fault=<reason> detail="…"` line to stderr unconditionally. - Catch image-missing pre-run (build failure or image absent) so a runtime 125 unambiguously means engine/runtime rejection. - Compose docker -i/-t independently from the fd shapes: allocate a PTY only when both stdout and stderr are terminals (so a redirected stdout capture stays clean — no CRLF, no [entrypoint] lines), and attach stdin only when something is connected. - Stop the session container before sync_back copies out, so a supervisor's signal on a headless run can't snapshot half-flushed state. - Gate the entrypoint state dump behind CLAUDE_BOX_DEBUG (off by default) and add the CLAUDE_BOX_EXEC acceptance hook that execs raw args instead of claude, dropping the launcher's leading --dangerously-skip-permissions. - Document the whole contract in docs/runbook-headless-exit.md (and un-ignore docs/ so the runbook is tracked). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Track .faffrc.yaml (Linear tracker, agile lens, nlspec spec) and ignore the local-only .faffrc.local.yaml / .faff / .env.* alongside it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Makes
claude-box -p …compose cleanly in scripts, CI, and one-shots by giving the launcher a real exit-status contract and clean redirected streams.125image-missing,126engine-start-failed,127harness-not-executable. Every fault also prints an unconditionalclaude-box: fault=<reason> detail="…"line to stderr so a caller can disambiguate.125unambiguously means engine/runtime rejection.-t(PTY) only when both stdout and stderr are terminals, soclaude-box -p … > out.txtcaptures no CRLF and no[entrypoint]noise;-ionly when stdin is actually connected.sync_backstops the container before copying out, so a supervisor's signal on a headless run can't snapshot mid-write.CLAUDE_BOX_DEBUG=1gates the entrypoint state dump (off by default);CLAUDE_BOX_EXEC=1execs raw args instead ofclaudefor acceptance testing.docs/runbook-headless-exit.mdmaps 1:1 to the acceptance criteria.Second commit tracks the repo's own
.faffrc.yamland ignores the local-only faff files.Verification
bash -npasses on both scripts. Full manual acceptance steps are in the runbook.🤖 Generated with Claude Code