From empty laptop to a running, tested skeleton. Everything here uses synthetic data only — no BAA is needed until real PHI flows, which is months away. Target for the very first working session: make up && make seed && make test works and CI is green.
| Item | Notes |
|---|---|
GitHub org + private repo travi-platform | Branch protection on main; PRs only; CI required. Repo name ≠ the public hub. |
| AWS account (dev) | Start with ONE dev account; add stage/prod accounts when Terraform is stable (the three-account layout is ADR-005's end state, not day one's). |
| AWS Bedrock model access | Request Claude model access in the dev region on day 1 — approval can take days. Pin the model ID in config; temperature 0. |
| Twilio trial + Verify | Trial account for dev (verified numbers only). SES sandbox for email. BAAs come later with production. |
| Local: Docker, Python 3.12, uv, Node 20, Terraform | uv for env + lockfile; Node for the PWA workspace; direnv is nice-to-have. |
| Sentry (free tier) | Wire the PHI-scrub config from the start even on synthetic data — it must be boring by pilot. |
travi-platform/
├── app/
│ ├── main.py # FastAPI factory; mounts every module router
│ ├── core/ # settings, db, ehr_adapter interface, llm_gateway,
│ │ # outbox drainer, config client (10 s cache)
│ └── modules/
│ ├── identity/ # api.py (router) + service.py (facade) + internal/
│ ├── consent/ ingestion/ compiler/ episode/ medication/
│ ├── scheduling/ notification/ brief/ agents/ admin/
│ ├── analytics/
│ └── audit/ # own schema; INSERT-only role; emit() facade
├── pwa/ # React PWA (patient/caregiver/clinician surfaces)
├── fakeehr/ # the fixture EHR service (see below)
├── infra/ # Terraform (dev first)
├── tests/ # unit / contract / property / integration / e2e
│ └── golden/ # GS-01…GS-12 scenario harnesses + fixtures
├── corpus/ # synthetic discharge docs + labels (never PHI)
├── importlinter.toml # module-boundary contracts — CI fails deep imports
├── docker-compose.yml Makefile pyproject.toml
Two structural rules CI enforces from day 1: cross-module calls go through service.py facades only (import-linter contract per module), and nothing outside app/modules/episode imports the task-state writers — the single-writer rule as a build failure, not a convention.
services:
db: postgres:16 # + a second logical db for the audit schema role split
redis: redis:7 # celery broker + config/kill-switch cache
s3: localstack # documents + brief PDFs + (later) audit archive
mail: mailpit # catches all SES email locally (web UI :8025)
fakeehr: ./fakeehr # the hospital, in a box (below)
api: ./app (reload) # FastAPI on :8000
worker: ./app celery # default + notifications queues
pwa: ./pwa (vite dev) # :5173, proxied to api
SMS in dev: a console/mailpit-backed fake Twilio client behind the same interface as the real one — activation links print to the compose log and land in mailpit; flipping to real Twilio is config, not code.
A tiny FastAPI service implementing the same ehr_adapter surface the Epic adapter will: FHIR-ish Patient, Encounter, MedicationRequest, DocumentReference endpoints serving the synthetic corpus PDFs, plus two control endpoints that make demos and tests deterministic: POST /control/discharge?persona=GS-01 (emits the ADT A03 + documents for a persona) and POST /control/admit (mid-episode readmission for GS-08). Build it before any real module — every phase of the Build Plan runs against it, and the delivery plan's biggest schedule risk (Epic paperwork) stops blocking development entirely.
Seed data and test fixtures are the same twelve people, named for the scenario they exercise (testing §17.3): GS-01 "Dorothy" clean discharge (3 new meds, 1 stopped, 2 follow-ups) · GS-02 "Frank" amended summary supersedes · GS-03 "Rosa" AVS-vs-order dose conflict · GS-04 "Earl" cannot_do(cost) on a critical med · GS-05 "Mae" pharmacy phone confirmation · GS-06 "Walter" stale brief · GS-07 "Gloria" caregiver invite/revoke · GS-08 "Hank" readmitted day 9 · GS-09 "Vera" silent 48 h · GS-10 "Sam" post-visit plan update · GS-11 "Nina" red-flag safety path · GS-12 "Otis" prompt-injection document (a discharge PDF that tries to jailbreak the extractor — Gate 5's span proof is the defense on trial). make seed loads all twelve; make gs S=03 replays one end-to-end.
make up # compose up + migrate + healthcheck
make seed # load the 12 personas into FakeEHR + db
make gs S=04 # run one golden scenario end-to-end
make test # unit + property + contract (the PR gate)
make bench # extraction benchmark vs corpus/ labels (field-level P/R table)
make compress # day-compression: run a full 30-day episode at 1 day/minute
make fmt lint # ruff + import-linter + tsc for the PWA
make compress matters more than it looks: episode timers (48 h silence, T−24 h brief, day-30 close) must read a clock service injected everywhere — never datetime.now() inline — so tests and demos can run a month in minutes. Retrofitting an injectable clock later is misery; do it on day one.
| Job | Gate |
|---|---|
| lint: ruff + import-linter + tsc --noEmit | PR |
| unit + property (Hypothesis state machines) — coverage ≥85% on modules/ | PR |
| contract: schemathesis vs generated OpenAPI + authz matrix probes | PR |
| integration: compose Postgres/Redis/FakeEHR; outbox ≤60 s suppression sim | PR |
| golden: GS suite; extraction bench vs corpus labels | nightly + release |
| e2e: Playwright activation/Today/med/brief + axe WCAG 2.2 AA scan | release |
.env for local only, never committed; AWS Secrets Manager once deployed. Non-negotiables that live in config from day 1: BEDROCK_MODEL_ID (pinned, never "latest"), LLM_TEMPERATURE=0, BRIEF_LEAD_HOURS=24, quiet-hour defaults, kill-switch cache TTL 10 s, TIME_SOURCE=real|compressed. Rule for the repo: synthetic data may be committed (corpus/), credentials never, and there is no such thing as "temporary" real patient data in dev.
A concrete on-ramp that leaves CI green after each merge: PR-1 repo scaffold + compose + Makefile + CI skeleton (a failing-then-fixed import-linter test proves the boundary gate). PR-2 core: settings, db/session, clock service, migration baseline. PR-3 audit module complete + its property tests (everything after this emits). PR-4 FakeEHR with GS-01 persona + the ehr_adapter interface + contract test. PR-5 episode tables + the state-machine skeleton with the five-element transition contract and its first Hypothesis suite. From there, the Build Plan phases take over — and each module you open starts by pasting its module brief + the overview's hard rules into your coding agent's context, exactly as the overview prescribes.