Skip to content
Development
Skill

/validate-pr

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

From plugin
claude-night-market
343200 skills60 agents162 commands1 MCP

Claude Code

$ npx -y skills add athola/claude-night-market --skill validate-pr --agent claude-code

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
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 exists to catch: a r

Read more
@athola
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.

343200 skills60 agents1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
343
Star history of athola/claude-night-market on GitHub, a chart by star-history.com

Other skills on claude-night-market.

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about validate-pr.

Which coding agents can use validate-pr?
validate-pr installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.