.claude/skills/prod-hotfix/SKILL.md
prod-hotfix
npx skillsauth add timelessco/recollect prod-hotfixInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Ships a small fix directly to main without going through the release PR pipeline, then backmerges main into dev so the two branches stay in sync. Normal changes still go through /release — this exists only for urgent fixes that cannot wait.
/release/releasemain bypasses branch protection — non-admins get rejected outright)gh pr list --label release)The workflow branches on what state the local checkout is in when the skill runs:
dev — working tree is dirty with the fixdev — you committed the fix before realizing it needs the hotfix path (what we hit in the motivating session)dev first, then rerunRun steps in order. Stop and report if any step fails — do not try to push through a red state.
Check where you are:
git branch --show-current
git status --short
git log --oneline origin/dev..HEAD
Handle each starting state:
If there is a local commit on dev (third command has output), uncommit it but keep the change in the working tree:
git reset --mixed HEAD~1
If the working tree has uncommitted changes (from the reset above, or because the user never committed), stash them so the pull cannot conflict:
git stash push -m "prod-hotfix"
Stash everything rather than cherry-picking paths — the fix may touch multiple files and we want the whole diff moved atomically.
If the working tree was clean AND there was no local commit, stop. There is nothing to hotfix. Ask the user to make the edit on dev first.
git fetch origin
git pull --ff-only origin dev
git checkout main
git pull --ff-only origin main
Both pulls must be fast-forwards. If either refuses to fast-forward, stop — local divergence means this hotfix path is not safe and the user should investigate manually. Do not use git pull --rebase or git reset --hard to force it.
git stash pop
git diff
Read the diff carefully. Confirm it is exactly the intended fix and nothing else — no stray formatter churn, no unrelated files. If the stash pop conflicts, main has diverged from dev in the file being fixed and the fix probably needs to be rewritten against the current main contents; stop and report.
Run the project verification chain before committing. All three must pass:
pnpm fix
pnpm lint
pnpm build
pnpm fix runs auto-fixers (may modify files — if it does, re-inspect with git diff). pnpm lint runs all quality gates in parallel. pnpm build confirms the production build still compiles. Do not skip pnpm build even for trivial changes — silent build regressions on main are exactly what branch protection exists to prevent, and the skill is already bypassing that.
Use a conventional commit matching the project style (see .claude/rules/commands.md). Example:
git add <path/to/file>
git commit -m "$(cat <<'EOF'
<type>(<scope>): <gitmoji> <subject>
- <why / what changed>
EOF
)"
Types used in this repo: fix for bugs, style for UI/cosmetic, perf, chore, docs, refactor, feat. Gitmoji goes after the colon+space. Subject under 50 chars, imperative mood, no trailing period.
git push origin main
Expected output includes a bypass notice:
remote: Bypassed rule violations for refs/heads/main:
remote: - Changes must be made through a pull request.
remote: - Required status check "Vercel" is expected.
This is normal — branch protection on main is configured to allow admin bypass. If the push is rejected outright (not just annotated with "Bypassed"), the user does not have admin and this path cannot continue. Stop and tell them to open a hotfix PR against main instead.
git checkout dev
git merge main --no-ff -m "chore(release): merge main back into dev after hotfix"
git push origin dev
The --no-ff preserves the merge commit so the backmerge is visible in history, matching the commit style the CI release pipeline uses (grep for d0196bd5 in the log as a reference). This keeps dev ahead of main by exactly the already-unreleased work plus the merge commit.
git pull
git log --oneline -5
git log --oneline origin/main..origin/dev | head -3
The log should show, top-down: the backmerge commit, the hotfix commit, the previous dev tip. origin/main..origin/dev should still list every unreleased commit — confirming the hotfix did not accidentally ship any of the in-flight work.
main but does NOT run release-it, so no new tag or GitHub Release is created. The next proper release (via /release) will pick up the hotfix commit in the changelog automatically.docs/API_CHANGELOG.md on dev after the backmerge — it will be posted as a PR comment during the next release./release skill convention): a short note that a hotfix shipped directly to main, with the commit SHA and a one-line description.| Signal | What it means | Action |
|---|---|---|
| Either pull refused to fast-forward | Local dev or main has diverged | Investigate manually; do not force |
| pnpm fix / lint / build fails | Fix has a secondary regression | Fix the regression, rerun verification, only then commit |
| git stash pop conflicts | main diverged from dev in the target file | Rewrite the fix against current main |
| Push to main rejected (not bypassed) | No admin permissions | Open a hotfix PR against main instead |
| origin/main..origin/dev shows unexpected commits after Step 8 | Backmerge picked up stale state | Investigate; may need to reset dev |
The normal release pipeline (/release) bundles commits from dev into a release/* branch, runs CodeRabbit + CI, merges to main via admin-approved PR, and runs release-it to cut a tag. That pipeline is the right default — it exists because shipping straight to main is risky.
This skill exists for the narrow case where:
Keep the bar high for invoking it. If in doubt, use /release.
testing
v2-route-audit
tools
release
development
recollect-mutation-hook-refactoring
databases
recollect-caller-migration