- docs/DESIGN.md: the written source of truth for the UI. Decided chapters (principles, device adaptation, action placement, text stays selectable, interaction rules, copy, anti-patterns, decision log); controls and surfaces are marked Pending until the owner picks from the lab. - /design-lab (dev only): renders each open decision's variants with the app's real CSS, themes and font scale. Batch A = six control decisions. - devOnlyGuard: a production build fails if anything under src/design-lab reaches the bundle (seen red with an unguarded import). - Size contract: tablet portrait + landscape and up, replacing the 1024x768 floor with its never-built "too small" message.
3.2 KiB
Development
How to run Siftlode locally and pass the gate. Conventions for the backend code itself live in
backend/app/README.md.
Node
One major, everywhere. .nvmrc pins it; the CI container, the Dockerfile's frontend build stage,
frontend/package.json's engines and @types/node must all match it — and so must the machine you
run the gate on. frontend/src/lib/nodeEngine.contract.test.ts fails the gate the moment any of them
drift apart, including when yours is the odd one out. nvm use in the repo root picks it up.
This is a contract, not a preference: tsc and vitest read the engine's own Intl and web APIs, and
those change between majors — the same currency format renders $160M on one and $160.0M on the
next. A gate that is green on a major you do not ship is green about nothing.
Run it locally
The whole stack (Postgres + API + worker) comes up from the localdev compose file:
docker compose -f docker-compose.localdev.yml up --build
Browse at http://localhost:8080 (Google login works — the OAuth redirect is
http://localhost:8080/auth/callback). The API runs alembic upgrade head on startup, so the schema
is always current.
For a fast frontend edit loop, run the Vite dev server with HMR against that API:
cd frontend && npm install && npm run dev # http://localhost:5173, proxying the API on :8080
The dev server also serves the design lab at http://localhost:5173/design-lab: the open design
decisions and the chosen patterns, rendered with the app's real CSS. It never reaches a production build
(see DESIGN.md).
The gate
A change is done when all of this is green. CI runs the same checks server-side
(.gitea/workflows/check.yml), so "main is always green".
Backend
# lint + format check
python -m ruff check --config backend/ruff.toml backend scripts
python -m ruff format --check --config backend/ruff.toml backend scripts
# tests — pure-logic + the DB-backed integration lane, against an ephemeral Postgres.
# The backend tree is bind-mounted, so it always tests your working files.
docker compose -f backend/docker-compose.test.yml up --build --abort-on-container-exit --exit-code-from test
docker compose -f backend/docker-compose.test.yml down -v
The test compose sets SIFTLODE_TEST_REQUIRE_DB=1, which turns a would-be silent skip (no DB, or no
ffmpeg) into a hard failure — so the gate can't print green while the DB-backed tests were quietly not
run. Tests that need a database take the db fixture (backend/tests/conftest.py); the rest are
pure-logic and run without one.
Frontend (from frontend/)
npm run typecheck # tsc x3 (app / node / e2e)
npm run lint # eslint
npm run format:check # prettier
npm run knip # unused exports/deps
npm run test # vitest
Branching
Trunk-based: branch off main, keep the branch gate-green, and land the whole change back to main
in one reviewed step. Migrations are Alembic revisions under backend/alembic/versions; the API
applies them on startup, and the DB-lane rebuilds the schema via alembic upgrade head, so a broken
migration chain fails the tests.