---
name: hotfix
description: Promote a merged development fix to main and production via the Hub train.
---

**Host-native invocation:** Claude `/mmi:hotfix` · Codex `$mmi:hotfix` · jervcode `/hotfix` · Hermes skill_view.

**Argument:** none; PATCH-only (leave `MMI_BUMP_INTENT`, `MMI_RELEASE_VERSION` unset).

# /hotfix — promote a development fix to main + prod

The fix lands on `development` first; Hub ports that merged PR or SHA onto a main-base branch (cherry-pick with `-x`) and releases the PATCH. Never author the fix on a branch off `main`. The fix is NOT a back-merge (`rc` absorbs it next `/rcand`; the hotfix-coverage guard blocks a candidate omitting it).

## Lane

MM-Work suite seats: read [suite.md](suite.md). Lane from META:

```bash
mmi-cli oracle org project get {owner}/{repo} --json
```

A hotfix skips rc on every track; `deployModel: vercel` waits for the Production Deployment, then probes it. Unreadable META → stop; never infer the lane.

## Preflight

```bash
mmi-cli devops train doctor --lane hotfix --heal --json
```

Parse stdout only; never `2>&1`. `ready` false → stop and quote every blocker's `code` and `remedy`; an unread origin is never green. Findings carry `code`, `severity`, `source`, `remedy`, `anchor`.

## Apply

From the primary checkout.

1. Start (derives PATCH from the latest `origin/main` Release tag, branches `hotfix/vX.Y.Z`, folds the version, opens the PR; never repeat by hand):

```bash
mkdir -p .jerv/tmp && r1=$(mktemp .jerv/tmp/hotfix-start.XXXXXX)
mmi-cli devops hotfix start --from <pr|sha>[,<pr|sha>…] --json --out "$r1"
```

2. Verify: local gate, required review, acceptance criteria. Merge with `mmi-cli devops pr merge <number> --squash` (waits for CI and review; keep the `-x` trailer). Because Hub's default branch is `development`, it closes no issue; no raw GitHub issue operations.

3. Release:

```bash
r2=$(mktemp .jerv/tmp/hotfix-release.XXXXXX)
mmi-cli devops hotfix status --json
mmi-cli devops hotfix release vX.Y.Z --carries <pr|sha>[,<pr|sha>…] --json --out "$r2"
```

MMI-Hub adds `--announce-summary-file "$f"`: a fresh 3–6 line neutral summary, no product or brand name, never a product's board (`mutmutco/MMI-Hub` only). It tags, publishes, watches, announces, verifies, opens the `hotfix-fold/<tag>` PR. Do not hand-dispatch a second publish or deploy.

## Verify

Read receipts, not the exit code (`1` = follow-up failed after tag; pending exits `0`):

- `runs[]`: watch each non-success: `gh run watch <run-id> --repo {owner}/{repo} --exit-status --interval 30`. `deploy.yml`, `publish.yml`, `tenant-deploy.yml`, `jerv-gateway` are deploy/publish verdicts; a `gate` run is ordinary CI.
- `foldStatus`/`foldNote`: land the fold PR by true merge (Merge floor).
- `announceNote`, `verifyNote`, `checkout`, `localSync`, `ledger`: pending or failed = unresolved.
- `localInstall` (MMI-Hub): via `mmi-hub update`, never `npm i -g`.

`gh api repos/{owner}/{repo}/releases/latest --jq .tag_name` must equal the tag, on `origin/main`.

## Recovery

On any failure or interruption, read [recovery.md](recovery.md).

## Merge floor

- **Never squash-merge a tagged commit (#3167).** A squash re-mints the SHA and orphans the tag, so tag-anchored checks mis-read. Any merge that carries an already-tagged commit — the `main → development` roll-forward, an alignment PR, a hotfix fold PR — lands as a true merge: `mmi-cli devops pr merge <number> --auto --merge` (or `--wait --merge`); `--wait` alone still squashes. The hotfix main-base PR itself may squash: its tag lands after, on the merged `main` HEAD, and the `-x` trailer must survive the squash message.
- **A refused train is a stop, never a license to finish by hand.** Every fail-closed halt — doctor blocker, authority probe, coverage guard, untolerated conflict, required checks, stray tag, ledger — ends the run. Fix the cause on `development` through a normal PR and rerun the train; never hand-resolve on `main`, hand-cherry-pick, bare-push a train branch, ack to save time, or bypass a check.
- **Version probes anchor on the latest Release tag, not a branch (#2734).** A branch manifest or a stale local tag is not a version source; a surviving local tag silently mints the next version.
- **One train at a time on a repo (#5380).** Two overlapping runs race the shared tag namespace; the ledger refuses the second — never start one to help the first.
- **Tag and Release deletion need Jervaise's yes (principle 12),** named in the guide entry that prescribes it.

## Report

Verdict first: the resolved tag (never a placeholder like `vX.Y.0`), Release URL, `releaseStatus`, `followUpStatus`, `deployStatus`, `promoted`, each run's conclusion and URL (or the no-run-URL note), which run is red (deploy/publish verdict or ordinary CI), `announceNote`, the alignment note (`devRollForward`, `rcAlignment` or the fold PR), `checkout`, the dispatch used. Pending is unverified, never success. Product names only when releasing that product's repo.

## Retro

Before the final report: did this skill misfire? Process only. If yes, file one lesson (hard cap: one per run); a clean run is silent; if the call fails, note it and continue. Lessons are deduplicated on the owning board and change the skill only by reviewed PR, never live; no issue references outside Merge floor.

```bash
mmi-cli learning skill-lesson --skill hotfix --title "<what misfired>" --body "<what; evidence; proposed amendment>"
```
