Rootly is a plant care management app: FastAPI (Python) backend + React 19 (TypeScript) frontend, containerised with Docker Compose. PostgreSQL for persistence, Redis for replay-attack prevention.
Branching: Claude Code sessions each develop on their own claude/ branch. The active branch for the current session is set in the session configuration — commit and push there when work is complete.
Run the linters before making any changes, and again before committing:
# TypeScript + ESLint
cd frontend && npm run lint
cd frontend && npx tsc --project tsconfig.app.json
# Python
cd api && flake8 . --max-line-length=120 --exclude=__pycache__,alembic/versionsAny new error introduced by your changes must be fixed before pushing.
See docs/architecture.md for the full system diagram, service topology, and decision log.
| Layer | Path | Notes |
|---|---|---|
| API | api/ |
FastAPI, async SQLAlchemy, Alembic |
| Frontend | frontend/src/ |
React 19, Vite, TypeScript |
| DB models | api/models/ |
SQLAlchemy ORM |
| Schemas | api/schemas/ |
Pydantic v2 |
| Routes | api/routes/ |
auth.py, plants.py, admin.py |
| Auth helpers | api/auth.py |
JWT + cookie logic |
| API client | frontend/src/api/ |
client.ts, auth.ts, plants.ts |
| Screens | frontend/src/screens/ |
mobile/ and desktop/ sub-dirs |
| Components | frontend/src/components/index.tsx |
Shared UI primitives |
| Tokens | frontend/src/tokens.ts |
Design token object T — import from here, NOT components |
| Types | frontend/src/types/plant.ts |
Plant, PlantStatus, STATUS_META |
See docs/brand.md for the complete brand guide.
- Never import
Tfrom./componentsor../../components. Import from./tokensor../../tokens. components/index.tsxmust export only React components (react-refresh rule).- Breakpoint:
700px. Below = mobile tab layout. Above = sidebar + panel layout. - All inline styles use values from
T. No hard-coded hex colours unless they are status-specific (defined inSTATUS_META). - Font families:
T.sansfor body,T.displayfor headings,T.monofor numbers/code. - Border radius pattern:
999for pills/buttons,18–20for cards. - Transitions:
all .18s cubic-bezier(.22,.61,.36,1).
See docs/auth.md for the full auth flow.
- Auth is cookie-based (HTTP-only). The backend sets
access_tokenandrefresh_tokencookies. Never read/write auth cookies from JavaScript — the browser handles them automatically. get_current_userFastAPI dependency validates the cookie and returns theUserORM object. Use it on every protected route.- Role check: use
require_role("admin")dependency, not inlineif user.role != "admin". - Never return
password_hashin any response schema. - All plant routes must check
Plant.user_id == current_user.id— no cross-user access.
See docs/database.md for schema details and migration guidelines.
- All migrations live in
api/alembic/versions/. Never edit a committed migration — create a new one. - Create migrations with
make migration "describe the change". Review the generated file before applying. - Apply with
make migrate. Rollback one step withmake downgrade. - Always use
await db.commit()after writes; alwaysawait db.refresh(obj)before returning a newly created object. emailuses PostgreSQLCITEXT— comparisons are case-insensitive at the DB level.- Enum values are stored in the DB as lowercase strings matching the Python enum names.
When building new MPM (Modular Plant Management) modules, read docs/architecture.md#mpm-modules first. Each module follows the same four-layer pattern: DB model → Pydantic schemas → route file → frontend API client + screens.
Checklist for a new module:
-
api/models/<name>.py— SQLAlchemy model withuser_idFK -
api/schemas/<name>.py—Create,Update,Responseschemas -
api/routes/<name>.py— router with ownership checks - Register router in
api/main.py -
make migration "add <name> table"— create and review migration -
frontend/src/api/<name>.ts— typed API client functions -
frontend/src/types/<name>.ts— TypeScript types mirroring the schema -
frontend/src/screens/mobile/<Name>Screen.tsx— mobile view -
frontend/src/screens/desktop/<Name>Desktop.tsx— desktop view - Wire into
App.tsxtab/sidebar navigation
make up # Start all containers
make down # Stop all containers
make build-prod # Rebuild + start
make logs # Tail API logs
make shell # Bash into API container
make psql # PostgreSQL CLI
make migrate # Apply pending migrations
make migration "x" # Generate new migration
make dev # Frontend dev server (port 5173)
make nuke # Destroy everything (asks confirmation)- Short imperative subject line (≤72 chars)
- Body: explain the why, not the what
- Include session URL on the last line