Skip to content

Auto-cancel webhook-launched runs when their PR merges or closes #101

Description

@devthejo

Problem

A run launched by a forge webhook against a PR branch (Billy push-back, Vetty guard, Revi review) keeps running — or worse, starts from the queue — after the PR it targets has merged or closed. Observed live on 2026-07-10: a /billy fired on PR #98 stayed queued behind another run; #98 merged meanwhile; the run then started, cloned an empty ref (branch deleted), fell back to the default branch, and churned through a full campaign pass on an empty diff (~7 min of runner time + LLM cost for nothing). With per-team concurrency this orphan also blocks legitimate queued runs.

Two gaps compound:

  1. At claim time (cheap, high value): a queued run whose subject PR is no longer open should be cancelled instead of started. The launch already records the PR context (pr_url launch var, webhook delivery SubjectURL); the runner or the queue dispatcher can check PR state before executing.
  2. On PR terminal events (pull_request closed/merged deliveries we already receive on the same webhook): look up in-flight runs bound to that PR (match on repo + PR number from the launch metadata) and cancel them (failed/cancelled with a clear reason, checkpoint preserved per the normal cancel path).

Scope / non-goals

  • In scope: cloud/webhook-launched runs bound to a PR. Both the claim-time guard and the event-driven cancel.
  • Persist the PR binding on the run record if it isn't already queryable (repo + PR number), so the cancel path is an indexed lookup, not a scan.
  • Emit a visible event/log line on auto-cancel (run_cancelled with reason pr_closed), and record it on the webhook delivery trail.
  • Non-goal: cancelling operator-launched (studio/CLI) runs — only runs whose provenance is a PR-bound webhook launch.
  • Non-goal: GitLab MR parity in the first pass (same seam, can follow).

Acceptance

  • A queued run whose PR merged before claim never executes (cancelled with reason).
  • A running PR-bound run is cancelled within one poll/event cycle of the PR closing.
  • A run NOT bound to a PR is never touched.
  • Unit tests for both paths; the PR-closed webhook delivery records the cancel outcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions