Skip to content

ci(automerge): hold the arm behind a quiet period + unaddressed-findings check - #149

Merged
topcoder1 merged 1 commit into
mainfrom
claude/automerge-findings-quiet-gate
Aug 9, 2026
Merged

ci(automerge): hold the arm behind a quiet period + unaddressed-findings check#149
topcoder1 merged 1 commit into
mainfrom
claude/automerge-findings-quiet-gate

Conversation

@topcoder1

Copy link
Copy Markdown
Owner

Implements the chosen option for whois-api-llc/wxa_vpn#1392 ("nothing holds the door between the last review and the merge"): the automerge lane consults the review conversation at ARM time — a quiet period plus the unaddressed-findings detector — instead of arming the instant the gates clear.

Why arm time, not merge time

The reusable never merges; it enables GitHub's native --auto, and the server merges the moment the last required check goes green. No hook runs at that instant, so the only place a consultation can live is before the arm. Measured gaps on the four incident PRs (wxa_vpn#1385/#1376/#1378/#1345) were 3m18s–16m17s from finding to merge — all inside the 20-minute default window.

What the new gate does

Runs only when every existing gate has cleared (i.e. the PR would otherwise arm right now):

  1. Quiet — wait until ≥ findings_quiet_minutes (default 20, 0 disables) have passed since the newest of: the newest authored (non-merge) commit, the newest inline review comment, and the newest top-level [bot] comment that isn't this workflow's own sticky comment or pr-classify chatter (the gate must not reset its own clock). Sleeping is safe: callers run per-PR cancel-in-progress, so a new push replaces the sleeping run, and the arm still binds --match-head-commit. Chronic chatter is bounded by a 3× cap that declines + disarms.
  2. Consult — run .github/scripts/unaddressed-findings.sh (vendored twin of dotclaude's bb-unaddressed-findings.sh, fetched from this repo's default branch exactly like classify.mjs). Findings newer than the last authored commit ⇒ decline the arm, disarm any arm an earlier run placed, and leave a sticky comment with the report.

Uniform across every arm path, bypass labels included: wxa_vpn#1404 merged 56 seconds after a (false) Codex P1 because an approval decided minutes earlier raced a review that hadn't finished speaking. The escape for a wrong finding is the one thing this lane never touches — the manual merge click. Detector precision measured 90.9% on a fresh 25-PR corpus (#1369–#1401), and 40% of merges carried a real unaddressed finding, so the decline-to-manual cost is small relative to the caught class.

What it deliberately does not do

  • No new required status check — the gate can decline an arm but can never wedge a PR ("Expected — waiting" is the failure mode this fleet has been burned by). Every error path exits nonzero, which skips the arm and lets the existing always() error-revoke disarm stale arms.
  • Manual / risk:blocked merges stay advisory — both incident P1s were on human click-merges, which only a required check could block, and the detector has no acknowledgment path yet (only a new commit clears a finding). Blocking there is explicitly deferred until an ack path exists; the bb-unaddressed-findings.sh pre-merge sweep remains the manual-lane practice.

Testing

  • selftest/test_automerge_findings_gate.sh — 18 cases: 4 structural pins (error-revoke coverage, Enable-condition wiring, interpolation-free extraction, pagination on all three anchor reads) + 9 behavioral cases against stubbed gh/date/sleep (disabled short-circuit, quiet+clean arms, findings decline+disarm+comment, detector-error fails closed, young-activity sleeps the remainder, no-anchor fails closed, own-marker comments don't reset the clock, chatter cap declines+disarms, unreadable commits fail closed) + 2 fixture-driven runs of the vendored detector.
  • Existing test_automerge_hold_gate.sh / base_gate / risk_patterns / riskfile_gate all still pass; actionlint and shellcheck -S warning clean.

Rollout notes

  • All 44+ fleet callers pick this up on merge with the 20-minute default — automerge latency grows by up to ~20 min per PR, and runner minutes grow by the slept window per arm-eligible event burst. Callers can set findings_quiet_minutes: 0 to opt out wholesale.
  • The vendored detector must stay byte-identical (below its header) to dotclaude's operator copy — the sync contract is documented in both files.

Auto-merge rationale: none — PRs to topcoder1/ci-workflows are always manual-merge (production CI infrastructure).

🤖 Generated with Claude Code

…ngs check

wxa_vpn#1392: four PRs merged 3m18s-16m17s after a review finding posted,
with no commit in response — nothing holds the door between the last
review and the merge. The merge is server-side once required checks go
green, so the consultation moves to ARM time: wait findings_quiet_minutes
(default 20) of review silence, then run the unaddressed-findings detector
(vendored twin of dotclaude's bb-unaddressed-findings.sh, fetched from
this repo's default branch like classify.mjs); findings decline the arm,
disarm any stale arm, and leave a sticky comment.

Applies to every arm path, bypass labels included (wxa_vpn#1404 merged 56s
after a false Codex P1 — an approval says "I read what was there", not
"nothing further may be said"). Never a required check; the manual merge
button stays untouched, so a wrong finding costs one click, never a wedge.
Detector precision measured 90.9% on a fresh 25-PR corpus (#1369-#1401);
40% of merges carried a real unaddressed finding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cursor

cursor Bot commented Aug 9, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@github-actions github-actions Bot added the risk:blocked Risk class: blocked label Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

Risk class: blocked — manual merge required.

This PR touches one of the blocked path categories from .github/risk-paths.yml (Dockerfiles, docker-compose, .github/workflows/**, **/.env*, **/secrets*, infra/, terraform/, k8s/, or the classifier config itself).

Auto-merge is refused by claude-author-automerge.yml. A maintainer should review the diff and click "Squash and merge" themselves.

(This is a policy notice, not a code-quality failure. The classify job itself does not fail — required CI checks remain authoritative for "is the code green.")

@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

Coverage Floor — mode: enforce

metric value
measured 100.0%
floor (current) 99.0%
target 100.0%
last bumped 2026-05-12

@claude

claude Bot commented Aug 9, 2026

Copy link
Copy Markdown

No issues found. Quiet-period + findings gate is well-structured: fail-closed on every error path, timestamp shape validated before use, paginate handling correct in both the workflow step and the vendored detector, and the 18-case selftest covers the critical behavioral branches.

@topcoder1
topcoder1 merged commit 98c48ca into main Aug 9, 2026
16 checks passed
@topcoder1
topcoder1 deleted the claude/automerge-findings-quiet-gate branch August 9, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:blocked Risk class: blocked

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant