Skip to content

fix(program): a measure's rate is its own rows, and the Maui e2e suite runs the configuration the pilot deploys - #543

Merged
Taleef7 merged 4 commits into
mainfrom
fix/measure-rate-scope-and-e2e-routed
Sep 8, 2026
Merged

fix(program): a measure's rate is its own rows, and the Maui e2e suite runs the configuration the pilot deploys#543
Taleef7 merged 4 commits into
mainfrom
fix/measure-rate-scope-and-e2e-routed

Conversation

@Taleef7

@Taleef7 Taleef7 commented Sep 8, 2026

Copy link
Copy Markdown
Owner

Two things the six-measure flip exposed once it was live on the sandbox: a dashboard rate that was the wrong measure's, and an e2e suite that had stopped testing the pilot's configuration.

A measure's rate was the whole run's

aggregateOfficialRun paged listOutcomes(runId) with no measure filter, under a comment asserting that "a run evaluates one measure with one engine". That is true of a MEASURE-scoped run and false of the ALL_PROGRAMS run the pilot actually runs, which holds one row per (subject, measure) pair. So it summed every measure the run touched and served that one number under whichever measure was asked about.

On the sandbox, CMS122's card carried CMS125's initial population, CMS125's 59.2% and ecqmId: 125FHIR. With six measures routed it gets six times wronger.

The MeasureReport and reconciliation routes were unaffected — both refuse a multi-measure run with a 422 via distinctMeasuresForRun — so the programs overview was the single unguarded consumer.

listOutcomes now takes an optional measureId and narrows in SQL, before the page window, on both stores. Filtering after the read would page 120,000 rows once per measure on a six-measure nightly. runProducedOfficialEvidence is scoped the same way, so a run mixing an official and an authored measure (ADR-072 admits one) no longer answers for both from whichever row sorted first.

Guarded by two tests chosen so they cannot pass vacuously:

  • a store-contract test pinning that the narrowing precedes the page window — an offset must walk the measure's rows, not the run's, which is also what a mis-numbered Postgres bind would break;
  • a mixed-run unit test whose fake honours the filter, because a fake that ignored it would pass while the shipped query narrowed.

The e2e suite had been green by absence since 2026-09-03

The job is workflow_dispatch-only, so nothing ran it across seven merges. A manual dispatch returned 32 passed, 11 failed. None was a backend regression:

  • 3 needed the routed stack. The job ran with no VSAC credential, so cms2/cms130/cms165 booted official-pending and the specs feat(corpus): MM-1 U2 — the Maui roster is a 20,000-patient data-first corpus, and a multi-rate measure is read as every one of its rates (ADR-073/074/075) #528 added for them waited 30s for chips that could not exist.
  • 2 asserted the case fan-out ADR-078 removed. "chip count equals case count" is exactly what the ADR broke — an out-of-population subject is persisted MISSING_DATA and opens no case. Now bounds, with the gap named.
  • 3 signed in as the quality lead for pages that are engineering-gated to ADMIN. Two of those had been asserting against an AccessDenied panel and would have passed whatever the catalog contained.
  • 1 contradicted the §6.3 patient-column contract (employeeExternalId where a patient deployment emits patientExternalId) — the stack was right and the test was wrong.
  • 1 matched a PCP <option> inside a closed select, because the panel filters gave those options names containing clinic names.
  • 1 was the roster filter selects having no accessible name at all: Chrome computes none from the wrapping <label>, so getByLabel(/^PCP$/) resolved nothing. They now carry aria-labels, matching Panel/System/Segment which already had them.

The job now runs the configuration the sandbox deploys — it vendors the six terminology sidecars with the VSAC credential and sets WORKWELL_OFFICIAL_MEASURES. A suite that boots a stack nobody deploys is not a pilot test. Confirmed by the backend's own boot line on every run since:

runnable=cms122:official,cms125:official,cms2:official,cms130:official,cms165:official,cms137:official

On the credential, since this is a public repo

The key is scoped to a single step, never job-level env, and that step runs before the frontend and e2e dependency trees are installed — so it executes with the backend tree only. The job is workflow_dispatch-gated, which needs write access, and GitHub does not pass secrets to fork-PR workflows regardless.

The completed expansions are deliberately not cached. They are VSAC value-set expansions — licensed content, gitignored for that reason — and an Actions cache is restorable by a fork pull request. Re-vendoring costs 45s.

Speed

The suite was never slow; the failures were. Of the original 8m24s, the passing tests were about 20 seconds and the rest was 11 failures burning 20-60s timeouts and retrying.

before after
Job total 10m 00s 2m 54s
Playwright step 8m 24s 46.6s
Tests 43 (2 measures, 11 failing) 26 (6 measures, 0 failing, 0 flaky)

