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:
GhMerge.integrate()pushes the branch as today, then callspetrova.act.request_review(via a Fleet MCP client) instead ofgh pr createdirectly.- If
required_checksis set (today's auto-merge-on-green path), callpetrova.act.request_merge_when_greeninstead of the_wait_for_checks+gh pr merge --autosequence. - Both verbs return a PR URL / result shape
MergeResultcan wrap without changingMergeDriver's protocol orrunner.py's caller contract — this is scoped tomerge_driver.pyonly. - 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
- Whether RALPH should default to
livemode more broadly — out of scope, unrelated to the gap itself. - Whether
LocalMerge(non-livemode) needs any change — it never pushes or opens a PR, so it isn't the ungoverned surface. - Timeline or owner — this is a proposal for rocky-hq's own roadmap call,
not a petrova-codes mandate.
fleets_allowed: []forpetrova-codesitself means this can never be actioned by fleet automation; a human or rocky-hq's own agents pick this up if and when it's prioritized.
References
ralph/src/rocky/merge_driver.py(GhMerge,bb1ff88, 2026-05-02)ralph/src/rocky/cli.py:62-65,ralph/src/rocky/serve/registry.py:70petrova-codes/host/src/registry.ts(petrova.act.request_review,petrova.act.request_merge_when_green)petrova-codes/docs/decisions/2026-09-14-ir-002-step4-remaining-seam-tiers.mdpetrova-codes/docs/decisions/2026-09-23-ir-012-burn-in-against-real-dispatches.mdpetrova-codes/docs/findings/20260925-0900-ralph-pr-path-does-not-exist.md(correction section, same date)