You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(pr-pipeline): hold lanes App human-merge pull requests with a label only a listed human can remove #9437
A lanes App pull request's "a person merges this" decision lives in text the lanes App can edit. The App has pull-requests: write, so a lane run could delete the Hold: line report-outcome writes for a human-merge pull request, or rewrite its Refs: #N first line to Closes #N. enable-auto-merge.mjs would then arm it on the next run, and ci-status's lanes App hold step (merge-holds.mjs) would pass, because both read the same live body. The do-not-merge label is the enforced hold today, and a body Hold: line is advisory. Hold paths and a trusted approval of the head still bind the App on denied paths.
Proposal
When report-outcome opens a human-merge pull request, give it a label that only a person on the trusted-actor list can remove. Reuse the needs-human lift step in ci-status (Fail a review-lane hold an untrusted account lifted) so removal by any other account fails ci-status, and have arm time skip any pull request that carries the label. Cover both cases:
The Hold: line is removed from the body.
Refs: #N is rewritten to Closes #N.
Acceptance
A human-merge lanes App pull request carries the label from opening.
Neither body edit arms it, and ci-status stays red until a listed human removes the label.
Proposed work class: work-class: scoped
Basis: automated intake triage; the lanes App applied this class.
Size: Medium
Complexity: Medium
Uncertainty: Known
Flags: none
Blocked by: none
Understood: when report-outcome opens a human-merge lanes App pull request, it should add a label that only a trusted-actor-listed person can remove. ci-status would fail if anyone else lifts the label, and arm time would skip any pull request carrying it. That way neither editing out the Hold: line nor rewriting Refs: to Closes can get the pull request armed. The acceptance criteria are clear and tests are required for both cases. I found no duplicate among the open issues. This is hardening of an advisory hold, not a leaked secret, so no person is needed at triage.
Released for re-triage under the owner's 2026-10-26 rule: a person is involved only for "1. A change in original intent/vision 2. Something comes up during implementation ... that alters intent/vision or the primary design 3. If there is a HIGH/CRITICAL security issue". None applies here, so the hold label came off and intake triage runs again now.
To hold this issue again, add needs-human (or status: needs-decision) and name which of the three applies.
Refinement. A read-only run explored this repository, searched its issues and pull requests, and read the allowed docs to draft what follows. On an issue a listed human filed, it counts as the issue's spec once posted, while unedited; it is still model text, so check it.
Intended outcome: report-outcome labels every human-merge lanes App pull request with the human-gated label (needs-human) when it opens it, so a Hold: line removal or a Refs-to-Closes rewrite neither arms it nor turns ci-status green until a trusted-actor-listed person removes the label.
Acceptance criteria
report-outcome.mjs adds the human-gated label (humanLabel, needs-human by default) to the pull request it opens or reuses whenever the plan is a human-merge hold, and a report-outcome.test.mjs case pins this
A report-outcome.test.mjs case pins that a pull request that is not human-merge gets no such label
pr-require-checks.yml step 'Fail a review-lane hold an untrusted account lifted' counts a labeled event of the human-gated label by the lanes App bot as a hold, alongside github-actions[bot] and claude[bot]
scripts/ci-hold-lifted-by.test.sh has a case where the lanes App labeled the pull request and a non-listed account removed the label, and ci-status exits 1
scripts/ci-hold-lifted-by.test.sh has a case where a person on trusted-actors.json removes the lanes App's label and the step exits 0
enable-auto-merge.test.mjs has a case where a body with the Hold: line deleted but the human-gated label still present gets no auto-merge armed (holdReason returns 'it carries needs-human')
enable-auto-merge.test.mjs has a case where the first line is rewritten from 'Refs: #N' to 'Closes #N' with the label still present and nothing is armed
The existing pr-pipeline test suites (report-outcome, enable-auto-merge, ci-hold-lifted-by, resolve-config live-config) pass
Dependencies
#9399 (merged as the native auto-merge arm step this builds on; closed, nothing to wait on)
.github/scripts/pr-merge/enable-auto-merge.mjs:312-326: holdReason already refuses to arm a pull request carrying do-not-merge or the human-gated label, and separately refuses on the LANE_HOLD body line
.github/workflows/pr-require-checks.yml:4118-4190: the lift step counts a hold only from claude-lane-hold comments or labeled events by github-actions[bot] or claude[bot], then fails unless the first later removal is by a human in trusted-actors.json; the lanes App is not among the hold sources
.github/scripts/work-resolve-issue/report-outcome.mjs:1376-1381: humanMerge is computed from the issue's labeler; report-outcome.mjs:1476 creates the pull request with no labels
.github/scripts/work-resolve-issue/report-outcome.mjs:107: HOLD_LINE is 'Hold: a human merges this', the advisory body line
docs/conventions/pr-pipeline.yaml:186-206: some lane slots use labels-absent needs-human, so labeling these pull requests also pauses those lanes; accepted side effect to check in live-config tests
Automated refinement by the lanes App. Intake triages the issue again with it.
The work-resolve-issue lane stopped on this issue (patch-protected-path): the change needs a path the issue lane may not push, which a maintainer session builds by hand.
It added host-run and removed agent-ready: an agent run on a host builds it, and it waits on no decision from a person.
In the run's words:
The run stopped early (patch-protected-path): its change needs a path the issue lane may not push, which a person changes instead. In the run's words: patch-protected-path: .github/workflows/pr-require-checks.yml. The acceptance criterion 'ci-status stays red until a listed human removes the label' needs the step 'Fail a review-lane hold an untrusted account lifted' (pr-require-checks.yml:4464) to count a labeled event of the human-gated label by the lanes App bot as a hold. Its jq filter at line 4498-4499 hardcodes the hold actors to github-actions[bot] and claude[bot], so a needs-human label the lanes App adds is not counted and any account's removal of it would turn ci-status green. That workflow path is one this lane cannot push, so the issue cannot be finished as it asks; the report-outcome.mjs labeling and enable-auto-merge tests could land separately, but without the workflow change they would not meet the acceptance.
What the run says it did not do:
report-outcome.mjs labeling of human-merge pull requests, its tests, the enable-auto-merge.test.mjs cases and the scripts/ci-hold-lifted-by.test.sh cases: not started, because the workflow change they depend on is on a protected path
check-changelog-fragments and check-affected were not run: the working tree has no change to check
Activity
github-actions commentedon Oct 23, 2026
Proposed work class: work-class: scoped
Basis: automated intake triage; the lanes App applied this class.
Size: Medium
Complexity: Medium
Uncertainty: Known
Flags: none
Blocked by: none
Understood: when report-outcome opens a human-merge lanes App pull request, it should add a label that only a trusted-actor-listed person can remove. ci-status would fail if anyone else lifts the label, and arm time would skip any pull request carrying it. That way neither editing out the
Hold:line nor rewritingRefs:toClosescan get the pull request armed. The acceptance criteria are clear and tests are required for both cases. I found no duplicate among the open issues. This is hardening of an advisory hold, not a leaked secret, so no person is needed at triage.Automated triage, applied by the lanes App.
kyle-sexton commentedon Oct 26, 2026
Released for re-triage under the owner's 2026-10-26 rule: a person is involved only for "1. A change in original intent/vision 2. Something comes up during implementation ... that alters intent/vision or the primary design 3. If there is a HIGH/CRITICAL security issue". None applies here, so the hold label came off and intake triage runs again now.
To hold this issue again, add
needs-human(orstatus: needs-decision) and name which of the three applies.4 remaining items
melodic-automation-lanes commentedon Oct 26, 2026
Refinement. A read-only run explored this repository, searched its issues and pull requests, and read the allowed docs to draft what follows. On an issue a listed human filed, it counts as the issue's spec once posted, while unedited; it is still model text, so check it.
Intended outcome: report-outcome labels every human-merge lanes App pull request with the human-gated label (needs-human) when it opens it, so a Hold: line removal or a Refs-to-Closes rewrite neither arms it nor turns ci-status green until a trusted-actor-listed person removes the label.
Acceptance criteria
Dependencies
Related work
Sources read
Automated refinement by the lanes App. Intake triages the issue again with it.
melodic-automation-lanes commentedon Oct 28, 2026
The work-resolve-issue lane stopped on this issue (
patch-protected-path): the change needs a path the issue lane may not push, which a maintainer session builds by hand.It added
host-runand removedagent-ready: an agent run on a host builds it, and it waits on no decision from a person.In the run's words:
What the run says it did not do:
Run: https://github.com/melodic-software/claude-code-plugins/actions/runs/40790007973
To hand it back to the lane, remove
host-runand addagent-readyagain.