| name | project-review-cleanup |
| description | Discover this board's PRs that still need review attention, then carry every GitHub review thread through fresh forge-cli evidence, convergence triage, repair or disposition, reply/resolve, and final zero-unresolved verification.
|
| argument-hint | [--pr <iid>] [--repo <owner/name>] [--days <n>] [--all-actors] [--limit <n>] |
| allowed-tools | Bash, Read, Edit, Write |
Project Review Cleanup
Use the board's canonical discovery surface to find change requests that still
need review attention, then own the complete per-PR cleanup loop. This project
skill coordinates board discovery, live provider reads, evidence-backed triage,
normal source delivery, provider reply/resolve mutations, and the final fresh
live convergence check.
Contract
Prerequisites:
- Run inside the
symphony-board git work tree with Node 24 and installed pnpm
dependencies.
- Use the released
forge-cli pr review-threads list/resolve/reply surfaces.
Do not replace them with raw provider mutations.
- Read the active runtime's
core/policies/review-thread-convergence.md before dispositioning threads.
This policy owns fix / stale / follow_up / accepted judgment and its
stopping rule; do not reconstruct a competing policy here.
- Target the active board endpoint. The default base URL is
http://127.0.0.1:18080; override it with
SYMPHONY_BOARD_REVIEW_CANDIDATES_URL or SYMPHONY_BOARD_BASE_URL.
Never substitute an incidental local SQLite store for cleanup discovery.
Optional discovery arguments are forwarded to review-candidates:
--repo <owner/name> restricts discovery to one project.
--pr <iid> focuses one change request and relaxes the late/closed gate.
--days <n> changes only the late-review activity window; default 7.
--actor <login> (repeatable) or --all-actors widens the late-review bot
allowlist. Open-thread discovery is already actor-agnostic.
--limit <n> caps the candidate set; default 20.
Discovery may report GitLab MRs because the board tracks their open-thread
counts. Released reply/resolve mutations are GitHub-only in this workflow. When
forge-cli returns provider_unsupported, stop and report the provider route
that must be completed instead of claiming convergence.
Workflow
1. Discover from the active board
Run the read-only owner command:
export SYMPHONY_BOARD_BASE_URL="${SYMPHONY_BOARD_BASE_URL:-http://127.0.0.1:18080}"
pnpm review-candidates --json
Forward requested filters, for example:
pnpm review-candidates --json --repo sympoies/symphony-board --pr 181
pnpm review-candidates --json --all-actors --days 14 --limit 40
Read each candidate's source_id, repo, pr, reasons, and thread counts.
Process open_review_threads first, ordered by open count. Treat
late_review and review_on_closed_pr as timing context, not proof that a live
thread is still unresolved.
2. Refresh live provider evidence
For every GitHub {repo, pr}, ignore the board's point-in-time thread count and
fetch the current provider state:
forge-cli --provider github --repo <repo> --format json pr review-threads list <pr>
Use only entries where resolved == false. If data.unresolved == 0, record
the candidate as already converged only after checking the completeness boundary
below. The board may continue showing it until the next sync.
The currently released forge-cli list surface returns one unpaginated page of
at most 100 review threads and does not expose a provider completeness signal.
Treat data.total >= 100 as incomplete evidence and stop for that PR; a hidden
101st thread may still be unresolved. Only data.total < 100 together with
data.unresolved == 0 proves that an initial candidate is already converged.
Keep this fail-closed boundary until the released list envelope proves complete
pagination with an explicit field that this skill can require.
Before triage, inspect the full thread/reply context needed to understand the
finding: the thread body and URL, every current reply, the current PR diff/head,
the referenced source, and tests. The normalized list envelope does not carry
reply history, so supplement it with read-only provider tooling. Stop when the
complete current reply set cannot be established.
3. Triage every unresolved thread
Apply core/policies/review-thread-convergence.md and record one disposition:
fix: a genuine in-scope defect.
stale: the finding no longer applies to current code.
follow_up: a real issue outside this pass, with a durable issue link.
accepted: a verified preference or genuine ambiguity, with rationale.
Use code-review-specialists only when an independent read-only review would
materially improve uncertain correctness or risk judgment. Give it the actual
diff/evidence and use focused or follow-up mode. It must not edit source, post
provider comments, resolve threads, or make the final disposition.
Escalate security, data-loss, contract/schema, destructive-migration, and
cross-repository architecture findings before changing source or resolving the
thread. Never use accepted merely to end the sweep.
4. Deliver source fixes through the owner workflow
For fix, work in the repository that owns the defect and follow its preflight,
test-first, validation, review, and delivery requirements. Deliver the repair
through pr:deliver-pr; do not commit to main, bypass validation, or resolve
the original thread before the fix has a durable PR/MR result.
For follow_up, create or reuse the repository-owned issue through its active
issue workflow. Preserve the issue URL or reference for the provider note.
Return to the original {repo, pr, thread} after the fix or follow-up exists.
5. Reply and resolve through forge-cli
Prepare concise body files outside the repository when practical. Include
the disposition and its evidence: merged/follow-up link for fix or
follow_up, current-code evidence for stale, or the verified rationale for
accepted.
Immediately before writing, refresh the full thread/reply context again. Compare
it with the evidence used for triage. If the thread is resolved, stop mutating
it; if a new or changed reply materially affects the finding, return to triage.
Do not write from a stale or partial context snapshot.
Post exactly one disposition reply as a separate mutation:
forge-cli --provider github --repo <repo> --format json \
pr review-threads reply <pr> --thread <thread-id> --body-file <reply-file>
Keep the JSON result and retain the returned reply comment id or URL with the
disposition evidence. Then re-read the full thread/reply context and confirm the
reply exists exactly once. If the reply command failed or returned an ambiguous
result, do not replay it blindly: re-read the full context before any retry,
skip the reply when the disposition reply already exists, and retry the reply
only when complete evidence proves it absent and the triaged context is still
current. Stop rather than guess when the read is incomplete.
After the reply is confirmed and the refreshed context still supports the same
disposition, resolve without a note so resolution cannot duplicate the reply:
forge-cli --provider github --repo <repo> --format json \
pr review-threads resolve <pr> --thread <thread-id>
If resolution fails or has an ambiguous result, re-read full context before any
retry. A refreshed resolved thread is complete; an unresolved thread with the
same context may receive a resolve-only retry. Never resend the disposition
reply during resolve recovery, and return to triage if participant context
changed.
Do not resolve an unread finding, an undelivered repair, an uncreated follow-up,
or a high-risk finding awaiting user direction.
6. Prove live convergence
After all mutations for a PR, run a new provider read; do not reuse the earlier
JSON and do not use board rediscovery as the final gate:
forge-cli --provider github --repo <repo> --format json pr review-threads list <pr>
Fail closed when the fresh envelope reports data.total >= 100; that page is
incomplete evidence even when its visible data.unresolved value is zero. The
PR is complete only when the same fresh envelope proves data.total < 100 and
data.unresolved == 0. Repeat this final live check for every swept PR. Run
pnpm review-candidates --json again only after a board sync when starting a
new discovery pass; its cached count is not proof of provider convergence.
Output
Report:
- discovery command and filters;
- each candidate
{repo, pr};
- every thread id, disposition, and evidence/reference;
- any optional
code-review-specialists result;
- source-fix or follow-up PR/issue links;
- reply/resolve results, including retained reply identity and any recovery
reads; and
- the fresh final
data.total and data.unresolved counts for every PR.
Stop with an explicit blocker when discovery is unavailable, live provider
evidence is incomplete, provider writes are unsupported, a high-risk finding
needs user direction, a repair cannot pass its owner gates, or the final fresh
count is nonzero.
Boundary
This project skill owns orchestration from board discovery through final live
verification. review-candidates owns candidate computation from the board's
canonical store, forge-cli owns provider thread reads and reply/resolve
mutations, core/policies/review-thread-convergence.md owns triage and stopping
judgment, code-review-specialists optionally supplies independent read-only
analysis, and pr:deliver-pr owns source-fix delivery. Keep those boundaries
separate and do not add bespoke GraphQL or provider mutation scripts here.