The Crimsonland project follows strict commit guidelines to maintain a clear, reviewable, and useful git history.
All commits must follow the Conventional Commits format:
<type>: <description>
[optional body]
[optional footer]
Types
feat
fix
refactor
perf
test
docs
chore
style
New feature - A wholly new capability added to the codebase.feat: add plasma rifle weapon
feat: implement replay checkpoint verification
Bug fix - Corrects incorrect behavior.fix: correct RNG seed initialization
fix: resolve creature heading wrap at tau boundary
Refactoring - Code restructuring without behavior change.refactor: simplify perk selection logic
refactor: extract weapon damage calculation to helper
Performance improvement - Makes code faster without changing behavior.perf: optimize collision detection grid
perf: cache weapon table lookups
Test changes - Add or modify tests.test: add deterministic replay verification tests
test: add regression test for creature heading wrap
Documentation - Documentation-only changes.docs: document float parity policy
docs: add parity workflow guide
Maintenance - Build, dependencies, tooling.chore: update pytest to 9.0.2
chore: add ast-grep rule for float normalization
Code style - Formatting, whitespace, imports (no behavior change).style: fix import ordering
style: apply ruff auto-fixes
Description Guidelines
Use Imperative Mood
Write as if giving a command: “add”, “fix”, “refactor” (not “added”, “fixes”, “refactoring”). ✅ fix: correct weapon damage calculation
❌ fix: corrects weapon damage calculation
❌ fix: correcting weapon damage calculation
Start with Lowercase
Begin the description with a lowercase letter. ✅ feat: add new weapon system
❌ feat: Add new weapon system
No Period at End
Do not end with a period. ✅ fix: resolve collision detection bug
❌ fix: resolve collision detection bug.
Keep Under 72 Characters
The first line should be concise (ideally under 72 characters).
Commit Body (Optional)
For complex changes, add a body explaining why (not what):
fix: preserve native float32 spill point in creature movement
The original decompile shows an explicit f32 cast at the assignment
point. Moving the spill earlier causes divergence in quest_1_8 capture
at frame 7756.
Evidence: analysis/ghidra/raw/crimsonland.exe_decompiled.c:21767
Session: docs/frida/differential-sessions/session-19.md
When to Include a Body
Always include evidence and session references for parity-related changes.
Explain surprising or counter-intuitive decisions.
Document what breaks and migration path (also use BREAKING CHANGE: footer).
Explain the refactoring strategy and why it’s safe.
Commit Size
Keep commits small and reviewable - one subsystem at a time.
Good Commit Sizes
✅ Single logical change:
fix: correct zombie spawn rate calculation
✅ Focused refactor:
refactor: extract weapon damage helpers to module
✅ Isolated feature:
feat: add checkpoint verification to replay system
Bad Commit Sizes
❌ Too large (multiple unrelated changes):
fix: various gameplay fixes
- Fix zombie spawn rate
- Update weapon damage
- Refactor perk system
- Add new tests
❌ Too small (incomplete change):
fix: update zombie
fix: update zombie again
fix: finish zombie update
Commit Atomicity
Each commit should:
Pass All Checks Every commit should pass just check (tests, linting, type checking).
Be Self-Contained Changes should be complete and not depend on future commits.
Be Revertable Should be safe to revert without breaking the codebase.
Have Clear Intent Purpose should be obvious from message and changes.
Pre-Commit Verification
Before committing, verify your changes:
# Run full verification
just check
# Or use pre-commit hook (automatic)
prek install -c prek.toml -t pre-commit -t pre-push
The pre-commit hook will automatically run:
ruff check (linting)
ty check (type checking)
lint-imports (import contracts)
sg scan (structural rules)
Pull Request Guidelines
When using gh CLI for pull requests:
PR titles must follow the same conventional commit format:
feat: add replay checkpoint verification
fix: resolve creature heading wrap bug
refactor: simplify perk selection logic
Creating PRs
Use body files to avoid escaping issues:
# Write PR body to file
cat > pr_body.md << EOF
## Summary
- Add checkpoint verification to replay system
- Compare replay output to sidecar files
- Add deterministic tests for verification
## Evidence
- Implements design from docs/frida/differential-playbook.md
- Tested with quest_1_8 capture
## Testing
- [x] All tests pass
- [x] Replay verification works end-to-end
- [x] Checkpoint comparison produces expected diffs
EOF
# Create PR
gh pr create --body-file pr_body.md
Updating PRs
# Update PR body
gh pr edit --body-file pr_body.md
Merging PRs
Use squash merge to maintain clean history:
gh pr merge --squash < pr-numbe r >
Squash merging combines all commits into one, so the PR title becomes the commit message.
Git Hygiene
Keep Changes Small
Prefer multiple small PRs over one large PR:
✅ Good:
PR #123: refactor: extract weapon damage helpers
PR #124: feat: add plasma rifle weapon
PR #125: test: add weapon damage parity tests
❌ Bad:
PR #123: feat: complete weapon system overhaul (300 files changed)
Avoid Merge Commits
Use rebase or squash to keep history linear:
# Rebase on main before merging
git fetch origin
git rebase origin/main
# Or use squash merge (preferred for PRs)
gh pr merge --squash
Interactive Rebase for Cleanup
Before pushing, clean up commit history:
# Rebase last 3 commits
git rebase -i HEAD~3
# Options:
# - squash: combine commits
# - reword: change commit message
# - edit: modify commit
Never rebase commits that have been pushed to shared branches.
Commit Message Examples
Good Examples
fix: correct zombie spawn rate calculation
feat: add replay checkpoint verification
Implements deterministic checkpoint comparison for replay files.
Compares runtime state to checkpoint sidecar at frame boundaries.
Part of parity verification workflow from differential-playbook.md.
fix: preserve native f32 spill in creature heading calculation
The original decompile shows explicit float cast at assignment.
Moving spill earlier causes divergence at quest_1_8 frame 7756.
Evidence: analysis/ghidra/raw/crimsonland.exe_decompiled.c:21767
Session: docs/frida/differential-sessions/session-19.md
Verified: quest_1_8 capture now passes checkpoint verification
refactor: migrate weapon system to typed schemas
BREAKING CHANGE: Weapon serialization format changed from dict to
msgspec Struct. Old save files will not load.
Migration: Run `uv run scripts/migrate_saves.py` to convert.
Bad Examples
❌ fix: bug fixes ✅ fix: correct zombie spawn rate calculation
❌ fix: change constant to 0.6000000238418579 ✅ fix: preserve native f32 constant in turn rate calculation
Multiple Unrelated Changes
❌ chore: various updates and fixes ✅ Split into multiple focused commits
❌ feat: fix zombie spawn bug (should be fix:) ✅ fix: correct zombie spawn rate calculation
Commit Workflow
Recommended workflow:
Make Focused Changes
Work on one logical change at a time.
Commit with Conventional Message
git commit -m "fix: correct zombie spawn rate calculation"
Push to Branch
git push origin feature-branch
Create PR
gh pr create --body-file pr_body.md
Resources
Next Steps
Development Workflow Learn the full contribution workflow
Verification Process Ensure commits pass all checks
Code Style Follow project coding standards
Parity Workflow Understand parity-first development