ADR-0027: Ralph merge gate re-polls CI in place instead of rebuilding - #48
Merged
Merged
Conversation
The workflow's merge gate is dispatched as a subagent, which structurally cannot idle-wait on CI (a background sleep never resumes it). Given one turn it polls CI a few times, sees `in_progress`, and returns `ci-timeout` — after a minute or two, not the "~20 minutes" the prompt claimed. `drive()` then treated `ci-timeout` like any failure and re-dispatched the *build* stage, so a CI that was merely still running (the ~3-4 min norm) cost a full fresh implementer (`build:#n:retry`) whose only real work was re-adopting the already-open, already-green PR. ADR-0022 pre-registered exactly this under "Revisit when: the gate-agent retry loop too expensive versus a repair-in- place step." Decouple the CI-wait re-poll from the build attempt: - On `ci-timeout` the loop re-polls the same cheap gate agent in place (reads only until it can merge, effort low) up to POLICY.gateWaits times (default 6, per-run arg) before the ticket spends a fresh implementer attempt. Every real verdict — merged, ci-failed, not-mergeable — leaves the loop immediately; a CI that never lands still falls back to the unchanged attempt/park path once re-polls run out, so it can neither merge an unfinished run nor loop forever. - Make the gate prompt honest: state the one-turn constraint, poll a handful of Monitor-spaced times, then report `ci-timeout` as a cheap re-poll signal (never a reason to merge on an unfinished run) instead of promising a 20-min wait it cannot perform. Concurrent tickets in a round supply the wall-clock the re-polls ride on; at width 1 they still cost only cheap gate agents, never an implementer. Adds ADR-0027 (amends 0022), updates the registry, and notes the new `gate:#n:ci-wait*` shape in the supervisor's "healthy shapes" list. Covered by tests that execute the workflow script against mock agents and assert the dispatch sequence: ci-timeout -> in-place re-poll with no build retry; persistent timeout -> bounded re-polls -> fresh attempt -> park. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EVZHX5ov5vFVjZcbHcya36
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements ADR-0027 to prevent slow CI from triggering expensive rebuild attempts in the Ralph workflow.
Summary
When the merge gate agent encounters a still-running CI (
ci-timeout), the loop now re-polls the cheap gate in place up toPOLICY.gateWaitstimes (default 6) before falling back to a fresh implementer attempt. This avoids wasting a full rebuild on CI that is merely slow, not broken.Key Changes
Workflow gate re-poll loop: Modified
drive()in bothralph.jsandralph.workflow.jsto wrap the gate agent dispatch in a bounded loop that re-polls onci-timeoutwithout triggering a fresh build attempt.New policy knob: Added
gateWaitstoPOLICY(default 6, overridable per run) to bound the re-poll attempts. This is a workflow-only setting; skill-mode orchestration is unchanged.Updated gate prompt: Clarified that the gate agent has one turn and cannot idle-wait. It now polls CI a few times and reports
ci-timeoutif still running—framed as a signal for cheap re-polling, not a failure. Re-polls include awaitedcounter so the prompt can indicate "it has very likely finished by now."Test coverage: Added two new test suites that execute the workflow script against mock agents:
ci-timeouttriggers in-place re-polls (gate:#n:ci-wait*) without spawning a fresh build attempt.Documentation: Added ADR-0027 explaining the decision, alternatives considered, and consequences. Updated ADR-0022 reference to note it is amended by this ADR.
Implementation Details
The re-poll loop in
drive()checksgate.status !== 'ci-timeout' || waited >= POLICY.gateWaitsto exit early on any real verdict (merged, ci-failed, not-mergeable, failed) or when the re-poll budget is exhausted. Only then does the ticket fall back to the existing attempt/retry/park path. This ensures a slow-but-passing CI merges on the first attempt via cheap re-polls, while a stuck CI is still bounded and eventually parked rather than looping forever.https://claude.ai/code/session_01EVZHX5ov5vFVjZcbHcya36
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.