Development
This page covers how to set up a local development environment and contribute to XTM One.
Prerequisites
- Python 3.14+
- Node.js 20+ with Yarn
- Docker and Docker Compose (for dependencies)
- Git
Local setup
# Clone the repository
git clone https://github.com/XTM-One-Platform/xtm-one.git && cd xtm-one
# Copy environment file
cp .env.sample .env
# Start dependencies (PostgreSQL + pgvector, Redis, MinIO)
docker compose -f docker-compose.dev.yml up -d
# Run the full stack (backend + worker + frontend)
./dev.sh # Linux/macOS
.\dev.ps1 # Windows
The dev script installs dependencies, runs migrations, and starts all services with hot reload.
Project structure
xtm-one/
├── backend/ → Python / FastAPI (see backend/AGENTS.md)
├── frontend/ → React 19 / TypeScript (see frontend/AGENTS.md)
├── docs/ → Product documentation (MkDocs)
├── apps/ → Platform app packages
└── docker-compose.yml → Production stack
Running checks
# Backend lint
cd backend && ruff check app/
# Frontend lint and type check
cd frontend && yarn check
# Backend tests
cd backend && SECRET_KEY=test python -m pytest -m "not integration" -q
Migrations
Schema changes must use Alembic migrations. Never use inline SQL.
The platform auto-migrates on startup — no manual alembic upgrade needed in production.
One head, always
The migration history must have exactly one head. Two pull requests that each add a
migration chained off the same parent fork the history the moment both land — and each one
is green on its own, because pull request CI validates the branch against main as it was
when that run started. This has happened twice on main, and both times it surfaced days
later as an unrelated pull request going red.
Three guards catch it:
| Guard | Where | Catches |
|---|---|---|
tests/test_migration_integrity.py |
Migrations (Alembic) job, every pull request | A fork you created yourself |
| Post-merge integrity check | Migrations (Alembic) job, pull requests targeting main |
A fork created by another migration landing on main after your branch was cut |
CI on push to main |
Whole CI workflow | A fork that landed anyway — flagged on the merge commit that created it |
If the post-merge check fails, main has moved under you:
git fetch origin main && git rebase origin/main
# re-point your earliest migration's down_revision to main's new head
cd backend && python -m pytest tests/test_migration_integrity.py
When a migration lands on main, every open pull request that touches
backend/alembic/versions/ also gets an automatic comment asking for that rebase.
Pull requests stacked on an integration branch
CI runs in full whatever branch you target. The post-merge check is the exception: it merges
your branch into main, which only says something when that merge is what your pull request
proposes. A fork you introduced is still caught by the first guard above. Rebase the
integration branch itself onto main to clear what the post-merge check would have flagged.
Repository settings
The guards above detect a fork; they cannot prevent two green pull requests from being
merged seconds apart. That needs a repository setting: either a merge queue on main,
or required status checks with "require branches to be up to date". The merge_group
triggers in .github/workflows/ci.yml and .github/workflows/e2e.yml are the prerequisite
for the merge queue — enabling the queue itself is done in the repository settings, and
every required check must run on merge_group events or the queue never drains.
Commit conventions
All commits follow Conventional Commits:
Types: feat, fix, chore, docs, style, refactor, perf, test, build, ci, revert.
Next step
For product documentation guidelines, see docs/AGENTS.md. For backend and frontend specifics, see backend/AGENTS.md and frontend/AGENTS.md.