← Ledger


Date: 2026-09-25 Status: Proposed

Context

RALPH's live-mode merge driver (ralph/src/rocky/merge_driver.py, GhMerge, wired in cli.py/serve/registry.py when cfg.mode == "live") does:

git push -u origin <branch>
gh pr create --base <base> --head <branch> --title ... --body ...
gh pr merge --auto --squash <pr_url>

with zero cross-runner WIP-cap awareness and zero seam-tier gating. This is the same class of unmanaged concurrent-write risk that PETROVA's IR-012 burn-in proved out for irina-cycle.yml (petrova-codes/docs/decisions/2026-09-23-ir-012-burn-in-against-real-dispatches.md): two RALPH runs against the same target repo in live mode, overlapping, can open and auto-merge two PRs into the same branch with no lock between them. Unlike irina-cycle.yml, there is no concurrency: block or equivalent here — GhMerge.integrate() has no coordination with any other RALPH invocation, in-process or cross-process.

This was surfaced by a petrova-codes scoping taskset investigating whether so1-io's automation could be folded into a rocky-hq fix (finding: petrova-codes/docs/findings/20260925-0900-ralph-pr-path-does-not-exist.md, corrected same day — the original finding wrongly concluded no such code existed). so1-io has no reusable branch→PR groundwork; it was a dead end. What does exist and is directly reusable: PETROVA's petrova.act.request_review and petrova.act.request_merge_when_green Fleet MCP verbs (petrova-codes/host/src/registry.ts), which already wrap PR-opening and auto-merge-on-green with WIP-cap and seam-tier gating for every other governed consumer repo.

Decision (proposed — not yet accepted)

Replace GhMerge's direct gh pr create / gh pr merge --auto calls with calls to the Fleet MCP verbs, so RALPH inherits WIP-cap/seam-tier governance for free instead of rocky-hq building and maintaining its own lock:

  1. GhMerge.integrate() pushes the branch as today, then calls petrova.act.request_review (via a Fleet MCP client) instead of gh pr create directly.
  2. If required_checks is set (today's auto-merge-on-green path), call petrova.act.request_merge_when_green instead of the _wait_for_checks + gh pr merge --auto sequence.
  3. Both verbs return a PR URL / result shape MergeResult can wrap without changing MergeDriver's protocol or runner.py's caller contract — this is scoped to merge_driver.py only.
  4. No new WIP-cap or seam-tier code needed in rocky-hq — it is inherited from the verb, the same way every other petrova.act.* consumer gets it.

What this doesn't decide

References