Verify PR Fix
Prove a bug-fix PR works by reproducing the failure first, then showing the fix removes it, with evidence a reader can check. A fix that "passes" means nothing unless you first showed the bug.
Resolve writing style before authoring human-facing prose. Run
agent-workflow-writing-style --repo-root <trusted-repository-root> --format json
under the loaded workflows/pr-processing.md contract before writing PR or
issue comments. Apply it only to explanatory prose; keep the output template,
captured proof, results table, caveat, and UNKNOWN evidence intact.
This is behavioral verification, distinct from the local lint/test loop in .agents/skills/verify/SKILL.md
($verify) and from review skills ($adversarial-pr-review, $post-merge-audit). Use this when the
question is "does the fix actually fix the reported problem?", not "does it lint and pass CI?".
Memorable invocation: $verify-pr-fix <PR> or "manually verify this fix and reproduce the issue".
Core principles
- Show the bug before the fix. Always capture a failing "before" and a passing "after" of the same reproduction. Skipping the "before" is the most common way a verification lies.
- Faithful over convenient. Reproduce through the same code path / API the product uses. If you must
build a harness, build it on the real mechanism (the actual module, the real
cluster/HTTP/render path), not a paraphrase of it. - Evidence before assertions. Never claim "verified" without captured output, a state check
(
ps/pgrep, HTTP status, DOM, exit code), or a screenshot. Paste the real output, including PIDs, codes, and timings. Never fabricate or assume output (seeAGENTS.md). - State what you did NOT exercise. If the reproduction is mechanism-level rather than the full app, say so plainly and name the residual-risk path. Honesty about scope is part of the deliverable.
- Reproduce the actual condition. Many bugs are intermittent or race-dependent. Recreate the triggering condition deterministically (the right input, a settle delay, the blocking state, the concurrency) before concluding "no repro". A clean run can mean you never triggered the bug, not that it is absent.
- Leave the machine clean. Kill every process you spawned and remove scratch files. Verify with
pgrep/psthat nothing leaked.
Instructions
Read the PR and the linked issue.
gh pr view <n> --json title,body,files,commits,url,stateandgh issue view <linked> --json title,body,url. Extract: the claimed bug, the expected vs actual behavior, the reproduction the reporter described, and the validation the author already ran (look for a "Manual / Residual Risk" or "Validation" section — verify what they left UNKNOWN).Locate the changed surface. Read the diff (
gh pr diff <n>or the files in the worktree). Identify the exact behavioral change and the smallest observable signal that distinguishes broken from fixed (an orphaned process, an HTTP 500 vs 200, a hydration mismatch, a cache key collision, an exit code).Choose the cheapest faithful reproduction, in this order:
- Full app run when feasible — highest fidelity. This often means the repo's integration test
app(s) plus
.agents/bin/testand any repo-specific e2e/manual-testing docs for browser-visible behavior. - Minimal faithful harness when the full app is too heavy to stand up quickly (needs a license,
real bundles, a renderer, external services). Build it on the same real API the product uses and
label it mechanism-level. For example, drive the same underlying runtime/process API the product
uses (such as Node's real
clustermodule a renderer uses) rather than booting the whole subsystem. - For renderer/process-level changes, follow any repo-specific validation docs (see
AGENTS.md).
- Full app run when feasible — highest fidelity. This often means the repo's integration test
app(s) plus
Reproduce the bug (the "before"). Run the reproduction against pre-fix behavior. Get pre-fix code by the least invasive means: check out the parent commit in a scratch worktree,
git stashan uncommitted change, check out one file at its pre-fix revision (git checkout <fix-commit>~1 -- <file>), or (for a harness) model the pre-fix path explicitly. Capture the failure. If it does not fail, you have not reproduced it — recreate the triggering condition (input, timing, blocking, concurrency) and retry before concluding anything.Verify the fix (the "after"). Restore the post-fix code first (
git stash pop,git checkout HEAD -- <file>, or leave the worktree), then run the identical reproduction. Capture the now-passing result and confirm the specific signal flipped (orphans 6/6 -> 0/6, exit code, status, DOM). Confirmgit statusis clean so the "after" really ran against post-fix code.Capture evidence. Save real terminal output. For a static UI/browser change, capture paired before/after screenshots. For interaction, transition, loading, timing, or other temporal behavior, capture paired short clips through the repository's browser harness. Follow the Durable Visual Evidence Gate for portable recorder setup, reviewer-visible evidence, and artifact handling: prefer
.agents/workflows/pr-processing.md; otherwise resolve../../workflows/pr-processing.mdrelative to the loaded skill pack. For a shareable visual of terminal results you may render the captured output with the visualize tool, but the render must reproduce real output verbatim — never stage numbers.Clean up. Kill spawned processes and remove scratch dirs/worktrees, but preserve or move locally prepared
human_attachment_pendingevidence to a non-scratch location until it is attached. Confirm nothing leaked (pgrep -fl <marker>should report none).Report to the PR. Post a comment with the structured format below. Before posting to GitHub (an outward-facing action), confirm with the user unless they already told you to post. Write the body to a temp file and store its path in
comment_body_file. ResolvePR_BATCH_SKILL_DIRfrom an explicit environment value, thepr-batchsibling of the exact loaded skill directory, or repo-local.agents/skills/pr-batch, in that order; stop with a precise blocker if none exists. SetAGENT_COMMENT_RUNNERto exactlycodex,claude, orcursor,AGENT_COMMENT_HOSTto the actual non-empty single-line host label, andAGENT_COMMENT_TASK_OR_RUNto the stable task or run identifier. Stop if any value is unavailable; do not invent attribution values. Send the file through the helper's stdin explicitly:"${PR_BATCH_SKILL_DIR}/bin/github-comment-envelope" post-issue \ --repo "${REPO}" --number "${PR_NUMBER}" \ --runner "${AGENT_COMMENT_RUNNER:?}" --host "${AGENT_COMMENT_HOST:?}" \ --task-or-run "${AGENT_COMMENT_TASK_OR_RUN:?}" < "${comment_body_file}"For local evidence, add a repeatable
--attach 'path#alt text'to that helper command for images (omit#alt textfor video). The helper preserves the envelope while routing the upload through the GitHub CLI attachment path. Attachments require GitHub CLI 2.99.0+, GitHub.com or GitHub Enterprise Cloud, repository write access, and an OAuth, classic PAT, or fine-grained PAT credential; GitHub Actions and App tokens are unsupported. Read the posted comment back and retain rewritten durable URLs. Fall back to the Durable Visual Evidence Gate's browser or human path when upload is unavailable.Cross-link the issue (optional). If asked, comment on the linked issue with a 2-3 sentence summary and a link to the PR comment URL returned by step 8 through the same
${PR_BATCH_SKILL_DIR}/bin/github-comment-envelope post-issuehelper.
Reproduction tactics by change type
- Process / renderer lifecycle (signals, workers, teardown, ports): drive the real
cluster/child process API; assert withps/pgrep/kill -0on captured PIDs and on the master's exit code. Emulate the real supervisor (e.g. Foreman: signal only the master PID, SIGKILL after its ~5s window). - Rendering / hydration / framework output (render output, FOUC, streaming, cache keys): boot the relevant integration test app, hit the affected route, compare server-rendered output vs hydrated DOM, watch the renderer log, diff cache keys. For browser-visible output, use paired screenshots for static states and paired clips for temporal behavior.
- Generators / installers / scaffolding: run the generator into a temp app and diff the produced files against expectation; for behavioral output, boot the generated app.
- Caching / dedupe / digests: construct the colliding or repeated inputs and assert hit/miss and that failed renders are not cached.
- Types-only changes: usually covered by
.agents/bin/buildor the repo's documented type-check command; behavioral reproduction is normally not warranted — say so rather than staging a fake one.
Environment notes
- Pre-fix-on-one-file tactic (cheap before/after for an already-merged PR): when the fix is in a single
file and the PR added regression specs, run the post-fix spec against the pre-fix file —
git checkout <fix-commit>~1 -- <file>, run the spec (it should fail on exactly the new tests), then always restore withgit checkout HEAD -- <file>and confirmgit statusis clean before moving on. - Read the changed function from git, not from memory: when a harness needs the exact pre/post logic
(a regex, a digest, a key format), extract it verbatim with
git show <rev>:<file>so the reproduction can't drift from the real code.
When NOT to use this skill
- Docs, comments, changelog, CI/workflow plumbing, benchmark tooling, refactors with no behavioral change,
license-header enforcement, and pure type narrowing rarely have a user-observable runtime symptom to
reproduce. Route those to
$verify(local checks) or a review skill instead, and say why a behavioral repro adds nothing.
Output format (PR comment)
## Manual verification: reproduced the bug and confirmed the fix ✅ / ❌
<one line on what was verified and how (full app vs mechanism-level harness)>
### Reproduction
<how the bug was triggered; the key condition that makes it deterministic>
### Results
| Scenario | Behavior | Result |
| --------------------- | ---------- | --------------- |
| Pre-fix, <condition> | <observed> | <BROKEN signal> |
| Post-fix, <condition> | <observed> | <FIXED signal> |
```text
<real captured output, before and after>
```
### Caveat
<what was NOT exercised; the residual-risk path that remains UNKNOWN>
Keep the comment evidence-first and honest about scope. End behavioral claims with the captured proof, not adjectives.