Fewer tests, more covered: six measures instead of two, and the two vacuous assertions made real. The trimming was collapsing per-page loops and a drill-down that navigated and reloaded once per chip per measure. Beyond that: four workers instead of one, and one sign-in per (role, spec file) instead of ~40.

What review caught, and what the first CI runs caught

Three rounds, all recorded because they are the interesting part:

  1. The routing guard was near-vacuous. classifyRunnable returns official-pending for one reason — the id missing from WORKWELL_OFFICIAL_MEASURES — and never returns it for cms122/cms125, which reach the authored branch first. A routing list that lost those two would have printed cms122:authored, passed a negative grep, and run the pilot's flagship measures on the authored engine while runs.spec's 48x6 held either way. Now a positive exact match on <id>:official, tested against four boot-line shapes including the one the old job produced. Terminology is not part of that classification at all, so the sidecar control is and always was the truncated refusal in the vendor step.
  2. A regression that would have broken the twh job. It runs npx playwright test with no --project, loading both projects, and Playwright resolves storageState before the beforeEach that skips — so the maui specs would have thrown ENOENT instead of skipping. The session helpers now return {} off the maui profile; verified by listing both projects with no .auth/ present.
  3. Three reintroduced vacuous assertions, the same defect class as the two being fixed: a roster search asserting only rows <= 2 (satisfied by matching nothing), a chip/worklist bound satisfied by any sum when the worklist rendered 0, and a drill-down rowCount <= chip.count that passed at zero — which is precisely what a chip href that stopped filtering would produce, and the settle-wait accepts the empty state, so nothing else would have caught it.

Then CI itself caught what review could not: the first four-worker run came back 4 flaky, 22 passed — green only via retry, including a roster reading "0 patients" for 20 seconds while runs.spec's ALL_PROGRAMS run was in flight. The Maui project now splits into maui (read-only, four workers) and maui-writes (runs.spec alone) with dependencies: ["maui"], making the ordering a constraint rather than a hope. The last remaining flake was mine: the split left readChips counting locators before the cards had rendered.

Not asserted as a product bug: the roster read model already excludes in-flight runs in SQL and again in app code, so the mechanism behind that empty read is unidentified. If an empty roster appears when no run is in flight, that is the thread to pull.

Verification

  • pnpm typecheck clean; backend fhir/program/export 288 pass, routes 397 pass, SQLite store contract 89 pass.
  • The Postgres store contract self-skips locally (no local postgres:16); CI covers it.
  • Frontend lint clean, 38 compliance-page tests pass.
  • Maui e2e: 26 passed, 0 flaky, against all six measures routed.

Known and not addressed here

The live six-measure run is slow — about 9.7 (subject, measure) pairs per second against Neon, steady between 17% and 50% completion. Outcomes are already batched; the case pass is not, and costs 2-3 sequential round trips per pair (findByKey, then insert/update, then the audit append). That is ~300,000 round trips for a 120,000-pair run, and the flat rate says it is a fixed per-pair charge rather than anything compounding. Batching it per chunk the way recordOutcomes already batches outcomes is a follow-up PR, deliberately separate: it touches the idempotency contract (DATA_MODEL_CONTRACTS §4) and ADR-076 d2's compare-and-set, so the store-contract tests go first.

Taleef added 4 commits September 8, 2026 17:23
…e runs the configuration the pilot deploys

The programs overview served the whole run's official evidence under each measure's name.
`aggregateOfficialRun` paged `listOutcomes(runId)` unfiltered, under a comment claiming a run
evaluates one measure with one engine — true of a MEASURE-scoped run, false of the ALL_PROGRAMS run
the pilot actually runs, which holds a row per (subject, measure) pair. On the sandbox CMS122's card
carried CMS125's initial population, CMS125's 59.2% and `ecqmId: 125FHIR`. The MeasureReport and
reconciliation routes were unaffected (both refuse a multi-measure run with 422); the overview was
the one unguarded caller.

`listOutcomes` now takes an optional `measureId` and narrows in SQL, before the page window, in both
stores — the app-side alternative would page 120,000 rows once per measure on a six-measure nightly.
`runProducedOfficialEvidence` is scoped the same way, so a run that mixes an official and an authored
measure (ADR-072 admits one) no longer answers for both from whichever row sorted first.

The Maui Playwright job ran with no VSAC credential, so cms2/cms130/cms165/cms137 booted
`official-pending` and the suite's green was about a stack nobody deploys. It now vendors the six
terminology sidecars with the credential and sets WORKWELL_OFFICIAL_MEASURES to the deployed value.
Because a missing sidecar degrades rather than crashes, the backend's own `runnable=` boot line is
asserted — without it the job runs two measures and reports green, which is what it had been doing
since 2026-09-06. The job is dispatch-only and this is a public repo, so the key is scoped to a
single step that runs before the frontend and e2e dependency trees exist, and the completed
expansions are not cached: an Actions cache is restorable by a fork pull request.

