Repository navigation
feat(pr-review-cli): add an optional hold label step - #864
Conversation
The review schema gains an optional hold (criteria cost, trust-gate or critical-security, and a reason up to 2000 characters), checked by the extract step in both legs. New inputs hold-label (empty turns holds off) and hold-paths (globs; a changed file matching one adds trust-gate whatever the model says). The status job's add-only "Apply the hold" step, through the new apply-review-hold action, adds the label and one claude-lane-hold marker comment, never removes a label, and fails the job when a write fails. The workflow outputs hold and hold-criteria. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ames safely A failed changed-file listing now adds trust-gate and still applies the hold before the step fails, so a model hold is never lost to it. Matched file names in the hold comment lose backticks and line breaks, and their mentions are broken. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Claude finished @kyle-sexton's task in 3m 48s —— View job Security review in progress
|
|
Claude finished @kyle-sexton's task in 6m 55s —— View job Review completeReviewed the new 1 finding posted inline ( Things I checked and found sound:
Not evaluated: Author-claimed and unverified (I did not run them myself — the sandbox here didn't allow invoking claude-lane-summary: lane=claude-review sha=8a90441cdcf990a1f0cf277fb19bbab0c07b8933 run=39902079656 |
|
claude-review has reviewed this pull request through 3649a41; a later push is reviewed from there. |
The hold step now logs the publish gate's warn-only labels the way publish-review does, instead of dropping them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 59s —— View job Code review complete
0 findings. No inline comments posted. The head ( Re-checked on this pass, nothing new found:
Author-claimed and unverified (sandboxed claude-lane-summary: lane=claude-review sha=3649a4160b1f6b80cb1910845b54db20461db2e4 run=39903610229 |
|
Claude finished @kyle-sexton's task in 1m 28s —— View job Security review completeScope of this run: HEAD moved from Prior finding — fixed. No new security issues found in the delta. No manifest changes in this diff (no critical-findings: 0 claude-lane-summary: lane=claude-security-review sha=3649a4160b1f6b80cb1910845b54db20461db2e4 run=39903610229 |
|
claude-security-review has reviewed this pull request through 3649a41; a later push is reviewed from there. |
Refs: melodic-software/claude-code-plugins#9276 ## Summary Registers `pr-review-cli.yml@4edeccd87cf60ef539ce5109b78454217d5f2049` (ci-workflows [v0.64.0](https://github.com/melodic-software/ci-workflows/releases/tag/v0.64.0)) so callers can pin it and pass its two new optional inputs, `hold-label` and `hold-paths`. ## Fix v0.64.0 adds an optional hold step to the status job ([ci-workflows#864](melodic-software/ci-workflows#864)). The new contract copies the v0.63.1 entry forward and appends `hold-label` and `hold-paths` to `allowedInputs`. Routing (`hosted-only`), `fixedRunsOn` (`ubuntu-24.04`) and the two reviews-role secrets are unchanged. No caller permission is added: the hold step writes through the status job's existing `pull-requests: write` grant. A diff of the workflow between v0.63.1 and v0.64.0 shows no other input, secret or `runs-on` change. ## Verification - `node --test runner-policy.test.mjs` in `components/runner-policy`: 338 pass, 0 fail, including the new test that the v0.64.0 entry equals v0.63.1 plus the two hold inputs. - `npm run lint:runner-policy`: passed. - `node --test components/claude-lanes/repin-policy-lockstep.test.mjs`: 23 pass. - Pre-commit (typos, editorconfig, markdownlint, gitleaks, biome): clean. ## Related - Same shape as #849 (v0.63.1) and #848 (v0.63.0). - After merge, the push-triggered `maintenance-sync-standards` run opens the sync PR into claude-code-plugins' `.github/standards/runner-policy/policy.json`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

Refs: melodic-software/claude-code-plugins#9276
Summary
Lets the
pr-review-cli.ymlreview lane hold a pull request for a person: the review can return a hold, a configured path list forces one, and the lane marks the hold with a label that the caller's required check can enforce. This is PR 3 of the claude-code-plugins#9276 plan (review lanes hold risky PRs withneeds-human,ci-statusenforces it, clean PRs auto-merge natively). It targetspr-review-cli.ymlonly, because both review lanes now call it.pr-review.ymlandpr-review-security.ymlare unchanged.Fix
hold: {criteria: [cost | trust-gate | critical-security] (1-3, unique), reason: string ≤ 2000}. The extract step strips HTML comments from the reason and carries the hold inside the existingreviewoutput. A malformed hold makes the reviewincomplete, the same rule a malformed comment already follows. One prompt line names the field. The review skills decide when to return it (plan PR 7).hold-label(default'', which turns holds off) andhold-paths(default'', newline-separated globs converted by the sameglobToRegExpas the scope step; a test keeps the two copies identical)..github/actions/apply-review-hold). It runs after "Publish the review" withalways(), only in comments mode and only whenhold-labelis set:judge).pulls.listFilesaddstrust-gatewhen any changed file, or a renamed file's old path, matcheshold-paths. This applies whatever the model says, including when the review failed. A file list shorter thanchanged_filesalso holds.issues.addLabelsand posts one marker comment,<!-- claude-lane-hold lane=<lane> sha=<head> criteria=<list> -->, followed by the reason and aclaude-lane:footer. A rerun does not repeat the marker. It never removes, sets or replaces a label.trust-gatehold: the step applies it, then fails.@mentions in them are broken.stripHtmland the fetched publish gate. A credential-shaped reason, or a gate that cannot load, has its text withheld, and the label is still added.hold(true/false,falsewhen the step did not run) andhold-criteria.pull-requests: write. The fine-grained permission table listsPOST /repos/{owner}/{repo}/issues/{issue_number}/labelsunder "Pull requests: write" (docs), and the plan's sandbox canary probes it with the job token. Create the label in the repository first: whether this grant can create a missing label is unverified. The standards runner-policy contract for this SHA (allowedInputsplushold-labelandhold-paths) follows as plan PR 4.ci-status), under the ADR 0011 supersession tracked in the plan.Verification
node --test .github/scripts/*.test.cjs: 628 pass, 0 fail (.github/scripts/apply-review-hold.test.cjsis new;.github/scripts/pr-review-cli.test.cjsgains hold cases).node --test .github/actions/report-lane-outcome/*.test.cjs .github/actions/run-claude-cli/*.test.cjs: 146 pass.removeLabel,removeAllLabels,setLabels,deleteLabelandupdateLabel, and a static check finds none of them inhold.cjs.trust-gate, including a failed review, a renamed file's old path, and rootCODEOWNERSmatched by**/CODEOWNERS.hold-labelmakes no API call and outputshold=false.always().trust-gateadd, turns 1 and 6 tests red.actionlint: clean.zizmor --offlineon the workflow and the new action: no findings.biome checkandmarkdownlint-cli2 README.md: clean.typos: clean.review:security-reviewersubagent reviewed the diff before the push. Fixed: a failed file listing no longer drops the hold, and file names in the comment are sanitized. Left to the enforcing side, by design: whenever the status job is skipped (fork, Dependabot, an unlisted bot) or the review fails, no label is added. The plan's ci-status step (PR 6) requires both review checks to be green on the head, so a missing or red lane holds the merge anyway. Who removed the label is also for the enforcing check to judge.Related
hold-label: needs-humanand the trust-gatehold-paths).🤖 Generated with Claude Code