#!/usr/bin/env bash
set -euo pipefail

tmp_dir="$(mktemp -d)"
cleanup() {
  rm -rf "$tmp_dir"
}
trap cleanup EXIT

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
agentrail="${repo_dir}/scripts/agentrail"

assert_grep() {
  local pattern="$1"
  local file="$2"
  local message="$3"

  if ! grep -q -- "$pattern" "$file"; then
    echo "$message" >&2
    echo "--- output ---" >&2
    cat "$file" >&2
    exit 1
  fi
}

target="${tmp_dir}/target"
log_dir="${tmp_dir}/runs"
mkdir -p "$target" "$log_dir"
"$agentrail" install --target "$target" >/dev/null
cat >"${target}/package.json" <<'JSON'
{
  "dependencies": {
    "react": "latest"
  }
}
JSON

config_file="${target}/.agentrail/config.json"
[[ -f "$config_file" ]] || { echo "AgentRail install did not create .agentrail/config.json" >&2; exit 1; }

cat >"${target}/scripts/ralph-loop" <<'STALE_RALPH'
#!/usr/bin/env bash
echo stale legacy ralph should not run >&2
exit 42
STALE_RALPH
chmod +x "${target}/scripts/ralph-loop"

if [[ "$(node - "$config_file" <<'NODE'
const fs = require("fs");
const config = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
console.log(Boolean(config.runner && config.runner.name === "codex" && config.runner.command === "codex exec --sandbox danger-full-access -"));
NODE
)" != "true" ]]; then
  echo "Default runner config was not written" >&2
  cat "$config_file" >&2
  exit 1
fi

"$agentrail" run issue 12 --agent codex --target "$target" --command cat >"${tmp_dir}/codex-run.out"
assert_grep "AgentRail run: issue #12" "${tmp_dir}/codex-run.out" "Codex run did not announce issue"
assert_grep "agent: codex" "${tmp_dir}/codex-run.out" "Codex run did not announce agent"
assert_grep "Run one bounded AgentRail issue execution for exactly one GitHub issue: #12" "${tmp_dir}/codex-run.out" "Codex run did not pipe generated issue prompt"
assert_grep "AgentRail state: present" "${tmp_dir}/codex-run.out" "Codex run prompt did not include state summary"
assert_grep "Resolved AgentRail skills" "${tmp_dir}/codex-run.out" "Codex run prompt did not include resolved skills"
assert_grep "frontend-web" "${tmp_dir}/codex-run.out" "Codex run prompt did not include resolved frontend-web skill"

codex_default_runs="${target}/.agentrail/runs"
codex_metadata="$(find "$codex_default_runs" -name run.json -print | head -1)"
codex_prompt="$(find "$codex_default_runs" -name prompt.md -print | head -1)"
codex_resolved_skills="$(find "$codex_default_runs" -name resolved-skills.json -print | head -1)"
[[ -n "$codex_metadata" && -n "$codex_prompt" && -n "$codex_resolved_skills" ]] || { echo "Codex run did not write prompt, resolved skills, and metadata logs" >&2; exit 1; }
codex_run_dir="$(dirname "$codex_metadata")"
assert_grep '"targetIssue": 12' "$codex_metadata" "Codex metadata did not record target issue"
assert_grep '"agent": "codex"' "$codex_metadata" "Codex metadata did not record agent"
assert_grep '"command": "cat"' "$codex_metadata" "Codex metadata did not record command"
assert_grep "prompt.md" "$codex_metadata" "Codex metadata did not record prompt reference"
assert_grep "resolved-skills.json" "$codex_metadata" "Codex metadata did not record resolved skills reference"
assert_grep '"resolvedSkills"' "$codex_metadata" "Codex metadata did not include resolved skills"
assert_grep '"frontend-web"' "$codex_metadata" "Codex metadata did not include frontend-web resolution"
assert_grep '"contextPackFile": ".agentrail/context/packs/issue-12-plan-' "$codex_metadata" "Codex metadata did not record the issue context pack"
assert_grep '"frontend-web"' "$codex_resolved_skills" "Resolved skills log did not include frontend-web"
for phase in plan execute verify; do
  [[ -f "${codex_run_dir}/${phase}/prompt.md" ]] || { echo "${phase} phase prompt was not written" >&2; exit 1; }
  [[ -f "${codex_run_dir}/${phase}/output.md" ]] || { echo "${phase} phase output was not written" >&2; exit 1; }
  [[ -f "${codex_run_dir}/${phase}/status.json" ]] || { echo "${phase} phase status was not written" >&2; exit 1; }
  [[ -f "${codex_run_dir}/${phase}/metadata.json" ]] || { echo "${phase} phase metadata was not written" >&2; exit 1; }
  assert_grep "\"phase\": \"${phase}\"" "${codex_run_dir}/${phase}/status.json" "${phase} phase status did not record phase"
  assert_grep '"status": "completed"' "${codex_run_dir}/${phase}/status.json" "${phase} phase status did not record completion"
  assert_grep '"startedAt":' "${codex_run_dir}/${phase}/status.json" "${phase} phase status did not record start timestamp"
  assert_grep '"finishedAt":' "${codex_run_dir}/${phase}/status.json" "${phase} phase status did not record finish timestamp"
  assert_grep "\"phase\": \"${phase}\"" "${codex_run_dir}/${phase}/metadata.json" "${phase} phase metadata did not record phase"
  assert_grep '"contextPackFile": ".agentrail/context/packs/issue-12-'"${phase}"'-' "${codex_run_dir}/${phase}/metadata.json" "${phase} phase metadata did not record context pack file"
  if [[ "$phase" == "execute" ]]; then
    assert_grep '.agentrail/source/templates/scripts/ralph-loop --issue 12 --agent-command cat' "${codex_run_dir}/${phase}/metadata.json" "Execute phase metadata did not use hidden Ralph executor"
    assert_grep 'ralph-loop --issue 12 --agent-command cat' "${codex_run_dir}/${phase}/metadata.json" "Execute phase metadata did not record Ralph executor command"
  else
    assert_grep '"command": "cat"' "${codex_run_dir}/${phase}/metadata.json" "${phase} phase metadata did not record command"
  fi
done
assert_grep "Goal" "${codex_run_dir}/plan/prompt.md" "Plan prompt did not require goal"
assert_grep "Context pack:" "${codex_run_dir}/plan/prompt.md" "Plan prompt did not include context pack summary"
assert_grep "Pack file: .agentrail/context/packs/issue-12-plan-" "${codex_run_dir}/plan/prompt.md" "Plan prompt did not point to issue plan context pack"
assert_grep "Non-goals" "${codex_run_dir}/plan/prompt.md" "Plan prompt did not require non-goals"
assert_grep "Acceptance criteria mapping" "${codex_run_dir}/plan/prompt.md" "Plan prompt did not require acceptance criteria mapping"
assert_grep "Expected files/areas" "${codex_run_dir}/plan/prompt.md" "Plan prompt did not require expected files/areas"
assert_grep "Required skills" "${codex_run_dir}/plan/prompt.md" "Plan prompt did not require required skills"
assert_grep "Verification commands" "${codex_run_dir}/plan/prompt.md" "Plan prompt did not require verification commands"
assert_grep "Risks" "${codex_run_dir}/plan/prompt.md" "Plan prompt did not require risks"
assert_grep "Approved plan from the plan phase" "${codex_run_dir}/execute/prompt.md" "Execute prompt did not include the approved plan"
assert_grep "Issue context:" "${codex_run_dir}/execute/prompt.md" "Execute prompt did not include issue context"
assert_grep "AgentRail will invoke the Ralph one-issue executor" "${codex_run_dir}/execute/prompt.md" "Execute prompt did not explain Ralph delegation"
assert_grep "Approved plan from the plan phase" "${codex_run_dir}/execute/output.md" "Execute output did not pass approved plan to Ralph"
assert_grep "Handle exactly one issue: #12" "${codex_run_dir}/execute/output.md" "Execute output did not capture Ralph executor output"
assert_grep "Implementation evidence from the working tree" "${codex_run_dir}/verify/prompt.md" "Verify prompt did not include implementation evidence"
assert_grep "PR/diff references" "${codex_run_dir}/verify/prompt.md" "Verify prompt did not include PR/diff references"
assert_grep "every acceptance criterion" "${codex_run_dir}/verify/prompt.md" "Verify prompt did not include acceptance criteria"
assert_grep "active goal success criteria" "${codex_run_dir}/verify/prompt.md" "Verify prompt did not include active goal success criteria framing"

cat >"${tmp_dir}/large-plan-runner" <<'RUNNER'
#!/usr/bin/env bash
set -euo pipefail
prompt="$(cat)"
first_line="$(printf '%s\n' "$prompt" | sed -n '1p')"
if [[ "$first_line" == "This is phase 1 of 3: plan." ]]; then
  printf 'Goal\n\n'
  printf '%*s\n' 500 '' | tr ' ' X
  exit 0
fi
printf '%s\n' "$prompt"
RUNNER
chmod +x "${tmp_dir}/large-plan-runner"

AGENTRAIL_PHASE_INLINE_MAX_CHARS=120 \
  "$agentrail" run issue 86 --agent codex --target "$target" --command "${tmp_dir}/large-plan-runner" --log-dir "$log_dir" >"${tmp_dir}/large-plan-run.out"
large_plan_run_dir="$(find "$log_dir" -path '*/run.json' -print | xargs grep -l '"targetIssue": 86' | head -1 | xargs dirname)"
[[ -n "$large_plan_run_dir" ]] || { echo "Large-plan run directory was not found" >&2; exit 1; }
assert_grep "AgentRail truncated approved plan output" "${large_plan_run_dir}/execute/prompt.md" "Execute prompt did not truncate oversized plan output"
assert_grep "AgentRail truncated approved plan output" "${large_plan_run_dir}/verify/prompt.md" "Verify prompt did not truncate oversized plan output"
if grep -q "$(printf '%*s' 200 '' | tr ' ' X)" "${large_plan_run_dir}/execute/prompt.md"; then
  echo "Execute prompt included an oversized plan output blob" >&2
  exit 1
fi

state_file="${target}/.agentrail/state.json"
if [[ "$(node - "$state_file" <<'NODE'
const fs = require("fs");
const state = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
const workflow = state.workflow || {};
const completedRun = Array.isArray(workflow.completedRuns) && workflow.completedRuns.find((run) => run.targetIssue === 12 && run.agent === "codex");
const goal = Array.isArray(workflow.goals) && workflow.goals.find((item) => item.id === "issue-12");
console.log(Boolean(
  workflow.phase === "completed" &&
  workflow.activePhase === null &&
  workflow.activeRun === null &&
  workflow.activeIssue === null &&
  completedRun &&
  completedRun.status === "completed" &&
  completedRun.activePhase === "verify" &&
  completedRun.completedAt &&
  completedRun.promptFile &&
  completedRun.metadataFile &&
  goal &&
  goal.kind === "issue" &&
  goal.source === "github:issue/12" &&
  goal.status === "completed" &&
  goal.activeIssue === 12 &&
  Array.isArray(goal.successCriteria) &&
  goal.successCriteria.length > 0 &&
  Array.isArray(goal.nonGoals) &&
  goal.createdAt &&
  goal.updatedAt &&
  goal.completedAt
));
NODE
)" != "true" ]]; then
  echo "AgentRail run did not persist completed run and goal state" >&2
  cat "$state_file" >&2
  exit 1
fi

cat >"$state_file" <<'JSON'
{
  "workflow": {
    "phase": "implementation",
    "activeIssue": 34,
    "activeRun": {
      "runId": "active-34",
      "targetType": "issue",
      "targetIssue": 34,
      "agent": "codex",
      "status": "running",
      "pickedAt": "2026-05-31T08:00:00Z",
      "promptFile": ".agentrail/runs/active-34/prompt.md",
      "metadataFile": ".agentrail/runs/active-34/run.json",
      "runDir": ".agentrail/runs/active-34"
    },
    "completedRuns": [],
    "nextSuggestedAction": "Continue issue #34."
  }
}
JSON

"$agentrail" run --target "$target" >"${tmp_dir}/active-run.out"
assert_grep "active run exists" "${tmp_dir}/active-run.out" "State-first run did not report active run"
assert_grep "issue #34 via codex (running)" "${tmp_dir}/active-run.out" "State-first run did not summarize active run"
assert_grep ".agentrail/runs/active-34/run.json" "${tmp_dir}/active-run.out" "State-first run did not point at metadata"
assert_grep ".agentrail/runs/active-34/prompt.md" "${tmp_dir}/active-run.out" "State-first run did not point at prompt"
assert_grep "next action: Continue issue #34." "${tmp_dir}/active-run.out" "State-first run did not print next action"

if "$agentrail" run issue 35 --target "$target" --command cat --log-dir "$log_dir" >"${tmp_dir}/conflicting-run.out" 2>&1; then
  echo "Explicit issue run ignored conflicting active state" >&2
  exit 1
fi
assert_grep "active run already exists for issue #34" "${tmp_dir}/conflicting-run.out" "Explicit issue run did not reject conflicting active run"

rm -f "$state_file"
if "$agentrail" run --target "$target" >"${tmp_dir}/missing-state-run.out" 2>&1; then
  echo "State-first run accepted missing AgentRail state" >&2
  exit 1
fi
assert_grep "AgentRail state was not found" "${tmp_dir}/missing-state-run.out" "State-first run did not explain missing state"

"$agentrail" install --target "$target" --force >/dev/null

fake_bin="${tmp_dir}/fake-bin"
mkdir -p "$fake_bin"
cat >"${fake_bin}/gh" <<'GH'
#!/usr/bin/env bash
set -euo pipefail

if [[ "$1 $2" == "issue list" ]]; then
  printf '%s\n' "${AGENTRAIL_FAKE_ISSUES_JSON:-[]}"
  exit 0
fi

echo "unexpected gh invocation: $*" >&2
exit 1
GH
chmod +x "${fake_bin}/gh"

AGENTRAIL_FAKE_ISSUES_JSON='[]' PATH="${fake_bin}:$PATH" "$agentrail" run --target "$target" >"${tmp_dir}/no-issues.out"
assert_grep "No pickable GitHub issues found" "${tmp_dir}/no-issues.out" "State-first run did not handle empty queue"

AGENTRAIL_FAKE_ISSUES_JSON='[{"number":77,"title":"Ready work","url":"https://github.com/acme/repo/issues/77"}]' \
  PATH="${fake_bin}:$PATH" \
  "$agentrail" run --target "$target" --command cat --log-dir "$log_dir" >"${tmp_dir}/picked-run.out"
assert_grep "selected issue #77" "${tmp_dir}/picked-run.out" "State-first run did not pick the ready issue"
assert_grep "AgentRail run: issue #77" "${tmp_dir}/picked-run.out" "State-first run did not execute selected issue"

relative_parent="${tmp_dir}/relative-parent"
relative_child="${relative_parent}/child"
mkdir -p "$relative_child"
"$agentrail" install --target "$relative_child" >/dev/null
(
  cd "$relative_parent"
  "$agentrail" run issue 82 --target child --command cat --log-dir logs >"${tmp_dir}/relative-target-run.out"
)
assert_grep "AgentRail run: issue #82" "${tmp_dir}/relative-target-run.out" "Relative target run did not start"
assert_grep "Handle exactly one issue: #82" "${tmp_dir}/relative-target-run.out" "Relative target run did not reach Ralph executor"

cat >"$config_file" <<'JSON'
{
  "schemaVersion": 1,
  "runner": {
    "name": "cursor",
    "command": "cat"
  }
}
JSON

"$agentrail" run issue 78 --target "$target" --log-dir "$log_dir" >"${tmp_dir}/cursor-run.out"
assert_grep "agent: cursor" "${tmp_dir}/cursor-run.out" "Configured cursor runner was not announced"
cursor_metadata="$(grep -Rl '"targetIssue": 78' "$log_dir" | head -1)"
[[ -n "$cursor_metadata" ]] || { echo "Configured cursor runner metadata was not found" >&2; exit 1; }
assert_grep '"agent": "cursor"' "$cursor_metadata" "Configured cursor runner was not written to metadata"

cat >"$config_file" <<'JSON'
{
  "schemaVersion": 1,
  "runner": {
    "name": "custom",
    "command": "cat"
  }
}
JSON

"$agentrail" run issue 79 --target "$target" --log-dir "$log_dir" >"${tmp_dir}/custom-run.out"
assert_grep "agent: custom" "${tmp_dir}/custom-run.out" "Configured custom runner was not announced"

cat >"$config_file" <<'JSON'
{
  "schemaVersion": 1,
  "runner": {
    "name": "custom",
    "command": ""
  }
}
JSON

if "$agentrail" run issue 80 --target "$target" --log-dir "$log_dir" >"${tmp_dir}/empty-custom.out" 2>&1; then
  echo "Runner accepted an empty custom command" >&2
  exit 1
fi
assert_grep "runner command is empty" "${tmp_dir}/empty-custom.out" "Runner did not reject empty custom command"

AGENTRAIL_CLAUDE_COMMAND=cat "$agentrail" run issue 12 --agent claude --target "$target" --log-dir "$log_dir" >"${tmp_dir}/claude-run.out"
assert_grep "agent: claude" "${tmp_dir}/claude-run.out" "Claude run did not announce agent"
assert_grep "Use Claude Code through AgentRail to run one bounded implementation loop for exactly one GitHub issue: #12" "${tmp_dir}/claude-run.out" "Claude run did not use Claude-oriented prompt"

AGENTRAIL_HERMES_COMMAND=cat "$agentrail" run issue 12 --agent hermes --target "$target" --log-dir "$log_dir" >"${tmp_dir}/hermes-run.out"
assert_grep "agent: hermes" "${tmp_dir}/hermes-run.out" "Hermes run did not announce agent"
hermes_metadata="$(grep -Rl '"agent": "hermes"' "$log_dir" | head -1)"
[[ -n "$hermes_metadata" ]] || { echo "Hermes runner metadata was not found" >&2; exit 1; }
assert_grep '"command": "cat"' "$hermes_metadata" "Hermes runner command was not written to metadata"

if "$agentrail" run issue 12 --agent codex --target "$target" --command false --log-dir "$log_dir" >"${tmp_dir}/failed-run.out" 2>&1; then
  echo "Runner accepted a failing agent command" >&2
  exit 1
fi

if [[ "$(node - "$state_file" <<'NODE'
const fs = require("fs");
const state = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
const workflow = state.workflow || {};
const failedRun = Array.isArray(workflow.completedRuns) && workflow.completedRuns.find((run) => run.targetIssue === 12 && run.status === "failed");
console.log(Boolean(
  workflow.phase === "blocked" &&
  workflow.activePhase === null &&
  workflow.activeRun === null &&
  failedRun &&
  failedRun.exitStatus === 1 &&
  /failed/.test(workflow.nextSuggestedAction || "")
));
NODE
)" != "true" ]]; then
  echo "AgentRail run did not persist failed run state" >&2
  cat "$state_file" >&2
  exit 1
fi

cat >"${tmp_dir}/fail-execute" <<'RUNNER'
#!/usr/bin/env bash
set -euo pipefail
prompt="$(cat)"
printf '%s\n' "$prompt"
if grep -q "Handle exactly one issue: #81" <<<"$prompt"; then
  exit 7
fi
RUNNER
chmod +x "${tmp_dir}/fail-execute"

"$agentrail" install --target "$target" --force >/dev/null
if "$agentrail" run issue 81 --agent codex --target "$target" --command "${tmp_dir}/fail-execute" --log-dir "$log_dir" >"${tmp_dir}/failed-execute.out" 2>&1; then
  echo "Runner accepted a failing execute phase" >&2
  exit 1
fi
failed_execute_status="$(find "$log_dir" -path '*/execute/status.json' -print | xargs grep -l '"exitStatus": 7' | head -1)"
[[ -n "$failed_execute_status" ]] || { echo "Failed execute phase status was not found" >&2; exit 1; }
assert_grep '"status": "failed"' "$failed_execute_status" "Failed execute phase did not record failed status"

if [[ "$(node - "$state_file" <<'NODE'
const fs = require("fs");
const state = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
const workflow = state.workflow || {};
const failedRun = Array.isArray(workflow.completedRuns) && workflow.completedRuns.find((run) => run.targetIssue === 81 && run.status === "failed");
const goal = Array.isArray(workflow.goals) && workflow.goals.find((item) => item.id === "issue-81");
console.log(Boolean(
  workflow.phase === "blocked" &&
  workflow.activePhase === null &&
  workflow.activeRun === null &&
  failedRun &&
  failedRun.activePhase === "execute" &&
  failedRun.exitStatus === 7 &&
  goal &&
  goal.status === "blocked" &&
  goal.blockedAt &&
  /execute phase/.test(workflow.nextSuggestedAction || "")
));
NODE
)" != "true" ]]; then
  echo "AgentRail run did not persist failed execute phase state" >&2
  cat "$state_file" >&2
  exit 1
fi

cat >"${tmp_dir}/verify-retry-once" <<'RUNNER'
#!/usr/bin/env bash
set -euo pipefail
prompt="$(cat)"
printf '%s\n' "$prompt"
if [[ "$(printf '%s\n' "$prompt" | sed -n '1p')" == "This is phase 3 of 3: verify." ]]; then
  count_file="${AGENTRAIL_RETRY_COUNT_FILE}"
  count=0
  [[ -f "$count_file" ]] && count="$(cat "$count_file")"
  count=$((count + 1))
  printf '%s\n' "$count" >"$count_file"
  if [[ "$count" -eq 1 ]]; then
    cat <<'JSON'
{
  "summary": "PR body is missing acceptance criteria coverage.",
  "findings": [
    {
      "criterion": "Acceptance criteria coverage",
      "evidence": "PR body has no AC table.",
      "requestedAction": "Add the AC coverage table before verification."
    }
  ]
}
JSON
    exit 9
  fi
fi
RUNNER
chmod +x "${tmp_dir}/verify-retry-once"

"$agentrail" install --target "$target" --force >/dev/null
AGENTRAIL_RETRY_COUNT_FILE="${tmp_dir}/retry-once-count" \
  "$agentrail" run issue 83 --agent codex --target "$target" --command "${tmp_dir}/verify-retry-once" --log-dir "$log_dir" >"${tmp_dir}/retry-once.out"
retry_once_run_dir="$(find "$log_dir" -path '*/run.json' -print | xargs grep -l '"targetIssue": 83' | head -1 | xargs dirname)"
[[ -n "$retry_once_run_dir" ]] || { echo "Retry-once run directory was not found" >&2; exit 1; }
[[ -f "${retry_once_run_dir}/execute-2/prompt.md" ]] || { echo "Second execute attempt prompt was not written" >&2; exit 1; }
assert_grep "Verifier findings from previous failed verify attempt" "${retry_once_run_dir}/execute-2/prompt.md" "Second execute attempt did not receive verifier findings"
assert_grep "PR body is missing acceptance criteria coverage" "${retry_once_run_dir}/execute-2/prompt.md" "Verifier findings summary was not passed to retry execute"
assert_grep '"executionAttempt": 2' "${retry_once_run_dir}/execute-2/metadata.json" "Second execute metadata did not record attempt"

if [[ "$(node - "$state_file" <<'NODE'
const fs = require("fs");
const state = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
const workflow = state.workflow || {};
const completedRun = Array.isArray(workflow.completedRuns) && workflow.completedRuns.find((run) => run.targetIssue === 83 && run.status === "completed");
console.log(Boolean(
  workflow.phase === "completed" &&
  completedRun &&
  completedRun.executionAttempt === 2 &&
  completedRun.maxExecutionAttempts === 5 &&
  completedRun.failedVerificationAttempts === 1
));
NODE
)" != "true" ]]; then
  echo "AgentRail run did not persist pass-after-retry state" >&2
  cat "$state_file" >&2
  exit 1
fi

cat >"${tmp_dir}/verify-always-fails" <<'RUNNER'
#!/usr/bin/env bash
set -euo pipefail
prompt="$(cat)"
printf '%s\n' "$prompt"
if [[ "$(printf '%s\n' "$prompt" | sed -n '1p')" == "This is phase 3 of 3: verify." ]]; then
  printf '{"summary":"Still failing","findings":[{"requestedAction":"Fix remaining verifier finding."}]}\n'
  exit 6
fi
RUNNER
chmod +x "${tmp_dir}/verify-always-fails"

"$agentrail" install --target "$target" --force >/dev/null
if "$agentrail" run issue 84 --agent codex --target "$target" --command "${tmp_dir}/verify-always-fails" --log-dir "$log_dir" >"${tmp_dir}/retry-five.out" 2>&1; then
  echo "Runner accepted verifier failures after max attempts" >&2
  exit 1
fi
retry_five_run_dir="$(find "$log_dir" -path '*/run.json' -print | xargs grep -l '"targetIssue": 84' | head -1 | xargs dirname)"
[[ -n "$retry_five_run_dir" ]] || { echo "Fail-after-5 run directory was not found" >&2; exit 1; }
[[ -f "${retry_five_run_dir}/verify-5/status.json" ]] || { echo "Fifth verify attempt status was not written" >&2; exit 1; }
[[ ! -e "${retry_five_run_dir}/execute-6" ]] || { echo "Runner executed more than five attempts" >&2; exit 1; }
assert_grep '"status": "failed"' "${retry_five_run_dir}/verify-5/status.json" "Fifth verify attempt did not fail"

if [[ "$(node - "$state_file" <<'NODE'
const fs = require("fs");
const state = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
const workflow = state.workflow || {};
const failedRun = Array.isArray(workflow.completedRuns) && workflow.completedRuns.find((run) => run.targetIssue === 84 && run.status === "failed");
const goal = Array.isArray(workflow.goals) && workflow.goals.find((item) => item.id === "issue-84");
console.log(Boolean(
  workflow.phase === "blocked" &&
  failedRun &&
  failedRun.activePhase === "verify" &&
  failedRun.executionAttempt === 5 &&
  failedRun.maxExecutionAttempts === 5 &&
  failedRun.failedVerificationAttempts === 5 &&
  goal &&
  goal.status === "blocked" &&
  /maximum verifier retry attempts/.test(goal.blockedReason || "") &&
  /maximum verifier retry attempts/.test(failedRun.blockedReason || "") &&
  /maximum verifier retry attempts/.test(workflow.nextSuggestedAction || "")
));
NODE
)" != "true" ]]; then
  echo "AgentRail run did not persist fail-after-5 blocked state" >&2
  cat "$state_file" >&2
  exit 1
fi

cat >"${tmp_dir}/verify-invalid-output" <<'RUNNER'
#!/usr/bin/env bash
set -euo pipefail
prompt="$(cat)"
printf '%s\n' "$prompt"
if [[ "$(printf '%s\n' "$prompt" | sed -n '1p')" == "This is phase 3 of 3: verify." ]]; then
  count_file="${AGENTRAIL_INVALID_COUNT_FILE}"
  count=0
  [[ -f "$count_file" ]] && count="$(cat "$count_file")"
  count=$((count + 1))
  printf '%s\n' "$count" >"$count_file"
  if [[ "$count" -eq 1 ]]; then
    printf 'not json verifier output\n'
    exit 5
  fi
fi
RUNNER
chmod +x "${tmp_dir}/verify-invalid-output"

"$agentrail" install --target "$target" --force >/dev/null
AGENTRAIL_INVALID_COUNT_FILE="${tmp_dir}/invalid-count" \
  "$agentrail" run issue 85 --agent codex --target "$target" --command "${tmp_dir}/verify-invalid-output" --log-dir "$log_dir" >"${tmp_dir}/invalid-retry.out"
invalid_retry_run_dir="$(find "$log_dir" -path '*/run.json' -print | xargs grep -l '"targetIssue": 85' | head -1 | xargs dirname)"
[[ -n "$invalid_retry_run_dir" ]] || { echo "Invalid-output retry run directory was not found" >&2; exit 1; }
assert_grep '"source": "fallback"' "${invalid_retry_run_dir}/verify/findings.json" "Invalid verifier output did not produce fallback findings"
assert_grep "Verifier failed but did not provide structured JSON findings" "${invalid_retry_run_dir}/execute-2/prompt.md" "Fallback findings were not passed to retry execute"

if "$agentrail" run issue 12 --agent codex --target "$target" --command definitely-not-agentrail-runner --log-dir "$log_dir" >"${tmp_dir}/missing-command.out" 2>&1; then
  echo "Runner accepted a missing command" >&2
  exit 1
fi
assert_grep "missing required command: definitely-not-agentrail-runner" "${tmp_dir}/missing-command.out" "Runner did not reject missing command"

if "$agentrail" run issue nope --agent codex --target "$target" --command cat --log-dir "$log_dir" >"${tmp_dir}/invalid-issue.out" 2>&1; then
  echo "Runner accepted a non-numeric issue" >&2
  exit 1
fi
assert_grep "run issue argument must be numeric" "${tmp_dir}/invalid-issue.out" "Runner did not reject non-numeric issue"

echo "runner adapter test passed"
