run-test-plan

$npx mdskill add existential-birds/beagle/run-test-plan

Execute YAML test plans sequentially, stopping on first failure with debug output.

  • Solves the need to run structured test plans with setup and health checks.
  • Depends on agent-browser CLI for browser tests and Python for YAML parsing.
  • Parses a YAML file to extract setup commands, health checks, and test cases.
  • Outputs rich debug prompt on first failure, enabling quick troubleshooting.

SKILL.md

.github/skills/run-test-planView on GitHub ↗
---
description: Execute YAML test plan, stop on first failure, output rich debug prompt
name: run-test-plan
disable-model-invocation: true
---

# Run Test Plan

Execute a YAML test plan, run setup commands, health checks, and each test sequentially. Stop on first failure with rich debug output.

## Prerequisites

- **agent-browser CLI**: Browser tests require the `agent-browser` command-line tool to be available on `PATH`

## Arguments

- `--plan <path>`: Path to test plan (default: `docs/testing/test-plan.yaml`)
- `--skip-setup`: Skip setup commands and health checks (for re-running after failure)

## Step 1: Parse Test Plan

Read and validate the test plan:

```bash
# Resolve plan path from --plan (default shown)
PLAN_PATH="${PLAN_PATH:-docs/testing/test-plan.yaml}"

# Check file exists
ls "$PLAN_PATH" || { echo "Error: Test plan not found: $PLAN_PATH"; exit 1; }

# Validate YAML
python3 -c "import yaml; yaml.safe_load(open('$PLAN_PATH'))" || { echo "Error: Invalid YAML: $PLAN_PATH"; exit 1; }
```

Extract from the YAML:
- `setup.commands`: List of setup commands
- `setup.health_checks`: List of URLs to poll
- `tests`: Array of test cases

## Step 2: Run Setup (unless --skip-setup)

### 2a. Check Prerequisites

If `setup.prerequisites` exists, verify each one:

```bash
# For each prerequisite in setup.prerequisites
<prerequisite.check> || { echo "Prerequisite not met: <prerequisite.name>"; exit 1; }
```

### 2b. Set Environment Variables

If `setup.env` exists, export each variable. Variables using `${VAR}` syntax should be resolved from the current environment:

```bash
# For each key/value in setup.env
export <key>="<value>"
```

### 2c. Build

If `setup.build` exists, execute build commands sequentially:

```bash
# For each command in setup.build
<command> || { echo "Build failed: <command>"; exit 1; }
```

### 2d. Start Services

If `setup.services` exists, start long-running processes and wait for health checks:

```bash
# For each service in setup.services
nohup <service.command> > .beagle/service-<index>.log 2>&1 &
echo $! > .beagle/service-<index>.pid
```

For each service with a `health_check`, poll until ready:

```bash
timeout=<service.health_check.timeout or 30>
url=<service.health_check.url>
elapsed=0

while [ $elapsed -lt $timeout ]; do
  if curl -s -o /dev/null -w "%{http_code}" "$url" | grep -qE "^(200|301|302)"; then
    echo "✓ Health check passed: $url"
    break
  fi
  sleep 2
  elapsed=$((elapsed + 2))
done

if [ $elapsed -ge $timeout ]; then
  echo "✗ Health check timeout: $url"
  exit 1
fi
```

### 2e. Legacy Setup Format

If the plan uses the older flat format (`setup.commands` + `setup.health_checks` instead of `prerequisites`/`build`/`services`), fall back to executing `setup.commands` sequentially and polling `setup.health_checks` as before.

## Step 3: Gate — setup ready before tests

Do not start **Step 4** until each condition you can check is true:

1. **Plan load:** The file from `--plan` (default `docs/testing/test-plan.yaml`) exists and parses as YAML (same checks as Step 1).
2. **Setup branch:**
   - If **not** using `--skip-setup`: Every `setup.prerequisites` check that exists exited 0; every `setup.build` command succeeded; every service `health_check` reached HTTP 200, 301, or 302 within its timeout **or** legacy `setup.health_checks` passed after `setup.commands`.
   - If using `--skip-setup`: Before TC-01, confirm anything the plan still needs is alive—at minimum one successful `curl` (or equivalent) to each URL in `setup.health_checks` or each `setup.services[].health_check.url` that the tests depend on.
