Branching model based on Git Flow. Default branch is main.
| Branch | Purpose | Direct commits? |
|---|---|---|
main |
Production-ready code. Mirrors what is (or will be) deployed. | No — only via PR from develop |
develop |
Integration branch. Accumulates features until next release. | No — only via PR from feature/* or fix/* |
feature/<slug> |
New feature work. Branched from develop. |
Yes (the author's local branch) |
fix/<slug> |
Bug fixes targeting the next release. Branched from develop. |
Yes |
hotfix/<slug> |
Urgent production fix. Branched from main, merged to both main and develop. |
Yes |
docs/<slug> |
Documentation-only changes. Branched from develop. |
Yes |
chore/<slug> |
Tooling, deps, build config. Branched from develop. |
Yes |
main ────●────────────────●────────────●─────►
│ ▲ ▲
│ │ PR │ PR
develop ────●────●────●──────●────●───────●─────►
▲ ▲ ▲
│ PR │ PR │ PR
feature/x ────● │ │
feature/y ────● │
fix/z ────────●
- Branch
feature/<slug>fromdevelop. - Commit + push to
feature/<slug>. - Open PR
feature/<slug>→develop. Merge when reviewed. - When release is ready, open PR
develop→main. Merge. - Vercel auto-deploys on push to
mainonly (vercel.json:git.deploymentEnabled.main). Other branches won't trigger a deploy — open a Preview deployment manually via the Vercel dashboard if you need one.
feature/repo-detail-expansionfix/auth-token-expiryhotfix/missing-env-vardocs/roadmap-updatechore/bump-deps- Use kebab-case slugs. No issue numbers in branch name (link them in the PR).
Conventional Commits. See docs/code-standards.md.
feat:new featurefix:bug fixdocs:docs onlyrefactor:no behavior changetest:tests onlychore:tooling, deps, build
- PRs to
develop: squash or merge commit, author's choice. - PRs to
main: merge commit (preserves release boundary in history). - Never force-push to
developormain. - Never commit directly to
developormain— open a PR. - Delete the feature branch (
feature/*,fix/*,docs/*,chore/*) right after its PR merges. Reusing a merged branch for new commits leads to "X commits behind" drift and confusing diffs.
GitHub default branch must be main. Verify:
gh repo view --json defaultBranchRef -q .defaultBranchRef.name
# expected: mainTo fix:
gh repo edit --default-branch main
git remote set-head origin mainAfter a PR develop → main merges, the merge commit lives only on main.
Without sync-back, develop will report "X commits behind main" forever
even though every line of code is identical.
Run sync-back once per release, immediately after the release PR merges:
# 1. Pull main's release merge commit
git checkout main
git pull origin main
# 2. Bring it back into develop so the branches stop drifting
git checkout develop
git pull origin develop
git merge origin/main --no-edit
git push origin developAfter this, develop should report "0 commits behind main". If you ever
see develop X behind main with no actual diff (git diff origin/main
shows nothing important), it's the same drift — run sync-back to clear it.
If you don't need the merge commit on main as a release boundary, change
the GitHub repo's "Allow merge commits" setting for main to "Squash" or
"Rebase". Then main advances with the same SHAs as develop and there
is no drift to sync. This trades an explicit release marker for a cleaner
history — pick whichever the team prefers and stay consistent.
The repo has a Release workflow at .github/workflows/release.yml that
publishes a GitHub Release whenever a tag matching v*.*.* is pushed.
The Release shows up automatically on the in-app /changelog page.
-
After the release PR
develop → mainis merged, syncmainlocally (see "Sync after a release merge" above). -
Tag the release commit on
main:git checkout main git pull origin main git tag -a v0.7.0 -m "v0.7.0" git push origin v0.7.0 -
The workflow runs and creates the GitHub Release with auto-generated notes derived from PRs/commits since the previous semver tag.
- Use semver:
vMAJOR.MINOR.PATCH. Example:v0.7.0,v1.0.0. - Pre-releases:
v1.0.0-rc.1,v0.8.0-beta.1. The workflow detects-alpha|-beta|-rcand marks the Release as pre-release. - Tag the commit on
main, notdevelop. The tag should match the exact code shipped to production. - The workflow uses
--generate-notes --notes-start-tag <previous>so notes only cover work since the last release. Keep PR titles descriptive — they become the changelog body.
Notes can be edited freely from the GitHub UI (Releases → Edit). The
publish date (published_at) is not editable after creation, which
is why the in-app /changelog shows the tagged commit's date rather
than published_at.
git remote prune originafter deleting branches on GitHub to drop stale remote-tracking refs locally.git branch -D <name>for local branches whose remote was deleted.- Never commit to a branch whose PR already merged. Open a fresh
branch from
developfor new work.