Files
claude/skills/hotfix/SKILL.md
T
bastien d82c06f572 refactor(doctrine): C2 coherence — 30 doctrine/skill tensions resolved, doctrine wins (BDR-099)
One ask policy; mandated executors exempt from the delegation rule; skill plan satisfies the planning rule; journal line exempt from the approval gate; chore = maintenance without new behaviour; small fix on develop = bugfix; BDR-068 written as the one auto-finish exception; deploy routes to /deploy. Skills and agents follow: hotfix types by base + skips the design gate on trivial; capitalize/close create missing registries; commit-change asks the branch type; doc/seo/web-validate/refactor branch through the aiguillage; tour reports BREAKING fixes as needs-decision and runs doc-syncer two-mode; client-handover applies audit bundles from its main loop behind one gate; init-project/onboard use the 200-file graphify signal and bootstrap memory; release-candidate gates the tag push only; push wording aligned with the BDR-095 hooks; stale pointers fixed (§ Language, .gsd/ROADMAP.md, handover script path, design-gate lists).
2026-09-24 20:25:40 +02:00

13 KiB

name, description, argument-hint, allowed-tools
name description argument-hint allowed-tools
hotfix Quick fix for superficial bugs: typos, CSS issues, config errors, off-by-one, wrong variable name, missing import, broken link. Use when the root cause is obvious and the fix is 1-2 files max. Trigger: "hotfix", "quick fix", "typo", "fix this small thing", "c'est juste un petit bug", "patch rapide". Do NOT use for bugs requiring investigation — use /bugfix instead. <bug description or error message>
Read
Edit
Write
Bash
Grep
Glob
Agent

/hotfix — quick-fix orchestrator (reflection inline, execution dispatched)

MODEL GATE (blocking): run $HOME/.claude/lib/model-gate.md BEFORE any step below. Verdict small → STOP — print the gate's remedy, end the turn, dispatch nothing.

REQUEST

$ARGUMENTS


STEP 1 — LOCATE (reflection)

Find the bug. Use the description and any error message to go straight to the source:

git status
git log --oneline -3
  • Read the relevant file(s). Confirm the root cause is obvious and superficial (typo, wrong value, missing import, etc.).
  • If the bug turns out to be deeper than expected (unclear cause, multiple files involved, logic error): STOP and say: "This looks deeper than a hotfix — it needs investigation. Re-run this as /bugfix (root-cause investigation, then a scoped fix)."
  • Settle the proposed fix HERE — the executor cannot ask questions, so the exact edit (what changes, in which file(s)) must be closed before dispatch.
  • Then run pass B of $HOME/.claude/lib/contract-interview.md against that edit: a VISIBLE / PUBLIC NAME / SCOPE choice the bug description leaves open (which way the icon aligns, the label's wording) → ask before dispatch. A typo or a wrong value asks nothing.

OPTIONAL — memory check (exempt by default; hotfix = obvious fix, mirror of its capitalize skip). For a RECURRING or urgent bug only, a quick blockers-only glance may save time:

  [ -d .claude/memory ] && grep -nE '^## BLK-' .claude/memory/blockers.md   # "déjà vu ?"

If a prior BLK names this bug, jump to its solution. Not mandatory; no RELATED MEMORY disposition required at hotfix weight.

STEP 1.5 — DESIGN GATE

Follow $HOME/.claude/lib/design-gate.md:

  • Scan $ARGUMENTS and target files for design/UI/style signals (CSS, component, styling, animation).
  • Signals found → a hotfix is the trivial tier by definition (≤2 files, one cosmetic value — design-gate.md §1): skip the gate, no toolchain. If the signals reveal real UI work (new component, layout, motion), this is not a hotfix → route to /feat or /bugfix instead of running design-tool-gate.sh.
  • If no signals → skip (zero overhead).

STEP 1.7 — CONTRACT (silent autofill)

Run $HOME/.claude/lib/contract-interview.md at hotfix weight: pass A is a silent autofill (a hotfix is an obvious fix by definition); pass B already ran at STEP 1, ask nothing more here. Autofill the contract — REQUEST verbatim = the bug description as given; ACCEPTANCE CRITERIA = "symptom gone; build/tests green"; FILE SCOPE = the 1-2 target files from STEP 1. It writes .claude/tasks/contracts/<date>-<slug>-<HHMM>.md. This is the reference the executor reads first, and the scope for STEP 4's security gate and the escalation report if a gate fails. No verifier is dispatched at hotfix weight — STEP 4's smoke result already verifies these trivial criteria; the gate hotfix adds is security (STEP 4).

STEP 1.8 — CHALLENGE THE FIX (logic fixes only)

GUARD — this is the one place the plan-challenge phase is kept proportionate to hotfix's speed. SKIP entirely for a purely cosmetic fix (CSS value, copy/typo, a broken link): there is nothing for three lenses to bite on, and speed is the point. Run it ONLY when the settled fix touches control flow or behaviour — an off-by-one, a wrong operator/variable, a behaviour-changing config value, or a missing import that alters execution. In doubt → it is probably a /bugfix.

For a logic fix: persist the STEP 1 located fix (root cause + the exact edit) to .claude/tasks/plans/<date>-<slug>-<HHMM>.md, then run $HOME/.claude/lib/challenge-plan.md with PLAN = that file, KIND = build-plan, SCOPE = the 1-2 target files, CONSTRAINTS = the STEP 1.7 contract's acceptance criteria. Three blind challengers attack the fix; the main loop RE-THINKS any aspect a BLOCKER lands (a named change to the fix, or [deferred]) and re-challenges once if it materially changed. Print a CHALLENGE SUMMARY (BLOCKERs addressed / deferred / lenses returned). A BLOCKER that shows the fix is wrong or incomplete means this was never a hotfix — escalate to /bugfix (its STEP 3b runs the same phase under the full verify+secure loop).

STEP 2 — PRE-FLIGHT

Gitflow aiguillage (before dispatch): follow $HOME/.claude/lib/gitflow-aiguillage.md — your type follows the base: on main → hotfix (prod incident; finish fans out to main + develop); on develop → bugfix (the fix forks from develop — a hotfix/* off main would miss develop's code and later merge to prod). Either protected base branches first; on a working branch it's a no-op (commit in place). The /hotfix size rules and the hotfixer executor are unchanged either way. Never finish.

Snapshot current state so revert is possible:

git diff HEAD --stat   # confirm working tree is clean OR carries only the
                       # in-progress hotfix area; if unrelated dirty files are
                       # present, ask user whether to stash them first
# Snapshot the TREE STATE (incl. tolerated uncommitted edits) without touching it.
# A bare SHA is not enough: restoring to HEAD would wipe the user's own
# in-progress edits in the hotfix area.
PRE=$(git stash create "hotfix-preflight"); [ -n "$PRE" ] || PRE=$(git rev-parse HEAD)
echo "PRE=$PRE"       # the revert source for every failure branch below

If the working tree contains unrelated uncommitted changes the user has not mentioned: STOP and ask "working tree dirty: stash and continue, or abort?".

STEP 3 — DISPATCH EXECUTOR

Dispatch the executor — sonnet by frontmatter pin, do not override:

Agent(subagent_type="hotfixer")
prompt: "CONTRACT: <path from STEP 1.7>
LOCATED: <file(s) found in STEP 1 + the confirmed root cause>
FIX: <the proposed minimal fix, closed in STEP 1>
BRANCH: <current branch — verify with git branch --show-current, never switch>
Apply the minimal fix. No refactoring, no commit, no branch ops, no
security dispatch, no revert. Finish with the HOTFIX-EXEC REPORT."

Parse the HOTFIX-EXEC REPORT:

  • STATUS : DONE → STEP 4 (the SMOKE line in the report decides pass/fail there; DONE here means execution completed, not that it verified clean).
  • STATUS : BLOCKED with CLASS: visible | public-name | scope in NOTES → the executor halted at an open choice before editing (nothing to revert): ask the user per MID-RUN CLARIFICATION in $HOME/.claude/lib/contract-interview.md, append the answer to the contract [gated], re-dispatch ONCE with the closed choice. This is the one re-dispatch hotfix allows; it is not a retry of a failed attempt.
  • STATUS : BLOCKED otherwise → if any edits were made, revert ONLY the executor's files: git restore --source=$PRE -- <FILE(S) from the report> and delete any NEW file the report lists (untracked, absent from $PRE). Never git restore . — it would wipe the tolerated pre-existing edits too. Surface the blocker to the user; STOP. One attempt only — hotfix never re-dispatches (escalate to /bugfix for deeper work).

STEP 4 — VERIFY + SECURE + COMMIT (main loop, LRN-083)

  1. Read the SMOKE line from the executor's report. Failure branch — if it reports a failing test/build result:
    • Print the failure output verbatim (under 30 lines).
    • Revert ONLY the executor's files: git restore --source=$PRE -- <FILE(S) from the report> + delete report-listed NEW files. Never git restore . (wipes tolerated pre-existing edits).
    • STOP and tell user: "Hotfix introduced a regression. Reverted. Escalate to /bugfix or /analyze for deeper investigation."
    • Do NOT commit a broken fix.
  2. Security gate (fresh auditor) — failure REVERTS, never loops. Dispatch a FRESH security-auditor (subagent_type: security-auditor — always a fresh dispatch, never inline-load: the repo convention and the FRESH requirement both forbid it) with MODE: gate, SCOPE: the working-tree diff vs $PRE. Parse its SECURITY — VERDICT: line:
    • PASS (or DEGRADED with no BLOCK) → proceed to commit.
    • BLOCK(n) → this is hotfix: do NOT loop. Revert ONLY the executor's files (git restore --source=$PRE -- <FILE(S)> + delete report-listed NEW files), print the BLOCKING list, and STOP: "Hotfix introduced a security finding. Reverted. Escalate to /bugfix for a fix under the full verify+security loop." The hotfix model is one attempt; any gate failure (smoke OR security) reverts and escalates.
    • Structural failure (mute / unparsable / no VERDICT line) → treat as a failed gate: retry ONCE fresh; a 2nd structural failure → revert + escalate. A mute auditor is never a PASS.
  3. Commit using conventional format (only after smoke AND security pass):
    fix(<scope>): <what was wrong>
    
  4. Print summary:
    HOTFIX APPLIED
    FILE(S) : <changed files>
    FIX     : <one-line description>
    VERIFIED: <test name or smoke check that passed>
    SECURITY: <PASS | DEGRADED (checklist only)>
    

STEP 5 — DOC SYNC (automatic)

Dispatch the doc pipeline (BDR-077 — audit judgment on opus, patch on the sonnet pin, gate HERE):

  1. Agent(subagent_type="doc-syncer", model="opus") — MODE: audit + auto-mode scope: <list of files modified during this session>.
  2. Silence (NONE) → done. [MINOR] PATCH PLAN → re-dispatch Agent(subagent_type="doc-syncer") with MODE: patch + the plan verbatim (no gate — auto behavior preserved; a SHAPE ESCALATION in its report comes back here, gated as SIGNIFICANT).
  3. SIGNIFICANT → gate here (Apply? yes / no / select), then MODE: patch with the approved subset.

Then commit the docs — follow $HOME/.claude/lib/doc-commit.md: it surgically commits ONLY the files doc-syncer patched (its PATCHED_FILES output), never git add -A, never .claude//CLAUDE.md (rc 4 = a loud BDR-022 anomaly, not a silent skip), and no-ops when nothing was patched — the common case for a trivial hotfix. No FINISH in an inline flow, so it just commits the docs on the current branch (no ordering concern).

STEP 6 — CAPITALIZE (memory registries, lightweight)

Hotfixes are often trivial (typo, config, import) — skip by default. But if the fix revealed something non-obvious:

  • Wrong default that should never have been merged → propose LRN-XXX in .claude/memory/learnings.md.
  • Bug that cost real time to locate despite being "superficial" → propose BLK-XXX in .claude/memory/blockers.md (status: resolved).

Default behaviour: CAPITALIZE: hotfix trivial, skip (no prompt, no output). Ask the user only when there is an actual candidate to propose.

Always append a 1-line entry to today's heading in .claude/memory/journal.md (even trivial hotfix — journal is timeline, not signal).

Language rule: the journal line and any proposed BLK/LRN entries are ALWAYS written English AND caveman — fragments, articles dropped, code/IDs/quoted errors verbatim — per CLAUDE.md "Memory registries" (Always English, always caveman).

Then commit the memory — follow $HOME/.claude/lib/capitalize-commit.md: it surgically commits what capitalize just wrote (.claude/memory + .claude/tasks only, never git add -A) as one chore(memory) commit, reports the memory-commit hash, and no-ops if nothing was written. The always-on journal line means a trivial hotfix still produces a chore(memory): journal — … commit (Frame 2 / F3).


RULES

  • Max 2 files changed. If more needed → /bugfix.
  • Reflection (LOCATE, contract, gate decisions) NEVER leaves this main loop; execution NEVER stays in it — the executor is the sonnet-pinned hotfixer subagent (BDR-066). A skill-mandated executor is exempt from the doctrine's "don't delegate few-tool-call work" rule (CLAUDE.md "Workflow" names that exception: skill-mandated dispatches run as written).
  • The executor is dispatched FRESH, once — hotfix never re-dispatches after a failed or blocked attempt (it reverts and escalates to /bugfix, it does not retry). Sole exception: a class-tagged BLOCKED answered by the user (STEP 3), re-dispatched once with the closed choice.
  • Design gate only if CSS/style signals detected. See STEP 1.5.
  • Revert-not-loop preserved: smoke FAIL or security BLOCK → file-scoped revert from $PRE (STEP 4's protocol — never git restore .) + STOP + escalate to /bugfix; hotfix never loops. No verifier is dispatched at hotfix weight.
  • If root cause is unclear → escalate to /bugfix (STEP 1).
  • If fix touches >5 lines of logic → reconsider if this is truly a hotfix.