v0.4.0 — Arm auto-merge, derive the merge method, unblock the agents' view of CI
Upgrade if you are on v0.3.x
Two of these are defects that make the pipeline quietly do less than it reports. Re-run the installer to pick them up.
The review agent could not see CI, and said so to nobody
A job-level permissions: block sets every unlisted scope to none. The review job granted contents, pull-requests and id-token only — but the prompt orders gh pr checks (Checks API) and gh run view --log-failed (Actions API), and both are allow-listed in --allowedTools. So the agent got a 403 on the CI it was told to read, reviewed the PR blind, and the job still reported success.
Worse in the autofix job, which fires only on a CI failure: reading that failure is its entire first step.
Both jobs now grant checks: read + actions: read.
New test/permissions.test.mjs locks it semantically: it derives what each job needs from that job's own allow-list and the prompt it loads, then checks the granted scopes are a superset. A hardcoded assertion would pass forever while a newly allow-listed command went un-scoped, and an unmapped command throws rather than being skipped.
A merge that lost a race told nobody
The gate merged imperatively — read CI, then ask GitHub to merge. Anything that changed mergeability in between became a refusal with no retry, because the job only wakes on a new commit or a new comment.
It now arms GitHub's native auto-merge, which holds the request and merges when every requirement is met. Arming is a latch and the gate re-derives on every wake, so a later refusal disarms — a PR gone red, or one whose review posts a superseding escalate, cannot stay armed against the gate's current answer. The disarm fails closed: an unreadable "who armed it" answer disarms rather than skipping. An auto-merge a human enabled is reported, never silently revoked.
Past authorization, every failure now escalates — label, assignee, comment, red job. A merge that fails silently is indistinguishable from one that never ran.
The merge method is now read off your repo, not assumed
GitHub lets you forbid merge commits, squashes, or rebases in two independent places: the repository settings, and the default branch's ruleset (pull_request rule → allowed_merge_methods). Reading only the first is a live trap — it produced a downstream workaround that hardcoded rebase around a restriction that actually lived in the ruleset and later went away.
dep-steward now reads both, intersects them, and gate.cjs picks by ordered preference:
- workflow-touching → merge, rebase, squash.
GITHUB_TOKENcan never hold theworkflowspermission, so an App-authored new commit editing a workflow file is refused. A merge commit authors none; a rebase rewrites SHAs and may be refused for the same reason; a squash always is. - everything else → squash, rebase, merge.
If your repo permits no method at all, the job escalates rather than passing an invalid flag to gh. "Could not read the setting" and "nothing is allowed" stay distinguishable end to end — collapsing those two is what produced the hardcode.
Also
claude-code-actionpinned to v1.0.183 (from v1.0.165). Both jobs, asserted equal.- A per-run agent diagnostic step: prints the turn-by-turn tool calls, the denials, and the final error. A denied tool call is how a review silently degrades while still reporting success — exactly what the permissions defect looked like from the outside.
Breaking-ish
gate.cjs now reads a new optional env var, ALLOWED_MERGE_METHODS, and always prints a third output line, method=. If you have hand-edited your installed gate or workflow, re-render rather than merging by hand — the workflow's new "Resolve the merge methods this repo actually allows" step is what populates it.
Full changelog: v0.3.1...v0.4.0