Skip to content
Back to skills

Hotfix Charter

ASecurity

Gates `hotfix/*` branches (and `fix/*` when `CHARTER_BRANCH_PREFIXES` is set) on a HOTFIX-CHARTER.md (symptom + diff budget + cr-battery pre-commit verdict + refactoring disposition). Prevents the incident-2026-1507 failure mode (a hotfix that grew to 73 files / +8,750 / -4,195 LOC because nobody asked 'is this the smallest thing that fixes the symptom?').

  • 8 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 6, 2026
ai-agentsazurerefactoringcode-reviewgitdocumentation

Works with

  • cli

Security analysis

A100/100

Scanned October 6, 2026

npx -y skills add bordenet/superpowers-plus --skill hotfix-charter --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Hotfix Charter?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Hotfix Charter
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/bordenet-hotfix-charter/badge)](https://www.skillsdirectory.com/skills/bordenet-hotfix-charter)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: hotfix-charter
disable-model-invocation: true
source: superpowers-plus
augment_menu: true
auto_invoke: true
triggers:
  - "git checkout -b hotfix/"
  - "git checkout -B hotfix/"
  - "git switch -c hotfix/"
  - "git checkout -b fix/<TICKET-ID>-"
  - "git switch -c fix/<TICKET-ID>-"
  - "creating a hotfix"
  - "hotfix branch"
  - "production hotfix"
  - "shipping a hotfix"
  - "fix for prod"
  - "I need to hotfix"
  - "/sp-hotfix-charter"
anti_triggers:
  - "feature branch"
  - "refactor branch"
  - "experimental branch"
description: "Gates `hotfix/*` branches (and `fix/*` when `CHARTER_BRANCH_PREFIXES` is set) on a HOTFIX-CHARTER.md (symptom + diff budget + cr-battery pre-commit verdict + refactoring disposition). Prevents the incident-2026-1507 failure mode (a hotfix that grew to 73 files / +8,750 / -4,195 LOC because nobody asked 'is this the smallest thing that fixes the symptom?')."
summary: "Charter discipline for prod hotfixes: one-sentence symptom, explicit LOC budget, cr-battery on staged diff BEFORE first commit, recorded refactoring disposition. Enforced by tools/hotfix-charter-check.sh (pre-commit hook). Layered on top of unified-commit-gate, not a replacement."
coordination:
  group: commit-gates
  order: -10
  requires: []
  enables: ["unified-commit-gate"]
  internal: false
composition:
  consumes: [branch-context]
  produces: [hotfix-charter]
  capabilities: [gates-quality]
  priority: 50
---

# Hotfix Charter

> **Wrong skill?** Branch-naming sanity check → `git-branch-conventions`. Lint/test/IP gate at commit time → `unified-commit-gate`. Per-commit size cap → `pre-push-loc-gate` (`tools/pre-push-loc-gate.sh`). Adversarial code review → `code-review-battery`.

> **2026-06-10 incident calibration:** incident-2026-1507 MR grew to +8,750 / -4,195 LOC across 73 files on a hotfix branch because nobody captured the symptom upfront, set an LOC ceiling, or ran cr-battery on the staged diff before the first commit. This skill forces those three things at the moment the hotfix branch is created.

## When to Use

- The agent (or operator) runs `git checkout -b hotfix/...`. By default the hook gates the `hotfix/` prefix only; to also gate ticketed `fix/<TICKET-ID>-...` branches, set `CHARTER_BRANCH_PREFIXES="hotfix/ fix/"` (this then gates every `fix/*` branch — generic `fix/anything` feature work included, which normally runs through `branch-flow-gate`).
- A production bug needs a same-day fix (symptom is a behavior the customer can observe, NOT "let's redesign this properly")
- Skip when: feature work, refactors, experimental branches, documentation. Those have their own gates (`branch-flow-gate`, `unified-commit-gate`).

## The Charter Sections

The hotfix MUST start with a `HOTFIX-CHARTER.md` file at the repo root containing exactly these four sections:

```markdown
## Symptom (one sentence)

(What the customer sees. NOT what's broken in the architecture, NOT what
you'd ideally redesign. The observable bug: "Greeting clips at 700ms on
Azure-via-failover path.")

## Diff budget (LOC ceiling)

(An integer. The smallest change that fixes the symptom. incident-2026-1507 minimum
was ~80 LOC; the broken MR shipped at 12,945 LOC because nobody set
this number upfront. Examples: `80`, `200`, `500`. Anything > 500 needs
explicit PM sign-off in this section.)

## cr-battery pre-commit verdict

(Result of running cr-battery on the STAGED diff BEFORE the first commit
on this branch. Must be PASS or PASS_WITH_NITS at the project's quality
floor. The hook below refuses commits if this section reads anything else.
Re-run cr-battery and update this section if the staged diff changes.)

## Refactoring disposition

PARK — deferred extracting the duplicated `buildRetryPayload` helper (added twice in this diff); minimal-diff hotfix.
```

Replace the example with your real decision — do not paste it unedited. The rules:

- **Token.** The first non-blank body line contains exactly one of
  `REFACTOR_NOW` / `PARK` / `NONE` as a whole word — put it first, then one
  concrete reason. Only that first line is scanned for the token (backticked
  spans stripped), so a later reason line may say "none of the callers" freely.
- **Bias.** `NONE` for a clean fix; `PARK` when you actually spotted deferrable
  structure. `REFACTOR_NOW` only when the refactor *is* the fix or a true
  preparatory refactor.
- **Citation (`PARK` / `REFACTOR_NOW`).** Cite in backticks a symbol whose
  identifier run matches `[A-Za-z_][A-Za-z0-9_]{2,}` (no leading digit, 3+
  chars) and that appears in your staged added content. A bare (non-backticked)
  symbol, or a one-character "symbol" like `.`, fails.
- **Ceiling (`NONE`).** The staged diff must change at most
  `DISPOSITION_NONE_MAX_LINES` (default 30) lines, additions and deletions
  counted together.
- **Reason.** The hook does not check it is meaningful — a body of just `PARK`
  plus a backticked symbol passes. Writing a real reason is yours and the
  reviewer's job.
- **Structure.** An `## ` (h2) heading closes the section; keep no other h2 in
  the body.

## Enforcement: tools/hotfix-charter-check.sh

The pre-commit hook at `tools/hotfix-charter-check.sh` (wire into `.git/hooks/pre-commit`) reads the current branch name and, if it matches a `CHARTER_BRANCH_PREFIXES` prefix (default `hotfix/`):

1. Checks that `HOTFIX-CHARTER.md` exists at the repo root
2. Parses each of the four sections (case-sensitive PREFIX-match)
3. Refuses the commit if any section is missing OR if the cr-battery section doesn't contain `PASS` or `PASS_WITH_NITS`
4. Enforces the `## Refactoring disposition` template exactly as shown above — the token rule, the citation rule, and the `NONE` change-line ceiling. It does not judge whether the reason is meaningful.
5. Bypass: `ALLOW_NO_CHARTER=1 git commit ...` (prints WARNING)

**Env overrides:**
- `ALLOW_NO_CHARTER=1` — bypass the whole hook (prints WARNING, no audit trail)
- `CHARTER_BRANCH_PREFIXES` — space-separated gating prefixes (default `hotfix/`)
- `REQUIRE_DISPOSITION` — set `0`/`false`/`no`/`off` to skip ONLY the disposition check (rollout hatch; prints a WARNING); the three original sections still enforce. A truthy-looking value still enforces. Default on. For a legitimately large-but-flat or deletion-heavy hotfix, use this — not `ALLOW_NO_CHARTER=1`, which drops every check.
- `DISPOSITION_NONE_MAX_LINES=N` — max total changed lines (added + deleted) allowed with a `NONE` disposition (default `30`); a non-integer value exits 2

**Exit codes** (unchanged contract):
- 0: Branch not gated, OR charter valid with PASS/PASS_WITH_NITS
- 1: Charter missing, section missing, bad verdict, OR disposition problem
- 2: Git/env error (detached HEAD, not in repo, non-integer `DISPOSITION_NONE_MAX_LINES`) — fails CLOSED

## Layering with peer skills

This skill runs FIRST (`order: -10`), then `unified-commit-gate` (`order: 0`) handles lint/test/review. `hotfix-charter` does NOT replace `unified-commit-gate`; it runs BEFORE it.

## How to Apply

On the FIRST turn after `git checkout -b hotfix/...` (or a `fix/...` branch when `CHARTER_BRANCH_PREFIXES` gates it):

1. **Capture the symptom** in one sentence (customer-observable behavior, NOT architecture)
2. **Set a diff budget** (smallest LOC ceiling that fixes the symptom; default 200)
3. **Sketch the minimum diff** (don't redesign, don't refactor)
4. **Run cr-battery** on the STAGED diff; write verdict to HOTFIX-CHARTER.md
5. **Record the refactoring disposition** — first line opens with one token (`NONE` for a clean fix, `PARK` if you spotted deferrable structure), then one concrete reason. If you `PARK`/`REFACTOR_NOW`, cite in backticks a real symbol from your staged diff. If the hotfix legitimately changes more than `DISPOSITION_NONE_MAX_LINES` lines of flat, un-refactorable structure, or is mostly a deletion, set `REQUIRE_DISPOSITION=0` for that commit and note it in the MR — never `ALLOW_NO_CHARTER=1`, which drops the entire charter gate.
6. **Commit** — on a gated branch the hook verifies all four sections and that the verdict is PASS/PASS_WITH_NITS
   - If hook rejects: fix the failing section (missing heading or non-PASS verdict) and re-run cr-battery on the staged diff before retrying the commit. If cr-battery fails a second time, do not loop — escalate to human with both verdict outputs.

**Rollout:** a `hotfix/*` branch created before this section existed fails its next commit until you add `## Refactoring disposition`. `REQUIRE_DISPOSITION=0` is the transition hatch for a hotfix already in flight.

## Failure Modes

| Failure | Symptom | Recovery |
|---------|---------|----------|
| `... must open with EXACTLY ONE ... (found N on the first line)` | The first body line has no token, or a bare uppercase `PARK`/`NONE`/`REFACTOR_NOW` sits in the reason | Put exactly one token first; lowercase any token-word in the reason ("none of the callers") |
| `no cited symbol from '## Refactoring disposition' appears in the staged diff` | `PARK`/`REFACTOR_NOW` cites a symbol your diff never touched, or you pasted the template example unedited | Cite a real identifier from your added lines, or record `NONE` plus a follow-up issue for out-of-diff cleanup |
| `a NONE disposition is capped at N` | The change (added + deleted) exceeds the ceiling but has nothing to refactor-cite | `REQUIRE_DISPOSITION=0` for this commit + an MR note — never `ALLOW_NO_CHARTER=1` |
| Whole hook bypassed silently | `git commit --no-verify`, or `ALLOW_NO_CHARTER=1` (git design limit / documented bypass) | Out of scope for this skill; call the bypass out in the MR description |

If diff grows past budget: update the `## Diff budget` section with the new ceiling and a one-line rationale, then re-run cr-battery on the updated staged diff before retrying.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…