HIP-517: Branch Naming — main is the Trunk, Everywhere. Status Active. Hanzo's own standard — read this before implementing against it.
A branch name is not a preference. It is an address that automation resolves: CI triggers on it, GitOps Applications pin targetRevision to it, release gates check it, mirrors sync it, and every README that says "clone and go" assumes it. When the address varies per repo, every one of those systems needs a per-repo exception, and the exception is invisible until the day it silently does nothing.
This HIP fixes the address. Across Hanzo, Lux, and Zoo the trunk is main.
A survey of 1,190 non-archived repositories across hanzoai, hanzo-apps, hanzo-templates, luxfi, lux-apps, zooai, zoo-apps, zenlm, luxcpp and hanzo-js:
| default branch | repos | |---|---| | main | 1066 | | master | 108 | | develop / dev | 8 | | a leftover feature or CI branch | 5 | | none (empty repo) | 2 |
The tail is the part that matters. Five repositories have a transient branch as their default — luxfi/pqclean on fix-sphincs-duplicate-consistency, zenlm/zen-private on fix-ci-1782365623, luxcpp/papers on fix-ci-1782355258, luxfi/cggmp21.rs on m, luxfi/verkle on account-leaf. A throwaway branch became the thing every clone, every CI run and every dependency resolution reads first. Nobody chose that; it is what happens when the trunk has no name the tooling can assume.
mainEvery repository in every Hanzo, Lux, and Zoo organization has a default branch named main. Not master, not develop, not trunk, and never a feature or CI branch.
This applies to repositories we own. Upstream forks keep whatever the upstream uses on the branches that track upstream — but our own default branch is still main, because that is the branch our tooling reads.
dev is the only other long-lived branch, and it is optionalA repository that needs an integration branch uses exactly one, named dev. develop is not a synonym; pick one spelling or the tooling needs two.
Most repositories should not have one. Feature branches integrate into main rapidly; a long-lived dev that drifts is a merge debt that compounds, and the hanzoai/templates case below shows what the end state looks like.
<kind>/<subject>feat/, fix/, chore/, docs/. No dates in branch names — a date in a branch name is a comment that cannot be updated and is wrong by the second commit.
If a default branch is ever observed pointing at a fix/, feat/ or a CI- generated name, that is an incident, not a preference: it means a force-push or a branch deletion took the trunk with it.
Use the rename operation, which moves open pull requests and installs a redirect:
gh api -X POST repos/<org>/<repo>/branches/master/rename -f new_name=main
Do NOT create main as a fresh branch beside master and switch the default. That produces two branches with unrelated histories in one repository, and every later merge fails with refusing to merge unrelated histories. This is not hypothetical — see below.
hanzoai/templates carries both. master held 30 commits main lacked; main held 7 commits master lacked; the two shared no common ancestor. master is the submodule-based catalog with 68 .gitmodules entries. main is a flattened rewrite with no .gitmodules at all and a flatten-apps.sh explaining the intent.
Both are real work. Neither is stale. The repository cannot merge them, and no tool can tell which one a reader should trust — git will happily serve either. That is the cost of creating main alongside master instead of renaming, and it is unrecoverable without a human deciding which history is canonical.