From 1790e378d4af9ad60ea51d893e9de3c0706b6111 Mon Sep 17 00:00:00 2001 From: elkaix Date: Thu, 13 Aug 2026 19:35:02 -0400 Subject: [PATCH] fix: accept a punctuation-delimited reason on the RESULT line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The result parser only matched a RESULT line whose terminal token was the entire rest of the line, but the implementer contract in the same file presents the tokens as "RESULT: BLOCKED — name the blocker". Implementers reasonably emit that shape, the line failed to match, and a correct run with a correct report was classified IMPLEMENTER_STATE: COMPANION_FAILURE — exiting 11 without ever running the caller's --verify. Accept an optional trailing reason that starts with a punctuation character, so the parser agrees with the shape its own contract displays. Every existing rejection survives: RESULT: DONEISH still fails on the token boundary, bare trailing prose ("RESULT: DONE and everything passed") stays unparseable, and the ^ anchor keeps the contract's own four indented token lines inert, so a report quoting the contract cannot self-classify. The fix goes in the parser rather than the contract wording, because the parser is the layer that authors the disagreement, and rewording cannot help reports already written in that shape. Verified: tests/run.sh 18 passed 0 failed; tests/stop-report.sh 24 passed. Reverting only lib-write-turn.sh turns the new t7b case red while t7 keeps passing, so the new case isolates exactly the new behaviour. --- hooks/lib-write-turn.sh | 2 +- tests/stop-report.sh | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/hooks/lib-write-turn.sh b/hooks/lib-write-turn.sh index c4d8e78..d9df756 100755 --- a/hooks/lib-write-turn.sh +++ b/hooks/lib-write-turn.sh @@ -24,7 +24,7 @@ _write_turn_positive_integer() { _write_turn_result_state() { printf '%s\n' "$1" | - sed -nE 's/^RESULT:[[:space:]]*(DONE|NEEDS_ANSWERS|BLOCKED|FAILED)[[:space:]]*$/\1/p' | + sed -nE 's/^RESULT:[[:space:]]*(DONE|NEEDS_ANSWERS|BLOCKED|FAILED)([[:space:]]*[^[:alnum:]_[:space:]].*)?[[:space:]]*$/\1/p' | tail -1 } # Implementer contract — appended to every dispatch so each run is disciplined by diff --git a/tests/stop-report.sh b/tests/stop-report.sh index b3fcb68..87d70f9 100755 --- a/tests/stop-report.sh +++ b/tests/stop-report.sh @@ -70,13 +70,14 @@ printf 'Objective: fail closed when status is lost.\n' > "$STATUS_LOSS_PLAN" printf 'Objective: carry failed evidence.\n' > "$FAILED_PLAN" printf 'Objective: reject a result prefix.\n' > "$REPO/prefix-plan.md" printf 'Objective: accept the last result record.\n' > "$REPO/last-record-plan.md" +printf 'Objective: accept a trailing result reason.\n' > "$REPO/reason-plan.md" cp "$DONE_PLAN" "$TEST_ROOT/done-plan.before" git init -q "$REPO" ( cd "$REPO" && git config user.email p@p && git config user.name p && - git add needs-plan.md done-plan.md status-loss-plan.md failed-plan.md prefix-plan.md last-record-plan.md && + git add needs-plan.md done-plan.md status-loss-plan.md failed-plan.md prefix-plan.md last-record-plan.md reason-plan.md && git commit -q -m init ) @@ -168,6 +169,8 @@ run_loop prefix "$REPO/prefix-plan.md" 'RESULT: DONEISH' 2 PREFIX_RC=$? run_loop last-record "$REPO/last-record-plan.md" $'RESULT: FAILED\nearlier failure\nRESULT: DONE' LAST_RECORD_RC=$? +run_loop reason "$REPO/reason-plan.md" 'RESULT: BLOCKED — active lock prevented the bounded tests' +REASON_RC=$? run_failed_loop run_status_loss @@ -273,6 +276,15 @@ t7_result_records_are_full_line_and_last_wins() { { echo "last anchored DONE record did not win"; return 1; } } +t7b_result_trailing_reason_is_blocked() { + local starts + [ "$REASON_RC" -eq 11 ] || { echo "trailing-reason rc=$REASON_RC want 11"; return 1; } + starts=$(grep -c '^task ' "$TEST_ROOT/reason.calls" || true) + [ "$starts" -eq 1 ] || { echo "trailing-reason starts=$starts want 1"; return 1; } + ! grep -qx 'IMPLEMENTER_STATE: COMPANION_FAILURE' "$TEST_ROOT/reason.stderr" || + { echo "trailing-reason was classified as companion failure"; return 1; } +} + t8_failed_result_evidence_reaches_next_dispatch() { local starts evidence samples first second [ "$FAILED_LOOP_TIMED_OUT" -eq 0 ] || { echo "failed-evidence loop timed out"; return 1; } @@ -911,6 +923,7 @@ check t4_second_stop_appended "second stop appends a second history block" check t5_verified_done_unchanged "VERIFIED_DONE appends nothing" check t6_status_loss_fails_closed "status loss blocks after one dispatch and retains poison" check t7_result_records_are_full_line_and_last_wins "RESULT records are anchored and the last record wins" +check t7b_result_trailing_reason_is_blocked "RESULT trailing reason remains a real BLOCKED result" check t8_failed_result_evidence_reaches_next_dispatch "FAILED result evidence reaches the next dispatch" check t8b_stuck_attempt_history_persists "STUCK persists bounded attempt history in the plan" check t8c_result_transport_failure_blocks_without_retry "result transport failure blocks without billing a retry"