Issue→Branch Naming Rules — Prefix, Issue Number, Collisions

If you only tell an agent “open an issue and cut a branch,” you often get random names, missing issue links, or overwrites of an existing branch. Treat branch naming like any other team convention: lock prefix · issue number · collision procedure into Rules and Done criteria.

This post covers three axes only: prefix? · issue number? · collisions? agent-pr-body is PR body text; agent-patch-commits is commit splitting. Here the focus is issue → branch name. No pricing, plans, affiliates, or invented reviews.

Grounded in Git — git-check-ref-format (ref name rules), GitHub Docs — Linking a pull request to an issue, GitHub Docs — Creating a pull request, and common type/issue-slug branch practice.

What prefix should you use?

One-line answer: A prefix is a short token so humans see the change kind at a glance. Fix an allowlist; agents must not invent prefixes outside it.

Common prefixes (public practice/convention; not product pricing):

PrefixMeaningWhen
feat/FeatureNew behavior, API, UI
fix/Bug fixRestore broken behavior
chore/ChoreDeps, config, CI tuning (not a feature)
docs/Docs onlyREADME, guides, doc-only edits
refactor/Behavior-preserving refactorStructure only
test/Tests onlyCoverage, fixtures
ci/CI/CDWorkflows, pipelines

Format examples (pick one team pattern):

# Pattern A — type + issue + slug
feat/123-add-login-rate-limit
fix/456-null-ptr-on-empty-list

# Pattern B — issue-first (some teams)
123-feat-add-login-rate-limit

# Pattern C — explicit issue word
issue/123-add-login-rate-limit

One line for the agent:

Branch name = <allowed-prefix>/<issue-number>-<kebab-slug>
Allowed prefixes: feat, fix, chore, docs, refactor, test, ci
Slug: lowercase ASCII, hyphens only, max ~50 chars, no spaces.
Do not invent prefixes outside the allowlist.

Do not:

  • Mix Feature/, FIX/, spaces, or heavy underscores → parsers and humans diverge. Default to lowercase + hyphens.
  • Names that are only temp/, wip/, or agent/ → issue and kind disappear from review/cleanup.
  • Bare just-fixing with no prefix → weaker search, filters, and automation hooks.

Git ref constraints (git-check-ref-format): avoid spaces, .., control chars, and specials like ~^:?*[\\. Putting # in the branch name is uncommon and awkward in shells/URLs—use digits only for the number segment.

Where does the issue number go?

One-line answer: Put the tracker issue number as digits in the branch name, and use GitHub’s linking keywords in the PR body/commits. Fail Done if the agent invents a nameless “vibes” branch.

PlacePreferWhy
Branch namefeat/123-short-slugNumber shows up in git branch, CI filters, local search
PR titlefeat: … (#123) or team templateVisible in review/notifications
PR body / commitsFixes #123 / Closes #123 / Resolves #123GitHub linking keywords close/link on merge
No issueExplicit exception token e.g. chore/no-issue-bump-depsSeparates “forgot the number” from “intentionally no ticket”

Copy-paste rule:

If the task cites an issue URL or #N:
  1. Extract N as digits only for the branch segment.
  2. Never put literal "#" in the branch name.
  3. In the PR body, include "Fixes #N" (or Closes/Resolves) when the PR should close it.
If no issue exists:
  Stop and ask, OR use the team’s no-issue prefix token — do not invent a fake number.

Note: GitHub closes issues via keywords on PRs that merge into the default branch. A number in the branch name alone does not auto-close. Put PR-body keywords in Done—not “I put the number in the branch, done.”

Multiple issues: put the primary issue in the branch name; list others in the PR body as Related to #A, #B to keep names short and collisions rarer.

What if the name collides?

One-line answer: If the same name already exists locally or on the remote, do not overwrite or force-push. Check existence → decide reuse → append a suffix for a new name.

Typical collision cases:

SituationSymptomAgent action
Local same nameAlready on git branch / checkout -b failsSame issue → reuse/rebase per policy. Other work → new name
Remote same namegit ls-remote --heads origin <name> hitsMay be someone else’s WIP → new name or ask a human. No -f push
Slug clash onlyfeat/123-login vs fix/123-loginDifferent prefixes OK if team allows. Same type+number → change slug/suffix
Concurrent agentsTwo sessions create for the same issuefeat/123-slug-<short-id> or -2. Whether to merge into one PR is a human call

Check order (copy-paste):

Before creating a branch named NAME:
  [ ] git show-ref --verify --quiet refs/heads/NAME
  [ ] git ls-remote --heads origin NAME
  If either exists:
    - Same issue + abandoned → ask human before reset
    - Same issue + active → checkout existing; do not recreate
    - Different work or unclear → use NAME-2 or NAME-<yyyymmdd> or NAME-<agent-short>
  Never: git push --force to “win” a name collision
  Never: delete someone else’s remote branch to free the name

Suffix examples:

feat/123-add-login-rate-limit
feat/123-add-login-rate-limit-2
feat/123-add-login-rate-limit-20260924

One-line wrap-up: Build allowed prefix + issue digits + kebab slug; on collision reuse or suffix—never steal the name with force.

FAQ

Can the branch include #123?

It can, but shells/URLs/tools often need escaping. Prefer digits 123 in the branch; keep #123 for PR/commit text.

Should Conventional Commit types match branch prefixes?

Matching helps search (feat/ branch → feat: commit). Not required, but a Rule like “branch prefix = commit type” keeps agents consistent. See agent-patch-commits for commit splitting.

Hotfix with no issue?

Allow only the team’s exception (hotfix/ + timestamp, or chore/no-issue-…). Ban inventing fake issue numbers. Prefer opening an issue first, then the branch.

What if the agent commits straight on main?

That bypasses issue→branch rules. Add Done: “work branch ≠ default branch.”

Sources