Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
# WorkWell Measure Studio environment template
# Copy to environment-specific secret stores; do not commit real values.

# Fly backend runtime
# Backend runtime (MIE container)
DATABASE_URL=
DATABASE_URL_DIRECT=
OPENAI_API_KEY=
SPRING_PROFILES_ACTIVE=prod
WORKWELL_AUTH_ENABLED=true
WORKWELL_AUTH_JWT_SECRET=replace-with-a-strong-random-secret-at-least-32-characters
# Frontend (Vercel) and backend (Fly) are different sites, so the refresh cookie
# must be SameSite=None + Secure or the browser never sends it on the cross-site
# The frontend and backend run on split origins (twh.os.mieweb.org / twh-api.os.mieweb.org),
# so the refresh cookie must be SameSite=None + Secure or the browser never sends it on the
# /api/auth/refresh fetch (silent refresh fails, users get logged out on reload).
# Production startup fails fast if these are not set to None/true.
WORKWELL_AUTH_COOKIE_SAME_SITE=None
WORKWELL_AUTH_COOKIE_SECURE=true
WORKWELL_CORS_ALLOWED_ORIGINS=https://frontend-seven-eta-24.vercel.app
WORKWELL_CORS_ALLOWED_ORIGINS=https://twh.os.mieweb.org
WORKWELL_DEMO_ENABLED=false
WORKWELL_DEMO_ALLOW_PUBLIC_DEMO=false
# Optional safety override if a deployment does not use Spring profiles:
# WORKWELL_ENVIRONMENT=production

# Vercel frontend
# Frontend (MIE container)
NEXT_PUBLIC_API_BASE_URL=
NEXT_PUBLIC_APP_NAME=WorkWell Measure Studio
NEXT_PUBLIC_DEMO_MODE=false
Expand Down
34 changes: 17 additions & 17 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,39 @@ Operating manual for any AI coding agent (Claude Code, Codex, Cursor, etc.) work

## What this project is
- Single-developer Spring Boot + Next.js monorepo
- Goal: implement the gaps and improvements identified in `docs/sprints/` to showcase and overdeliver on the project's original vision
- Build phase: sprint-based feature implementation — see `docs/sprints/README.md` for the ordered work queue
- Goal: keep the merged WorkWell Measure Studio MVP stable, showcaseable, and easy to review
- Phase (as of 2026-06-08): all planned sprints (0–7) are merged to `main`; active work is post-merge closeout and polish. `docs/sprints/` is historical context now, not an active queue.

## Read before any task
1. `docs/sprints/README.md` — sprint index and critical path. This is your active work queue.
2. The specific sprint file for the issue you're working on (e.g., `docs/sprints/SPRINT_00_critical_demo_fixes.md`)
3. `docs/JOURNAL.md` — latest state of the project
4. `README.md` — public project overview and API surface
1. `docs/JOURNAL.md` — latest state of the project (newest entry on top). This is the current source of truth.
2. `CLAUDE.md` — current focus, hard rules, and build/verify commands
3. `README.md` — public project overview and API surface
4. `docs/sprints/README.md` — historical sprint index (all sprints merged; reference only)

`docs/archive/SPIKE_PLAN.md` and `docs/archive/PROJECT_PLAN_v1.md` are historical only — do not act on them.

## Sprint execution protocol
- Work **one sprint at a time**, in the order defined in `docs/sprints/README.md`
- Within a sprint, work **one issue at a time** from top to bottom
- Every issue has an **Acceptance Criteria** checklist — every box must pass before the issue is done
- Create a feature branch per issue: `fix/sprint-0-<slug>` or `feat/sprint-1-<slug>`
- Open a PR for review after each issue — do not batch multiple issues into one PR unless they are tightly coupled (e.g., a migration + the service that uses it)
- **Stop and ask** before starting the next sprint — Taleef reviews before proceeding
## Feature work protocol
- Planned sprint work (0–7) is complete; new work is post-merge polish or follow-up features
- Work **one task at a time**; keep changes small and focused
- Where a sprint file defined acceptance criteria, every box must still pass before that work is considered done
- Create a feature branch per task: `fix/<slug>` or `feat/<slug>`
- Open a PR for review per task — do not batch unrelated changes; tightly coupled changes (e.g., a migration + the service that uses it) may share a PR
- **Stop and ask** before starting a new workstream — Taleef reviews before proceeding
- Update `docs/JOURNAL.md` with a dated entry for everything that ships

## Tech stack (immutable without ADR in docs/DECISIONS.md)
- Backend: Java 21, Spring Boot 3.x, Gradle Kotlin DSL, PostgreSQL 16, Flyway
- CQL/FHIR: HAPI FHIR JPA + `org.opencds.cqf.fhir:cqf-fhir-cr` 3.26.0 (see CQF_FHIR_CR_REFERENCE.md)
- Frontend: Next.js 14+ App Router, TypeScript, Tailwind, shadcn/ui, Monaco
- AI: Spring AI (Anthropic), MCP via `io.modelcontextprotocol/java-sdk`
- Infra: Docker Compose local; Fly.io + Vercel + Neon prod; GitHub Actions; pnpm
- Frontend: Next.js 16 App Router + React 19, TypeScript, Tailwind, shadcn/ui, Monaco
- AI: Spring AI (OpenAI starter, `spring-ai-openai-spring-boot-starter`), MCP via `io.modelcontextprotocol/java-sdk`
- Infra: Docker Compose local; MIE Create-a-Container + Neon prod (Fly.io + Vercel preview decommissioned); GitHub Actions; pnpm

## Hard rules
- Avoid new dependencies unless explicitly approved — if a sprint file calls for a dependency, it is pre-approved; anything else requires asking first
- One Spring Boot app, modular packages — no microservices
- Spring Application Events + DB audit log — no Kafka or external streaming
- Auth: JWT refresh token flow (HttpOnly cookie, token rotation, `/api/auth/refresh`) is approved and specified in Sprint 4. User accounts remain hardcoded — no SSO, no real user directory.
- Email: `WORKWELL_EMAIL_PROVIDER=simulated` is the mandatory default on the demo stack. Do not set `SENDGRID_API_KEY` in any demo environment config.
- Email: `WORKWELL_EMAIL_PROVIDER=simulated` is the mandatory default on the demo stack. Do not set `WORKWELL_EMAIL_SENDGRID_API_KEY` in any demo environment config.
- AI never decides compliance (docs/AI_GUARDRAILS.md). CQL engine is sole source of truth.
- Every state change writes `audit_event` — no exceptions
- No silent scope changes — if something in a sprint file doesn't match the codebase, stop and report before proceeding
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,20 @@ and this project follows [Semantic Versioning](https://semver.org/) intent for r

## [Unreleased]

### Added
- Scoped-run parity (Sprint 8): `SITE` and `EMPLOYEE` manual runs and same-scope reruns now route through the async run-job path, matching `ALL_PROGRAMS`/`MEASURE`; the `/runs` UI exposes the new scopes.

### Changed
- CI backend test suite ~3.8× faster (44m → 11m30s) via 8-way test sharding plus a per-class population-run fix (PR #57).
- Deployment consolidated onto MIE Create-a-Container; the Vercel + Fly.io public-preview stack is decommissioned. Living docs (README, DEPLOY, ARCHITECTURE, CLAUDE, AGENTS, sprint index) reconciled to the single live MIE TWH stack.
- Repository standards polish: badges, contribution/security/support docs, community templates, and metadata alignment.

### Fixed
- MIE Container Manager deploy migrated to the v1 API contract (`/api/v1` base, `{"data": ...}` envelope, `template`/`services` create body, `.data.status` polling) after the manager API changed (PRs #55, #56).

### Docs
- Synced CLAUDE.md, AGENTS.md, README, DEPLOY, and the sprint index to the post-Sprint-7 / Sprint-8 state (measure catalog 60/49, Next.js 16 + React 19, OpenAI Spring AI starter, MIE-only deployment).

## [2026-05-22]

### Added
Expand Down
49 changes: 22 additions & 27 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,21 @@
## Tech stack (immutable without ADR in docs/DECISIONS.md)
- Backend: Java 21 + Spring Boot 3.x + Gradle Kotlin DSL + PostgreSQL 16 + Flyway
- CQL/FHIR: HAPI FHIR JPA + `org.opencds.cqf.fhir:cqf-fhir-cr` 3.26.0 (see CQF_FHIR_CR_REFERENCE.md)
- Frontend: Next.js 14+ App Router + TypeScript + Tailwind + shadcn/ui + Monaco
- AI: Spring AI (Anthropic starter); MCP via `io.modelcontextprotocol/java-sdk`
- Infra: Docker Compose locally; Fly.io + Vercel + Neon for deploy; GitHub Actions CI; pnpm
- Frontend: Next.js 16 App Router + React 19 + TypeScript + Tailwind + shadcn/ui + Monaco
- AI: Spring AI (OpenAI starter, `spring-ai-openai-spring-boot-starter`); MCP via `io.modelcontextprotocol/java-sdk`
- Infra: Docker Compose locally; MIE Create-a-Container + Neon for deploy (Fly.io + Vercel public-preview stack decommissioned — MIE TWH is the sole live stack); GitHub Actions CI; pnpm

## Build & verify
- Backend: `cd backend; .\gradlew.bat test` — 239 tests; CI shards 8-way. **Never run two backend `gradlew test` concurrently** (shared temp binary-results race).
- Frontend: `cd frontend; npm run lint; npm run build`
- Run the app: backend `.\gradlew.bat bootRun`; frontend `npm run dev`

## Hard rules
- Avoid new dependencies unless they are explicitly approved and documented
- One Spring Boot app, modular packages — no microservices
- Spring Application Events + DB audit log — no Kafka or external streaming
- Auth: user accounts remain hardcoded (no SSO, no real user directory). JWT refresh token flow (HttpOnly cookie, token rotation, `/api/auth/refresh`) is approved and implemented in Sprint 4 — this replaces the prior "stub auth only" constraint.
- Email: `WORKWELL_EMAIL_PROVIDER=simulated` is the default and must remain so on the demo stack. SendGrid wiring exists in the code (Sprint 6) but must not be activated unless `SENDGRID_API_KEY` is explicitly set in a non-demo environment.
- Email: `WORKWELL_EMAIL_PROVIDER=simulated` is the default and must remain so on the demo stack. SendGrid wiring exists in the code (Sprint 6) but must not be activated unless `WORKWELL_EMAIL_SENDGRID_API_KEY` is explicitly set (with `WORKWELL_EMAIL_PROVIDER=sendgrid`) in a non-demo environment.
- AI never decides compliance (see docs/AI_GUARDRAILS.md). CQL engine is sole source of truth.
- Every state change writes `audit_event` — no exceptions
- No silent scope changes. If a stop condition triggers, document fallback in JOURNAL.md.
Expand Down Expand Up @@ -71,40 +76,30 @@

## Other docs to consult on demand
- @docs/archive/SPIKE_PLAN.md — archived sprint context
- @docs/DEPLOY.md — Vercel + Fly + Neon setup, env vars, rollback
- @docs/MEASURES.md — the 4 demo measures in plain English
- @docs/DEPLOY.md — MIE Create-a-Container + Neon setup, env vars, rollback
- @docs/MEASURES.md — the TWH measure catalog (60 measures) in plain English
- @docs/ARCHITECTURE.md — system architecture diagrams + boundaries
- @docs/DATA_MODEL.md — schema invariants
- @docs/AI_GUARDRAILS.md — AI usage policy
- @docs/CQF_FHIR_CR_REFERENCE.md — proven library wiring from spike
- @README.md — quickstart

## Current Focus (as of 2026-05-21)

**All planned sprints merged. TWH consolidation complete. Sprint 7 (overdelivery) is next.**
## Current Focus (as of 2026-06-08)

Sprints merged (all into `main`):
- Sprint 0 (bugs) → PR #16
- Sprint 2 (data) → PR #17
- Sprint 1 (pipeline) → PR #18
- Sprint 3 (employee/SLA) → PR #19
- Sprint 4 (security) → PR #20
- Sprint 6 (admin) → PR #21
- Sprint 5 (tests/CI) → PR #22
- eCQM + TWH instance support → PR #46 (merged to main)
**All sprints through Sprint 7 are merged and closed; Sprint 8 scoped-run parity has landed. The stack is in post-merge polish / showcase mode.**

Post-merge work completed (all on `main`):
- Real-time run progress (spinner, live timer, auto-reload)
- AI integration health check fix (GET /v1/models)
- TWH consolidation: single MIE container, 47 CMS eCQMs seeded in catalog
- Fly.io decommissioned; MIE TWH is sole deployment
History (all on `main`):
- Sprints 0–6 → PRs #16–#22; eCQM + TWH instance support → PR #46
- Sprint 7 overdelivery (AI Draft CQL, AI Test Fixtures, Risk Scoring, MAT Export, Mobile Responsive) → issues #47–#51, closed
- Sprint 8 scoped-run parity: `SITE`/`EMPLOYEE` manual runs + rerun now route through the async run-job path
- CI test suite 3.8x faster via 8-way test sharding (44m → 11m30s) → PR #57
- MIE Container Manager deploy migrated to the v1 API envelope → PRs #55, #56

Current posture:
- **Live URL:** `https://twh.os.mieweb.org` — login: `admin@workwell.dev` / `Workwell123!`
- **Deployment:** MIE Create-a-Container only (`deploy-twh-mieweb.yml`); triggers on every push to `main`
- **Measure catalog:** 58 total — 4 OSHA active (CQL), 3 OSHA catalog, 4 HEDIS wellness active (CQL), 47 CMS eCQM Draft entries
- **Deployment:** MIE Create-a-Container only (`deploy-twh-mieweb.yml`); triggers on every push to `main`. The earlier Fly.io + Vercel public-preview stack is decommissioned; MIE TWH is the sole live stack.
- **Measure catalog:** 60 total — 4 OSHA active (CQL), 3 OSHA catalog, 4 HEDIS wellness active (CQL), 49 CMS eCQM Draft entries
- **Supported run scopes:** `ALL_PROGRAMS`, `MEASURE`, `SITE`, `EMPLOYEE`, `CASE`
- `main` is fully up to date; no open feature branches
- Schema migrations are owned by Taleef — stop and ask before writing any `V0xx__*.sql` file
- Sprint 7 spec is in `docs/sprints/SPRINT_07_overdelivery_features.md` — 5 issues (AI Draft CQL, AI Test Fixtures, Risk Scoring, MAT Export, Mobile Responsive)
- Treat `docs/archive/SPIKE_PLAN.md` as historical context only

12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ WorkWell Measure Studio is a Spring Boot + Next.js monorepo for **Total Worker H

## Status

- Sprint queue through **Sprint 7** is implemented in the repo.
- Sprint 7 issues `#47`-`#51` are completed and closed.
- All planned sprints (**0–7**) are implemented and merged to `main`; Sprint 7 issues `#47`–`#51` are closed.
- Post-merge work continues on `main`: Sprint 8 scoped-run parity (`SITE`/`EMPLOYEE` manual runs + reruns), an 8-way CI test-sharding speedup (~3.8×), and the MIE Container Manager v1 API deploy migration.
- Default branch: `main` only (stale sprint branches cleaned up).

## Production surfaces

- Primary demo frontend: `https://twh.os.mieweb.org`
- Primary demo backend API: `https://twh-api.os.mieweb.org`
- Public preview frontend: `https://workwell-measure-studio.vercel.app`
- Public preview backend API: `https://workwell-measure-studio-api.fly.dev`
- Live frontend: `https://twh.os.mieweb.org`
- Live backend API: `https://twh-api.os.mieweb.org`

> The earlier Vercel + Fly.io public-preview stack (`workwell-measure-studio.vercel.app`, `workwell-measure-studio-api.fly.dev`) is **decommissioned**. MIE TWH is the sole live deployment.

## Technology stack

Expand Down
76 changes: 47 additions & 29 deletions docs/DEMO_RUNBOOK.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,55 @@
# Last verified: 2026-05-07
# Last updated: 2026-06-08 (capture-based; originally verified 2026-05-07 on the legacy stack)

# Demo Runbook (Production)

> **Stack note:** URLs point to the live MIE TWH stack. Run/case IDs are environment-specific and
> change every run, so this runbook captures them at demo time via the API (see "Capture current
> IDs") rather than hardcoding them.

## Production Surfaces
- Frontend: `https://workwell-measure-studio.vercel.app`
- Backend API: `https://workwell-measure-studio-api.fly.dev`
- Frontend: `https://twh.os.mieweb.org`
- Backend API: `https://twh-api.os.mieweb.org`

## Capture current IDs (run at demo time)

## Pinned Production IDs
Run and case IDs are environment-specific and change on every run, so capture them live rather
than relying on pinned values. All `/api/**` calls require a bearer token.

### Measures
- Audiogram: `4ae5d865-3d64-4a17-905d-f1b315a037e2`
- TB Surveillance: `8c9fda6f-b9bb-413a-be4d-8ce4faa72999`
- HAZWOPER Surveillance: `eaa81302-b6f6-4aba-a143-bb72941f9c00`
- Flu Vaccine: `9db33281-0933-4dd6-86e9-e4c6df2b9a94`
```bash
# 1) Mint an access token (admin or case-manager account)
TOKEN=$(curl -fsS -X POST https://twh-api.os.mieweb.org/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"admin@workwell.dev","password":"Workwell123!"}' | jq -r .token)

### Latest run IDs (per measure query, `limit=1`)
- Audiogram latest run: `3866d69a-2519-4051-bad0-98da9ea696bf`
- TB Surveillance latest run: `fba26713-92ff-49e3-84d0-fa8d137881f7`
- HAZWOPER Surveillance latest run: `3866d69a-2519-4051-bad0-98da9ea696bf`
- Flu Vaccine latest run: `3866d69a-2519-4051-bad0-98da9ea696bf`
# 2) Measure IDs (names are stable across reseeds; UUIDs differ per instance)
curl -fsS https://twh-api.os.mieweb.org/api/measures \
-H "Authorization: Bearer $TOKEN" | jq -r '.[] | "\(.name): \(.id)"'

### Pinned Audiogram open case for MCP `explain_outcome`
- Case ID: `32fee6f4-6e69-4675-b44e-5f6392de7dbd`
- Employee: `emp-006` (Omar Siddiq)
- Outcome status: `OVERDUE`
# 3) Latest Audiogram-scoped run ID. /api/runs has no measureId filter — it only
# supports status/scopeType/triggerType/site/from/to/limit — so post-filter the
# returned JSON by measure name. If no measure-scoped Audiogram run exists, drop
# the map(...) and take .[0].id: the latest ALL_PROGRAMS run also covers Audiogram.
MEASURE_ID=<audiogram-measure-id> # from step 2; reused by the case filter below
curl -fsS "https://twh-api.os.mieweb.org/api/runs?limit=50" \
-H "Authorization: Bearer $TOKEN" | jq -r 'map(select(.measureName | test("Audiogram"))) | .[0].id'

# 4) An open Audiogram case ID (for MCP explain_outcome). /api/cases filters by
# measureId (the logical measure UUID), not measureName — reuse $MEASURE_ID.
curl -fsS "https://twh-api.os.mieweb.org/api/cases?status=open&measureId=$MEASURE_ID" \
-H "Authorization: Bearer $TOKEN" | jq -r '.[0].id'
```

Stable seeded reference: employee `emp-006` (Omar Siddiq) carries an Audiogram `OVERDUE` outcome
and is a reliable persona for a deterministic `explain_outcome` demo — external IDs survive
reseeds, the case UUID does not.

## Pre-flight Smoke Check (curl)

```bash
curl -fsS https://workwell-measure-studio-api.fly.dev/actuator/health
curl -fsS https://workwell-measure-studio-api.fly.dev/api/measures
curl -fsS "https://workwell-measure-studio-api.fly.dev/api/runs?measureId=4ae5d865-3d64-4a17-905d-f1b315a037e2&limit=1"
curl -fsS "https://workwell-measure-studio-api.fly.dev/api/cases?status=open&measureName=Audiogram"
curl -fsS https://twh-api.os.mieweb.org/actuator/health
curl -fsS https://twh-api.os.mieweb.org/api/measures -H "Authorization: Bearer $TOKEN"
curl -fsS "https://twh-api.os.mieweb.org/api/runs?limit=50" -H "Authorization: Bearer $TOKEN" | jq 'map(select(.measureName | test("Audiogram")))'
curl -fsS "https://twh-api.os.mieweb.org/api/cases?status=open&measureId=$MEASURE_ID" -H "Authorization: Bearer $TOKEN"
```

Expected:
Expand All @@ -42,18 +60,18 @@ Expected:

## 30-Minute Pre-Demo Checklist
- Verify backend is up (`/actuator/health` is UP).
- Verify frontend opens at `https://workwell-measure-studio.vercel.app/programs`.
- Verify frontend opens at `https://twh.os.mieweb.org/programs`.
- Verify all 4 measures are Active in `GET /api/measures`.
- Verify at least one open Audiogram case exists.
- Verify MCP server is running and can execute:
- `list_measures`
- `get_run_summary`
- `explain_outcome` with case ID `32fee6f4-6e69-4675-b44e-5f6392de7dbd`
- Capture a current run ID and open Audiogram case ID (see "Capture current IDs").
- Verify MCP server is running and can execute `list_measures`, `get_run_summary`, `explain_outcome`.

## Reference MCP Calls for Live Demo

Substitute the IDs captured above:

```text
list_measures
get_run_summary {"runId":"3866d69a-2519-4051-bad0-98da9ea696bf"}
explain_outcome {"caseId":"32fee6f4-6e69-4675-b44e-5f6392de7dbd"}
get_run_summary {"runId":"<run-id>"}
explain_outcome {"caseId":"<open-case-id>"}
```
Loading
Loading