just bootstrapjust dev
API: http://127.0.0.1:8000
Client: http://127.0.0.1:8080
Auth for local testing: set request header x-user-id to one of:
admin@example.comreviewer@example.comviewer@example.com
Client keyboard shortcuts:
Left/Right: previous/next itemP: PASS decisionF: FAIL decisionR: force sync
Client state tools:
Export Local State: downloads local browser state JSONImport Local State: restores exported JSON (schema + project validated)Crash Replay Test: simulates restart/replay and verifies pending queue flush
Implemented:
- FastAPI reference backend with core Phase-1 endpoints:
- projects/config/items/item/url
- events ingest + decisions resume
- exports create/list/get/cancel
- SQLite schema managed by Alembic (
alembic/+0001_initial_schema). - Local-first browser client with:
- IndexedDB queue/state (
pending_events,local_decisions,last_position,sync_state) - optimistic decisions + sync manager/backoff
- keyboard navigation/actions
- local export/import of browser state
- crash-replay harness
- IndexedDB queue/state (
- Django adapter (
django_app) with models, permissions, urls, and API views including export endpoints. - Automated tests:
- FastAPI API tests
- live HTTP contract tests (auto-skip when sockets are unavailable)
- browser workflow tests (auto-skip when Playwright/browser/sockets unavailable)
- Django adapter tests
- migration smoke tests
Current test baseline in this environment:
20 passed, 7 skipped
Not complete yet:
- Full spec parity across every endpoint/edge case in
docs/spec.md(remaining hardening and breadth work). - Full production-grade export artifact pipeline for Django adapter (current implementation is functional baseline).
just bootstrapjust devjust testjust test-api(core API tests + live HTTP contract tests with explicit skip reasons)just test-client(browser workflow tests; skips with reason if Playwright/browser/socket unavailable)just lintjust fmtjust checkjust db-upgradejust db-resetjust db-migrate <name>(create Alembic revision)just seedjust export-smoke
Playwright setup (for just test-client):
uv add --dev playwrightuv run playwright install chromium
- Bootstrap and run
just bootstrapjust dev- Open
http://127.0.0.1:8080
- UI smoke test
- Use
reviewer@example.com, clickLoad. - Press
P/Fto create decisions. - Navigate with
Left/Right. - Click
Export Local State, then re-import withImport Local State. - Click
Crash Replay Testand confirm success in the log.
- Offline replay test (browser)
- In DevTools, set Network to
Offline. - Press
P: expectSYNC_ERRORand queued count increase. - Return online, press
R: expectSYNC_OKand queue returns to0.
- API smoke test (terminal)
PROJECT_ID=$(curl -s -H 'x-user-id: reviewer@example.com' http://127.0.0.1:8000/api/v1/projects | jq -r '.projects[0].project_id')
ITEM_ID=$(curl -s -H 'x-user-id: reviewer@example.com' "http://127.0.0.1:8000/api/v1/projects/$PROJECT_ID/items?limit=1" | jq -r '.items[0].item_id')
curl -s -H 'x-user-id: reviewer@example.com' -H 'content-type: application/json' \
-d "{\"client_id\":\"$(uuidgen)\",\"session_id\":\"$(uuidgen)\",\"events\":[{\"event_id\":\"$(uuidgen)\",\"item_id\":\"$ITEM_ID\",\"decision_id\":\"pass\",\"note\":\"manual\",\"ts_client\":$(date +%s%3N)}]}" \
"http://127.0.0.1:8000/api/v1/projects/$PROJECT_ID/events" | jq- Export API smoke test (terminal)
EXPORT_ID=$(curl -s -H 'x-user-id: reviewer@example.com' -H 'content-type: application/json' \
-d '{"mode":"labels_only","label_policy":"latest_per_user","format":"jsonl","filters":{},"include_fields":["item_id","external_id","decision_id","note","ts_server"]}' \
"http://127.0.0.1:8000/api/v1/projects/$PROJECT_ID/exports" | jq -r '.export_id')
curl -s -H 'x-user-id: reviewer@example.com' "http://127.0.0.1:8000/api/v1/projects/$PROJECT_ID/exports/$EXPORT_ID" | jq- Metrics check
curl -s http://127.0.0.1:8000/metrics | jq