Eleven failing specs, none a backend regression: three needed the routed stack, two asserted the
case fan-out ADR-078 removed (now bounds, with the out-of-population gap named), three signed in as
the quality lead for pages that are engineering-gated to ADMIN — two of those had been asserting
against an AccessDenied panel — one contradicted the §6.3 patient-column contract, and one matched a
PCP option inside a closed select. 43 tests to 26 with six measures covered instead of two; four
workers and one shared sign-in per role replace ~40 logins.

The roster's Status/PCP/Age/Sex/Page-size selects had no accessible name (Chrome computes none from
the wrapping label) and now carry aria-labels, matching Panel/System/Segment.
…torage state cannot break the twh project

Review found the boot-line guard close to vacuous. `classifyRunnable` returns `official-pending` for
one reason — the id is absent from WORKWELL_OFFICIAL_MEASURES — and never returns it for
cms122/cms125, which reach the `authored` branch first. So a routing list that lost those two would
print `cms122:authored`, pass a negative grep, and run the pilot's flagship measures on the authored
engine while `runs.spec`'s 48 x 6 held either way. The check is now a positive exact match on
`<id>:official`, verified against the four boot-line shapes including the one the old job produced.
Terminology is not part of the classification at all, so the sidecar control is and always was the
`truncated` refusal in the vendor step; the comment and the journal said otherwise and now do not.

The maui specs declared `test.use({ storageState })` at describe scope. The twh job runs
`npx playwright test` with no `--project`, which loads BOTH projects, and Playwright resolves the
`page` fixture before the `beforeEach` that calls `test.skip` — so those specs would have thrown
ENOENT on the twh run instead of skipping. The session helpers now return `{}` off the maui profile.
Verified by listing both projects with no `.auth/` present.

Storage state is per (role, spec file) rather than per role. Contexts built from one state share a
refresh-token family, and presenting an already-rotated token revokes the family, so two parallel
workers refreshing in the same window would sign each other out with failures pointing nowhere near
the cause. Nine sign-ins instead of two, still far short of the ~40 the per-test login cost.

Three assertion floors restored, all the vacuous-guard shape: the roster search asserted only
`rows <= 2`, satisfied by a search that matched nothing; the chip/worklist bound was satisfied by any
chip sum when the worklist rendered 0; and the drill-down's `rowCount <= chip.count` passed at zero,
which is what a chip href that stopped filtering would produce — the settle-wait accepts the empty
state, so nothing else would have caught it. The out-of-population relaxation only ever justified
MISSING_DATA, so OVERDUE and DUE_SOON now require a non-empty chip to list cases.
…beside them

The first four-worker CI run came back "4 flaky, 22 passed" — green only because retries hid it. One
flake was the roster reading "0 patients" for a full 20 seconds while `runs.spec`'s manual
ALL_PROGRAMS run was in flight; the others were a roster filter, a panel filter and a case-detail
navigation, all under the same window.

Green-by-retry is how a suite starts rotting, so the ordering is now a constraint rather than a hope:
the Maui project splits into `maui` (every read-only spec, still four workers) and `maui-writes`
(`runs.spec` alone) with `dependencies: ["maui"]`, which makes Playwright finish the first before
starting the second. `case-workflow` stays in the parallel set: its mutations are case-level, and
IN_PROGRESS is inside ACTIVE_CASE_STATUSES so it does not move an Open Worklist count.

Not asserted as a product bug: the roster read model already excludes in-flight runs in SQL
(`UPPER(r2.status) IN ('COMPLETED','PARTIAL_FAILURE')`) and again in app code, so the mechanism behind
the empty read is not yet identified. Removing the overlap makes the suite honest either way; if the
empty roster shows up somewhere a run is not in flight, that is the thread to pull.
The remaining flake was mine, not contention. Splitting the jelly-bean tests left the second one
calling readChips straight out of a beforeEach that waits only for a heading — which renders before
the measure cards have data. locator.count() is a snapshot, so a slow load read zero chips and the
caller failed on 'chips.length > 0' while the page was simply not ready. The wait now lives inside
readChips, where every caller gets it, and it doubles as the assertion that a routed measure renders
a card at all.
@Taleef7 Taleef7 self-assigned this Sep 8, 2026
@Taleef7

Taleef7 commented Sep 8, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 3aaa6a2e59

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Taleef7
Taleef7 merged commit 8f90ff5 into main Sep 8, 2026
33 checks passed
@Taleef7
Taleef7 deleted the fix/measure-rate-scope-and-e2e-routed branch September 8, 2026 22:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant