Resolve PR review comments for an in-review issue — fix them in the issue's worktree, re-run the gate + C as needed, push, and reply per thread. Fourth stage of the CQLite delivery pipeline. Use when the owner leaves PR comments and says "address #N" or "handle the review on #N".
Install
npx skillscat add pmcfadin/cqlite/flow-address Install via the SkillsCat registry.
SKILL.md
flow-address — resolve PR review feedback
You are the CQLite delivery lead. The owner (or roborev) left comments on the PR for issue #N.
Resolve them in the worktree and reply per thread.
Steps
- Resolve the PR for the issue first. The issue number
<N>is NOT the PR number — resolve the PR
from the issue's branch:
UsePR=$(gh pr list --head "issue-<N>-<slug>" --state open --json number --jq '.[0].number')$PRfor every PR command below (never<N>). - Gather the feedback.
gh pr view "$PR" --commentsand the review threads
(gh api repos/:owner/:repo/pulls/$PR/comments). List the asks. - Triage each (receiving-code-review discipline — verify, don't perform agreement): a mechanical
fix is yours to make; a genuine design/scope question goes to the owner. In an attended session
ask ONE at a time viaAskUserQuestion.AskUserQuestionis attended-sessions-ONLY (#2666) — in an
unattended session it is FORBIDDEN (the worker would hang until the log-tail watchdog pages it):
instead park — post ONE structured question comment (options + recommendation + default), add theneeds-decisionlabel, write ablockedmarker withreason: needs-decision, and EXIT, releasing
the machine. Push back with a reason where a suggestion is wrong, rather than complying blindly. - Re-acquire before the first edit — this stage RESUMES work, which is where the collision window
reopens (#3436). Addressing review comments is by definition restarting work on an existing branch,
so the #3436 trigger applies verbatim: "I am about to commit to a branch for an issue I do not
currently hold". Do BOTH, before any fix:
AND TAKE THE BOARD OFFbash scripts/flow/claim.sh verify <N> # on failure read reason=; see flow-implement step 2 # From the SESSION's own cwd, never a subshell. `$(cd … && pwd)` computes only a path # and is REFUSED (reason=unresolved-identity, #3436 FIX 5); `( cd … && acquire )` is # worse — it SUCCEEDS while recording the subshell, which exits on return, so the # record reads DEAD-NO-PROCESS and a peer is granted the lane by auto-reclaim # (#3436 FIX 14). The cwd test finds a process working in the lane; it does not prove # the process outlives the command. cd <worktree> || exit 1 bash scripts/flow/lane-lock.sh acquire <N> --lane-dir "$PWD" || exit 0ReadyIN THE SAME BREATH (#3436 AC6). Re-acquiring the claim
closes the window for a session that reads the ref; the board is what a session reads
first. #3393 ran 20+ commits with no claim ref while the board advertised the issue as
available, so a well-behaved peer doing exactly what doctrine says — read the board, take aReadyitem — would have collided, and the claim ref could not stop it because no ref existed.gh project item-list 1 --owner pmcfadin --query 'status:Ready' --format json -L 100 \ --jq '.items[]|select(.content.number==<N>)|.id' # empty => already off Ready # still Ready? set board Status=In Progress -- board Status ONLY, never a status:* label, # which the #2855 mirror owns and will revert.advertised-collision-scan.shreports exactly this shape (Ready + a pushed branch + no claim
ref), so leaving it unfixed on resume is a row someone else has to chase.
Areleased-then-resumedrefusal is NOT a stale lock and NOT an abandoned peer lane — it means the
lane lock holds THIS SESSION's own token; take the documentedadopt --expect none --reason <why>
path. Alane-occupied-by-live-peerrefusal is its opposite: a DIFFERENT live process on this box is
in that lane — adopt nothing, reap nothing, stop. Same for alane-lockOCCUPIEDwithliveness=ALIVE: do not edit. Only a verifiably DEAD holder is auto-reclaimed.lane-lock=occupied-alive-unattributed(or alane-evidence=…unattributed…) means a live holder
exists and the run could NOT establish whether it is you — runlane-lock.sh verify <N>from
INSIDE the lane before writing anything. - Fix in the worktree (
.claude/worktrees/issue-<N>-<slug>), spawningsstable-developerfor
non-trivial code changes. Set the transientaddressingsub-marker (a skill-managed marker the
board→label mirror #2855 does not own); clear the sibling transientspec-reviewmarker. Do NOT
write status:ready/in-progress/in-review — those are the mirror's, derived from the board Status:gh issue edit <N> --remove-label status:spec-review --add-label status:addressing - Re-verify what the change touched —
--liteper address round, NEVER a full gate here. The tiered
loop (#1821/#2087) gives each fix round--lite; the ONE full gate of record runs insideflow-closer
(#2084). Always use the mandatory summary-file redirect (#1175/#2079) — never stream raw gate stdout
into a persistent context:
Add any diff-relevant parity/integrationAGENT_GATE_SUMMARY_FILE=/tmp/lite-<N>.txt \ bash scripts/agent-gate.sh --lite > /tmp/lite-<N>.log 2>&1 < /dev/null cat /tmp/lite-<N>.txt # the LITE block (MODE: lite) is the ONLY gate text you retain--testtarget (run withCQLITE_DATASETS_ROOTpointed at
the main repo). Re-run C (spec-auditor) if requirements/tests changed; if code changed materially,
push first, then re-run roborev through the ONLY sanctioned invocation (#2964) —bash scripts/flow/roborev-review.sh --agent codex --model gpt-5.6-sol(both flags always; never a bareroborev review --branch, which from a worktree reviewsorigin/mainand reports clean having reviewed
nothing). Retain only its==== ROBOREV REVIEW SUMMARY ====block; any non-PASS terminalRESULT,NOTHING-TO-REVIEWincluded, is a failed round, not clean.
If the certified SHA moved, re-certification is the closer's full (or--delta) gate per
the gate contract — not a full gate in this skill. - Push + reply.
git -C <worktree> push, then reply on each$PRthread with what changed (commit
ref), and clear the transientaddressingsub-marker. The board staysIn Review(PR still open),
so the mirror keepsstatus:in-review— do NOT hand-write it:gh issue edit <N> --remove-label status:addressing - Re-certify and re-arm merge-on-green (#2667). The owner's comments are input, NOT a merge
gate — unless a comment is an explicitHOLD:or raises a product/scope question. After addressing
them, re-certify (lite + any diff-relevant targets), then re-run premerge-assert in the shape that
matches what you changed (#3465) and re-armgh pr merge --auto --squash --delete-branch:
A CODE FIX ALSO VOIDS THE ROBOREV ROUND, not just the gate (#3752). Re-review AFTER the# A CODE fix moved the certified SHA -> it needs a NEW full gate at the new head: bash scripts/flow/premerge-assert.sh <pr> <certified-sha> <full-gate-summary> # A TEST/DOCS-ONLY fix on top of a full PASS at anchor X -> #1892's --delta re-cert, # never a repeat full gate; pass BOTH the anchor's full summary AND the delta summary: bash scripts/flow/premerge-assert.sh <pr> <certified-sha> <anchor-full-summary> <delta-summary>
new gate — a roborev round changes no bytes, so it can never invalidate a gate PASS, while a
rebase or a src fix changes bytes and invalidates both — and post the new==== ROBOREV REVIEW SUMMARY ====block as a top-level PR comment, becausepremerge-assert'sreview-bindingleg reads the job id from the PR and refuses a merge whose
recorded review does not cover the certified head. Itshold-checkleg refuses on a column-zeroHOLD:COMMENT on the PR or the issue it closes (a comment, never the PR description — a body is
editable with no per-edit attribution, so it is not an authorization channel), and on a lead
disarm inside 30 minutes; a lead clears
one with a column-zeroGO:orRELEASE:line.
The third argument is REQUIRED and must always be a FULL-gateRESULT: PASSblock;--liteis
refused by name everywhere, and a--deltablock is accepted ONLY as the optional fourth argument
(where the script checks that itsdelta-anchor:names the third argument's block and that its owncommit:/tree-start:are at the certified SHA).--liteis never the gate of record.
Exit 0 proves the diff is unchanged since certification and that a full gate PASSed on that exact
tree — not that it was certified against currentmain(a squash-merge composes the diff with
main's tip; the merge-result gate is #3650 slice 2). Report it that way.
ItsPREMERGE: ADVISORYlines (#3650 slice 1) reportNcommits behind the merge-base andM
of those in this diff's blast radius, measured at the CERTIFIED SHA rather than the localHEAD(paths the diff touches + a hard-coded gate-global set; every run declares TWO gaps — it is
not a dependency closure, and the gate-global list is itself curated and NON-CLOSED). They are information, never a verdict: the advisory cannot change the exit
code, an absent/failing/UNMEASUREDadvisory is non-fatal, and a consumer that ever acts on it
must treatUNMEASUREDas STALE rather than fresh.