Repository navigation
feat(pr-pipeline): lift a lanes App needs-human once trusted pushes clear its cause - #10389
Conversation
…lear its cause clear-escalations.mjs now clears more pr-unblock stops without a person, from evidence comment text cannot forge (the branch activity API and check runs, read with the job token; actors matched by id against the base trusted-actors.json): - workflow-staged: also once a trusted move took the head off every resume key and the head contains main as it was at the escalation. - ci-fix-*: once a trusted move took the head off every resume key and no check but ci-status fails, runs or was cancelled at the new head. - unexpected-path-staged, conflict-patch-*: once GitHub reports the PR mergeable. Security, intent and decision stops still wait for a person. 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-security-review reviewed Security review of #10389critical-findings: 0 Scope read: the whole diff (15 files), the PR context, Controls I checked, all fail closed:
Finding (IMPORTANT, 1): a trusted push is treated as the person's approval to lift the stop. See the inline comment. manifest: no plugin manifests changed, so nothing is flagged. Spend: none. This is label logic and CI reads on the job token. I found no billing, plan or paid-resource change, so I set no hold. |
|
claude-security-review holds this pull request for a person (critical-security) at The PR weakens a merge-hold control, claude-lane: claude-security-review |
|
claude-review reviewed Code review: PR #10389 (head b70cc23)No correctness or maintainability findings. I read the full diff ( Checked and found sound
Cost: none. This adds a few extra read calls per scheduled Not covered: whether |
|
claude-security-review holds this pull request for a person (critical-security) at Trust-control change: .github/scripts/pr-merge/clear-escalations.mjs (a merge-hold control) now lets the lanes App remove the human-gated needs-human label with no person acting. It does so for workflow-staged (even while main still changes workflows) and for six ci-fix-* stops, on evidence of a trusted pusher id (including the lanes App's own id) plus passing checks. The content a person would have reviewed is never checked. The review rules treat a change that may widen what a merge hold lets through as CRITICAL. claude-lane: claude-security-review |
…w imports Narrow clear-escalations' new lifts to the set the owner approved: workflow-staged and the ci-fix-* stops. unexpected-path-staged and the conflict-patch-* stops wait for a person again; a test pins that they stay even once the PR is mergeable after a trusted move. clear-escalations.mjs now loads pr-unblock/fix-ci-context.mjs and, through it, maintenance-reconcile-lost-runs/infra-failure.mjs. Add both to the path floor as enforcement paths, like the other pr-unblock modules it loads, in babysit_risk.FLOOR_CLASSES, merge-holds.mjs HOLD_PATH_CLASSES, the review callers' hold-paths and ci-status's hold_patterns, so the floor-closure test passes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
c6453af to
9bcfb25
Compare
|
claude-security-review holds this pull request for a person (critical-security) at Trust control: this PR extends .github/scripts/pr-merge/clear-escalations.mjs (a listed merge-hold script) so the lanes App lifts the human-gated needs-human label on the six ci-fix-* stops and on a second workflow-staged path with no person involved, based on a trusted-actor push and green checks. Stops that waited for a person are now cleared by automation. The trusted set also skips the denied-actor-id list that check-trusted-trigger applies. Per the review rules, a hold the code now lifts without a person is a CRITICAL weakening, so it needs an owner decision. claude-lane: claude-security-review |
|
The pr-unblock lane stopped on this pull request and needs a person: a conflicted path is a workflow, submodule config or a settings file, which the lane does not resolve.
To resume, deal with the reason above, remove the gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=10389 -f activity=resolve-conflicts |
Compose main's last-report.mjs floor entry with this branch's fix-ci-context.mjs and infra-failure.mjs entries in ci-status's hold_patterns, and count both in test_merge_holds_sync (54 floor classes, 23 review-floor paths). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ci-fix-protected-path Answer the security review's gaps in clear-escalations.mjs: - A trusted move counts only pushes by a `human` of trusted-actors.json. The lanes App's own push no longer counts, so it cannot lift its own stop. Every lane that sets check-trusted-trigger's denied-actor-ids denies the App's sender id, so this also applies that denial. - ciPasses allowlists conclusions: each check's latest run at the head must end success, neutral or skipped, so action_required, stale or a null conclusion keeps the label. - ci-fix-protected-path leaves CI_FIX: its fix needed a protected path, so a person decides it, like unexpected-path-staged and the conflict-patch-* stops. The pr-merge.yml step comment, the pr-pipeline README clear table and the changelog fragment now say the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # .github/workflows/pr-require-checks.yml # plugins/source-control/skills/babysit-prs/scripts/tests/test_merge_holds_sync.py
|
The pr-unblock lane escalated this pull request as |
…-escalations comments The header of clear-escalations.mjs and the trusted-move comment in its test still said the lanes App's own entry counts as a trusted pusher, and the header's ci-fix row named every ci-fix-* stop. The code counts only `human` ids, keeps ci-fix-protected-path for a person, and requires each check's latest run to end success, neutral or skipped. The comments now say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The pr-unblock lane stopped on this pull request and needs a person: the merge stages a change under .github/workflows/, which the lane does not push.
To resume, deal with the reason above, remove the gh workflow run pr-unblock.yml -R melodic-software/claude-code-plugins -f pr-number=10389 -f activity=resolve-conflicts |
# Conflicts: # .github/scripts/pr-merge/clear-escalations.mjs # .github/scripts/pr-merge/clear-escalations.test.mjs # .github/scripts/pr-merge/merge-holds.mjs # .github/scripts/pr-merge/merge-holds.test.mjs # .github/workflows/pr-require-checks.yml # .github/workflows/pr-review-hosted.yml # .github/workflows/pr-review-security-hosted.yml # docs/conventions/pr-pipeline/README.md # plugins/source-control/skills/babysit-prs/scripts/babysit_risk.py # plugins/source-control/skills/babysit-prs/scripts/tests/test_babysit_risk.py # plugins/source-control/skills/babysit-prs/scripts/tests/test_merge_holds_sync.py # scripts/ci-lanes-pr-hold.test.sh
Post-merge mutation testingKilled 0, survived 0, timeout 0, no coverage 0, invalid 0, not run 8.
The run's summary has the full report. |
No related issue: owner direction 2026-10-26 (a person only for intent, design or HIGH/CRITICAL security)
Summary
The lanes App adds
needs-humanwhen a pr-unblock lane stops, and today only a person can take it off. That holds even after the cause is gone. Scheduledpr-mergerun 40407031993 kept every App-labeled pull request: threeci-fix-escalated(#10174, #10120, #10307), threefeedback-uncertain(#10125, #10326, #10321), and oneworkflow-staged(#9835).This change extends the existing clear step,
.github/scripts/pr-merge/clear-escalations.mjs(run bypr-merge.yml's "Clear escalations whose reason no longer holds"). It adds no new workflow.Fix
Each pr-unblock reason below now has a cause-gone check. Every check reads evidence that comment text cannot forge:
workflow-stagedcommits?sha=main&until=<escalation>, then compare it to the head: behind by 0)ci-fix-round-cap,ci-fix-empty,ci-fix-invalid,ci-fix-not-applied,ci-fix-escalatedci-statusfails, is still running or was cancelled, each check's latest run endedsuccess,neutralorskipped(soaction_required,staleor no conclusion keeps the label), and at least one passed. The check uses fix-ci's ownfailingChecksoverfilter=all, so a later skipped run cannot hide a failure.ci-statusis excluded because the label itself fails itA trusted move requires all of the following:
/repos/{repo}/activity?ref=refs/heads/<branch>) shows every activity since the label went on was by an id the basetrusted-actors.jsonlists ashuman. A lanes App push never counts, so the App cannot lift its own stop (every lane that setsdenied-actor-idsdenies the App's sender id).A failed read keeps the label and logs
::warning; the run stays green.The "org member" test is the base
trusted-actors.jsonhumans, matched by numeric id, the same listci-statususes. The org membership API is not called, because it may need amembersscope the lanes token lacks.The following reasons are never lifted, even after a trusted move with every check green:
feedback-uncertain,feedback-disputed,feedback-unadjudicated,feedback-question-*,feedback-round-cap,feedback-not-converging,dependency-security-close,dependency-escalate.ci-fix-protected-path,unexpected-path-staged,conflict-patch-invalid,conflict-patch-not-applied,conflict-patch-protected-path.repeat-cap,feedback-not-landed,feedback-invalid,feedback-partial,feedback-incomplete, and the otherdependency-*stops.clear-escalations.mjsnow imports.github/scripts/pr-unblock/fix-ci-context.mjsand.github/scripts/maintenance-reconcile-lost-runs/infra-failure.mjs, so both join the enforcement floor beside it, in every copy of that list:merge-holds.mjsHOLD_PATH_CLASSES,babysit_risk.py, thehold_patternsofpr-require-checks.yml, andpr-review-hosted.ymlandpr-review-security-hosted.yml. This only adds floor entries; it removes none.Permissions and tokens are unchanged:
github.token, already passed to the step asBABYSIT_VERIFY_GH_TOKEN. The job already holdscontents: readandchecks: read.TRUSTED_ACTORS_PATH, pointing at the base copy.Basis for those two permissions: GitHub's permissions required for GitHub Apps page lists
GET /repos/{owner}/{repo}/activityunder Contents (read) and the check-runs listing under Checks.Pushes by the lanes App, Dependabot or
github-actions[bot]are not trusted moves, so theirci-fix-*stops still wait for a person.Does an App unlabel launder approval?
No. An App removal does not satisfy any floor that needs a trusted person:
ci-status"Fail a pull request carrying the human-gated label" (pr-require-checks.yml:4086) treats any removal as clearing that one step.pr-require-checks.yml:4159), requires the first removal after a review-lane hold to be by ahumanontrusted-actors.json. An App removal fails it.clear-escalations.mjsalso keeps the label whileopenReviewHoldfinds a hold.performed_via_github_app == nullby a trusted human id (pr-require-checks.yml:3886).HOLD_PATH_FLOOR) on a lanes App PR are lifted only by a trusted person's approving review of the head (pr-require-checks.yml:4650,merge-holds.mjs). They never readunlabeled.Userwithperformed_via_github_appNone (babysit_merge.py:3810).enable-auto-mergeliftedByPersonrequires actor__typename == "User"(enable-auto-merge.mjs:665), so it puts its person-refusal label back after an App removal.unlabeledreader (pr-require-checks.yml:4599) reads only an untrusted-work label on linked issues, and requires a trustedUserwith no App behind the removal.intake-reenter/reenter.mjsskips pull requests (liftReentersreturns false onissue.pull_request).Verification
node --test .github/scripts/pr-merge/clear-escalations.test.mjs: 70 pass, 0 fail at the merged head. The activity-page reach guard was mutation-checked: deleting it turns its test red. New tests that failed on the code before them: workflow-staged trusted move, ci-fix lift, and (security-review follow-up) the lanes App push is not a trusted move, anaction_required,staleor null check keeps a ci-fix stop, andci-fix-protected-pathstays. A neutral check does not block the lift. The rest pin the keep cases:ci-fix-protected-path,unexpected-path-stagedand theconflict-patch-*stops, even when mergeable after a trusted movenode --test .github/scripts/pr-merge/clear-escalations.test.mjs .github/scripts/pr-merge/merge-holds.test.mjs: 109 pass, 0 fail at the merged head.python3 -m unittest discoverover the babysit-prs tests: 3225 run, 3224 pass; the one error istest_babysit_lane_roomfailing togit commitin a temp repo on this host (not a file this PR touches).actionlint .github/workflows/pr-merge.yml: clean.bash scripts/ci-lanes-pr-hold.test.sh: PASS=218 FAIL=0 at the merged head.scripts/check-affected.sh: 10 passed, 4 skipped. markdownlint and typos are clean on the changed docs.ghthis session:2fc9ba91eato841aebc3ad, and the head is 207 ahead and 0 behind main as of the escalation, so it clears under the new path.ci-statusfails at its head, so it clears unless a person commented since the label. The log stopped at the reason check, so that rule was not reached there.test-bashfails at its head, so it stays.feedback-uncertainPRs stay by design.Related
refusal-clearedandhead-movedto the same script.docs/conventions/pr-pipeline/README.md, the clear table and the reasons that still wait for a person.🤖 Generated with Claude Code