Git Workflow¶
1. Branches¶
| Branch | Purpose |
|---|---|
main |
Always builds, always passes tests. Never committed to directly. |
feat/<module>-<thing> |
A feature |
fix/<module>-<thing> |
A bug fix |
docs/<area> |
Documentation only |
refactor/<area> |
Behaviour-preserving change |
One branch per logical change. If a branch needs "and" to describe it, split it.
2. Commits¶
Small and focused. A commit is one logical change that leaves the tree building.
Format¶
<type>(<scope>): <subject, imperative, lower case, no full stop>
<body: what changed and WHY. Wrap at 80. Include findings, not just actions.>
Co-Authored-By: Claude <noreply@anthropic.com>
| Type | For |
|---|---|
feat |
New behaviour |
fix |
Bug fix |
refactor |
No behaviour change |
perf |
Performance |
docs |
Documentation |
test |
Tests |
build |
Build, dependencies, tooling |
chore |
Everything else |
Scope is the module: bulletin, character, core, economy, interaction, inventory,
items, ownership, ui, world, legacy, or — once built, per
ROADMAP.md — crime, jobs, needs, chat,
commands. Same taxonomy as the area labels in
05_ISSUE_TRACKING.md §3.
feat(character): implement identity and permission resolution
feat(items): add base item component
fix(save): prevent duplicate inventories on reconnect
refactor(network): reduce RPC usage in the door flow
docs(inventory): update serialization notes
test(economy): cover door tax repossession
The body matters¶
The subject says what. The body says why, and records anything the next reader would otherwise have to rediscover:
fix(inventory): enforce per-item stack limits
The legacy implementation read item.max and item.plural, but every definition
sets ITEM.Max and ITEM.Plural. Lua is case-sensitive, so the branch was dead
code and no item has ever had a working stack limit -- see D-03.
Adds ItemDefinition.MaximumStack as a typed property so the casing class of
bug cannot recur, plus a regression test named for the defect.
3. Before you push¶
Cadence. Commit locally after every logically complete piece — small and often, per §2.
Push to origin at least once per milestone (see
ROADMAP.md) or logical unit of work within one, whichever is more
frequent, so origin/main never falls far enough behind local main that a lost machine
would lose real work. Do not batch a milestone's worth of commits into a single end-of-session
push.
- [ ] Builds with no warnings
- [ ]
dotnet testpasses - [ ] Documentation updated in the same commits — XML docs, module README,
Architecture/ - [ ] An ADR exists if an architectural decision was made
- [ ] Self-reviewed the diff line by line
- [ ] No commented-out code, no stray
TODOwithout an owner, no debug logging
4. Merging¶
Squash if the branch is a messy sequence of one idea. Preserve history if the commits are each meaningful and independently buildable — that is the preferred outcome and the reason for committing in small pieces.
main must never be broken. If it is, fixing it takes priority over everything else.
5. Tags and releases¶
Tag milestones, not commits.
v0.1.0 Core framework
v0.2.0 Persistence
v0.3.0 Character
v0.4.0 Inventory and items
v0.5.0 First playable
v9.8.0 Release-prep (Milestone 10) -- everything short of a live server, done for real
v9.9.0 Player presence & admin tools (Milestone 11) -- pawn binding, teleport/noclip/
spectate, real ban records and mute enforcement
v1.0.0 The first stable, feature-complete release
v1.0.0 means the first stable, feature-complete release, not "the next number after
v0.5.0." It originally also marked the repository going public, but
ADR-0008 moved the public launch up to
Milestone 10, specifically to host a public documentation website — the repository is public
now, well ahead of v1.0.0. The v9.x range for pre-launch hardening tags is kept anyway:
renumbering already-released tags is worse than an explanation that's evolved, and the range
still usefully signals "hardening, not the stable release" even though it no longer counts
down to a visibility change. If more hardening lands after v9.8.0, it continues in the same
range (v9.9.0, and so on) until v1.0.0 is actually cut.
GitHub Releases are for milestones only — major versions, public previews, release
candidates, and v1.0. Nightly development stays on git with no release attached. v9.8.0 and
any further v9.x tags do not get a GitHub Release on their own, even though the repository
is now public — a Release is for announcing something to an audience, and pre-v1.0.0
hardening tags aren't that moment.
6. What never enters history¶
legacy/content/— 162 MB of copyrighted Source 1 assets. Gitignored, and it stays that way.CLAUDE.md,tasks.md,handoverdaily.md, and the rest of.claude/— assistant session artifacts, centralized in a separate privateclaude-configsrepo per the global assistant instructions. Gitignored;CLAUDE.mdis a symlink back to the real file so tooling that expects it at the repo root still works. Now the repository is public (ADR-0008) this matters more than it did — none of this is meant for an outside reader. Exception:.claude/skills/is tracked — shared skill definitions (e.g. thesboxskill, #42) are reusable engineering reference material, not session state, and are useful to an outside contributor rather than exposing anything private. Everything else under.claude/(settings.local.jsonand other local/session state) stays gitignored.- Credentials, connection strings, API keys, server configuration with secrets.
- Build output,
bin/,obj/, compiled assets. - Large binaries generally. If something big is genuinely needed, discuss it before committing — history is forever.