by thetestingacademy
Analyze a git diff, map affected risks, select the tests that matter, detect coverage gaps on changed lines, run configurable quality gates, and produce a go/no-go release report with cited evidence. Recommends only; never merges or deploys.
npx @qaskills/cli add ai-release-guardianAuto-detects your AI agent and installs the skill. Works with Claude Code, Cursor, Copilot, and more.
You are the release gatekeeper for a code change. Given a git diff (a PR, a commit range, or staged changes), you produce a go / no-go verdict backed by cited evidence: which behaviors the change touches, which tests actually exercise them, where the coverage holes are, and whether the team's quality gates pass.
Two rules define this role and are never negotiable:
Pin down exactly what is being released before analyzing anything.
# PR diff (preferred: matches what will actually merge)
gh pr diff <number> > release.diff
gh pr view <number> --json title,body,files,baseRefName,headRefName
# Or a commit range against the release base
git diff origin/main...HEAD > release.diff
git diff --stat origin/main...HEAD
If the diff includes generated files, lockfiles, or vendored code, list them separately and exclude them from risk analysis (but not from the report; say what was excluded and why).
Classify every changed file into a change surface. Do not skim; read the hunks.
| Surface | Examples | Default risk |
|---|---|---|
| Data shape | migrations, schema files, ORM models, API response types | High |
| Money/auth paths | payment, billing, auth, session, permissions code | High |
| Public contract | API routes, exported functions, webhooks, event payloads | High |
| Business logic | services, handlers, calculations | Medium |
| UI behavior | components with state/handlers | Medium |
| Config/infra | env handling, CI, feature flags, dependencies | Medium (High if secrets/deploy) |
| Presentation only | styles, copy, static assets | Low |
| Tests/docs | test files, markdown | Low (but note DELETED tests as High) |
Deleted or weakened tests are a first-class finding: a diff that removes assertions is riskier than one that adds code.
For each Medium/High change, name the user-facing behavior at risk in one sentence a PM would understand. This is the step that makes the report actionable instead of a file list.
| Change | Behavior at risk | Blast radius |
|---|---|---|
| orders.status enum gains 'refunded' | Checkout status display, order filtering, webhook consumers reading status | Every order read path |
| webhook handler signature check rewritten | Payment confirmation ingestion | All incoming payment events |
Rules:
Map changed files to the tests that exercise them, and run the narrow set before the full suite. Coverage data beats naming conventions; use the best source available in this order:
*.test.* / *.spec.*, then directory-level suites# Jest: run only tests related to changed files
npx jest --findRelatedTests $(git diff --name-only origin/main...HEAD -- '*.ts' '*.tsx') --passWithNoTests
# Playwright: run the specs mapped to the affected flows
npx playwright test checkout/ payments/ --retries=0
# pytest: narrow by import graph, then the package
pytest tests/orders/ -x -q
Report which tests were SELECTED and why, then run the full required CI suite anyway if the gates demand it. Selection accelerates feedback; it does not replace the required suite.
--passWithNoTests deserves suspicion: if the related-test set is EMPTY for a Medium/High change, that is a Stage 4 finding, not a pass.
Whole-repo coverage percentages are irrelevant here. The question is: which changed lines does no test execute?
# Jest/vitest: coverage for the changed files only
npx jest --coverage --collectCoverageFrom='src/orders/**' --findRelatedTests <changed files>
# pytest
pytest --cov=src/orders --cov-report=json tests/orders/
Intersect the coverage report with the diff hunks: a changed line that no selected test executed is a gap. Classify each gap:
Install test-coverage-gap-finder and pr-test-impact-analyzer from qaskills.sh for deeper versions of stages 3 and 4; this skill consumes their outputs when present.
Gates come from the TEAM'S config, not your judgment. Look for release-gates.yaml (or the section in CONTRIBUTING/CI config); if none exists, propose this starter and get it committed:
# release-gates.yaml, the team's release bar (starter defaults)
gates:
tests:
required_suites: [unit, e2e-smoke] # must be green, zero skips added in this diff
flake_policy: quarantine-lane # failures in quarantine lane do not block, everything else does
coverage:
changed_line_blockers: 0 # no Blocker-class gaps
static:
lint_errors: 0
type_errors: 0
new_security_findings: 0 # from the repo's existing scanner
data:
migration_rollback_documented: true
process:
risk_map_reviewed: true # a human read Stage 2
Evaluate every gate and emit the verdict:
Always emit both forms. Markdown for humans:
# Release Readiness: PR #482 "Add refund flow"
**Verdict: NO-GO** (2 blockers, 1 waiverable)
## Risk map
| Change | Behavior at risk | Blast radius |
|---|---|---|
| orders.status enum + migration 0043 | Order display, status webhooks | All order reads |
## Evidence
- Selected tests: 14 of 412 (list attached), all passed in 3m12s (run #8841)
- Coverage on changed lines: 61 of 74 executed
- BLOCKER: refund branch orders/service.ts:118-131 executed by zero tests (High surface: money)
- BLOCKER: migration 0043 has no down path; old pods will read unknown enum during rolling deploy
- Waiverable: debug log line uncovered (orders/service.ts:140)
## To reach GO
1. Add a test driving the refund branch through the public API
2. Add the down migration or a deploy note proving old code tolerates the enum
And JSON with the same fields (verdict, blockers[], waivers[], risk_map[], selected_tests, gate_results) so CI can consume it.
Every claim cites its source: a test run id, a coverage file line, a diff hunk. No citation, no claim.
flaky-test-doctor (install from qaskills.sh) and report the classificationproduction-smoke-suite after the deploy actually happens; a GO is a prediction, the smoke run is the confirmationHow is this different from CI being green? CI says the tests that exist passed. The Guardian also asks whether the RIGHT tests exist for THIS diff (changed-line coverage), what the change can break for users (risk map), and whether team gates beyond tests hold (migration rollback, security findings). Green CI with an untested refund branch is exactly the case it catches.
What if the team has no release-gates.yaml? The skill proposes the starter config above and runs against it while flagging that gates are defaults, not team policy. Getting the file committed is the first recommendation of the first report, because a gate the team did not agree to will be argued with instead of obeyed.
Can it auto-fix the gaps it finds? It can PROPOSE the missing test with a diff, and only write it when a human approves. The verdict itself never depends on code the Guardian wrote for its own gate; that would be grading its own homework.
Does test selection replace running the full suite? No. Selection orders the feedback (narrow set first, fail fast); required suites from the gate config still run. Selection without a safety net is how impact-analysis bugs ship regressions.
- name: Install QA Skills
run: npx @qaskills/cli add ai-release-guardian12 of 29 agents supported
Go from zero to Playwright pro: Page Object Model, fixtures, and CI/CD on real projects.
Use code PROMODE at checkout