#!/usr/bin/env bash # # selftest.sh — quick, dependency-light verification for agent-queue. # # Runs: # 1. shellcheck (if installed) at --severity=error on the runner # 2. bash -n syntax check on the runner + this script # 3. node --check on the dashboard (if node installed) # 4. a live init/add/run --once cycle against a throwaway queue using a # no-op engine stub (no real agent CLI is ever invoked) # # It uses its own temp AGENT_QUEUE_ROOT so it never touches a real queue. # Exit 0 = all good. Run it before every commit. # set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" AQ="$HERE/agent-queue.sh" pass() { printf ' \033[32m✓\033[0m %s\n' "$*"; } info() { printf ' \033[36m•\033[0m %s\n' "$*"; } fail() { printf ' \033[31m✗ %s\033[0m\n' "$*" >&2; exit 1; } tmp="$(mktemp -d "${TMPDIR:-/tmp}/aq-selftest.XXXXXX")" cleanup() { rm -rf "$tmp"; } trap cleanup EXIT echo "agent-queue self-test" # 1. shellcheck (optional) if command -v shellcheck >/dev/null 2>&1; then shellcheck --severity=error --shell=bash "$AQ" "${BASH_SOURCE[0]}" && pass "shellcheck (errors): clean" else info "shellcheck not installed — skipping" fi # 2. syntax bash -n "$AQ" && pass "bash -n agent-queue.sh" bash -n "${BASH_SOURCE[0]}" && pass "bash -n selftest.sh" # 3. dashboard syntax (optional) if command -v node >/dev/null 2>&1; then node --check "$HERE/dashboard.mjs" && pass "node --check dashboard.mjs" else info "node not installed — skipping dashboard check" fi # 4. live no-op cycle export AGENT_QUEUE_ROOT="$tmp/queue" stub="$tmp/noop-engine" printf '#!/usr/bin/env bash\n# no-op engine stub: drain stdin, succeed\ncat >/dev/null 2>&1 || true\nexit 0\n' > "$stub" chmod +x "$stub" work="$tmp/work"; mkdir -p "$work" task="$tmp/task.md" printf '%s\n' '---' 'engine: devin' "cwd: $work" 'yolo: true' '---' '' '# self-test no-op task' > "$task" "$AQ" init >/dev/null DEVIN_BIN="$stub" "$AQ" add "$task" >/dev/null DEVIN_BIN="$stub" "$AQ" run --once >/dev/null 2>&1 if ls "$AGENT_QUEUE_ROOT"/review/*.md >/dev/null 2>&1; then pass "no-verify cycle → task parked in review/" else echo "--- queue state ---" >&2 ls -R "$AGENT_QUEUE_ROOT" >&2 || true fail "no-op cycle did not complete (expected a file in review/)" fi # 5. verify-pass gate: rc=0 + passing verify → testing/, then manual ship → shipped/ task2="$tmp/task-verify.md" printf '%s\n' '---' 'engine: devin' "cwd: $work" 'yolo: true' 'verify: true' '---' '' '# self-test verify-pass task' > "$task2" DEVIN_BIN="$stub" "$AQ" add "$task2" >/dev/null DEVIN_BIN="$stub" "$AQ" run --once >/dev/null 2>&1 if ls "$AGENT_QUEUE_ROOT"/testing/*.md >/dev/null 2>&1; then pass "verify-pass cycle → task promoted to testing/" else echo "--- queue state ---" >&2 ls -R "$AGENT_QUEUE_ROOT" >&2 || true fail "verify-pass cycle did not reach testing/ (expected a file in testing/)" fi shipjob="$(basename "$(ls -1t "$AGENT_QUEUE_ROOT"/testing/*.md | head -1)" .md)" "$AQ" ship "$shipjob" >/dev/null 2>&1 if ls "$AGENT_QUEUE_ROOT"/shipped/*.md >/dev/null 2>&1; then pass "manual ship → task landed in shipped/" else fail "ship did not move job to shipped/" fi # 6. verify-fail gate: rc=0 + failing verify → failed/ task3="$tmp/task-verifyfail.md" printf '%s\n' '---' 'engine: devin' "cwd: $work" 'yolo: true' 'verify: false' '---' '' '# self-test verify-fail task' > "$task3" DEVIN_BIN="$stub" "$AQ" add "$task3" >/dev/null DEVIN_BIN="$stub" "$AQ" run --once >/dev/null 2>&1 if ls "$AGENT_QUEUE_ROOT"/failed/*verifyfail*.md >/dev/null 2>&1; then pass "verify-fail cycle → task routed to failed/" else echo "--- queue state ---" >&2 ls -R "$AGENT_QUEUE_ROOT" >&2 || true fail "verify-fail cycle did not route to failed/" fi # status must not error "$AQ" status >/dev/null 2>&1 && pass "status runs clean" echo "self-test PASS"