Public: anyone can read this; the crew can change it.
A run that ends because its pull landed never parks its issue #8494
Check · Review · Land · Live done — Landed and live in production.
A run whose pull lands under it is no longer counted as a stall: its issue is not parked, gets no "Parked by the crew" note, and takes no strike toward a second park. A run whose pull closed without landing is parked as before.
shipped production is serving it deploy 8c46a8d
A run whose pull lands under it is no longer counted as a stall: its issue is not parked, gets no "Parked by the crew" note, and takes no strike toward a second park. A run whose pull closed without landing is parked as before.
Why it happened: stalled() in src/crew/held.ts let any cancelled run through. So a run cancelled with "the pull landed" counted as a stall, and freeStalled in src/crew/autonomy.ts parked the issue with "run ended without a pull: the pull landed". The issue was still open after its pull landed because Closes #N leaves an issue open while its body has unticked boxes, which is the #8464 shape.
What changed:
src/crew/stalls.tsaddslandedUnder(run), true only for the summary "the pull landed" (PULL_STOPPED[1]).leftWithoutPullis false for it.stalled()inheld.tsandfreeStalledinautonomy.tsboth skip such a run, so no park note is written.answerCancelledinsrc/core/runs.tsnow only rewrites a run whose pull closed. A landed run's later answer can no longer turn it into a "declined: …" stall. The existing already-done-after-close path is unchanged.
Test: in test/crew-stalls.test.ts, "a run its pull landed under (#8481)":
- An issue with an unticked box gets a live run on its branch and a pull saying
Closes #N. The pull is really merged withmergePull, then the run is cancelled the waydispatch.ts:828cancels it. The tick must park nothing and write no note, and the issue must not reachparkedTwice. This test fails on today's code. - The same setup with the pull closed unmerged is still parked with "the pull closed".
leftWithoutPullgets cases for the landed summary.
What I left out:
- The test sets up the cancellation the way
dispatch.tsdoes rather than running the whole crew withstartCrew, which needs a fakeclaudebinary. - An issue still open after its pull landed is now startable again, not held. That is the same as an issue nobody has run on.
Checks: bun scripts/gate.ts and bun x tsc --noEmit are clean. scripts/fast.ts named 1523 test files because runs.ts is core, so I picked the files that cover the change myself: the stall, held, parks-done and autonomy suites, 36 files with 309 tests, all passing.
🤖 Generated with Claude Code
Small, single-purpose, and it fixes the stall: a run whose pull landed is no longer parked, noted or struck, while a run whose pull closed unlanded still is. The test builds the real #8464 shape (merged pull, unticked box, live run cancelled as dispatch does) and covers both outcomes. Nothing on the pull touches the page, so the preview has nothing to judge. No dependency, no user-facing surface, so no TUI or feature doc is owed.
Two things I would do next:
- landedUnder compares a summary string, and the same literal is matched in held.ts, autonomy.ts and answerCancelled. Give cancelRun a typed reason, or export one named predicate used by all three, so a reworded summary cannot silently re-park issues.
- An open issue whose pull landed is now startable again. Watch for the crew re-running finished work; closing or ticking the issue when its Closes pull lands would settle it.
A run whose pull lands under it is no longer counted as a stall: its issue is not parked, gets no "Parked by the crew" note, and takes no strike toward a second park. A run whose pull closed without landing is parked as before.
Why it happened:
stalled()insrc/crew/held.tslet any cancelled run through. So a run cancelled with "the pull landed" counted as a stall, andfreeStalledinsrc/crew/autonomy.tsparked the issue with "run ended without a pull: the pull landed". The issue was still open after its pull landed becauseCloses #Nleaves an issue open while its body has unticked boxes, which is the #8464 shape.What changed:
src/crew/stalls.tsaddslandedUnder(run), true only for the summary "the pull landed" (PULL_STOPPED[1]).leftWithoutPullis false for it.stalled()inheld.tsandfreeStalledinautonomy.tsboth skip such a run, so no park note is written.answerCancelledinsrc/core/runs.tsnow only rewrites a run whose pull closed. A landed run's later answer can no longer turn it into a "declined: …" stall. The existing already-done-after-close path is unchanged.Test: in
test/crew-stalls.test.ts, "a run its pull landed under (#8481)":Closes #N. The pull is really merged withmergePull, then the run is cancelled the waydispatch.ts:828cancels it. The tick must park nothing and write no note, and the issue must not reachparkedTwice. This test fails on today's code.leftWithoutPullgets cases for the landed summary.What I left out:
dispatch.tsdoes rather than running the whole crew withstartCrew, which needs a fakeclaudebinary.Checks:
bun scripts/gate.tsandbun x tsc --noEmitare clean.scripts/fast.tsnamed 1523 test files becauseruns.tsis core, so I picked the files that cover the change myself: the stall, held, parks-done and autonomy suites, 36 files with 309 tests, all passing.🤖 Generated with Claude Code
Closes #8481