3. **Evidence path:** `mkdir -p docs/testing/evidence` succeeds and the directory exists.

If any gate fails, stop, fix setup or flags, and do not execute tests.

## Step 4: Execute Tests Sequentially

For each test in the plan:

### 4a. Log Test Start

```markdown
## Running: TC-XX - <test.name>

Context: <test.context>
```

### 4b. Execute Steps

For each step in `test.steps`, determine the step type and execute accordingly:

**Shell commands (`run:` steps):**

The most common step type. Execute the command via Bash and capture stdout, stderr, and exit code:

```bash
# Execute the command, capture output and exit code
<command> 2>&1
echo "EXIT_CODE: $?"
```

Capture all output for evaluation in step 4c. Shell steps cover:
- CLI binary invocations (e.g., `./target/debug/myapp status --all`)
- Database queries (e.g., `psql "${DATABASE_URL}" -c "SELECT ..."`)
- File inspection (e.g., `ls -la /path/to/expected/output`)
- Process lifecycle checks (e.g., `timeout 5 ./myapp 2>&1 || true`)
- Any other command a human would type in a terminal

**curl actions (`action: curl` steps):**

```bash
curl -X <method> \
  -H "Content-Type: application/json" \
  <additional headers> \
  -d '<body>' \
  "<url>" \
  -o response.json \
  -w "%{http_code}" > status_code.txt

# Capture response for evaluation
cat response.json
cat status_code.txt
```

**agent-browser CLI actions:**

Steps starting with `agent-browser` are browser automation commands:

```bash
# Navigate
agent-browser open <url>

# Snapshot interactive elements (always do before interacting)
agent-browser snapshot -i

# Interact using refs from snapshot output (@e1, @e2, etc.)
agent-browser fill @<ref> "<value>"
agent-browser click @<ref>

# Wait for conditions
agent-browser wait --url "<pattern>"
agent-browser wait --text "<text>"
agent-browser wait --load networkidle

# Capture evidence
agent-browser screenshot docs/testing/evidence/<test.id>.png
```

**Important:** Always run `agent-browser snapshot -i` before interacting with elements to get valid refs, and re-snapshot after navigation or significant DOM changes.

Save screenshots to `docs/testing/evidence/<test.id>.png`

### 4c. Evaluate Result

**Gate — artifacts before PASS/FAIL:**

- **`run:` steps:** Stdout and stderr captured; exit code recorded (e.g. `EXIT_CODE:` line or equivalent).
- **`action: curl` steps:** Response body and HTTP status captured to known paths (e.g. `response.json`, `status_code.txt` or paths the plan specifies).
- **agent-browser steps:** After any navigation or DOM change, a fresh `agent-browser snapshot -i` exists before asserting; if the test records evidence, the screenshot file path is created or failure is explicit.

Then, using agent reasoning, compare actual outcome against `test.expected`:

- Read the expected behavior description
- Compare with actual response/screenshot
- Determine PASS or FAIL

### 4d. On PASS

```markdown
✓ TC-XX PASSED: <test.name>
```

Continue to next test.

### 4e. On FAIL

Stop immediately. Go to Step 6.

## Step 5: On All Tests Pass

```markdown
## Test Results: ALL PASSED

| ID | Name | Result |
|----|------|--------|
| TC-01 | <name> | ✓ PASS |
| TC-02 | <name> | ✓ PASS |
| ... | ... | ... |

**Total:** N/N tests passed

### Evidence

Screenshots saved to `docs/testing/evidence/`

### Cleanup

Stopping background services...
```

Clean up:
```bash
# Kill background services
for pidfile in .beagle/service-*.pid .beagle/dev-server.pid; do
  if [ -f "$pidfile" ]; then
    kill $(cat "$pidfile") 2>/dev/null
    rm "$pidfile"
  fi
done
```

## Step 6: On Failure - Generate Debug Prompt

When a test fails, generate rich debug output:

