# Release recovery and ledger continuation


Every refusal and every doctor blocker names its `code` and `anchor`: open `docs/Guides/train-troubleshooting.md#<code>` and follow that entry, nothing else. That guide is test-parsed, never free text — the infra drift and plugin-set suites validate every anchor cited here and assert policy from its wording — so an edit to it re-runs the infra suite (`npm --prefix infra test`) in the same PR: a heading or slug change is a code change. A pre-tag abort with no finding code (for example, a transient secrets-preflight timeout) is a transient stop: nothing was promoted and no tag was cut. Confirm no tag reached origin (`git ls-remote --tags origin` or `gh api repos/{owner}/{repo}/tags`), then rerun `mmi-cli devops release --apply` as a fresh cut, never `--resume`; resume is only for a partial release with the tag already on origin. A partial release (tag on origin, later phases pending or failed) continues with `mmi-cli devops release --resume --watch --json --out <fresh-receipt>` — the only continuation form: `--resume` never re-tags or republishes, posts a deferred announcement only when it is the run that first proves deploy and publish green, and `--apply` belongs to a new cut only. A SUPERSEDED leg is not a pending one: a ledger that records a supersession is phases-green — `--resume` must not be used on it, and the deferred announcement is the next patch train's to carry. A refused train is a stop (Merge floor): fix the cause on `development` through a normal PR, then rerun.

**Exactly one continuation per ledger state.** Pick the row, run its verb, never combine two:

- No tag on origin (pre-tag stop): a fresh `mmi-cli devops release --apply`.
- A `deploy`/`publish` leg `pending` whose run already concluded (the `pending`, no-run-URL shape included): `mmi-cli devops train doctor --heal` folds it from that run (`ledger-unfolded`).
- An `alignment` leg `pending` whose PR already merged: `mmi-cli devops train doctor --heal` (`ledger-alignment-stale`).
- Any other `pending` leg (run in flight, alignment PR open): `mmi-cli devops release --resume --watch --json --out <fresh-receipt>`.
- A `failed` leg with a transient cause: `--resume` after the cause clears (it spends the one bounded rerun); hotfix lane: `hotfix release <tag> --retry-deploy`.
- A `failed` leg with a content cause on a published release: `--supersede-deploy` / `--supersede-publish` with master approval, then the next patch train.
- A superseded ledger (phases-green): the next patch train. Never `--resume`.

A **content-caused failed deploy leg on a published release** has no rerun that can succeed: the promoted tag is immutable, redeploying it rebuilds the same broken SHA, and the unresolved ledger blocks every lane — release, hotfix and candidate alike — while `--abort` is unavailable because the release is published. When the doctor's ledger blocker names `deploy` as the only unresolved leg and the deploy failure is proven content-caused (the deploy-time validator rejected the shipped content itself), the sanctioned exit is `mmi-cli devops release --supersede-deploy --apply` — an explicitly master-approved closure, run only after the cause fix is merged on `development`. It closes the deploy leg as superseded (the ledger turns phases-green), and the next patch train — usually `/hotfix`, which carries the fix to main — runs normally; its lifecycle gate archives the closed ledger aside. Never hand-edit the ledger file, never delete it to unblock a train.

A **content-caused failed publish leg on a fully promoted release** is the same deadlock with its own exit: the tag is on `main` and the GitHub Release is live, so `--abort` refuses (it only ever deletes a proven unpublished candidate); `--resume` only re-reads the failed publish run — the tag is immutable, so no rerun can pass; `--supersede-deploy` is deploy-hardwired. When the doctor's ledger blocker names `publish` as the only unresolved leg and the publish failure is proven content-caused (a release gate rejected the shipped content — e.g. a platform-dependent distribution BOM — not a transient npm/transport error), the sanctioned exit is `mmi-cli devops release --supersede-publish --apply`, run only after the cause fix is merged on `development`. It closes the publish leg as superseded, the next patch train carries the fix, and its lifecycle gate archives the closed ledger. A transient publish failure (credentials, network, quota) is NOT content-caused: retry `--retry-publish <run-id>` or resolve the run instead. Once a leg is superseded, `--resume` is not the continuation: the ledger is phases-green, and resume leaves a superseded leg untouched and announces nothing, because superseded content never shipped. A superseded release has no standalone announce verb on purpose: its note is carried by the NEXT patch train's own announcement (the train that ships the cause fix), and the closed ledger archives the superseded release's record. If no CLI path fits and the release must be rebuilt by hand, the manual sequence — proven on the v1.62.1 recovery — is: retire the ledger path FIRST (archive `phases.json` aside, exactly as the ledger-failed receipt names), THEN delete the failed tag and GitHub Release, THEN re-cut the same version with a fresh `--apply` which re-points the tag onto the new bump. The order is a hard constraint: deleting the tag before the ledger path is retired deadlocks both `--abort` and `--resume`, because the ledger then anchors a tag that no longer proves anything.

## Ledger reference

- With `--watch` the train waits for its own alignment PR to land (true merge) and folds the ledger in the same run, so the receipt ends `succeeded` / `complete`. An external `gh run watch` never folds anything: if the receipt still shows a `pending` leg (no `--watch`, the alignment PR outlived the bounded wait, or a release-event `publish.yml` run on a registry-publish repo had not been correlated by the time the receipt was written — `--watch` waits for the alignment PR, not for that run, so a `publish` leg `pending` with no run URL after `--watch` is the expected direct-track shape, not a failure), fold it live before declaring the cut done or starting any new train: `mmi-cli devops release --resume --watch --json --out <fresh-receipt>`; the NEXT train's doctor otherwise refuses with `ledger-pending`.
