Branching & release
prod ← dev ← feat/F-###, and the commit format.
docs/BRANCHING.md
main is the only long-lived branch, and merging to it deploys nothing. Work lands on main
through pull requests; prod changes only when someone ships a numbered release through nonprod
first (deploy/README.md → Shipping a release). The old
dev / prod branches and the Kubernetes targets no longer exist.
| Branch | Holds | Deploys to |
|---|---|---|
main | Everything merged and checked. Every merge also builds signed release candidates. | Nothing by itself |
feat/*, fix/*, chore/*, docs/* | One change, until its pull request merges (the branch is then deleted) | — |
| Environment | Runs | Changes when |
|---|---|---|
| nonprod — https://nonprod.omnichan.studio | The newest release | The promoter deploys a new release there automatically, or someone dispatches deploy nonprod |
| prod — https://studio.lightningleap.store | A release that ran on nonprod | Someone on PROD_DEPLOYERS dispatches deploy prod |
The loop
git switch main && git pull --ff-only
git switch -c feat/F-020-projects # one branch per change
# … build the slice: prisma model → api module → contract → ui → tests …
npm run verify # typecheck → lint → test → build
git commit # conventional commits, see below
gh pr create # CI runs; a reviewer approves; squash-merge
Rules:
- One branch per change, named for it:
feat/F-020-projects, notfeat/stuff. A feature id ties the branch to a row in FEATURES.md anddata/dashboard/features.json. - A branch is a vertical slice, not a layer: Prisma model → API module → contract → UI → tests, all in one branch.
npm run verifypasses before the PR, and CI passes before the merge. A pushed commit clears the PR's approval, so finish the change before asking for review.- Squash-merge. One change, one commit on
main. - Every PR carries its own notes, so concurrent PRs never collide on a shared file:
- a release note in
NEXT/(NEXT/README.md), which the next release turns intoCHANGELOG/vX.Y.Z.md. Mark a featureimpact: minor; - its
CHANGELOG.md/VERIFICATION.mdrows indocs/changes/<branch>.md(docs/changes/README.md), never in the tables themselves; - the board (
features.json, theFEATURES.mdrow) moved in the same commit as the code.
- a release note in
- No direct commits to
main. A fix to something on prod is a PR, then a release.
Commit format
Conventional commits, scoped by workspace:
feat(api): projects module with org scoping
feat(web): project list and detail
feat(contracts): project + budget schemas
fix(api): agency users could read unassigned projects
docs: record D-009 (Stripe over Paddle)
chore(deps): add stripe
Types: feat · fix · refactor · docs · chore · perf · test · breaking.
Scopes: api · web · contracts · infra · none for repo-wide changes.
Release
A release is a signed, numbered copy of one main build — the same bytes run on nonprod and then
prod. Cutting one, soaking it on nonprod, promoting it, holding it and rolling back are in
deploy/README.md → Shipping a release.