### 6a. Gather Context

```bash
# Get changed files relevant to the failure
git diff --name-only $(git merge-base HEAD origin/main)..HEAD

# Get recent changes in files mentioned in test.context
git diff $(git merge-base HEAD origin/main)..HEAD -- <relevant_files>
```

### 6b. Output Debug Report

```markdown
## Test Failure: TC-XX - <test.name>

### What Failed

**Test:** <test.name>
**Expected:**
<test.expected>

**Actual:**
<Describe what actually happened - response code, error message, screenshot description>

### Relevant Changes in This PR

<For each file mentioned in test.context or related to the failure:>
- `<file>` (lines X-Y) - <brief description of changes>

### Evidence

<If screenshot exists:>
- Screenshot: `docs/testing/evidence/<test.id>.png`

<If API response:>
- Status code: <code>
- Response body:
```json
<response>
```

### Error Details

<If error message in response or logs:>
```
<error message>
```

### Suggested Investigation

<Based on the error, suggest 2-3 specific things to check:>
1. <First thing to check based on error type>
2. <Second thing related to changed files>
3. <Third thing about environment/setup>

### Debug Session Prompt

Copy this to start a new agent session:

---
I'm debugging a test failure in branch `<branch>`.

**Test:** <test.name>
**Error:** <brief error description>

<Summarize what the test was checking and what went wrong>

Relevant files:
<List changed files related to this test>

Help me investigate why <specific failure reason>.
---
```

### 6c. Preserve Evidence

```bash
# Ensure evidence directory exists
mkdir -p docs/testing/evidence

# Save failure context
cat > docs/testing/evidence/<test.id>-failure.md << 'EOF'
# Failure Report: <test.id>

<Full debug report content>
EOF
```

### 6d. Cleanup and Exit

```bash
# Kill background services
for pidfile in .beagle/service-*.pid .beagle/dev-server.pid; do
  if [ -f "$pidfile" ]; then
    kill $(cat "$pidfile") 2>/dev/null
    rm "$pidfile"
  fi
done
```

## Test Results Summary Table

Always output a summary table showing progress:

```markdown
## Test Results

| ID | Name | Result |
|----|------|--------|
| TC-01 | <name> | ✓ PASS |
| TC-02 | <name> | ✗ FAIL |
| TC-03 | <name> | - SKIP |

**Passed:** 1/3
**Failed:** TC-02
```

Tests after a failure are marked as SKIP (not executed).

## Verification

Before completing:

```bash
# Verify evidence directory exists
ls -la docs/testing/evidence/

# List captured evidence
ls docs/testing/evidence/*.png docs/testing/evidence/*.md 2>/dev/null
```

**Pass conditions (all must be true to call the run complete):**

- **`ls -la docs/testing/evidence/`** exits 0 (directory exists).
- Every test that ran has an explicit PASS or FAIL (not only “felt right”).
- If any test failed: a failure artifact exists (`docs/testing/evidence/<test.id>-failure.md` or equivalent) **or** the debug report was emitted with expected vs actual.
- Cleanup ran: no stale `.beagle/service-*.pid` pointing at live processes you started, unless the plan says to leave them up.

**Verification Checklist:**
- [ ] Setup commands executed successfully (or `--skip-setup` with live dependencies confirmed)
- [ ] Health checks passed before test execution (Step 3 gate)
- [ ] Each executed test has recorded result
- [ ] Evidence captured in `docs/testing/evidence/`
- [ ] On failure: debug prompt includes expected vs actual
- [ ] On failure: relevant PR changes listed
- [ ] Background processes cleaned up

## Gates (ordered)

1. **Plan valid:** YAML parses; required keys for your branch (`setup` / `tests`) are present.
2. **Setup healthy:** Step 3 pass conditions met before TC-01.
3. **Per-test artifacts:** Step 4c gate satisfied before marking PASS.
4. **Stop on fail:** First failure → Step 6; later tests = SKIP in the summary table.
5. **Cleanup:** Step 5 or Step 6d executed so background PIDs from this run are released.

## Rules

