---
title: "Hotfix branches need different rules than develop"
canonical: https://dxdev.com/blog/hotfix-branches-need-different-rules/
datePublished: 2026-04-22
---
On April 22, 2026, we were working on a `hotfix/<version>` branch, pointed straight at production. We ran `/sync` mid-session and [watched it prepare to merge](/everyday-ai/update-that-was-about-to-bring-the/) `develop` in.

That would have been bad.

`develop` in this codebase is the staging lane. It carries half-finished features, experimental schema changes, and at any given moment, code that is not production-ready. Merging it into a hotfix branch would have pulled all of that toward `master`. One merge commit, and the release becomes a vehicle for everything that was supposed to wait.

The skill didn't know the difference. It saw a branch, it knew there was a `develop` and a `master`, and it ran the standard sync logic.

## What the branch model actually is

This is an older web application, and the release topology reflects that history. It works, but it has corners you have to know about.

`develop` is where features land before a release. `master` is production. There is also a preview branch the team uses to review work before it goes live. Hotfixes cut directly from `master`, get a version tag, and merge back into both `master` and `develop` in a specific order.

That last part matters. When a hotfix lands, the release pipeline carries it forward. `master` gets the fix, then `develop` gets it via the standard hotfix merge. If your skill runs its own `master` to `develop` sync, you manufacture a duplicate merge commit, race the actual release flow, and create phantom history that confuses future merges.

So there were two bugs in `/sync`, not one. Merging `develop` into a hotfix branch was wrong because it drags unfinished work toward production. Merging `master` into `develop` during sync was wrong because the release flow already handles that, and doing it again adds noise and timing risk.

## The fix: read the prefix first

The skill now reads the branch prefix before deciding what to do.

Branches matching `hotfix-*` or `hotfix/*` get a different sync path. No develop merge. No master-to-develop push. Just a targeted sync against the base they actually cut from. `develop` and `master` get their own handling. Everything else falls through to the original logic.

We also updated the workflow docs and the branch validation checks, so the agent has the branch semantics written down somewhere it can read them instead of inferring them from the codebase topology. The topology is visible. The meaning of the topology is not. Which branches exist is a `git branch -a`. What you are allowed to merge into a hotfix branch is a policy, and policies need to be stated explicitly or they don't exist.

We fixed one other thing at the same time. The sync skill had a hardcoded clone path that referenced a specific working directory on one machine. It was fine until we ran the skill from a different clone, at which point it tried to operate on the wrong repo. One line, obvious in hindsight, sitting there untouched because the nominal path always happened to be the one we were using.

## The /item fix alongside it

While we were in the tooling, we also updated `/item`, the skill we use to open an investigation on a ticket.

The old behavior was to start every investigation with an empty browser tab and wait for us to supply the repro URL. Every session started with us reading the ticket, finding the links field, copying the URL, and pasting it into the skill. A two-minute tax on every investigation.

The links field exists in the ticket tracker because repro URLs are part of ticket state. The skill now reads it on open and navigates there directly. If no link is present, it asks. If one is present, it uses it. Two minutes saved per ticket, and one fewer thing to hold in working memory.

Both fixes belong to the same category. The agent kept asking us for context that was already written down. The branch rules were in the workflow docs. The repro URL was in the ticket. The skill just wasn't reading either.

## Why workflow mistakes travel further

Earlier the same day, a redirect-handling fix we shipped in a separate ticket [saturated the IIS worker pool](/blog/recovery-code-hot-path/) by running a SELECT and UPDATE on every authenticated request. The damage was scoped to one server's thread queue. Recoverable in minutes.

If `/sync` had merged `develop` into the hotfix branch and we had pushed before catching it, the damage would have been whatever `develop` happened to contain that afternoon, arriving in a production release labeled as a hotfix.

Coding mistakes tend to fail locally and loudly. Workflow mistakes tend to fail silently or at a distance, when the release lands and the wrong code is in it. A bug in a feature affects one feature. A bug in the deployment flow affects every feature that touches it.

That is why the branch rules have to be explicit. Claude is not going to invent the deployment model of your repo from first principles. It reads what you've given it and does the most plausible thing. If nobody has written down that hotfix branches cannot accept develop merges, the most plausible thing is to run the default sync, and the default sync does what it always does.

Here, the sync skill ran its default logic because nothing stated that a hotfix branch cannot accept develop merges. So ask whoever runs your release process whether those rules are written down where your AI tooling can read them.
