A loop over assigned bugs fails where one bug does not: tickets started and left half done, merges piling up on dev before anyone tested them, and results nobody can find a week later. This skill runs the loop; bug-triage owns what happens to each bug, and this skill only adds the pick, the merge, the dev verification, and the demo report. Starting it is the user's explicit word to move each picked ticket to in progress and to merge each bug's PR/MR into the integration branch once section 4 allows it, and nothing more: never staging or prod.
The whole loop runs in the main session, one bug at a time, never in worker subagents. A bug often needs the user mid-way (a business decision, a confirmation, a credential, a merge prompt), and only the main session can ask and wait for the answer. fresh-review still spawns its own reviewer; that is a review, not a worker.
0. Setup, once per run
- Read the project-init block of the project's
AGENTS.mdfor the Jira project key, the integration branch<int>(integrationBranchin.agents/environments.json,worktree-flowsection 0; not recorded: ask before the first merge), and the dev environment in that registry. No block means runproject-initfirst. - Load
environmentsbefore anything touches dev, and follow dev's policy and testing scope there. - Jira access is the Atlassian MCP or the REST API; having neither is a blocker to report, not a reason to guess tickets.
- The report is one HTML file shared by every project, at the path
python3 <this skill>/scripts/hunt_report.py pathprints. Never create another report file, and never edit that one by hand. - Media inbox:
<report dir>/inbox/<TICKET>/, where recordings wait until section 6 copies them into the report.
The run ledger
It lives in the main checkout's private git dir and is never committed:
L="$(git rev-parse --path-format=absolute --git-common-dir)/bug-hunt.md"One section per ticket, replaced rather than appended:
## VOCOTMA-30
state: review https://github.com/org/repo/pull/41
status-before: To Do
worktree: <path>
prs: <every PR/MR url for this ticket, oldest first>
cause: <one line>
proof: <test command> failed without the fix, passed with it
reporter-sequence: <the reporter's steps, one per line below>
dev-checks: <observable checks with their expected values, one per line below>
dev-fails: 0
fresh-review round 1: base <sha> head <sha>state | Meaning | On resume |
|---|---|---|
queued | In the queue, not started | Picked in queue order (section 2) |
in-progress, review | The current bug (sections 2 and 3) | Continues where it stopped, before anything new is picked |
awaiting-merge <url> | The merge hook denied, waiting on the user (section 4) | Continues at section 5 once its PR/MR is merged |
merged <sha> | Merged, not yet verified on dev | Continues at section 5 for that merge |
verified | Passed on dev and handed back | Continues at section 6 |
reported | Its final report entry is written | Comes back only when Jira shows activity newer than the report (reopened, a new comment) |
parked needs-decision | Waiting on a user decision (section 2) | Continues where it parked once the user answers, in chat or in a ticket comment newer than the park |
parked <other verdict> | Ended without a fix (section 2) | Same as reported |
skipped <reason> | Left out by a section 1 skip rule | Judged again by the skip rules at the next scan |
skipped user | The user asked to skip it (section 1) | Stays skipped until the user lifts it, never re-judged |
fresh-reviewround lines for a ticket live here, not in the worktree's progress file, and restart at round 1 with each new PR/MR.- Read the ledger first on resume and after a context summary; trust it, Jira, and
gh pr listover memory.
1. Scan
project = <KEY> AND assignee = currentUser() AND issuetype in (<every issue type of the project whose name contains "Bug">) AND statusCategory != Done ORDER BY priority DESC, updated ASC- A ticket with a ledger section follows its
state(section 0 table) and is otherwise never re-judged by the skip rules below. - Skip the others, with the reason in the ledger:
- a status owned by someone else in the project's workflow (in QA testing, waiting for customer, blocked);
- an open PR/MR, or a branch with no PR/MR, from outside this run (
worktree-flowsection 1 step 1); a branch whose PR/MR is merged or closed is history, typical for a reopened ticket, and does not count; - a ticket that already went to QA twice (
bug-triagestep 7).
- Order: priority, then reopened before new (the reporter is waiting a second time), then oldest update.
- Show the queue to the user as one short table and start on the top ticket. Do not wait for approval; the user steers while it runs (reorder, skip, pick another). A skip the user asks for is written as
skipped user.
2. Pick one bug and work it to the end
- Exactly one ticket is in progress at a time: pick the next one only when the current one is
reported,parked, orawaiting-merge. - Pick:
- Re-read its assignee and status, since someone may have moved it; a ticket no longer yours or now in a skip status goes back to the scan with the reason.
- Write its
status-beforein the ledger, then transition it to the project's in-progress status right away (read the transition name from the ticket's available transitions, never guess it); a ticket already in that status stays. No such transition available from its status: say so to the user and continue, since the work does not wait on the board. - Write
in-progressin the ledger and tell the user in one line which ticket you took and why it was next.
- Work it yourself per
bug-triagesteps 0 to 5, in its own worktree, and open the PR/MR as a draft. Branches of this ticket whose PR/MR is merged or closed are history, not another agent: add a-2,-3suffix to yours instead of stopping. Runverify-loopthrough gate 4 on the branch; gate 5 is section 3 here, and gate 6 is section 5, after the merge. - Reproduce
beforein a real browser and save the video or screenshots to the inbox. With the Playwright MCP usebrowser_start_videoandbrowser_stop_video. When another session holds it ("Browser is already in use"), launch your own browser from a Playwright script withrecordVideoon (from the project's@playwright/testornpx -y playwright). - Write
cause,proof,reporter-sequence, anddev-checksinto the ledger as soon as you know them, since sections 5 and 6 need them after a context summary or in the next session. - Test data you create follows
environmentsand is listed for the report'sdataChanges. - Ask the user whenever
ask-or-decidesays it is their call, and wait for the answer in this session. That covers a business decisionbug-triagestep 2 escalates, a fix that changes an existing rule, a reporter's claim you cannot reproduce but are not sure about, and a test account or credential you lack. - The ticket parks only when the user says to come back later, or does not answer before the run stops:
- Post the ticket action
bug-triagestep 2 names for it (the evidence comment, the decision citation, or the escalation). - Move the ticket back to
status-before, unless that action set another status. - Write
parked <verdict>in the ledger, go to section 6 with that verdict, and pick the next ticket.
- Post the ticket action
- Any outcome other than a fix (
correct-per-decision,cannot-reproduce,blocked): confirm it with the user in one line with the evidence before posting anything, then handle it the same way as a park.
3. Review before merge, since no human reads the diff
- Rerun the proof once more on the final head, per
root-causesection 5. - Run
verify-loopgate 5 on the branch:fresh-review(it spawns the reviewer), plussecurity-passandflow-tracewhen their triggers are in the diff. Fixauto-fixfindings yourself and run the next round asfresh-reviewsays. - Every
fresh-reviewexit that needs the user is a question to the user now, per section 2: anask-userfinding,risk_levelhigh in the last round, orauto-fixfindings still open after round 3. A PR/MR with an open question is never merged. - Mark the PR/MR ready only when gate 5 is clean and the project's CI checks pass (
verify-loopsection 3). - Then fill the review rows of the
verify-looptable in the PR/MR body with your results and the head you reviewed. The behavior row and the requirement rows that cite E2E stayDEFER dev verification after merge; section 5 fills them in the merged PR/MR's body with the evidence file and the running SHA.
4. Merge
- Before merging, the PR/MR base is
<int>(gh pr view <n> --json baseRefName), and the branch is rebased on the latestorigin/<int>with the touched tests rerun. A rebase that moved the head gets the nextfresh-reviewround (its after-a-rebase range) and green CI again before the merge. - Merge with the method the repo's recent merged PRs/MRs used, then write
merged <sha>in the ledger. - The
guard-protected-brancheshook asks the user before every merge, and that prompt is their confirmation. - In a permission mode where the hook denies instead, never work around it:
- Write
awaiting-merge <url>in the ledger and keep the merge command for the final report. - Add the section 6 entry and pick the next ticket.
- Write
5. Deploy and verify on dev
- Deploy: wait for CI when it deploys
<int>on merge, otherwise run dev's deploy command perenvironments. - Confirm the running build contains the merge per
bug-triagestep 6. - Replay
reporter-sequencewith a real persona pere2e-verify(checks a to e), and measure everydev-checksitem. - Record it as
afterwithbrowser_start_videoandbrowser_stop_video, at 1440x900 and, for anything visual, again at 390x844. When another session holds the MCP browser, use the next tool in thee2e-verifyorder, and record video with a Playwright script of your own. - The branch is already merged, so the
e2e-verifyevidence file goes to<report dir>/evidence/<TICKET>-<ISO>.jsoninstead of the repo. - FAIL: raise
dev-failsin the ledger and leave the ticket in its status. Fix it on a new branch and PR/MR (the first one is merged), then repeat sections 3 to 5. Writein-progressin the ledger and append the new PR/MR toprsas soon as it is opened, so a resume continues the fix and does not re-verify the old merge. From the second FAIL work perroot-cause. At the third the verdict isnot-fixed, and the report and the decision list say what is merged on dev and still broken. - PASS: hand back per
bug-triagestep 7 (comment, transition, assign to the reporter), then writeverifiedin the ledger.
6. Add the result to the report
Every ticket that leaves the queue gets an entry on the day it left, whatever its verdict.
- Write the entry JSON in the scratchpad;
python3 <this skill>/scripts/hunt_report.py --helpowns its fields and rules. - Run
python3 <this skill>/scripts/hunt_report.py add <entry.json>, fix what it rejects, and never bypass it. - Final verdicts only (every verdict except
awaiting-mergeandpending-deploy):- Delete the ticket's inbox folder.
- Write
reportedin the ledger; a parked ticket keeps itsparked <verdict>state instead. - Clean up the worktree per
worktree-flowsection 6, which keeps worktrees whose PR/MR is still open (a parked ticket's draft), and list those in the final report.
- A ticket still
awaiting-mergeor not yet verified when the run stops gets an entry with that verdict and keeps its ledger state and inbox. The next run continues it and adds averifiedentry on the day it passes.
Write for the client who reads the page, in the language the project's report readers use (English when unknown):
before: what the reporter saw, in their words, with the date they reported it.asked: what the ticket asked for, including what the comments added.now: what the product does now on dev, or why the ticket ended with another verdict.checks: observed facts with measured values ("200x200 on desktop, 104x104 before"), never "works".steps: what a reader does to see it themselves; personas by email and role, never a password.- No file names, function names, or internal jargon outside
prandcommit; those belong in the PR/MR.
7. Stop
- The queue is empty: scan Jira once more for bugs assigned during the run, add them, and stop when that queue is empty too.
- Stop also when the user says stop, or when three tickets in a row end
blocked(the cause is probably shared: dev down, a credential, the test data). Then pick nothing new, push the current ticket's branch, and turn its PR/MR into a draft that lists what remains. Move the ticket back tostatus-beforeif no PR/MR was opened yet, then report.
Report to the user
- One table: ticket, verdict, PR/MR, Jira status now.
- The merges waiting on the user, each as a ready
! gh pr merge <n> ...line. - The decisions the user must make, per
ask-or-decidesection 4. - The report as a
file://link to the path from section 0.