Getting Started
Get from fresh checkout to a passing verification in under ten minutes.
Prerequisites
| Tool | Version | Notes |
|---|---|---|
| Python | 3.13.x (3.13.0–3.13.6) | .python-version pins it; uv will fetch it |
| uv | ≥0.9 | `curl -LsSf https://astral.sh/uv/install.sh |
| Flutter | 3.41.9 | flutter --version must match; Dart 3.11.5 |
| Node | ≥20 | For this docs site only (node -v shows 24.15 here) |
| POSIX shell | — | runtime.lock uses fcntl; Windows host needs WSL |
Both apps reject non-loopback bind and non-fixture/remote datasource at startup — by design, so a misconfig fails loudly.
1 — Backend (FastAPI mock)
cd kxinspect_backend_python
uv sync --locked
uv run uvicorn app.main:create_app --factory --host 127.0.0.1 --port 8000
# docs at http://127.0.0.1:8000/docs
Smoke:
curl -s http://127.0.0.1:8000/api/v1/health | python -m json.tool
curl -s http://127.0.0.1:8000/api/v1/bookings/BKG-001/hub | python -m json.tool
# complete Accept from a cold start
EPOCH=$(curl -s http://127.0.0.1:8000/api/v1/charges/CHG-001 | python -c 'import json,sys;print(json.load(sys.stdin)["meta"]["stateEpoch"])')
curl -s -X POST http://127.0.0.1:8000/api/v1/charges/CHG-001/accept \
-H 'Content-Type: application/json' \
-H "Idempotency-Key: $(uuidgen)" \
-d "{\"expectedStateEpoch\":\"$EPOCH\",\"expectedVersion\":1}" | python -m json.tool
# replay with same Idempotency-Key -> same committed response, no second event
Dev routes (optional, still token-checked even on loopback):
KX_ENABLE_DEV_ROUTES=true KX_DEV_TOKEN=local-demo \
uv run uvicorn app.main:create_app --factory
curl -s -X POST http://127.0.0.1:8000/api/v1/_dev/reset -H 'X-Dev-Token: local-demo' -i
2 — Frontend (Flutter)
Fixture mode needs no backend — deterministic bundle under assets/fixtures/:
cd kxinspect_frontend_flutter
flutter pub get
flutter gen-l10n
dart run build_runner build --delete-conflicting-outputs
flutter run -d chrome --dart-define=DATA_SOURCE=fixture
# alternative: native
flutter run -d macos --dart-define=DATA_SOURCE=fixture
Remote mode (after backend is up):
flutter run -d chrome \
--dart-define=DATA_SOURCE=remote \
--dart-define=API_BASE_URL=http://127.0.0.1:8000
# also supported: --dart-define=GRACE_PERIOD_DAYS=30 --dart-define=CHAOS_LATENCY_MS=0
Two-journey manual check (fixture mode is enough for review):
- Accept — Hub → Outstanding banner → Charge → Accept → banner turns success + Pay appears → History shows Accepted → reload persists.
- Contest — Charge → Contest → enter ≥10 graphemes (try emoji 🇬🇧 or combining marks) → optionally attach 1–5 photos/PDF/video → Submit → banner becomes warning, History shows reason → reload persists.
3 — Docs site (this site)
cd documentation
npm ci
npm start # http://localhost:3001
npm run build && npm run serve # production preview
No Python/Flutter needed to browse or build the docs.
4 — One-command verification
Backend:
cd kxinspect_backend_python
uv sync --locked
uv run ruff format --check .
uv run ruff check .
uv run mypy app
uv run pytest -q --cov=app --cov-fail-under=85
uv run python scripts/export_openapi.py --check docs/openapi-v1.json
uv run python scripts/verify_fixture_manifest.py
Frontend (from kxinspect_frontend_flutter/):
./tool/test_all.sh
# expands to: dart format --set-exit-if-changed, flutter analyze, flutter test, golden checks
flutter build web --debug # optional artefact proof
Why two suites? The contract tests under
tests/contract/test_contract_v1.pyand the shared vectors (charge_state_vectors.json,deadline_vectors.json) are the cross-repo oracle: one table, two replays, zero divergence.
Configuration cheat-sheet
Backend env (all KX_*, read once at startup)
| Variable | Default | Rule |
|---|---|---|
KX_HOST | 127.0.0.1 | loopback by default |
KX_PORT | 8000 | 1–65535 |
KX_WORKERS | 1 | anything else rejected |
KX_RUNTIME_ROOT | runtime | holds state.json, uploads/, runtime.lock |
KX_ENABLE_DEV_ROUTES | false | /_dev/* not even bound when false |
KX_DEV_TOKEN | "" | required non-empty when dev routes enabled |
KX_CORS_ORIGINS | http://localhost:8080,http://127.0.0.1:8080 | * rejected |
KX_GRACE_PERIOD_DAYS | 30 | seeds deadlineAt for dev-raised charges |
KX_DEMO_NOW | unset | anchors SystemClock to a fixed instant |
Frontend --dart-define (validated at bootstrap; bad values render a diagnostic screen)
| Key | Default | Example |
|---|---|---|
DATA_SOURCE | fixture | fixture | remote |
API_BASE_URL | — | http://127.0.0.1:8000 (required when remote) |
GRACE_PERIOD_DAYS | 30 | 30 |
CHAOS_LATENCY_MS | 0 | 0–5000 |
CHAOS_ERROR_RATE | 0 | 0.0–1.0 |
ENABLE_ANALYTICS_LOG | false | true |
ENABLE_DEV_MENU | false | true |
Next steps
- Understand the workspace layout: Project Structure.
- Deep-dive backend: Backend overview.
- Deep-dive frontend: Frontend overview.
- Reference the frozen wire contract: Shared contract.