hotfix · git:20260926.85fc6e6 · 2026-09-26 · sha256 5a1d919b1b28ed5c
hotfix git:20260926.85fc6e6A
Immutable. This exact content never changes and is served at /api/v1/blob/5a1d919b1b28ed5c.
---
name: hotfix
description: "Fast path for an urgent production fix — reproduce with a failing test, minimal fix on a hotfix branch from the release tag, mandatory security review for sensitive paths, expedited checklist, deploy and backport to main. Use for P1 production bugs."
argument-hint: "[issue description or bug id] | --chore <what>"
user-invocable: true
allowed-tools: Read, Glob, Grep, Write, Edit, Bash, Task, Skill, AskUserQuestion
model: sonnet
---
# Hotfix
Reply in the project conversation language (CLAUDE.md → Language); code, identifiers, paths and commit messages stay in English.
File writes and any mutation (git, deploy) happen only after an explicit "May I write?" / "Proceed?" — each one `AskUserQuestion` (proceed (Recommended) · show the draft/diff first · not now) → "yes". After the "write" answer: `touch .claude/.write-consent` (rule 7 — the consent-guard hook checks the marker). Subagents spawned through `Task` cannot ask the user: the consent is collected here, and an engineer that needs more than its brief reports back so the parent asks.
In the commands below, `<hooks>` is `.claude/hooks/` in copy mode and `${CLAUDE_PLUGIN_ROOT}/hooks/` in plugin mode; `<default>` is the default branch (`master`/`main`). **An open gate survives the next turn** (rule 7): before each commit, tag or push gate below, the skill records it — `<hooks>session-state.sh set Task "/hotfix <slug>" Gate "/hotfix Phase N: <question>"` — and clears it after the answer (`<hooks>session-state.sh set Gate "—"`), so a session that resumes with the `Gate:` line continues at that question instead of reading a new task from `Next:`.
**The parent writes no code** — not the fix and not the failing test either: both go through the relevant engineer via `Task` with a studio `subagent_type` (`web-studio:<name>` in plugin mode, `<name>` in copy mode; coordination-rules § Subagents). A cut-off engineer is resumed from its `Checkpoint:`, never replaced.
**Other studio skills run through the `Skill` tool** (`/web-studio:<name>` in plugin mode, `/<name>` in copy mode): `/code-review --diff` (Phase 2 and `--chore`), `/changelog` and `/deploy` (Phase 3). Each keeps all of its own phases and gates — its write gate, its commit gate, its confirmation before a production mutation — and its verdict comes back as text this skill quotes. They run one after another, after the running `Task` has returned, never two at once. `/incident` is a hand-off in the closing `AskUserQuestion`, not run from here.
**Two kinds of urgent, one skill.**
- `production` (the default): something is broken for users. Phases 1–4 below: reproduce with a failing test, minimal fix on a hotfix branch from the release tag, expedited security gate, release.
- `--chore`: the toolchain is broken or in the way — a red runner, a linter that blocks every commit, a dependency that must move now. It skips the release machinery and follows the chore/infra lane of `git-workflow.md`:
1. branch `chore/<slug>` (`git fetch origin`, then `git switch -c chore/<slug> origin/<default>`);
2. commits `ci(…)` / `chore(…)`, each behind one commit gate recorded in the session state as above;
3. a PR with `/web-studio:code-review --diff` (copy mode `/code-review --diff`) run through the `Skill` tool before the merge (workflow files → `devops-engineer`); experimental commits are squashed or rebased away before the merge;
4. the outcome recorded as a finding (`production/findings.md`) or a backlog entry (`production/backlog.md`).
Neither path is a place for a feature: work that changes what the product does is a story, however small it looks at the moment it is asked for.
## Phase 1: Reproduce
1. **Branch** `hotfix/<slug>` from the production tag (the release currently deployed): `git fetch origin --tags`, then `git switch -c hotfix/<slug> <tag>`. The branch starts on a commit that is already in `origin/<default>`; the commit hook (`validate-commit.sh`) is silent on `hotfix/*` branches, so its "already merged into origin/<default>" warning is not raised against the first commit here.
2. **A failing test** that reproduces the bug (mandatory), written by the relevant engineer through `Task` — the brief names the bug, the file the test goes into and the command that must go red; the parent writes no test itself (protocol). The engineer reports the red run's output, quoted in the report. No reproduction → stop with `BLOCKED (cannot reproduce — …)`: no fix is written without a failing test.
3. **Impact assessment**: data? security? → `security-lead` via Task.
## Phase 2: Minimal fix
1. **Through the relevant engineer**: `Task` with the studio `subagent_type`. The parent writes no code; a cut-off engineer is resumed from its `Checkpoint:` (coordination-rules § Subagents).
2. **Only what is needed.** A fix that needs a DB migration is not written straight away: the engineer reports it back, and the parent warns the user before it is written — a rollback restores the image, not the data, so the migration must be reversible or the hotfix goes forward only.
3. **Checks**: test green; lint/typecheck.
4. **Fix commit and push**, one `AskUserQuestion` recorded first as `Gate "/hotfix Phase 2: commit and push the fix?"` and cleared after the answer: commit and push (Recommended) · commit only · show the diff · not now. On "yes": `git commit -m "fix(<scope>): <bug>"` staging the fix and its test by name, on the hotfix branch, then `git push -u origin hotfix/<slug>` — the branch gets its upstream here, before any review, so a review fix can be pushed. "Commit only" → the push happens at the backport step with its own consent, and the report says the branch is local until then.
5. **Sensitive paths** (the `paths:` of `rules/security-sensitive.md`: auth, security, middleware, proxy config, payments, uploads, webhooks): `appsec-engineer` review — `/web-studio:code-review --diff --security` (copy mode `/code-review --diff --security`) through the `Skill` tool, after the fix commit of step 4, so the review and its `appsec-engineer` run are in the audit log and the review sees the fix; its verdict is quoted. `NEEDS CHANGES` → the fixes go back through the engineer inside `/code-review`'s own fix gate, which commits them on the hotfix branch as `fix(<scope>): apply /code-review findings` (there is no `S-NNN` here: `/code-review` takes the scope from the fix commit) and pushes them to the upstream set in step 4; nothing goes on to Phase 3 before the reviewers answer `APPROVED`.
## Phase 3: Expedited gate
1. Package tests + e2e smoke.
2. **`/changelog`** through the `Skill` tool: the patch version `vX.Y.Z` (the deployed tag's patch number + 1) with its own "May I write `CHANGELOG.md`?" gate and its own commit gate — the only gate for `CHANGELOG.md`; this skill adds none. `/changelog`'s gate recognises a `hotfix/*` HEAD and offers to commit there (Recommended — the patch tag must contain it): the `docs: changelog vX.Y.Z` commit lands on the hotfix branch, and the tag below must contain it. A changelog left uncommitted, or committed on another branch, is named in the report: the tag would not carry it, so the tag question is not asked before that is put right.
3. **Release file** `production/releases/vX.Y.Z.md`, the minimal one `/deploy` Phase 1 reads (it stops with `BLOCKED (no release file — …)` without it, and `/release-checklist` is not run for a hotfix): what changed (the bug and the fix in one paragraph), the fix commit (its hash, and the review-fix commit when there is one), the tag `vX.Y.Z`, the CI run that builds the image (the workflow file, and the run id in the report once the tag is pushed), the rollback tag (the deployed one), and the line `Verdict: READY (hotfix)`; the `/release-checklist` gates a hotfix skips are listed as skipped, never ticked. Show the draft, then "May I write `production/releases/vX.Y.Z.md`?" — one `AskUserQuestion`: write (Recommended) · show the draft/diff first · not now; after the "write" answer `touch .claude/.write-consent`. Right after the write, one commit gate, recorded first as `Gate "/hotfix Phase 3: commit the release file?"` and cleared after the answer: `docs: release vX.Y.Z (hotfix)` staging exactly that file, on the hotfix branch (commit (Recommended — the tag must contain it) · leave uncommitted). An uncommitted release file → no tag; say why.
4. **Patch tag**, created and pushed by this session, never by a subagent, and as its own `AskUserQuestion`, recorded first as `Gate "/hotfix Phase 3: tag vX.Y.Z and push it?"` and cleared after the answer: tag `vX.Y.Z` on the hotfix branch and push it now (Recommended) · not now. On "yes": `git tag -a vX.Y.Z -m "Hotfix vX.Y.Z: <bug>" && git push origin vX.Y.Z` (without `-m`, `git tag -a` opens an editor the session cannot use). A hotfix tag is the documented exception to "tags on the default branch" (git-workflow: a hotfix branches from the release tag and is backported afterwards); the tag sits on the release-file commit, which sits on the changelog commit, which sits on the fix commit. "Not now" → the report says the release is untagged and `/deploy` cannot run.
5. **CI builds the image from the tag.** Find out what starts on the tag before waiting for anything (as `/dev-story` Phase 6 step 6 checks `pull_request`): `grep -l "tags:" .github/workflows/*.yml` and each such workflow's `on: push: tags:` pattern — the tag must match it (`v*` matches `vX.Y.Z`). With a matching workflow, wait for the tag's run before deploying — one background `gh run watch <run-id> --exit-status` (as `/story-done`), only for a run that exists; no polling, no `AskUserQuestion` as a pause. Red → `BLOCKED (CI red on vX.Y.Z)`, nothing deployed. Without a tag trigger, nothing starts on the tag: say so, name the run to expect instead (a `push` run on the branch, or a `workflow_dispatch` the user starts by hand) or that no run starts at all, and that `/deploy` will then find no green run on the tag. Without `gh`, name the run to check by hand.
6. **`/deploy vX.Y.Z`** through the `Skill` tool, with its own confirmation before the production mutation (delegate verbs `deploy`/`rollback` by `.claude/docs/deploy-target-contract.md`); it reads the release file's `Verdict:` line, verifies the tag, the green run and the image itself. Its verdict line (`DEPLOYED vX.Y.Z` | `FAILED (…)`) is quoted in the report.
7. **Backport** to the default branch (`<default>`, `master`/`main`) through a PR: the branch is already on origin when step 2.4 pushed it (otherwise `git push -u origin hotfix/<slug>` now, with consent recorded as `Gate "/hotfix Phase 3: push hotfix/<slug>?"` and cleared after the answer), then `gh pr create --fill --base <default>`; the merge follows the PR's review, never a direct push.
## Phase 4: Postmortem note
A short entry in `docs/ops/incidents/` (or `/incident` if there was an incident), behind "May I write `docs/ops/incidents/<file>`?" — one `AskUserQuestion`: write (Recommended) · show the draft first · not now; after the "write" answer `touch .claude/.write-consent`. Right after the write, one commit gate (rule 7 (4), `git-workflow.md` § Documents), recorded first as `Gate "/hotfix Phase 4: commit the postmortem note?"` and cleared after the answer: `docs: incident <slug>` staging exactly that file. HEAD is the hotfix branch here, so name it and offer: switch to the default branch and commit there (Recommended — a postmortem is a pipeline-wide document; `git switch <default> && git pull --ff-only origin <default>` first, and back to `hotfix/<slug>` afterwards while the backport PR is open) · commit here (it rides the backport PR) · leave uncommitted. Nothing is committed without the answer; the fix never rides the `docs:` commit.
Verdict: `FIXED` | `BLOCKED`. Next step — one `AskUserQuestion`: `/web-studio:incident` (copy mode `/incident`) for root-cause analysis (Recommended) · backport to the default branch (when step 3.7 was skipped) · stop here.