Skip to content

Commit e1e8139

Browse files
committed
feat(tournament): cancel a running race with Ctrl-C
A tournament runs N agents to completion — minutes of work — with no way to bail until it finished. The first Ctrl-C now aborts the race (its contestants get the signal), tears down the worktrees, and skips the merge picker instead of exiting.
1 parent b51c8ba commit e1e8139

1 file changed

Lines changed: 20 additions & 1 deletion

File tree

src/ui-pi/app.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,13 @@ export class App extends Container {
537537
return { consume: true };
538538
}
539539
this.exitArmedAt = now;
540+
// A running tournament owns the first Ctrl-C: cancel the race
541+
// (its contestants get the abort signal) instead of exiting.
542+
if (this.tournamentRunning) {
543+
this.statusBar.note("cancelling tournament…");
544+
this.tournamentAbort?.abort();
545+
return { consume: true };
546+
}
540547
if (this.busy) {
541548
try {
542549
this.bundle.agent.abort();
@@ -706,10 +713,22 @@ export class App extends Container {
706713
signal: this.tournamentAbort.signal,
707714
onProgress,
708715
});
716+
// Cancelled mid-race: tear down the worktrees, skip the picker.
717+
if (this.tournamentAbort.signal.aborted) {
718+
await cleanupTournament(cwd, outcome.branches);
719+
this.statusBar.note("tournament cancelled — nothing merged.");
720+
this.tournamentRunning = false;
721+
return;
722+
}
709723
this.statusBar.note("");
710724
this.showTournamentOverlay(outcome, snap.sha, task);
711725
} catch (err) {
712-
this.statusBar.note(`tournament failed: ${err instanceof Error ? err.message : String(err)}`);
726+
const cancelled = this.tournamentAbort?.signal.aborted;
727+
this.statusBar.note(
728+
cancelled
729+
? "tournament cancelled — nothing merged."
730+
: `tournament failed: ${err instanceof Error ? err.message : String(err)}`,
731+
);
713732
this.tournamentRunning = false;
714733
}
715734
}

0 commit comments

Comments
 (0)