# Stopping runs after a bad deploy

> A workflow run keeps executing the version it started under, which is exactly what you want until that version is the problem. cancel-runs stops every run still executing a given version, without running their compensation logic.

<!-- id: project-cancel-runs · area: project · stability: stable · html: https://osysharp.com/reference/project/cancel-runs/ -->

## Summary        {#summary}
A run in flight goes on executing the version it started under — deliberately, and permanently. That is the guarantee
that makes deploying safe while work is in progress.

It is also a trap the first time you ship something broken. You notice, you deploy the fix, and the fix only applies to
*new* runs: every run already started keeps executing the broken code to completion. `cancel-runs` is how you stop them.

## Signature      {#signature}
```console
$ osy cancel-runs --version <version>           # local
$ osyrin app cancel-runs <version>              # a deployed app
```

## Description    {#description}

### What it does   {#what}
Every workflow run still in flight against the named version is stopped: marked cancelled, recorded in the run's audit
timeline as such, and any parked continuation discarded so nothing can wake it again. Runs on other versions are
untouched.

Find the version with [Deploying while workflows are running](https://osysharp.com/reference/project/app-versions/) — the `In flight` column is what you are about to cancel:

```console
$ osy versions
$ osy cancel-runs --version 1.1.0
```

It tells you how many runs you are about to stop and asks before doing it. `--yes` skips the prompt.

### It stops whole sagas, not single runs   {#sagas}
A saga is a tree: a parent waiting on a child, which may itself be waiting on another. Cancelling one run of that tree
cancels **all** of it, whichever run you name.

That is not overreach, it is the only coherent option. Cancelling just the child would leave the parent waiting forever
on something that can never finish — one stuck run traded for another. Cancelling just the parent would leave children
doing work whose result nothing will ever read.

### It does not run your compensation logic   {#no-compensation}
This is the important one, and it is deliberate.

When a saga fails normally, its `catch` runs and compensates — undoing what it did. **Cancelling does not do that.** The
reason you are reaching for `cancel-runs` is usually that the deployed code is wrong, and your compensation logic *is*
that same deployed code. Running it would execute the very thing you are trying to escape, and on a saga that is already
in a state you did not intend.

So: **side effects those runs already performed remain performed.** Money moved stays moved, emails sent stay sent. The
audit says the run was cancelled, not that it was compensated, so the record does not claim an unwind that never
happened. Undoing that work is yours to do deliberately — with full knowledge of what actually ran.

Cancelling is a blunt instrument on purpose. It is the tool for "stop, this is wrong", not for "unwind this cleanly".

### It also lets the version be cleaned up   {#reclaiming}
Runs in flight are exactly what stops an old version being reclaimed. Once you have cancelled them, nothing holds that
version open and the next deploy reclaims it — so the broken version stops appearing in `versions` too.

### Who can run it   {#permission}
The same permission as deploying to the app. Whoever can ship the bad version can stop it.

## Examples       {#examples}

The whole sequence, from noticing to clean:

```console
# 1. What is running, and against which version?
osy versions
# → 1.2.0  app_v3  14 in flight  oldest 09:12 (2h ago)  held by runs in flight
#   1.3.0  app_v4   0            —                      current

# 2. Deploy the fix. New runs are fine from here; the 14 are not.
osy compile --new-version

# 3. Stop the runs still executing the broken version.
osy cancel-runs --version 1.2.0
# → About to cancel 14 run(s) in flight against version 1.2.0 (app_v3).
#   No compensation runs. Side effects those runs already performed will remain.
#   Continue? [y/n]

# 4. Nothing holds app_v3 now, so the next deploy reclaims it.
osy versions
```

## See also       {#see-also}
- [Deploying while workflows are running](https://osysharp.com/reference/project/app-versions/) — why a run keeps its own version in the first place, and how to see what is in flight
- [Workflow.Run (start a workflow)](https://osysharp.com/reference/workflow/run/) — the parent/child await that makes a saga a tree
