feat(p9): auto-merge actuator — close MERGE_READY → merge gap#2
Conversation
… merge
Closes the largest gap in the original P9 vision: "merges are pushed or
automerged". Until now P9 only emitted MERGE_READY; merge had to be
performed manually. This PR adds the actuator.
New subcommand: `p9 auto-merge <pr>`.
Flow:
1. Load policy → bail if `auto_merge.enabled` is false (default false,
so this is opt-in per workspace).
2. Verify PR is in MERGE_READY state.
3. Fetch branch + touched paths via `gh pr view --json headRefName,files`.
4. Match against `auto_merge.rules` (first-match-wins, with a pre-pass
that always blocks `path_touched: require_human` rules — the
governance-paths-always-block invariant from the original
brainstorming).
5. action=auto → `gh pr merge --<method> [--delete-branch]` and emit
MERGE_READY → MERGED state event.
action=require_human/notify → idempotent self-transition with
extra payload indicating the block reason; exit 7
(EXIT_AUTO_MERGE_BLOCKED).
Policy schema (new top-level `auto_merge:` block in `.control/policy.yaml`):
auto_merge:
enabled: true
require_no_requested_changes: true # gh enforces these directly
require_branch_up_to_date: true
merge_method: squash # squash | merge | rebase
delete_branch: true
rules:
- path_touched: CLAUDE.md # governance always blocks
action: require_human
- branch_pattern: "docs/*"
action: auto
...
default_action: notify # fail-safe default
Implementation notes:
- Default `enabled: false` and default `default_action: notify` make the
whole subsystem fail-safe — adding the block to policy.yaml is
required to opt in. The matching policy block lands in the workspace
repo as a follow-up PR.
- The minimal stdlib YAML loader was extended to handle block-style
list-of-dicts (`- key: val\n other: val2`), required for the rules
list. Stdlib-only runtime guarantee preserved.
- 16 new tests across policy-parser, matcher (governance-blocks-first
invariant), and command (subprocess-mocked dry-run + auto + blocked
+ disabled-policy + external-merge-failure paths).
Test totals: 46 unit + 15 integration + 6 chaos + 16 auto-merge = 83
passing in 4.0s.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughAdds an opt-in, policy-gated auto-merge feature to ChangesAuto-Merge Feature Implementation
Sequence DiagramsequenceDiagram
participant User as User / CLI
participant P9 as cmd_auto_merge()
participant Policy as Policy Loader
participant GH as gh CLI
participant PR as PR State
User->>P9: p9 auto-merge --pr 42
P9->>Policy: Load & parse auto_merge config
Policy-->>P9: AutoMergePolicy
P9->>PR: Check PR state (expect MERGE_READY)
PR-->>P9: State confirmed
P9->>GH: gh pr view 42 (fetch branch & files)
GH-->>P9: branch=feat/p9-*, paths=[src/...]
P9->>P9: match_auto_merge_action(policy, branch, paths)
P9-->>P9: action=auto (matches feat/p9-* rule)
P9->>GH: gh pr merge 42 --squash --delete-branch
GH-->>P9: Merge successful
P9->>PR: Append MERGE_READY → MERGED event
PR-->>P9: State transitioned
P9-->>User: Success (exit 0)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
Closes the largest gap in the original P9 vision: "merges are pushed or automerged". Until now P9 only emitted `MERGE_READY`; merge had to be performed manually by the agent or human. This PR ships the actuator.
New subcommand: `p9 auto-merge `
```
p9 auto-merge [--repo OWNER/REPO] [--dry-run]
```
Flow:
`action=require_human`/`notify` → idempotent self-transition with `extra` payload indicating block reason; exit 7 (`EXIT_AUTO_MERGE_BLOCKED`).
Policy schema
New top-level `auto_merge:` block in `.control/policy.yaml`:
```yaml
auto_merge:
enabled: true
require_no_requested_changes: true
require_branch_up_to_date: true
merge_method: squash # squash | merge | rebase
delete_branch: true
rules:
- path_touched: CLAUDE.md # governance always blocks
action: require_human
- branch_pattern: "docs/*"
action: auto
# ...
default_action: notify # fail-safe default
```
Fail-safe defaults
Implementation notes
Test totals
Follow-up
Workspace PR (next): adds the matching `auto_merge:` block to `.control/policy.yaml` and a 4th trigger to the P9 reflexive rule in AGENTS.md ("on `MERGE_READY` → invoke `p9 auto-merge` rather than manual `gh pr merge`").
🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Tests