Arm auto merge
Arm keel's committed per-PROJECT auto-merge for a repo — `keel:arm-auto-merge` (or `on`) asserts branch protection + the three required checks + the repo's `allow_auto_merge` are live via `scripts/check-branch-protection.sh`, and ONLY on a green assertion writes the tracked `.claude/keel-auto-merge.json` (`scope: "project"`) and opens it as a plan-only PR, so every session in the repo may land an instructed, gate-passing `gh pr merge <pr> --auto` with no per-merge tap; `keel:arm-auto-merge off` opens the plan-only PR that removes it. The skill only writes or removes that one marker file and opens the PR — it never merges anything itself, and the verified-pin gate and the no-agent-initiative rule stay intact.From its SKILL.md
npx -y skills add mckissinger/keel --skill arm-auto-mergeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
13.2 KB, as published. Nobody here has run it
Arm auto-merge (committed, per-project)
Make auto-merge the standing posture of a repo, not one session's. Where keel:auto-merge
drops the per-merge tap for the session you're driving, this skill drops it for every session
in the project — by committing a marker to the default branch that both merge guards honor. It is
the deliberate, human-invoked switch that says "on this repo, the server-side required checks are
the review; a gate-passing --auto needs no human tap."
decisions/2026-08-02-committed-auto-merge-marker.md is the shape this executes, and
decisions/2026-07-04-attended-auto-merge.md is the doctrine it extends from the session grain to
the project grain: the tap — not the authority — is delegated to an explicit human invocation,
and only the --auto handoff is unlocked (GitHub's required checks still decide what actually
lands). The verified-pin gate and the no-agent-initiative rule are untouched.
Because the marker is a standing authority — it outlives this session and applies to sessions no human is watching — the bar to write it is higher than the session marker's: the skill refuses to arm a repo whose protection is not actually live. That gate is the whole point of the skill.
What the committed marker unlocks (and what it does not)
The marker is .claude/keel-auto-merge.json, honored only when present on the repo's default
branch (both guards read it from the server default branch, never the working tree). When it is
live — and no autonomy mode file and no attended session marker take precedence (precedence is
mode > attended > committed) — both guards honor it for one command shape: a bare
gh pr merge <pr> --auto [--squash|--merge|--rebase] in its own Bash call (the existing
detect_strict_auto whitelist).
scripts/merge-guard.sh: that shape on a verified-pin-gate-passing PR → allow (no tap) instead of the ask floor. Plaingh pr merge(no--auto) stays ask. Gate fail stays deny. Any PR whose diff touches.claude/keel-auto-merge.jsonis forced to a human tap (ask), before and regardless of every allow row — arming, disarming, or editing the marker is never itself auto-merged. Every other shape is byte-for-byte today's table.scripts/guard-branch-rules.sh(the build-session guard): that same bare shape → defer (exit 0) tomerge-guard.shinstead of the categoricalexit 2refusal. Every other merge-shaped command, andgit commiton the default branch, stillexit 2.
With no committed marker, every guard behaves exactly as today. The marker never unlocks a
plain merge, a push, a git merge to the default branch, or a bundled/chained --auto — only the
bare delegation shape, which is meaningful only where branch protection makes --auto real.
First, once per project: declare this repo's check names (if they aren't keel's)
The assertion certifies that specific required checks are live. By default it looks for keel's
own job names — verified-pin, plan-lint, security-review. A consuming repo's real CI almost
never uses those names (it's verified-pin gate, typecheck · lint · test, and so on), so on such
a repo the assertion would GAP on a pure name mismatch, not a real protection hole.
The easy path: keel:prep-auto-merge. On a repo that isn't set up for auto-merge at all — no
required security-review workflow, check names ≠ keel's, allow_auto_merge off — don't hand-assemble any
of this. Run keel:prep-auto-merge first: it discovers the gaps with this same assertion, scaffolds
the security-review workflow and generates this check-contract (from the repo's actually-reporting
contexts), and prints the branch-protection / allow_auto_merge commands — everything this skill then
certifies. It never arms; it points back here. The rest of this section is the manual fallback — the
shape to author by hand when you're not using prep, and the reference for what prep generates.
The fix is a committed per-project check-contract — .claude/keel-auto-merge-checks.json — where
the repo declares its own required-check contexts and which of them is its security-review check.
The assertion reads it fail-closed from the server default branch (the same transport as the marker), so
a working-tree or branch-only copy is ignored. Shape:
{
"required_checks": ["verified-pin gate", "typecheck · lint · test", "security-review"],
"security_review": { "check": "security-review" }
}
It declares names only — it can rename the security-review check but never remove it (a set
omitting it GAPs), and it cannot switch off the (b2) content scan or the allow_auto_merge check. It
carries no pattern and no external field, and any such key in the file is ignored: both stay
env-only / keel-default because a committed value the author picks can silently weaken the (b2) scan (a
committed external: true skips it; a committed pattern is a substring whose too-broad values — "",
.*, @, an org prefix — match every uses: line). The (b2) match pattern stays keel's default,
overridable only by the trusted per-invocation PREFLIGHT_SECREVIEW_PATTERN; a non-Actions provider is
attested only by PREFLIGHT_SECREVIEW_EXTERNAL=1. A repo using keel's own review action needs no
pattern at all; a repo using a different in-Actions review action sets PREFLIGHT_SECREVIEW_PATTERN at
arm time. A present-but-malformed or empty contract fails closed (GAP), never a silent fall-back.
Precedence is operator override (PREFLIGHT_* env, or an edited config-block default) > committed
contract > keel default (decisions/2026-08-03-arm-auto-merge-check-contract.md). Commit it as a
plan-only PR (it gets the same is_plan_path carve-out as the marker) — and note a PR editing it
takes a human tap (it is auto-merge trust base). If the repo's checks already match keel's names, skip
this; the default just works.
on (default) — assert protection is live, then commit the marker
The order is load-bearing: assert first, write only on green. Never write the marker on a red or unproven assertion — a marker on a repo whose protection is not live arms unattended merges with no server-side backstop, the exact failure this gate exists to prevent.
-
Assert branch protection. Run the shared assertion — do not re-author it:
scripts/check-branch-protection.shThis is the same code
scripts/check-auto-preflight.shruns for its (b)/(b2)/(d) checks (the merge-relevant subset: the required checks are actually required, thesecurity-reviewcheck is workflow content not just a name, andallow_auto_mergeis enabled on the repo). It is the single source of the assertion, so the arming check and the auto-entry check can never drift apart. It resolves which check names to certify from the committed check-contract above (orPREFLIGHT_*/ keel default). It fails closed: missinggh/jq, unreadable protection, or an API error is a GAP, not a silent pass. -
Halt on any GAP. If the assertion exits non-zero, stop — surface each GAP line and its named remediation verbatim, and do not write the marker. Every gap is fixed attended (wire the required check, make it required, enable
allow_auto_merge), never worked around by arming anyway. A red assertion is a full stop, not a warning to write past. The overrides the assertion documents (PREFLIGHT_SECREVIEW_PATTERNfor a different in-Actions review implementation,PREFLIGHT_SECREVIEW_EXTERNAL=1to attest a non-Actions provider) are the only legitimate ways to clear (b2) — never by renaming or dropping the check. -
On green, write the marker. Compute a real ISO-8601 UTC timestamp for
created(date -u +%Y-%m-%dT%H:%M:%SZ) and aninvokerstring identifying this human invocation. Write.claude/keel-auto-merge.jsonunder the project directory with exactly the three contract fields — all non-empty strings:{ "scope": "project", "created": "<the ISO-8601 UTC timestamp>", "invoker": "<who invoked this — e.g. human:keel-arm-auto-merge>" }scopemust equal"project"; any other value, a missing or emptycreated/invoker, or malformed JSON makes the marker invalid and both guards fail closed to today's behavior. There is no TTL — unlike the session marker, a committed project marker is a standing decision that stays in force until a human removes it (that is what "per-project" means). Removal is theoffpath below. -
Open it as a plan-only PR — never a direct push to
main. The marker is the only changed file, and M1's carve-out (decisions/2026-08-02-committed-auto-merge-marker.md) makes.claude/keel-auto-merge.jsona plan-path in the verified-pin gate, so a marker-only PR is plan-only → exempt from the pin → lands under the full protection contract with noverified:pin. Commit the marker on a branch and open the PR withgh pr create. Do not push tomain, and do not merge the PR — the human reviews and merges it, exactly like every other plan PR (and the human-tap rule above independently forces that PR to a tap even if arming were somehow attempted via--auto). -
Report what was armed. State that once the PR merges, this arms an instructed, gate-passing
gh pr merge <pr> --autofor every session in this repo with no per-merge tap, that the required checks are now the review, and that disarming iskeel:arm-auto-merge off(which opens the plan-only PR removing the file).
off — open the plan-only PR that removes the marker
Symmetric with on. If .claude/keel-auto-merge.json is present on the default branch, remove it
on a branch and open the plan-only PR that deletes it (a marker-only diff is still plan-only →
exempt). The human reviews + merges; once merged, both guards are back to today's ask / deny /
exit-2 matrix for the repo. If the marker is already absent, report that and open nothing. As with
on, this skill opens the PR and stops — it never merges it, and never pushes to main.
Boundaries
- This skill writes or removes only
.claude/keel-auto-merge.jsonand opens the plan-only PR. It is the committed marker's only writer. It issues no merge command of its own — it never callsgh pr merge; the human merges the arming PR, and thereafter humans still type eachgh pr merge <pr> --auto. The marker only changes how the guards score that shape. - The agent never invokes this skill to arm its own merges.
disable-model-invocation: truekeeps the model from calling it; the human invocation is the whole authorization trail — the same pattern by whichkeel:autois the sole writer of the autonomy mode file andkeel:auto-mergeis the sole writer of the session marker. - The assertion is reused, never re-authored. The protection check is
scripts/check-branch-protection.sh— the single source both this skill and the auto-preflight call. Never inline a second copy of the branch-protection logic here; if the assertion needs to change, it changes in that one script. - Precedence: mode > attended > committed. When a valid
.claude/keel-autonomy.json(autonomy mode) or.claude/keel-attended-merge.json(attended session) is active, that path governs the merge decision and the committed marker yields — the committed marker is the lowest-priority, always-on floor (decisions/2026-07-05-autonomy-modes-v2.md). - The load-bearing gates are untouched. The verified-pin gate still denies unverified code, and
--autostill lands only when the branch's required checks pass. A stale or forged marker cannot merge unsafe code — the marker (and the check-contract) are honored only from the server default branch behind a mandatory fail-closed fetch, and any PR touching the marker or the check-contract is forced to a human tap.