phase-pr cannot open a pull request without also waiting for CI, so an unattended phase spends its own budget on sleep #566

Closed
opened 2026-09-15 10:05:57 +00:00 by claude · 0 comments
Member

phase-pr pushes the sub-branch, opens the pull request, and then waits for CI. The wait is not
separable: --batch decides what happens after green (merge instead of stopping for review), not
whether to wait, and --timeout/--poll-window only size the wait.

For an interactive session that is right. The human is driving, one command carries the phase from
"commits are ready" to "CI proved it", and watching the result is the point.

For an unattended run it is the wrong owner. ./iw kanon/batch-implement gives each phase's Claude
session a wall-clock ceiling — 90 minutes by default — and the session spends its own budget doing
sleep 30. Measured on IW-555 on 2026-09-14: 17.3 minutes of a 90-minute session, two
phase-pr invocations of 481 s each, and the session was killed by the ceiling with the phase
unfinished. The orchestrator was idle throughout, and it already owns a CI wait it never got to use
(CiGate.awaitCi in the kanon plugin, which shells out to iw ci-gate).

The model's clock is the scarce resource. The orchestrator's is not.

What is asked for

A way for the caller to open the pull request and take the wait itself. Proposal: --no-wait.

iw phase-pr --title "Phase 1: ..." --no-wait
  • Pushes the sub-branch and opens the pull request, exactly as now.
  • Records the pull request in the review-state card (pr_url), so the following ci-gate finds it
    with no argument — ci-gate already reads "the pull request the review-state card already
    records".
  • Exits 0 once the request is open. It performs no wait, so it cannot return 3.
  • Leaves the card in a state that says the wait has not happened, rather than one that implies a
    verdict.

Nothing else changes. The wait already exists as a first-class command with the same three exit
codes and the same three flags, so this adds no waiting logic — it removes an assumption about who
runs it.

Both modes must keep working

  • Interactive is the default and is untouched. Without the flag, phase-pr behaves exactly as
    it does today: push, open, wait, and with --batch merge on green. No existing invocation
    changes behaviour, and no workflow document needs editing for the interactive path.
  • Unattended passes the flag. The phase session ends at the open pull request; the orchestrator
    runs iw ci-gate, and on red it starts a fresh session with the failure report — the recovery
    path phase-merge already ships a hook for.

--batch and --no-wait do not compose

--batch merges as soon as CI proves green, which requires the wait it was told to skip. Rejecting
the combination with a clear message is better than defining a winner: the unattended caller that
passes --no-wait merges with phase-merge afterwards, which is the command for it and which
already takes the one-CI-reading shortcut after a green gate.

Acceptance

  • iw phase-pr --title T --no-wait opens the request, writes pr_url to the card, and exits 0
    without polling the forge.
  • iw ci-gate immediately afterwards, with no arguments, finds that pull request and waits on it.
  • --no-wait together with --batch is refused, naming both flags.
  • phase-pr with no --no-wait is unchanged: the existing tests for the wait, its exit 3
    resumption and its --batch merge all still pass.
  • --help states that the caller owns the wait when the flag is given, and names ci-gate.
`phase-pr` pushes the sub-branch, opens the pull request, and then waits for CI. The wait is not separable: `--batch` decides what happens *after* green (merge instead of stopping for review), not whether to wait, and `--timeout`/`--poll-window` only size the wait. For an interactive session that is right. The human is driving, one command carries the phase from "commits are ready" to "CI proved it", and watching the result is the point. For an unattended run it is the wrong owner. `./iw kanon/batch-implement` gives each phase's Claude session a wall-clock ceiling — 90 minutes by default — and the session spends its own budget doing `sleep 30`. Measured on IW-555 on 2026-09-14: **17.3 minutes of a 90-minute session**, two `phase-pr` invocations of 481 s each, and the session was killed by the ceiling with the phase unfinished. The orchestrator was idle throughout, and it already owns a CI wait it never got to use (`CiGate.awaitCi` in the kanon plugin, which shells out to `iw ci-gate`). The model's clock is the scarce resource. The orchestrator's is not. ## What is asked for A way for the caller to open the pull request and take the wait itself. Proposal: **`--no-wait`**. ``` iw phase-pr --title "Phase 1: ..." --no-wait ``` - Pushes the sub-branch and opens the pull request, exactly as now. - Records the pull request in the review-state card (`pr_url`), so the following `ci-gate` finds it with no argument — `ci-gate` already reads "the pull request the review-state card already records". - Exits **0** once the request is open. It performs no wait, so it cannot return 3. - Leaves the card in a state that says the wait has not happened, rather than one that implies a verdict. Nothing else changes. The wait already exists as a first-class command with the same three exit codes and the same three flags, so this adds no waiting logic — it removes an assumption about who runs it. ## Both modes must keep working - **Interactive is the default and is untouched.** Without the flag, `phase-pr` behaves exactly as it does today: push, open, wait, and with `--batch` merge on green. No existing invocation changes behaviour, and no workflow document needs editing for the interactive path. - **Unattended passes the flag.** The phase session ends at the open pull request; the orchestrator runs `iw ci-gate`, and on red it starts a fresh session with the failure report — the recovery path `phase-merge` already ships a hook for. ## `--batch` and `--no-wait` do not compose `--batch` merges as soon as CI proves green, which requires the wait it was told to skip. Rejecting the combination with a clear message is better than defining a winner: the unattended caller that passes `--no-wait` merges with `phase-merge` afterwards, which is the command for it and which already takes the one-CI-reading shortcut after a green gate. ## Acceptance - `iw phase-pr --title T --no-wait` opens the request, writes `pr_url` to the card, and exits 0 without polling the forge. - `iw ci-gate` immediately afterwards, with no arguments, finds that pull request and waits on it. - `--no-wait` together with `--batch` is refused, naming both flags. - `phase-pr` with no `--no-wait` is unchanged: the existing tests for the wait, its exit 3 resumption and its `--batch` merge all still pass. - `--help` states that the caller owns the wait when the flag is given, and names `ci-gate`.
mph closed this issue 2026-09-16 09:32:26 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
iterative-works/iw-cli#566
No description provided.