Git & Commit Guidelines
TimeTiles uses conventional commits. The executable rules live in commitlint.config.mjs; .husky/commit-msg runs them for each commit. Check that configuration when a rule changes instead of copying an older example.
Setup
Run make setup from the repository root to configure the development environment, Git hooks, and commit template.
Message Format
<type>(<scope>): <subject>
<optional body>- Use a lowercase type and scope.
- Keep the complete first line within 72 characters.
- Give the subject at least 10 characters, use the imperative, and omit the final period.
- Describe the concrete change. The validator rejects subjects containing
stuff,things,updates,changes, orfixes. - Separate the optional body and footers with blank lines; wrap body lines at 100 characters.
- Explain why the change is needed when the title does not make that clear.
The type describes what changed; the scope describes where.
Types
| Type | Use |
|---|---|
feat | New user functionality |
fix | Bug fix |
refactor | Restructuring without changing behavior |
perf | Performance improvement |
test | Tests and test corrections |
docs | Documentation |
format / style | Formatting and code style |
build | Build system or dependencies |
ci | CI workflows |
chore | Other maintenance |
security | Security fix or improvement |
revert | Revert an earlier commit |
Scopes
Use ingest for the import pipeline. import is not an accepted commit scope; “import” remains the user-facing term.
| Area | Accepted scopes |
|---|---|
| Apps and packages | web, docs, ui, assets, config, scraper |
| Features | explore, ingest, geocoding, events, schema, deploy, quota, access, cache, webhooks, admin, auth, media, charts, map |
| Technical areas | db, api, jobs, deps, seed, test, e2e |
| Infrastructure | ci, build, infra |
The scraper scope covers TimeScrape and the scraper SDKs; the runner directory is apps/timescrape/. Use deps for dependency updates and ci or build for workflow/build configuration, rather than config.
The SCOPES array in commitlint.config.mjs is authoritative. A scope is optional, but usually helpful for fix, feat, and refactor. Omit redundant combinations such as ci(ci), build(build), and test(test), which the validator rejects. docs(docs) is accepted.
Examples
feat(ingest): add support for JSON API sources
fix(geocoding): handle addresses with special characters
refactor(events): centralize clustering query defaults
test(ingest): cover empty CSV uploads
docs: clarify database setup commands
ci: cache browser downloads
build(deps): upgrade PostgreSQL clientFor a change that needs context:
refactor(ingest): remove obsolete interpretation helpers
- Remove the unused legacy wrapper and identity conversion.
- Assert expected rows directly so shared selection code cannot mask a regression.Use a BREAKING CHANGE: footer when compatibility changes, and reference relevant issues when useful. Do not add generated-with-Claude text or Claude co-author trailers; the repository validator rejects them.
Commit Workflow
- Inspect
git statusandgit diff. Stage only files belonging to the change. - Keep unrelated changes in separate commits.
- Run the relevant checks and tests. See Development Setup and Testing Guidelines.
- Review
git diff --cachedbefore committing. - Read hook failures, correct the cause, and retry normally.
The pre-commit hook checks whole-tree formatting and runs lint/typecheck for staged web, docs, and UI changes. It does not replace relevant tests. Repair reported formatting with pnpm exec oxfmt <file> to avoid unrelated changes. pnpm format and make format run only the formatter. Avoid broad eslint --fix runs: they can remove suppressions for oxlint-owned rules.
To check a proposed message with the same validator as the hook, from the repository root:
echo 'refactor(ingest): remove obsolete interpretation helpers' | pnpm exec commitlintValidation includes staged-file context. If no files are staged, the configuration falls back to the latest commit’s changed files.