Anchor the --status exit-code footer scan to the log tail - #153
Conversation
Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: #150
`$ --status` derived a detached session's exit code from an unanchored `Exit Code: N` scan over the whole session log, so any text the wrapped command printed containing that substring was indistinguishable from the footer `start` appends itself, and the fabricated code was reported as the session's exit code. - Match the three-line footer block (separator / `Finished:` / `Exit Code:`) at line starts instead of a bare substring, in both the JS and Rust implementations. - Read only the last 16 KB of the log (the footer is always last), dropping the partial first line so it cannot act as a line start. This also removes a full-file read from every `--status` call. - Prefer `docker inspect .State.ExitCode` over the log text when a detached session has ended: the backend is authoritative and cannot be spoofed by command output. Adds regression tests (js/test/regression-150.js, rust/tests/regression_150.rs) and an end-to-end CLI reproduction in experiments/.
$ --status fabricates a detached session exit code from the command's own output (unanchored Exit Code: scan over the whole log)--status exit-code footer scan to the log tail
Working session summaryPR #153 is finalized and green. #153 — "Anchor the What shipped, in both implementations:
Issue item 4 (per-execution UUID marker in the footer) was deliberately left out — it's marked optional and would change the on-disk log format across every footer writer; this is stated in the PR description. Verification: 11 new regression tests per language ( This summary was automatically extracted from the AI working session output. |
🤖 Solution Draft LogThis log file contains the complete execution trace of the AI solution draft process. 💰 Cost: $4.588273📊 Context and tokens usage:Claude Opus 5: (2 sub-sessions)
Total: (2.7K new + 121.4K cache writes + 4.8M cache reads) input tokens, 38.0K output tokens, $4.588273 cost 🤖 Models used:
📎 Log file uploaded as Gist (1893KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
✅ Ready to mergeThis pull request is now ready to be merged:
Monitored by hive-mind with --auto-restart-until-mergeable flag |
This reverts commit cad1d26.
|
🤖 AI Work Session Started Starting automated work session at 2026-08-04T08:30:36.408Z The PR has been converted to draft mode while work is in progress. This comment marks the beginning of an AI work session. Please wait for the session to finish, and provide your feedback. |
Resolve the exit-code resolution order in enrichDetachedStatus / the Rust equivalent: backend (docker inspect .State.ExitCode, #150) -> anchored log footer -> 137 when only the OOM observation is left (#151) -> -1 sentinel. Update the two #151 test fixtures to write the real anchored footer block (with the '=' separator line) that start itself emits.
Working session summaryDone. PR #153 is merge-clean, green, and out of draft: #153 What I did this round: Merged
Resolution — combined both fixes into one order, backend first (issue #150's core point) then the anchored footer, then #151's OOM fallback: enriched.exitCode =
backendExitCode(dockerState) ?? // docker inspect — unspoofable
footerExit ?? // anchored footer block only
(oomKilled === true ? 137 : -1);Main's Test fixtures updated. Two pre-existing #151 tests wrote a bare
Verification: I also corrected the PR description's resolution-order bullet (it still claimed footer → This summary was automatically extracted from the AI working session output. |
🤖 Solution Draft LogThis log file contains the complete execution trace of the AI solution draft process. 💰 Cost: $1.660718📊 Context and tokens usage:
Total: (68 new + 49.6K cache writes + 1.7M cache reads) input tokens, 11.9K output tokens, $1.660718 cost 🤖 Models used:
📎 Log file uploaded as Gist (1145KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
🎉 Auto-mergedThis pull request has been automatically merged by hive-mind.
Auto-merged by hive-mind with --auto-merge flag |
Summary
Fixes #150.
$ --statuscould report an exit code the command never produced: the exit code of a detached session was derived from an unanchoredExit Code: Nscan over the whole session log, so any text the wrapped command printed containing that substring was indistinguishable from the terminal footerstartappends itself.Changes
Applied in both implementations (
js/src/lib/status-formatter.jsandrust/src/lib/status_formatter.rs):Anchored footer matching. The exit code is now read only from the three-line footer block
startwrites itself, matched at line starts:A bare
Exit Code: Ninside a JSON payload, a quoted log excerpt or anrg -ndump no longer counts.Tail-only read. Only the last 16 KB of the log are scanned (the footer is always last), which also removes a full-file read from every
--statuscall. The partial first line of the tail slice is dropped so a mid-line fragment cannot act as a line start.Backend beats the log text. In the
!alivebranch the resolution order is nowdocker inspect .State.ExitCode→ anchored footer →137when the only evidence left is the OOM observation ($ --statusreportsexecuted/exitCode 137from the container OOM flag whiledocker inspectstill saysrunning(session later exits 0) #151) →-1sentinel (was: footer first).docker inspectis authoritative and cannot be spoofed by command output.Item 4 from the issue (a machine-readable, per-execution marker such as the execution UUID in the footer) is not included — it touches every footer writer in both implementations and would change the on-disk log format; worth a separate change if the anchored footer proves insufficient.
Reproduction
The issue's CLI reproduction, run against this branch (Docker,
--isolated docker --detached, containerdocker rm -f'd with the host-side watcher killed so the window is permanent):executedexecuting1(fabricated)And with the container still inspectable (SIGKILL → 137) while the command printed a verbatim forged footer claiming
Exit Code: 0,--statusnow reports the backend'sexitCode 137.Tests
js/test/regression-150.js— 11 tests: the incident payload, bare/mid-lineExit Code:lines, CRLF logs, negative codes, tail-boundary handling, andenrichDetachedStatus()on a detached-docker record whose container cannot be inspected. 10 of them fail onmain.rust/tests/regression_150.rs— the same 11 cases for the Rust implementation.experiments/issue-150-forged-exit-code.mjs— end-to-end reproduction through the real$ --statusCLI (no mocks, no Docker daemon needed); exits non-zero onmain, zero with the fix.Full suites pass locally:
bun run test(706 pass, 1 pre-existingexecution-storelino failure that also fails onmain),cargo test(all targets), plusbun run lint,bun run format:check,cargo fmt --check,cargo clippy -D warnings.Release triggers:
js/.changeset/issue-150-anchored-exit-code-footer.mdandrust/changelog.d/issue-150-anchored-exit-code-footer.md(bothpatch).