Contributing¶
Development Workflow¶
- Fork the repository
- Create a feature branch from
main - Make your changes
- Run tests and linting locally
- Submit a pull request
Licensing of Contributions¶
From 1.6.1 onward, Milestone Planner is licensed under the Functional Source License 1.1 with MIT Future License (FSL-1.1-MIT). Any contribution that lands in the project is accepted under that same license — which means your work is guaranteed to become MIT two years after the release it ships in. Releases up to and including 1.6.0 remain under the MIT license.
Code Standards¶
Backend (Python)¶
- Formatter: Ruff (
ruff format app/) - Linter: Ruff (
ruff check app/) - Type checker: mypy (
mypy app/) - Tests: pytest (
pytest --cov=app)
Frontend (TypeScript/React)¶
- Linter: ESLint (
npm run lint) - Tests: Vitest (
npm test) - Build check:
npm run build(includes TypeScript compilation) - UI guidelines:
UI_GUIDELINES.mdin the repository root — design tokens, shared components and the layout patterns every new screen follows
Pull Request Checklist¶
- [ ] Backend tests pass (
pytest) - [ ] Frontend tests pass (
npm test) - [ ] New features and bug fixes come with automated tests (a bug fix with a regression test that fails without it)
- [ ] The lines you add or change are ≥ 80 % covered (CI runs diff-cover); raise the coverage floors if your PR lifts them
- [ ] Linting passes (Ruff, ESLint)
- [ ] Type checking passes (mypy, TypeScript)
- [ ] Docker build succeeds
- [ ] Database migrations included if schema changed
- [ ]
setup_databases.sqlupdated if schema changed - [ ]
/VERSIONbumped if the PR touches code paths listed in Versioning & Changelog - [ ]
CHANGELOG.mdentry added under a new## [<version>] - YYYY-MM-DDheading matching the bump
Versioning & Changelog¶
This project uses Semantic Versioning and the Keep a Changelog format. The single source of truth for the version is /VERSION (one line, e.g. 1.2.3, no v prefix). The backend reads it at startup in app/__init__.py and exposes it via /health and /api/health.
When you MUST bump¶
Bump /VERSION and add a CHANGELOG.md entry — in the same PR — for any change to:
| Path | Why |
|---|---|
app/** |
Backend code |
frontend/src/**, frontend/package.json, frontend/package-lock.json, frontend/vite.config.ts, frontend/tsconfig*.json, frontend/index.html |
Frontend code/build |
migrations/**, setup_databases.sql |
DB schema |
Dockerfile, docker-compose*.yml |
Runtime / deployment |
scripts/** |
Operational scripts that ship with the app |
requirements*.txt, pyproject.toml |
Backend dependencies |
When you must NOT bump¶
Changes confined to the following paths do not trigger a bump:
README.md,CLAUDE.md,CHANGELOG.mditselfdocs/**,mkdocs.yml,docs/requirements.txt— MkDocs siteLICENSE,.gitignore,.dockerignore,.editorconfig.github/**— workflows and repo metadata.devcontainer/**— Codespaces config
How to bump (SemVer)¶
| Bump type | Example | When |
|---|---|---|
| PATCH | 1.0.0 → 1.0.1 |
Bug fix, no API or UX change |
| MINOR | 1.0.0 → 1.1.0 |
New feature, backwards-compatible |
| MAJOR | 1.0.0 → 2.0.0 |
Breaking change to the API, non-additive DB schema, env-var rename, etc. |
One bump per PR, not per commit. All commits in a feature branch share one version.
CHANGELOG entry format¶
Add a new ## [<version>] - YYYY-MM-DD heading at the top of CHANGELOG.md, under the preamble. Use only the categories that apply, in this order:
- Added — new features
- Changed — changes to existing behaviour
- Deprecated — features marked for removal
- Removed — features removed in this release
- Fixed — bug fixes
- Security — security-relevant fixes
Each entry is a single, human-readable line written from the user's perspective — not implementation detail. Do not use an [Unreleased] section; every change belongs to a concrete numbered release.
CI gate¶
The workflow .github/workflows/version-check.yml runs on every PR:
- It diffs the changed files against the "must bump" path list above.
- If any of those paths changed and
/VERSIONdid not change, the check fails. - If
/VERSIONchanged butCHANGELOG.mddoes not contain a matching## [<new-version>]heading, the check fails.
The check is required to merge — there is no override. If the gate is wrong about your PR, fix the categorisation in the workflow rather than skipping it.
Note
frontend/package.json's version field is a static placeholder kept aligned with /VERSION at major releases. Do not edit it on every bump — only /VERSION is the source of truth, and the published release notes live in CHANGELOG.md (mirrored at Release Notes).
Database Changes¶
When modifying the database schema:
- Update the SQLAlchemy model in
app/models/ - Create a migration SQL file in
migrations/ - Update
setup_databases.sqlto reflect the new schema - Test the migration:
python migrations/run_migration.py <name>
Project Conventions¶
- Backend routes follow REST conventions
- Frontend uses Zustand for state management (not Redux)
- API clients are organized by resource in
frontend/src/api/endpoints/ - Use
MasterBasefor master DB models,Basefor tenant DB models - Session-based authentication (no JWT tokens)