Skip to content
Development
Skill

/validate-pr

Generates and self-executes a diff-derived test plan for a PR.

From plugin
claude-night-market
337200 skills59 agents162 commands1 MCP
Install
$ npx -y skills add athola/claude-night-market --skill validate-pr --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/validate-pr

Context preview

The summary Claude sees to decide when to auto-load this skill.

Generates and self-executes a diff-derived test plan for a PR.

SKILL.md

validate-pr.SKILL.md
name: validate-pr
description: Generates and self-executes a diff-derived test plan for a PR.
  Use when validating PR changes before merge. Do not use for code review;
  use sanctum:pr-review.
alwaysApply: false
category: validation
tags:
- pr
- validation
- test-plan
- diff
- revert-test
- evidence
tools: []
usage_patterns:
- diff-derived-test-plan
- revert-test-quality-check
- evidence-capture
complexity: intermediate
model_hint: standard
estimated_tokens: 650
progressive_loading: false
dependencies:
- leyline:git-platform
- imbue:proof-of-work
role: entrypoint

validate-pr: Diff-Derived Test Plan

Generate and self-execute a validation plan matched to what actually changed in a PR. Replaces generic "tests pass" with area-targeted evidence and revert-test quality checks that prove tests catch regressions.

When To Use

  • End of `/fix-pr` Step 5 (Validate), before Step 6 (Complete)
  • Standalone after any PR fix, to generate targeted validation evidence
  • When you need proof that revert-tests are genuine guards

When NOT To Use

  • `--scope minor` with only formatting or doc changes (no logic changed)
  • No diff available (clean branch, nothing changed)
  • `--skip-validate` passed to `/fix-pr`

Algorithm

fetch diff -> group by area -> generate steps -> execute -> revert-test -> table

Step 1: Fetch Diff and Detect Areas

# Get changed file list from the PR
PR_NUMBER=<number from invocation or current branch>
CHANGED=$(gh pr diff "$PR_NUMBER" --name-only)
# Fallback when no PR number:
# CHANGED=$(git diff "origin/$(git rev-parse --abbrev-ref HEAD@{upstream})...HEAD" \
#   --name-only 2>/dev/null)

Group changed files into areas using ripgrep (grep if rg unavailable):

RUST_FILES=$(echo "$CHANGED"  | rg '\.rs$|Cargo\.(toml|lock)$' || true)
PY_FILES=$(echo "$CHANGED"    | rg '\.py$|pyproject\.toml$|requirements.*\.txt$' || true)
SH_FILES=$(echo "$CHANGED"    | rg '\.sh$|\.githooks' || true)
GRAMMAR_FILES=$(echo "$CHANGED" | rg '\.(lark|peg|g4)$' || true)

Area routing table:

| Area | File patterns | Verification type | |------|---------------|-------------------| | Rust | `*.rs`, `Cargo.toml`, `Cargo.lock` | cargo build + per-crate test | | Python | `*.py`, `pyproject.toml` | pytest per changed module | | Shell | `*.sh`, `.githooks/*` | shellcheck | | Grammar | `*.lark`, `*.peg`, `*.g4` | language-specific lint | | Build/config | `*.yaml`, `*.json`, `*.toml` | parse check |

Step 2: Generate and Execute Steps per Area

For each non-empty area, generate and run at least one verification step. Assign `[E1]`, `[E2]`, ... labels to each captured output.

Rust

# Build with default features
cargo build --workspace 2>&1
# Evidence: [En] → "0 errors, 0 warnings"

# Build with --all-features
cargo build --workspace --all-features 2>&1
# Evidence: [En+1]

# Per-crate test for each changed crate
# Extract crate directory from changed path, e.g. crates/token-types/src/lib.rs
CHANGED_CRATES=$(echo "$RUST_FILES" \
  | rg -o '(?:crates|src)/[^/]+' \
  | sort -u \
  | xargs -I{} basename {})
for CRATE in $CHANGED_CRATES; do
  cargo test -p "$CRATE" 2>&1
done

Python

# Targeted test per changed module
for PY_FILE in $PY_FILES; do
  MODULE=$(basename "${PY_FILE%.py}")
  TEST_FILE="tests/test_${MODULE}.py"
  if [[ -f "$TEST_FILE" ]]; then
    uv run pytest "$TEST_FILE" -v 2>&1
  fi
done

# Or project-specific runner if Makefile target exists
make test 2>&1 || uv run pytest tests/ -v 2>&1

Shell

for SH_FILE in $SH_FILES; do
  [[ -f "$SH_FILE" ]] && shellcheck "$SH_FILE" 2>&1
done

Build/config parse check

# YAML files
for YML in $(echo "$CHANGED" | rg '\.ya?ml$' || true); do
  [[ -f "$YML" ]] && python3 -c "import yaml; yaml.safe_load(open('$YML'))" \
    && echo "PASS: $YML" || echo "FAIL: $YML"
done

# JSON files
for JSON_F in $(echo "$CHANGED" | rg '\.json$' || true); do
  [[ -f "$JSON_F" ]] && python3 -m json.tool "$JSON_F" > /dev/null \
    && echo "PASS: $JSON_F" || echo "FAIL: $JSON_F"
done

Step 3: Revert-Test Quality Check

Prove at least one test is a genuine guard, not a dead assertion.

**Safety: abort if the working tree has uncommitted changes.**

if ! git diff --exit-code > /dev/null 2>&1; then
  echo "[RT] SKIP: working tree dirty: revert-test unsafe"
  # Mark INCONCLUSIVE and continue
fi

**Algorithm (one representative fix):**

1. From the changed source files, find one that has a corresponding test.

  • Rust: a `#[test]` in the same crate that exercises a changed function.
  • Python: `tests/test_<module>.py` for a changed `<module>.py`.
  • Shell: a test harness that invokes the changed script.

2. Identify the specific changed line or block from the diff. 3. Edit that line to revert the fix to its broken state. 4. Run the targeted test: confirm it **FAILS** with exit code `1` specifically. A pytest usage error (`4`) or an empty collection (`5`) is also non-zero, so a harness that only checks "not zero" reports a dead assertion as a genuine guard. 5. Restore: `git checkout -- <file>` (git-based restore, safe on interrupt). 6. Run the targeted test again: confirm it **PASSES**. 7. If any step cannot complete, mark INCONCLUSIVE with the reason.

**Revert-test output format:**

[RT-1] Target: <file>:<line>: <description of fix>
[RT-2] Broke fix: <edit description>
[RT-3] Ran: <test command> → <test name> FAILED (expected)
[RT-4] Restored: git checkout -- <file>
[RT-5] Ran: <test command> → <test name> PASSED
Result: PASS: test is a genuine guard

**Reverting a test that guards document content:**

Content tests assert on prose, and this repo wraps prose at 80 columns, so any anchor phrase long enough to be meaningful eventually straddles a line break. Collapse whitespace before matching. Otherwise a pure reflow turns the test red and tempts an author to "fix" it by unwrapping the line.

Normalizing reintroduces the hazard the revert test

Read more
Ships withclaude-night-market

A plugin marketplace for Claude Code. Install only the plugins you need to run git workflows, code review, spec-driven development, and autonomous agents from inside your Claude Code session.

Get the whole plugin

Other skills on claude-night-market.