# Versions and releases

Publishing freezes a version with a semantic release, and callers choose the releases they accept.

## Publishing

Publishing turns the draft into a version that never changes. Open **Versions** in a Decision. The
**Next release** panel shows the version number it will get, what kind of release it is and the list
of changes. Add a label if you like, then choose **Publish**.

A draft publishes only when it compiles (see [Decisions and Definitions](https://typefuse.dev/guides/decisions#drafts-and-publishing)),
and only when something has changed since the last version. Publishing is done by people, in the app,
the CLI (`typefuse publish`) or through an agent; deploy tokens can't publish.

A published version isn't live until you deploy it. See
[Deploying, environments and approvals](https://typefuse.dev/guides/deployments).

## Two numbers for every version

Every version has:

- a **revision**, a plain number counting up from 1 (the page `/:org/:decision/v/3` is revision 3)
- a **semantic release**, such as `1.4.0`, worked out from what changed

The revision identifies one exact version, for replaying or debugging a call. The semantic release
tells callers whether a new version is safe for them.

## How releases are classified

TypeFuse compares the new Definition with the last one and chooses the release type for you. The
first release is always `1.0.0`.

| Release | Called | When |
| --- | --- | --- |
| Major (`2.0.0`) | New contract | A caller could break: an input field removed or newly required, a type narrowed or changed, allowed values removed, `additionalProperties` closed, a question removed, a question's type changed, per-item `each` added, removed or changed, or the set of choice options or score levels changed |
| Minor (`1.5.0`) | Behaviour | Callers keep working but answers may differ: an optional input field added, a requirement relaxed, a new question, changed instructions or criteria wording, a different model, or changed context |
| Patch (`1.4.1`) | Patch | Only descriptions and other annotations, the README, or the source's formatting and comments changed |

Each version's page shows what changed, the Definition exactly as published, the execution plan the
runtime uses, and the evidence behind it.

## Accepting a range of releases

Callers say which releases they accept, the way package managers do:

| `accepts` | Means |
| --- | --- |
| `^1.2.0` | 1.2.0 or any later 1.x |
| `~1.2.0` | 1.2.0 or any later 1.2.x |
| `1` or `1.x` | any 1.x |
| `1.2` or `1.2.x` | any 1.2.x |
| `1.2.3` | exactly 1.2.3 |

Pre-release and build tags such as `1.2.0-beta` are not supported.

With `accepts`, the release the environment serves answers when it is in the range; otherwise the
newest deployed release in the range does. If nothing deployed matches, the call is refused with
`409 INCOMPATIBLE_VERSION` rather than answered by a release the caller didn't accept.

TypeFuse never falls back from one major line to another on its own: a caller that accepts `^1`
gets 1.x or an error, never 2.x. The SDK also takes a list, such as `["^2.0.0", "^1.4.0"]`, and
tries each in order; that is the caller accepting both contracts. Only use a list like that if your
code handles both answer shapes. The answer's `version` and `semanticVersion` say which release
answered.

A call can name an exact revision with `version` instead, for replaying or debugging; that revision
must be deployed in the environment. Use `accepts` in application code.

## A new major keeps the old line

When you deploy a new major release, the newest release of each earlier major line stays live beside
it. Callers that accept `^1` keep getting 1.x until they change to `^2`, while callers without a
range get the new release. So after deploying 2.0.0 over 1.4.2, an environment has two live
releases:

| Release | Serves |
| --- | --- |
| 2.0.0 | every caller by default, and callers accepting `^2` |
| 1.4.2 | only callers accepting `^1` |

Each environment card on the Deployments page lists every live release with its calls in the last
24 hours, so you can see whether anyone still uses an older line. Call it, the Versions page and
the Environments page show every live release too.

### Retiring a line

When nobody needs an older line any more, choose **Retire this line** beside it on the environment's
card. The dialog says how many calls it answered in the last 7 days. Once it's retired, callers that
accept only that line get `409 INCOMPATIBLE_VERSION` from that environment; nothing else changes.
A retired line doesn't come back with later deployments.

Retiring follows the same rules as removing a Decision: where the environment requires approval, it
waits for an approver (a second person or the named approvers, where the environment asks for
them) and the line keeps serving until then. It is recorded in the history and the audit trail.

You can also retire the older lines as part of a deploy (the deploy dialog names them), or when you
remove the Decision from an environment.