- Stop on first test failure (do not continue to other tests)
- Always capture evidence (screenshots, responses)
- Include file:line references in debug prompts when possible
- Use `--skip-setup` flag to re-run after fixing issues
- Never hardcode secrets - use environment variables
- Clean up background processes even on failure
- Preserve failure evidence for debugging
- Make debug prompts copy-paste ready for new sessions

More from existential-birds/beagle

SkillDescription
adr-decision-extractionUse when you need to mine a conversation, session transcript, or design discussion for architectural decisions before writing ADRs. Identifies problem-solution pairs, trade-off debates, technology choices, and explicit \"[ADR]\" tags. Triggers on \"what decisions did we make\", \"extract decisions from this chat\", \"find the choices in our discussion\", or \"summarize architectural decisions\". Also useful after long planning sessions to capture decisions that were made implicitly. Does NOT write ADR documents \u2014 use adr-writing or write-adr for that.
adr-writingUse when writing or formatting an ADR document using the MADR template, applying Definition of Done (E.C.A.D.R.) criteria, or verifying ADR completeness. Triggers on \"write the ADR\", \"format as MADR\", \"check ADR quality\", \"mark gaps in ADR\". Also triggers when a decision has been extracted and needs to become a document. Does NOT extract decisions from conversations (use adr-decision-extraction) or orchestrate the full extract-confirm-write workflow (use write-adr).
agent-architecture-analysisUse when auditing an agent codebase against the 12-Factor Agents methodology, reviewing LLM-powered system architecture, or assessing agentic app compliance. Triggers on \"analyze agent architecture\", \"12-factor audit\", \"how compliant is this agent\", or \"evaluate this LLM app\". Also applies when comparing frameworks or planning agent improvements. Not for quick checklists \u2014 this performs deep per-factor codebase analysis with file-level evidence.
app-intents-code-reviewReviews App Intents code for intent structure, entities, shortcuts, and parameters. Use when reviewing code with import AppIntents, @AppIntent, AppEntity, AppShortcutsProvider, or @Parameter.
artifact-analysisUse when the user wants a cited, structured read of local documents and project knowledge. Triggers on: \"analyze these docs\", \"scan my project for context\", \"read the docs folder\", \"summarize what's in .beagle/concepts/\", \"extract context from docs/\", \"what's in this folder\", \"go read everything in X and tell me what's there\". Also invoked programmatically by other beagle skills (prfaq-beagle Ignition, brainstorm-beagle reference points, strategy-interview context grounding) via the companion contract. Does NOT trigger on codebase lookups (\"find this function\", \"search the repo\"), web research (use web-research), LLM-as-judge evaluation (use llm-judge), or document editing (use humanize-beagle). Produces a written scan plan, parallel-subagent findings, and a cited synthesis report on disk — never inline prose, never unsourced claims.
axum-code-reviewReviews axum web framework code for routing patterns, extractor usage, middleware, state management, and error handling. Use when reviewing Rust code that uses axum, tower, or hyper for HTTP services. Covers axum 0.7+ patterns including State, Path, Query, Json extractors.
brainstorm-beagleUse when the user has a fuzzy idea and wants to shape it into a concrete project spec before planning or building. Triggers on: \"brainstorm this\", \"I have an idea for...\", \"help me think through this project\", \"what should I build\", \"spec this out\". Also catches vague feature descriptions needing structured questioning to clarify scope. Does NOT write code, plan implementation, review strategy docs, or run strategy interviews \u2014 produces a WHAT/WHY spec through dialogue, not a HOW plan.
bubbletea-code-reviewReviews BubbleTea TUI code for proper Elm architecture, model/update/view patterns, and Lipgloss styling. Use when reviewing terminal UI code using charmbracelet/bubbletea.
cloudkit-code-reviewReviews CloudKit code for container setup, record handling, subscriptions, and sharing patterns. Use when reviewing code with import CloudKit, CKContainer, CKRecord, CKShare, or CKSubscription.
combine-code-reviewReviews Combine framework code for memory leaks, operator misuse, and error handling. Use when reviewing code with import Combine, AnyPublisher, @Published, PassthroughSubject, or CurrentValueSubject.