#!/usr/bin/env bats # hooks/inbox-boundary-check.sh — Stop hook that soft-nudges the agent to # process pending inbox/ handoffs before yielding. Blocks the stop ONCE (emits # a block decision + reason) when inbox-status reports pending items; on the # harness re-entry (stop_hook_active: true) it steps aside so an unprocessable # item or a mid-task pause never wedges. Self-skips where there's no inbox/ or # no inbox-status. The real inbox-status is copied into the test project so the # hook runs against real code, not a stub. setup() { REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../.." && pwd)" SCRIPT="$REPO_ROOT/hooks/inbox-boundary-check.sh" INBOX_STATUS="$REPO_ROOT/claude-templates/.ai/scripts/inbox-status" TMPDIR_T="$(mktemp -d)" CWD="$TMPDIR_T/proj" mkdir -p "$CWD/.ai/scripts" cp "$INBOX_STATUS" "$CWD/.ai/scripts/inbox-status" chmod +x "$CWD/.ai/scripts/inbox-status" } teardown() { rm -rf "$TMPDIR_T" } # Feed Stop-hook JSON on stdin. $1 = stop_hook_active (default false). run_hook() { local active="${1:-false}" printf '{"cwd":"%s","hook_event_name":"Stop","stop_hook_active":%s}' \ "$CWD" "$active" | bash "$SCRIPT" } @test "pending handoffs block the stop with a count in the reason" { mkdir -p "$CWD/inbox" printf 'x\n' >"$CWD/inbox/2026-07-19-from-home-thing.org" printf 'y\n' >"$CWD/inbox/2026-07-19-from-work-other.org" run run_hook [ "$status" -eq 0 ] # Valid JSON with a block decision. echo "$output" | jq -e '.decision == "block"' echo "$output" | jq -e '.reason | test("2 pending")' echo "$output" | jq -e '.reason | test("inbox.org")' } @test "a clean inbox (only artifacts) emits nothing" { mkdir -p "$CWD/inbox" touch "$CWD/inbox/.gitkeep" printf 'done\n' >"$CWD/inbox/PROCESSED-2026-07-19-old.org" run run_hook [ "$status" -eq 0 ] [ -z "$output" ] } @test "soft-nudge: stop_hook_active=true steps aside even with pending items" { mkdir -p "$CWD/inbox" printf 'x\n' >"$CWD/inbox/2026-07-19-from-home-thing.org" run run_hook true [ "$status" -eq 0 ] [ -z "$output" ] } @test "no inbox/ directory is a silent no-op" { run run_hook [ "$status" -eq 0 ] [ -z "$output" ] } @test "inbox-status absent degrades to a silent no-op" { mkdir -p "$CWD/inbox" printf 'x\n' >"$CWD/inbox/2026-07-19-from-home-thing.org" rm -f "$CWD/.ai/scripts/inbox-status" # Also ensure nothing named inbox-status is on PATH for this run. run env PATH="/usr/bin:/bin" bash -c ' printf "{\"cwd\":\"'"$CWD"'\",\"hook_event_name\":\"Stop\"}" | bash "'"$SCRIPT"'"' [ "$status" -eq 0 ] [ -z "$output" ] } @test "the emitted reason names the project for context" { mkdir -p "$CWD/inbox" printf 'x\n' >"$CWD/inbox/2026-07-19-from-home-thing.org" run run_hook echo "$output" | jq -e '.reason | test("proj")' }