One phase per PR. Tests green before the next. The agentic loop works this list:
/loop implement the next unchecked item — tests first, make them pass, run the suite, tick the item, commit.
- Monorepo layout,
docker-compose.yml,.env.example - FastAPI
/health(DB check) + Vite React app that calls it - Worker skeleton (APScheduler, no jobs)
- pre-commit,
CLAUDE.md,.claude/hooks + agents + skills - CI skeleton (lint + test on PR)
-
git init, first commit - Create the GitHub repo, push, branch protection on
main— deferred (infra) - Confirm
docker compose upis green on the dev machine — deferred; Docker blocked on this machine, seedocs/LOCAL_DEV.md. Tests run on SQLite locally and the CI Postgres service.
-
usersmodel (+refresh_tokens) + Alembic migration; Argon2 hashing inapp/security.py -
POST /auth/login,POST /auth/refresh(rotating + reuse detection),POST /auth/logout,GET /auth/me -
Roleenum +require_roles(...)dependency inapp/deps.py -
conftest.pyfixtures:client,db, per-role auth headers - Tests: login ok/bad, expired token, each role vs a protected probe route
-
loan_files,loan_documentsmodels + migration -
POST /loan-files(OPS_MAKER), document upload, size/type limits -
POST /loan-files/{id}/submit— atomically create 4 assigned review tasks (round-robin within UW), writetask_events, idempotent -
GET /loan-files— pagination + filtering + per-file task counts (no N+1) -
GET /loan-files/{id}— file + tasks + activity feed - Tests incl. idempotent submit and the N+1 guard
-
review_tasksmodel withversion;task_eventsmodel -
app/services/state_machine.py—transition()guard, single source of truth -
POST /tasks/{id}/maker-submit(findings → PENDING_CHECKER, needsversion) -
POST /tasks/{id}/checker-decide(approve → COMPLETED / reject → CHANGES_REQUESTED) - Enforce
checker_id != maker_id; last task completing → file FUND_READY_TO_RELEASE -
GET /tasks?assignee=me&as=maker|checker&status= - Tests: every transition, stale-version conflict, self-check rejection
- Worker job
purge_expired_files()— advisory-locked, idempotent - Archive summary row (no PII) + delete documents +
task_eventsentry -
POST /admin/run-housekeeping(ADMIN) for demos;PURGE_AFTER_DAYSconfig - Tests with
freezegun; run job twice → one archive row
- Router, auth context,
RequireAuth/RequireRole -
lib/http.tsAxios instance + refresh interceptor + request queue - TanStack Query setup; react-hook-form + zod
- Screens: Login (with demo-login buttons), Submission wizard, My Tasks, Loan File detail (4-check panel), Review drawer
- Optimistic task actions + rollback; toasts; error boundary
- RTL + MSW tests for each screen
-
GET /dashboard/teamandGET /dashboard/loan-filesaggregation endpoints - SQL: group by,
date_trunc, filtered aggregates, turnaround - Recharts: status funnel, per-member table + sparklines, aging histogram
- Filters by team + date range; tune with
EXPLAIN ANALYZE
- Playwright: full happy path + a reject loop
- Auth rate limiting; JSON logging + request IDs
-
app/seed_demo.py— deterministic demo board (see seed-demo-data skill) - Error tracking (Sentry/OTel); index audit;
SECURITY.md -
/security-reviewpass
- Multi-stage Dockerfiles (api, web, worker) — prod stages
-
infra/deploy config (fly.tomlper service, or VPS compose + Caddy) -
.github/workflows/deploy.yml— build, push GHCR, deploy, migrate, smoke - Secrets wired; rolling deploy; rollback tested and documented
- README badges; live URL
- README hero GIF, live link, demo creds, architecture diagram
-
docs/ARCHITECTURE.mdfinalised; 3–5 ADRs -
docs/ai-workflow.mdwith real hook/subagent/skill/loop examples -
DEMO.md3-minute script; 2-minute Loom; pin the repo