Skip to content

Latest commit

 

History

History
1115 lines (1054 loc) · 94.1 KB

JOURNAL.md

File metadata and controls

1115 lines (1054 loc) · 94.1 KB

Journal

Newest first. A few lines per working day: what changed, and what's next.

Entries before 2026-09-23 are in git history: git show before-docs-trim:docs/JOURNAL.md is the last long-form version, and earlier months were in docs/archive/ (git show before-docs-trim:docs/archive/JOURNAL_2026-07.md).

2026-10-21

  • The MCP OAuth flow is under Playwright (#863 follow-up). e2e/tests/maui/mcp-oauth.spec.ts (a write spec) covers:

    • a client's authorize link going through sign-in to the consent page;
    • Approve leading to a code, then tokens, the read-only tools, and a refresh;
    • a replayed code killing the grant;
    • an admin's Revoke in Admin → MCP access ending the connection;
    • a viewer able only to decline;
    • an off-site next never leaving the app.

    CI's Maui stack now sets WORKWELL_PUBLIC_API_URL.

2026-10-20

  • MCP clients can sign in with OAuth (MCP 2026-07-28 authorization). WorkWell is now its own OAuth 2.1 authorization server for /mcp, so claude.ai connectors, Claude Code and mcp-remote connect with the URL alone.
    • Flow: the client opens WorkWell, the user signs in and approves on a consent page, and the tokens refresh themselves.
    • Clients: CIMD documents are fetched behind an SSRF guard, and self-registration is loopback only and marked unverified.
    • Rules: PKCE S256 and the /mcp resource are required.
    • Codes: single use, and a replayed code kills the grant.
    • Refresh tokens: they rotate atomically, with a 5-second grace (audited) for concurrent refreshes; any other reuse revokes the grant. The user is re-checked on every refresh.
    • Admin: connected clients appear in Admin → MCP access and can be disconnected.
    • Storage: none new; codes and grants are refresh-family rows.
    • Hardened after review:
      • metadata documents are fetched only for Claude's two client ids (configurable; a host alone let random paths spend the fetch budget), with global fetch limits, separate caches and a day of stale fallback;
      • a loopback redirect is never shown as verified, and the page warns;
      • the login next returns only to the consent page (a /.//host path escaped the app);
      • a viewer can decline;
      • DCR and token bodies are capped; IPv6 fetches go to global unicast only;
      • resource is compared as a URL.
  • MCP moves to the 2026-07-28 spec, with tokens that last (#704). POST /mcp is served by the official TypeScript SDK v2 (@modelcontextprotocol/server 2.3.1, owner-approved). It answers 2026-07-28 requests and the 2025-era ones most clients still send, statelessly. The 2024-11-05 /sse transport is retired.
    • Tokens: admins mint MCP tokens on Admin → MCP access, 30 days by default and 90 at most.
    • Scope: each kind of token is signed with a key of its own, never the login key, and is valid only on /mcp, never on /api, CDS or $cql.
    • Revocation: one by one, through the refresh-family table, so no DDL.
    • What ends a token: a password change; and if the store cannot be read, the request is refused (fail closed).
    • Tools: they gained titles and read-only annotations. Bad arguments are still audited, and each tool audit names its credential.
    • Config: the new WORKWELL_PUBLIC_API_URL names each stack's public API; tokens are minted for that URL plus /mcp.
    • Behaviour change: a login token no longer opens /mcp.
    • Next: OAuth 2.1 for MCP, so claude.ai connectors work and tokens refresh themselves.
  • 1 January rehearsed at small scale (#854). pnpm rehearse:year-change runs the scheduler's own nightly (runNightlyRecompute) as of a given date against a Postgres copy, refusing any host but the one named and any WebChart tenant, then checks it. On a 1,000-patient corpus with Maui's routing: 31 December opened 783 cases; 1 January scored all six with the 2027 translations, closed all 783 as rolled over with 783 audit events in 1.8 s, and opened 17. The 2027 work list starts nearly empty, because each measure counts only patients with a 2027 visit. A run now logs and records (RUN_COMPLETED.rollover) how many it rolled over and how long it took. The 1 January checklist is in DEPLOY.md. Next: Maui's own numbers on a copy in December, where the Neon round trip sets the rollover's time.
  • A run says when its data is too thin to trust (#853). Three floors on live WebChart data, the owner's call: coded encounter types ≥95% for CMS-scored measures, final blood pressures ≥95% for CMS165, failed record fetches <1%. A measure below one ends the run PARTIAL_FAILURE with data_insufficient:<measure>, its outcomes carry the flag, its out-of-population rows close no case, and QRDA I/III, the MeasureReports, the Programs card and the attributed-list report refuse it. No DDL. The corpus never trips it. The coding floors count only the measurement period's encounters and blood pressures, the data the measures read, and wait for 100 of them; old uncoded history no longer fails a practice that codes well now. A review found four more places still stating the rate: the measure page, the trend, the quality snapshots and MCP. All four are fixed. The owner decided a thin measure's CDS cards are withheld.
  • The provider scorecard (#852). /scorecard (CM/ADMIN, Maui) gives each measure's rate per provider, location subtotals and the practice total, with the counts behind each rate, small numbers (<20) dimmed, and open gaps linked to the work list. It counts through the list report's own code, so a cell equals that report's rate for the provider's patients (asserted through both routes). Both now read one unordered, narrowed statement: 0.29 s for 20,000 rows on Postgres, down from 1.4 s for the paged full read. The list report never selects a seeded or import run, and a list of errors still reports every rate. Review caught open gaps stopping at the case store's default page of 50. The pooled "Overall compliance" is gone from Programs. No benchmarks yet (OPEN_QUESTIONS §2.2).

2026-10-19

  • The simulate-route test no longer fails on a machine with a VSAC key in its shell. engineForEnv falls back to process.env for the key, and a keyed engine opens the value-set store, which the test's {} env cannot. The test now clears the key, as the MADiE end-to-end tests do. Production is unchanged.
  • Terminology is taken at the release by its title, and Advanced Illness is the release's 2,030 codes (#851). The official completion used VSAC's drifting manifest= form and only refused an answer short of CMS's declared count, so Advanced Illness was pinned at 2,000 codes (33 short of the release, 3 outside it) in cms122/125/130/165. Every set is now expanded with expansion=<release title>, held to VSAC's exact total and its echo of the release; 1082 equals the release's published file code for code. The translations' member-by-member workaround is gone: one call per set reproduced all six sidecars byte for byte. cms138's …1278 is not in the release; it is sourced as VSAC serves it and recorded inRelease: false.
  • Every artifact claims only what it meets, and identity follows the logic (#848). Vendoring drops each Library's cql-library/elm-xml-library claim with the content it strips; CMS's Measure claims stay as shipped. The translations also drop crmi-publishablelibrary and publishable-measure-cqfm, carry no empty identifier, and publish the notation WorkWell's semantics imply (CMS122: decrease; rule D6 now checks that). Each official manifest records logicSha256 (ELM + Measure groups), the lineage keeps every vendoring's sha, and the four export gates ask whether a run was scored by the current logic, so this re-vendor (identical logic, two claims fewer) keeps CMS's identity on every older run's exports. Terminology is outside the logic hash, as it was outside the old bytes check: a run scored with the 2,000-code Advanced Illness list keeps CMS's identity too. The translations get the same continuity (TRANSLATION_LOGIC_LINEAGE): five of six rebuilt with identical logic; cms122's notation change moved its logic, so its older runs fall back.
  • The MADiE gate has no exemption, and every cross-engine disagreement has a cause (#846, closes #572). Six CMS122 cases passed as "reference agreement" when our engine answered numerator 1, on the premise that MADiE was wrong. That premise came from an old Java image's bug, so a regression on exactly those cases would have passed; the gate now compares every case and is 455/455.
    • The Java image is pinned by digest (v8.12.0-2), and a Java exception is counted as an engine error, not a disagreement. All nine measures swept on it: 397 of 455 agree with the period sent. The other 57 are Java's second-precision period (none remain with the measure's default period), plus one Java exception.
    • docs/evidence/CROSS_ENGINE_2026-10-19.md supersedes the August and September causes: the medication disagreements were the old image's convert … to days bug, and CMS165's was the second-precision period, not a harness difference.
    • Five stale conformance statements corrected (the QM IG 2.0.0 citation, the score's exception term, the formula test's ruler, Exp/Ln, the $cql addendum).
  • MeasureReports meet DEQM 1.0.0 on CMS's and the translations' reports (#849). Owner decisions: DEQM 1.0.0 (UV) is the target, and no stratifier code yet. The canonical carries |version, scoring is stated on the root, and every element carries the artifact's linkId. Measured with the HL7 validator: 0 DEQM 1.0.0 errors on an unstratified report scored by CMS's artifact or a translation (12 each before); a stratified one (cms125, cms130, cms137) keeps one deqm-7 per stratifier, an authored one its 11; STU5 stays at 3 per report; base R4 stays at 0. A report now states exactly the populations its measure declares.
  • Every process evaluates in UTC, and an engine upgrade cannot silently make a resolved diagnosis current (#845). A date-only FHIR value took the host's offset while fqm evaluates at 0, so a non-UTC host could change a result. Measured: a CMS165 onset entered the population under Tokyo time and not under UTC.
    • Every entry point now imports src/engine/utc.ts first: the server, the dev worker, the calculation thread, every CLI, every top-level script and every .mjs script that runs a measure (found by what it imports). A test refuses one that does not.
    • The image sets TZ=UTC, and the test runner and CI are pinned.
    • The published measure-engine package keeps evaluating in its caller's zone. Its patient data takes that zone too, and fixing the period alone would split them (Codex review).
    • /health reports the zone, and the deploy gate refuses a build whose offset is not 0.
    • A guard runs every committed bundle's QICoreCommon on our engine: a resolved Condition with no abatement is never prevalent. On cql-execution 4.0.0-beta.1 the same check fails, which is the upstream translator bug (CQL issue 1871).
  • The first deploy without code lists in the image left both stacks refusing their routing. Each stored its lists in its bucket and pushed a clean image, but each new backend reported 4 routing problems, and no container log was reachable to say why. Locally the fetch works end to end against an S3 stand-in, so the likely cause is a slow first bucket access on MIE's network: the 60 s boot bound ran out, the server listened, and /health kept the routing check its first request took. Now the fetch runs four at a time with a 30 s bound per object, the boot waits up to 180 s, a late fetch re-records the routing check, and /health publishes the fetch's counts (terminology), which the deploy gate prints when it refuses.
  • That deploy failed too, and the counts said why: reads failed fast, not slowly (TWH 10 of 11, Maui 13 of 15, in ~1.2 s). From GitHub's network the same client reads all 15 stored lists cleanly, one at a time or four at once, so the fault is between MIE's containers and the bucket. The fetch now reads one at a time, retries each read up to four times with backoff, counts write failures apart from read failures, and publishes each failed attempt's error code (ECONNRESET, EACCES, http503) on /health.

2026-10-18

  • The licensed code lists leave the published images. Every backend image carried the VSAC terminology.json files (AMA CPT and SNOMED CT codes), and the image package hands anonymous pull tokens to anyone, so every deploy republished them. Each deploy now stores its stack's copies in that stack's private evidence bucket under terminology/<sha256>.json, builds the image without them, checks the image holds none, and only then pushes it. The backend fetches them at boot and keeps only bytes that hash to the committed pin; a routed measure's list that cannot be fetched is a /health problem, as a missing file always was. When VSAC no longer reproduces a pin, deploys restore it from their bucket and CI from Maui's, in one step that alone holds the key (an Actions cache was ruled out: fork pull requests can read one). Left: delete the old image versions that still carry the files (owner's OK), then tell MIE the exposure and its fix date.
  • A push deploy waits for CI on its own commit. CI and both deploys started on the same merge, so a commit whose tests failed was already deploying. The deploys now wait for that commit's CI run and stop unless it passed; a manual dispatch is the urgent-fix override.

2026-10-17

  • No routed measure reaches a year without its logic, and a January rerun verifies the case's own year. CI now fails when a deploy routes a measure that neither CMS's artifact nor a routed translation covers for the current year, and from 1 October for the next one. Rerun-to-verify on an earlier year's official case evaluated as of today, so in January a 2026 case was scored by 2027's logic on 2027's nearly empty year and usually closed "not in the population"; it is now evaluated as of that year's 31 December.
  • #837 merged: every export and report download is recorded in the audit log before it is served, and is refused (503) when it cannot be.
  • Every bulk download is recorded before it leaves, and CSV cells cannot run as formulas. The CSVs, the audit ledger, QRDA I/III and MeasureReport now write EXPORT_GENERATED (who, which export, its run and filters) and answer 503 when that cannot be recorded, so no patient file leaves unaccounted for. A free-text cell starting =, +, - or @ (a typed next action, a name) gains a leading apostrophe.
  • Security floors for the dependency alerts. fqm-execution 1.8.5 pins handlebars, axios and moment below their fixes (two critical handlebars injections), and MIE's @mieweb/cloud pulls an old hono, so Dependabot could not move them. pnpm overrides now hold handlebars 4.7.10, axios 1.20.0, moment 2.31.0 and hono 4.13.13 in the backend, and sharp, source-map-js, vite and moment in the frontend, with Next.js 16.3.8. Each stays within its major. Left: csv-parse and uuid (the fix is a major jump their parents may not survive; medium) and braces and sprintf-js (no fix yet).

2026-10-16

  • Patient outreach in plain language, one message per patient. Maui's messages named the measure ("A screening you are due for: Diabetes: Glycemic Status Assessment Greater Than 9%"), and a campaign sent one message per gap. A message now names the care ("a mammogram and an A1c blood test") under a neutral subject, a campaign sends one message per patient, and it never sends for CMS2 or CMS137, whose names disclose a condition (a campaign aimed at either is refused; the page says how many gaps it left out). A guard test refuses any patient message carrying a measure's title, id or CMS/MIPS number. The wording needs a practice clinician's sign-off before real data.
  • Dependabot's backend lockfiles stop failing CI. Its pnpm (11) ignores package.json's pnpm field, so #774's lockfile dropped the better-sqlite3 override and the cql-execution patch and failed every frozen install. backend-ts keeps those settings in pnpm-workspace.yaml now, and backend-ts and frontend pin packageManager: pnpm@10.17.1, the version CI and the images use.
  • The WebChart dev-DB shim and export script need their database password set. Both defaulted to the dev-wcdb image's published root credential, so either would connect wherever its host pointed with no one choosing a password. WCDB_PASSWORD and WEBCHART_DEVDB_PASS are now required; the other defaults stay.
  • CMS122 and CMS165 no longer state a value a patient may not have. A patient with no HbA1c this year read "the most recent ... is above 9%", and one whose readings did not count read "at or above 140/90", on the work list, case page, roster and cards. CMS122's gap is now "none this period, none with a result, or above 9%" (draw an HbA1c, or review control), and CMS165's is "no reading that counts, or the most recent day's lowest not below 140/90". A guard test refuses an official OVERDUE line that states a threshold without the no-record path.
  • A patient WebChart could not read is an evaluation error, not "outside the population". The client degrades such a patient to a bare Patient, and official logic read that as out of population: the patient left the denominator and an open case closed on missing data. The run now records it as source fetch failed (PARTIAL_FAILURE, no case or rollover change) and never evaluates it. A Patient search that answers with something other than a Bundle is refused rather than read as an empty population.
  • A deploy no longer fails when one registry refuses the Node base image. AWS's public mirror answered "429 Data limit exceeded" on three of the day's eight deploy runs, each before the build ran (re-runs passed). The Dockerfiles now take the base image as a build arg, pinned by digest, and each deploy build pulls it from Google's mirror, then AWS's, then Docker Hub, as CI's Postgres already does (#808). The digest makes every registry serve the identical image. A newer Node 24 patch now needs the digest bumped by hand.
  • Merged #821 (TWH routes the CMS125/CMS122 translations), #824 (CMS2), #826 and #827; both stacks run them.
  • Three small staff-facing fixes. Each page names itself in the browser tab ("Work list · WorkWell Maui"), where every tab read the app name alone. A case manager lands on the work list after signing in. The "Due Soon" filter appears only where a measure in scope runs authored CQL: CMS's logic never emits it, so on Maui it matched nothing.
  • The WebChart record, corrected. The trial sends every visit type as text with no code (89 of 89), so no patient enters a measure's initial population from WebChart data: now a caveat on pnpm data-coverage's Encounter row and OPEN_QUESTIONS §4.15. Medications are labelled FDDC, but most of the codes are RxNorm ids. Group/$export is not a complete enumeration (Groups are partitions). PRODUCTION_READINESS no longer claims a complete audit trail, a refusal of demo configuration, or S3 storage; STANDARDS_CONFORMANCE says MADiE grades in the same engine WorkWell runs.
  • Correction to 2026-10-13 (#789): WebChart's blood-pressure components state UCUM mm[Hg] as their unit code (46 of 46 on the trial, 2026-10-15), so the unit does not block CMS165; its status and missing encounter link do (OPEN_QUESTIONS §4.7).

2026-10-15

  • Bulk downloads are CASE_MANAGER/ADMIN, the audit ledger ADMIN (owner decision D5). The read-only VIEWER (the public sandbox, the Maui clinician seat) could download every patient's rows, QRDA I documents and the audit ledger. Browsing in the app is unchanged; the run and cases pages no longer offer a download a role would only meet a 403 on.
  • Maui's outcome retention is paused (owner decision from the production-readiness review). No compaction had deleted yet; the first would have been an unattended, unmeasured bulk DELETE in the ~11-30 nightly. It returns after a measured hand pass on a restored copy. Storage grows meanwhile on the metered Neon plan.
  • VSAC moved a value set at a pinned release, and the deploy now survives it. Overnight, VSAC's expansion of Advanced Illness (1082) at the eCQM Update 2026-05-14 went from 2,025 codes (the published release) to 2,027: 1082 groups two member sets, one gained a version on 2026-10-15, and VSAC resolves a grouping's members at their latest version. The pin check failed closed, so CI and every deploy of cms122/125/130/165 stopped. Vendoring now expands each pure grouping member by member at the release (the members win, with a WARNING), and --fallback-image restores a sidecar from the last deployed image when VSAC still disagrees or is down, judged by the same pin. No pin changed. By afternoon the same two codes reached the official artifacts' Advanced Illness completion at ecqm-fhir-update-2025 (2,000 -> 2,002), failing every deploy's reproducibility gate on both stacks; the official vendor now restores the committed manifest and the pinned sidecar from the image when only the terminology moved. Member expansion is not used there: at that release the members disagree with both answers.

2026-10-14

  • CMS2 for 2027: WorkWell's translation of CMS2v16, routed on Maui (#822). Ported as written (owner decision): a positive screen counts with a follow-up from the screening to two days after the visit, with active depression management (new in v16), or with an antidepressant active over the visit (CMS's draft had folded that path into the follow-up). The QRDA importer now maps a performed procedure's one stated author time to QI-Core's recorded, which v16 falls back to. Cypress 379/379, MADiE one move (an old, still-active antidepressant), 19 hand-built patients (10 discriminating). All six ACO measures now have 2027 logic. Review found that a QRDA-imported medication's stated end is invisible to CMS's period logic (#823, every measure that reads one).
  • TWH scores CMS125 and CMS122 with the 2027 translations too (#820). TWH routes CMS's artifacts for these two only, and its synthetic bundles are dated from the evaluation year, so from 2027-01-01 the translations change its results as they do Maui's. The deploy vendors and pin-checks both, the health gate expects them, and the flip-config test pins TWH's list and holds its vendor lines to it.
  • CMS122 for 2027: WorkWell's translation of CMS122v15, routed on Maui (#818). The frailty edit, and v15's choice of HbA1c/GMI reading: the most recent day counts only a result above 0 %, and the reading is the lowest number that day, else the most recent number, else the most recent result-less one. CMS's v14 draft read a result-less test first, so a patient counted as poor control beside a numerical result. Cypress 64/64, MADiE no population moves, 12 hand-built patients (6 discriminating). A >14 % result reads as no result in FHIR, so v15 can fall back to an earlier normal one: pinned, and asked of MIE (§4.14).
  • The work list and the measure labels no longer state a wrong answer while loading or after a failure (#655). Both seen live on Maui right after a deploy:
    • The work list said "0 patients with open gaps" while its list was still coming, and again after a failed load. It now says it is loading, or that the list could not be loaded, with Try again.
    • Every result label (programs cards, work list, cases, patient pages) lost its "MIPS 112 ·" prefix for the life of the page when the first /api/measures fetch failed on a worker still warming up. No page reads the hook's error. A failed first fetch is now retried twice (after 1 s and 4 s).
  • The remaining 2027 translations move up (scope change, owner decision today). The plan was to wait until 2026-11-20 for CMS's own 2027 FHIR logic before building CMS122 and CMS165. Instead all three remaining measures are translated now (CMS165v15, then CMS122v15, then CMS2v16), and the CMS125 and CMS122 translations are to be routed on TWH too. If CMS publishes its own 2027 logic later, it replaces ours by year.
  • CMS165 for 2027: WorkWell's translation of CMS165v15, routed on Maui (#816). Ten edits; the blood-pressure logic is CMS's. The builder now supports a one-code retrieve (Z94.0, in CMS138's MADiE shape). The checker gained presence entries (a define only one side has) and listed Cypress deck misses (owner decision today): a miss passes only if CMS's own logic misses it the same way and no edit reaches its population. Cypress 42/44 plus 2 listed (blood pressures taken during an emergency stay), MADiE two explained moves, 14 hand-built patients (11 discriminating).

2026-10-13

  • An imported QRDA I Observation carries its author time, site and method (#794, Physical Exam attributes). QDM's Author dateTime becomes issued (only one time, stated to the second), and one stated Anatomical Location Site or Method becomes bodySite or method. Before, only a paired blood-pressure panel carried site and method; unpaired readings and every other Observation lost them. The performer is still not imported (no Practitioner is). Decks and C2 unchanged.

  • Every imported QRDA I resource names its patient (#794, subject). None carried subject, though QI-Core requires it, and so does the US Core blood-pressure profile preparation stamps on a paired panel, so the stamp claimed what the resource did not meet. A batch points a person's documents at the one Patient it keeps, so an entry two documents repeat is still merged once. Decks and C2 unchanged.

  • A blood pressure a QRDA I document states as one panel is imported as that panel (#794, QDM Components; owner decision and CLAUDE.md line today). A Physical Exam coded 85354-9 stating its two readings as QDM Components came in as a code-only Observation, silently, and CMS165 could not see it. It is now that panel when it states exactly one 8480-6 and one 8462-4, each one numeric value in mm[Hg] (§4A.8's value rule), states at least a day, and is its entry's own act; only the vital-signs category (and the UCUM system on each unit) is supplied. The reference QRDA generator's empty value on the exam states none, so its documents qualify. Through official CMS165, 128/78 so stated scores COMPLIANT and 150/78 OVERDUE. Anything short of that, or readings stated as Components under another code (55284-4) or template, is not translated (and named when nothing else in its entry is), and its readings still keep a pair at its time from forming. No Cypress deck document states a panel this way, so the decks and C2 are unchanged and the evidence is the tests.

  • CI starts Postgres from the first registry that serves postgres:16 (owner choice today). AWS's public mirror's anonymous data limit ("toomanyrequests: Data limit exceeded") failed a backend shard or the MADiE gate three times today before any test ran, and a services: container names one image with no fallback. A step now tries Google's mirror of Docker Hub's official images, then AWS's, then Docker Hub (Google's is a cache that may evict an image). No registry login was possible: the AWS account is suspended, and the repo holds no registry credentials. The deploy Dockerfiles still pull their Node base images from AWS's mirror.

  • A QRDA I batch import persists each person under the subject id the resolver gave them (#655). A successful evaluation was stored under the engine's Patient.id, which two people share when their identifiers share a shape (one MRN extension under two roots), so the second person's outcome read as the first's; only a failed evaluation used the resolver's ~N id. C2 unchanged.

  • QRDA III writes no performance rate (nullFlavor NA) when no one is left in the denominator (#655). It wrote 0, which CMS's validation refuses there (CYPRESS-2051): a rate over nobody is undefined, not zero, and the summary MeasureReport already omits its score in the same case. A denominator with no one in the numerator still writes 0.0000.

  • A staff-closed case whose winning run failed reads liveState: UNKNOWN, not GAP (#655). A failed evaluation persists MISSING_DATA, the bucket that opens a case, so the cases CSV, the case list's "still counted by CQL" note and the roster's staff-closure marker read it as a gap. A failure is not a finding: such a cell is now marked as failed, and its live state is UNKNOWN ("current CQL status unavailable").

  • The audit-events CSV export finishes (#619, #655). It paged the ledger by OFFSET, so each page made the database skip every row already sent, and it looked up each page's cases one at a time; on the pilot's ledger it stalled until a proxy cut it off. It now pages by keyset on the export's own order (occurred_at, then id; the cursor keeps Postgres's microseconds) and reads each page's cases in one batch. Local Postgres 16, 1,000,000 rows: a page at row 900,000 takes 0.5 ms (448 ms by OFFSET), paging the whole ledger 2.8 s (117 s by OFFSET), and the whole CSV streams in 6.3 s with every row once.

2026-10-12

  • A QRDA I entry is imported only when it states its template's own mood and status (#790). The importer wrote every encounter finished, every procedure completed and every observation final whatever the document said, so an aborted, cancelled or nullified procedure, or a screening only intended, counted as done. Through official CMS125, an exported mammogram restated aborted now scores OVERDUE (COMPLIANT before). Such an entry is named as untranslated, as a negated one is. So is a lab, which is its result, whose own results are all preliminary, aborted or nullified: through official CMS122, a later preliminary 9.5% HbA1c no longer reads as poor control. A study, assessment or exam with a pending result was still performed and is still imported. An encounter's diagnosis stated as negated or only intended is no longer a Condition. Every entry in both Cypress decks states its template's values, so the decks and C2 are unchanged. A blood-pressure reading that did not happen still blocks a pair at its time, so §4A.8's pairing is unchanged.
  • A QRDA I time keeps the offset the document states (#788). The importer converted every time to UTC, so a Maui visit at 5 pm on 31 December (03:00 UTC on 1 January) left the measurement year: CMS's FHIR logic reads an event's day in its own offset, and official CMS125 now keeps that patient in 2025's population. With both Cypress decks restated in -10:00 at the same clock times, the UTC conversion lost one patient each on cms130, cms165 and cms2; keeping the offset restores every report. The original decks state no offsets, so they and C2 are unchanged. A time with no offset is still read as UTC. Mixed offsets within hours of each other can now meet cql-execution's text sort of DateTimes, which the official measures use too: #750, widened today, holds the engine-level fix.
  • One patient's unreadable data no longer fails a whole batch (#789). fqm-execution scores a chunk of 500 patients in one call, so one blood pressure in mmHg (UCUM's is mm[Hg]) made cql-execution reject the call and every patient in the chunk lost their CMS165 result. An error cql-execution raises on the patients' data is now marked, and its patients are evaluated one at a time: only the patient the engine refuses fails (MISSING_DATA with its own reason, the run PARTIAL_FAILURE). A refusal ("retrieved nothing for anyone"), an artifact or terminology problem, or a crashed worker still fails the batch. Alone, a patient costs about nine times its share of a batch, so per measure and run the retry stops after 25 patients fail alone or 1,000 have been retried (review: sparse bad data, one patient per chunk, could otherwise double a nightly); each retried chunk's counts and time are logged. WebChart's readings carry mmHg, so CMS165 over WebChart data still needs the unit fixed at the source (#591); this keeps it from costing everyone else.
  • The QRDA I export writes each time as its FHIR value states it (#791). It wrote every time through JavaScript's Date: a year became midnight on 1 January, 30 February became 2 March, a time with no zone was read in the exporting machine's zone, and a zoned time became the UTC instant with no offset, so a Maui visit at 5 pm on 31 December came back from its own document in 2026 (official CMS125 now keeps it in 2025 both ways). Times are now written at their stated precision and in their stated offset. CMS_0121 asks for offsets everywhere or nowhere: when any time of day is in a zone other than UTC, every time of day that states a zone is written with its offset; otherwise none does. A time whose source states no zone is never given one (nothing invents data): beside zoned times it is written as stated and the document is reported non-conformant to CMS_0121. The corpus export is byte-identical (it states no zones).
  • The measure engine orders dates in different time zones as instants (#750, owner-approved patch). cql-execution sorted two DateTimes by their text, which ends in the offset, so close times in different offsets sorted out of order: the official measures' Last(... sort by ...) (CMS2's screening, the housing-status exclusion in CMS122/125/130/165) and the authored "most recent" could pick an older result. A pnpm patch of cql-execution 3.3.2 converts the second DateTime to the first's offset before comparing, as the library's own comparisons do; one offset compares exactly as before, so the decks, C2 and the corpus cannot move. The published measure-engine package does not carry the patch.
  • The QRDA I export writes more of the blood pressures CMS165 reads, and nothing the run could not read (#792, #793). A panel stated with a date alone or over an open period was refused, so when it was a patient's latest reading the run scored it and the document did not carry it (official CMS165: a later date-only 150/95 makes the run OVERDUE). It is now written as stated, with a caveat: §4A.8 pairs only a time of day, so our own import pairs neither it nor any panel whose time could fall within its own, while a QDM receiver reads every reading as stated. A period mixing a zoned and a zoneless bound, or whose end starts before its start, is still refused: the import reads either as any time at all, which would keep every other blood pressure in the document from pairing. What the run could not read is not written, each with its own reason: a lone systolic or diastolic, or a panel coded 35094-2 (before, each was a Laboratory Test), and LOINC spelled other than http://loinc.org, since CQL matches a code's system exactly (before, reported as "no CDA code system"). Decks, C2 and the corpus export unchanged.
  • The synthetic builders write their times in UTC (#795). The builders behind TWH's employees, the scale tenant and the cms2/cms130/cms165 synthetic bundles wrote times of day with no zone, which FHIR forbids (the Maui corpus already wrote Z). They now write Z. Every TWH subject on every measure TWH runs, scale-tenant subjects on every authored measure and the official-only bundles score identically before and after, on three evaluation dates and in three host time zones; the stored evidence is unchanged.
  • A QRDA I period that ends before it starts imports as no time (#794, dates). It imported as a FHIR period whose start was after its end, which FHIR forbids and cql-execution compares with rather than refusing: a diagnosis so stated was marked resolved, abated before its onset. It is now silence, as a malformed date is, and as the blood-pressure pairing already read it. Two times compare as instants in their own offsets; beside a date, as dates at the coarser precision. #794's out-of-range years were fixed by #788. Decks and C2 unchanged.
  • The QRDA I importer refuses what is not XML and reads nothing from what states nothing (#794, parser). An attribute stated twice made the last value win, so negationInd="true" negationInd="false" imported a negated act as done; such a document is now refused, as any XML parser refuses it (a batch reports it and keeps the others). An entry stating its code, status or time twice (a medication: two periods beside its frequencies), or a diagnosis its value twice, is not translated and is named; a bound stated twice is no time; a nullFlavored code or id is no code or id; and xsi:type is read by its namespace whatever prefix the document binds to it. §4A.8 still counts every time and every code a reading states, a second one included; a nullFlavored code states none, so it no longer blocks a pair or makes its translation a second half. Decks and C2 unchanged.
  • The first weekday nightly with #783-#787 live on Maui: 120,000 evaluated, 0 errors; CMS165 49.0%.

2026-10-11

  • #787 had two more review passes before merge (four reviewers on the whole PR, then two on the rework, fuzzing against independent oracles), after Codex kept finding defects. The pairing was rebuilt rather than patched: a third systolic or diastolic of any template whose time could fall within the pair's blocks it, judged across time zones; an identical time means the same instant at the same precision, both or neither stating a zone; each half has exactly one value of its own; the panel keeps the document's offset, since CMS165 reads a reading's day in it (a panel converted to UTC moved 20:00 on 31 December in New York into the next year); wrapped readings never pair. The exporter writes readings only in LOINC's own system, the one CMS165 reads, and reports what it does not write as caveats. Decks and C2 unchanged; CMS165 42/44.
  • Preparation stamps a blood pressure only when CMS165 can read it. A panel whose systolic and diastolic were coded http://loinc.org/, as the OID or with no system was stamped us-core-blood-pressure, so CMS165 took its day as the latest and read no values: measured, a patient at 120/70 then 128/78 scored OVERDUE. Now only readings in LOINC's own system are stamped (US Core fixes the same system). The corpus codes them so, as do the trial instance's real readings (2026-07-23) and the dev-database shim, so no live result moves.
  • §4A.8 now states every condition the code enforces (owner decision today): a systolic is 8480-6 and a diastolic 8462-4, and a stated value is not judged (0/0 pairs). The defects the reviews found outside #787 are #788-#795; the largest is #788 (every other imported time is converted to UTC), and #789 (one non-UCUM blood-pressure unit fails cms165 for its whole batch) matters once cms165 runs on WebChart.

2026-10-10

  • A QRDA blood pressure is one panel when the document states one (LOCKED §4A.8, owner decision today). QRDA writes a systolic and a diastolic as two readings at one time; CMS165 reads only a US Core panel. The importer now pairs them under strict conditions: two readings that are each their own entry, at the same instant or closed interval with a time of day (never an open one), with no other blood-pressure reading of any kind at that time, one value each in mm[Hg], and the same stated performer, participants, method and site. The panel keeps both source ids, the shared method and site, and the document's time with its offset and fraction; anything else stays two readings and is counted. The exporter writes a final panel at such a time back as its two readings with its method and site, and names what it does not write (the encounter link, the performer, the recorded time, any other component) as caveats.
    • The Cypress deck, both years, as production runs it: CMS165 42/44, from 33/44 after #786 and 0/44 before. The two left are readings taken during an ED visit or inpatient stay, which CMS's draft leaves out only through Observation.encounter.
    • Every other measure's agreement, the translations' and C2 on both archives are unchanged.
    • CLAUDE.md's no-invented-data rule names the pairing as a mapping, and §4A.8 states the conditions.
    • Review (Codex and an adversarial pass) found wider importer defects, each stating something no document said, now fixed for every entry: an empty, blank or null-flavored value imported as a number; an impossible or malformed timestamp read as another instant; a negation written "1", in capitals, or on a wrapping act read as a fact; a reading lent another observation's nested result. No deck report moved.
  • cms165 trusts one profile, not all of them (#591). Its blood-pressure retrieve still keeps only readings stamped us-core-blood-pressure; every other retrieve now reads by resource type, as for the other five measures. Until now cms165 required every QI-Core profile, which only the corpus stamps, so every QRDA-imported patient threw.
    • Identical to trusting every profile on CMS's 68 MADiE cases and on all 20,000 corpus patients. Trusting none differs on 624 of a 2,500-patient sample.
    • Cypress deck, both years: from 0/44 to 33/44. The other 11 need the blood pressure itself, which QRDA writes as two readings (PR 7b, waiting on the pairing rule).
    • A test pins that the blood pressure is cms165's only code-less retrieve of a profile that picks out part of its type, so a re-vendor that adds another fails.
    • The executor package names cql-exec-fhir 2.1.6 directly, fqm-execution's own copy, to build the narrowed PatientSource inside the fqm worker.
    • #591 shrinks to WebChart's status: unknown and the missing encounter link.
  • QRDA I carries the body site both ways (#784), the part of #782 split out. A diagnosis's or procedure's <targetSiteCode> imports as bodySite and exports back, placed where Cypress places it. CMS125 reads it in both years. Through CMS's 2026 logic, a round-tripped patient with an unspecified-laterality mastectomy on each side is now excluded; without the sites, the same patient is not.
    • No Cypress deck patient moved: bundle agreement on every routed measure, both decks, official and translations, and C2 on both archives are identical before and after. No deck patient has a left and a right site on a path CMS125 reads.
    • Live data is untouched: only QRDA-imported subjects and the deck checks go through this code.
  • CMS125's 2027 translation (#782), the first that changes what a measure reads.
    • v15 adds CMS130's frailty edit, reads mastectomy laterality from a unilateral mastectomy procedure's body site (a new value set) instead of a qualified diagnosis, and makes the end-of-period comparisons day-precise.
    • fqm refuses any value set a library's computed data requirements name and it was not given, so a translation that moves a value set now has those lists edited to match its logic. The router's new D10 refuses a library whose lists and logic disagree. CMS's nine artifacts all pass it.
    • CMS's own MADiE deck now moves three populations, each listed and explained. The new value set is absent from CMS's 2026 terminology, so the deck runs it empty on the translation side.
    • 22 hand-built patients test every edit, 14 of them telling the logics apart.
    • The changed frailty library is one resource in CMS130's and CMS125's bundles, so its title no longer names CMS130. CMS130 was rebuilt and its checks re-recorded.
    • Scope changes from the plan's PR 6 row:
      • Mapping QRDA's targetSiteCode to bodySite is split out, because it would move 2026 CMS125 results on both stacks and needs its own re-verification.
      • Unilateral mastectomies in the synthetic corpus are deferred.
    • New question for MIE: does WebChart's FHIR Procedure carry a body site, coded how (OPEN_QUESTIONS §4.13)?
    • The Docker Hub limit that blocked #780's deploys was fixed in #781: the images build from AWS's mirror, with the runner's own Docker.

2026-10-09

  • CMS130's 2027 translation (#779), the first with a logic edit.

    • v14 → v15 changes one line, in the shared frailty library: an advanced-illness diagnosis now counts when it overlaps the year before or the measurement year, not only when it starts there.
    • The edit lands where CMS made it, in a WorkWell-named copy of that library, and the builder, the router's identity check and the MADiE check all learned changed libraries.
    • The Cypress 2027 deck scores 269/269, but CMS's unchanged logic scores the same, so it does not exercise the edit. 15 hand-built test patients do (6 tell the rules apart), run on every push.
    • Routed on Maui for 2027 only.
    • Scope change from the plan: its "builder recompute" became a guard. The builder refuses any edit that changes what a library retrieves, and nothing needs a recompute yet. A dev-only QDM engine for comparison was rejected (a new dependency); hand-built cases were chosen instead (owner, 2026-10-09).
    • New question for MIE: may modified NCQA logic be used beyond the sandbox (OPEN_QUESTIONS §4.12)?
  • WebChart ingest now composes medication orders, referrals and coverage (#713).

    • Checked read-only on the trial first: each answered 200 for all 36 patients.
    • If a server 404s one of these three types, the type is skipped for the run with one warning; no patient fails. Any other failure, and any failure of the original five types, still degrades the patient.
    • The coverage report now shows them served, each with what limits it:
      • the trial codes medications in FDDC, never RxNorm;
      • 125 of 134 orders carry no code;
      • Coverage has no type.
    • So the CMS exclusions and follow-ups these feed still cannot count on WebChart data until WebChart sends standard codes. That is a new question for MIE (OPEN_QUESTIONS §4.9).
    • Composition is now 8 searches a patient, which caps the 15,000-calls-a-day terms at under 1,900 patients.
  • #768's first half is live (#772). Both deploys passed the routing gate, maui-latest was promoted, and /health shows Maui's six measures plus the cms137 translation and TWH's two, each with 0 problems.

  • #768's second half: the routing lists are set only in the image.

    • The deploy and reconcile workflows no longer put either list in the container env. A self-heal now routes what its image was built with and can serve.
    • The guard tests read the build args as the one copy. They now fail if either key appears in any container env array, in any spelling.
    • DEPLOY.md: the rollback runbook drops the translation hazard. It now says what a rollback to an image built before #768 does: it routes nothing after the next self-heal, and a redeploy of a current SHA fixes it.
  • Per-measure data coverage (#776). pnpm data-coverage reports, for each logic Maui scores with, which FHIR types it reads and whether WebChart ingest supplies them, with a reason for each gap.

    • What a measure reads is computed from the committed artifact's ELM. The result equals MADiE's effective data requirements for every vendored CMS artifact (apart from one abstract-type element read in cms138, which is not data), and it covers the translation too.
    • What ingest supplies is one table, which the WebChart client now fetches from.
    • Result on the WebChart path:
      • every routed measure scores with types ingest does not fetch: MedicationRequest, Medication and ServiceRequest, plus DeviceRequest for four of them (#713);
      • cms165 retrieves by profile, and ingest stamps only the blood-pressure profile (#591);
      • the sandbox's synthetic corpus carries most of these, so its rates are not WebChart's.
    • A test pins the table: it goes red when a type is dropped, when a measure starts reading a new one, or when a gap has no reason.

2026-10-08

  • #767 is deployed and the nightlies are unaffected. Every weekday nightly since its deploy completed on Maui. Today's run evaluated 120,000 patient-measure pairs with 0 evaluation errors, so the router builds with the translation set; the translation itself scores only 2027 periods and has not run live.
  • The docs say what is true after #767.
    • The 2027 wording now says WorkWell translates, CMS137's translation is routed, and the rest warn.
    • The conformance matrix: Maui routes all six measures, plus a translation row.
    • Stale #598 lines: it was closed by a PR keyword, but applyCaseAction still does not exist and is untracked.
    • The audit sweep count is 64; the six new hits are a matcher artifact.
    • critical is described as the highest urgency, not "must not proceed".
    • "No CDS Hooks client" is narrowed to what we checked.
    • Bulk export works per partition.
    • OPEN_QUESTIONS: asked dates added, two entries answered on 09-09, seven new questions.
  • The order of the next work (a scope change, recorded here): versioned identity (#769) → #768 → data coverage → #713 → the CMS130 and CMS125 translations → CMS165 blood-pressure pairing.
    • The translations moved ahead of CMS165: they must be live for the 2027-01-01 nightly, and CMS165's pairing waits on rule edits and a new dependency.
    • Live cards are paused until MIE says how a WebChart client would call them.
    • No 2027 run on live Maui: it would close and reopen every 2026 case. The 2027 label is proven in CI (#654).
  • Every result names the logic that scored it (#769). The labels read each result's own evidence, never today's routing:
    • CMS's artifact: "MIPS 112 · CMS125FHIR v1.0.000 (from CMS125v14)" on wide surfaces, and the "(from CMS125v14)" chip form on narrow ones.
    • A translation: "WorkWell translation of CMS137v15 (ww-2027.1)".
    • A run, card or roster column that mixed logics or periods says so.
    • Fixes: the patient page's authored "2.0.0", "v1.0" on the Run dropdown and the measure page, MCP pairing "CMS125v14" with "v1.0", and the compliance API's bare "125FHIR".
    • The cases and outcomes CSVs append executedLogic.
    • The QDM lineage is pinned per artifact sha, so a catalog move to 2027 cannot relabel 2026 rows.
    • Measured: the run-detail read is 250–400 ms for a 120,000-row run on a local Postgres 16; a finished run's answer is memoized.
    • CI proves the translated label on a one-patient 2027 run on its throwaway stack.
  • #768, first half: the image carries its routing lists.
    • Each deploy bakes its routing lists into the backend image as build args: Maui both lists, TWH the official one, staging neither. The container env still sets the same values, and a test holds the two equal. Nothing changes at runtime.
    • /health now reports routing (the routed ids and the router's problem count).
    • A new gate waits for the deployed build on /health. Maui promotes maui-latest only if that build routes exactly its build args with 0 problems; on TWH a failed gate fails the deploy.
    • A shim-only merge no longer redeploys either stack. Dependabot no longer proposes Node 26 types for the shim.
    • Next: the second half, once this image is maui-latest. It drops the keys from the deploy and reconcile env, so a self-heal can no longer pair an older image with main's list.

2026-10-05

  • The first translation, CMS137v15 for 2027, is built, proven and on for Maui (C3a). CMS's CMS137FHIR v1.0.000 logic recompiled by WorkWell with no edit (v14 → v15 changes value sets only), carrying the eCQM Update 2026-05-14 value sets, labelled "WorkWell translation of CMS137v15" (ww-2027.1). Maui routes it (WORKWELL_DERIVED_MEASURES=cms137); TWH and staging do not. CMS's artifact still scores 2026, so a run today is byte-identical to before; the translation runs on 2027-dated evaluations only.
    • Proof. The Cypress 2027 deck agrees on all 36 patients, both rates and now every stratum row (the harness used to count them). But the deck scores 36/36 on either year's codes, so the value-set proof is a second oracle: all 28 declared sets equal the Cypress 2027 deck's value-set export of the same release (an independent VSAC export, not the steward's own list), and exactly ten differ from CMS's 2026 sidecar (43 codes added, 13 removed), pinned in the sidecar test. CI also runs CMS's MADiE deck through the translation and CMS's own run on every push: every case, rate, stratifier and define value equal.
    • How it is built. pnpm build:derived reads CMS's CQL from the hash-verified checkout, applies a hash-anchored edit list (only WorkWell's text is ever stored), compiles twice and refuses on any byte difference, strips the keys that carry CQL text (localId stays: fqm reads define values by it), and carries the six shared CMS libraries byte-for-byte. pnpm derived:check --record writes the two check records only when every check passed. The sidecar is 100% VSAC at the pinned release, gitignored, regenerated at CI and deploy with --verify-pin, so a bad or missing sidecar fails the build before an image exists (the worker would otherwise boot and 500 every evaluation).
    • Not done, on purpose. The catalog description is unchanged: it is one global text that TWH shows too, and the measure page already renders "For 2027: WorkWell translation of CMS137v15, not a CMS measure" only where it is routed. A 2027 run on the sandbox (what #654 is missing) is the owner's call.
    • Next: CMS130, then CMS125 — the first translations with real edits, where the main library's data requirements must be recomputed (today they are CMS's, correct only while nothing changes).

2026-10-03

  • Screens and APIs name a WorkWell translation (C2b). Wherever a translation scored a row, it is named by its label and never by a CMS eCQM id; CMS-scored responses are unchanged, and nothing routes a translation yet.

    • Compliance API: an additive populationsSource: "translation-evidence" and measure.logic; no ecqmId. The measure rate's source uses the same word.
    • Measure page: the top label, version and rate panel name the translation, today's CMS artifact never fills its missing id, and a routed translation adds a "For 2027: …" line. The vintage note says the translation applies from the next run instead of "2027 logic not yet available".
    • Runs page names it in the reconciliation and offers no QRDA III; the measures list badges it.
    • The attributed-list CSV appends executedLogic; the case screen's population rows, MCP's and the AI fallback's explanations and the EXCLUDED wording drop "official" for a translation. A run mixing two logics now has no measure rate.
    • Next: C3a, the CMS137 v15 translation. Its catalog description still names the 2026 draft only.
  • A WorkWell translation can score the year it covers, and nothing else (C2a). The engine now chooses the artifact after the period: CMS's draft for a year it covers, otherwise a translation under measures/derived/<id>/ named in the new WORKWELL_DERIVED_MEASURES, otherwise CMS's draft with the prior-year warning. Unset on every stack and no translation committed, so nothing changes today.

    • Caches and the terminology expander key by artifact, not measure id. Keyed by id, a 2027 translation would run on the 2026 value sets, which for CMS137 (a value-sets-only change) makes it the draft.
    • One isFqmScored predicate replaces five official-fqm: prefix checks, so the out-of-population rule (ADR-078) applies to a translation. logicVersionFor(id) became logicFor(id, date).
    • The router refuses an unfit translation (D1–D9: routed measure, WorkWell identity with "unchanged" libraries byte-identical to CMS's, one calendar year, the VSAC release named, CMS's populations and strata, passing checks on this exact artifact, a complete sidecar, and no embedded ValueSets, which fqm would prefer over the translation's own 2027 codes). The nightly sees the allowlist too.
    • A translated row writes official.kind: "derived" and a null ecqmId. QRDA I/III refuse it, the MeasureReport uses the translation's own canonical, and a run mixing logic or periods is refused.
  • WorkWell can compile CMS's QI-Core CQL, with results identical to CMS's own ELM. The 2027 translations need a compile path, and the docs said CMS's QI-Core CQL was uncompilable under our pinned translator (a 2026-07-05 spike). It compiles: the same @cqframework/cql@4.0.0-beta.1 needs only cqframework's QICore 6.0.0 model info (Apache-2.0, now in measures/derived/_modelinfo/, hash-checked) and CMS's recorded options. pnpm test:compiled-cases compiles the six pilot measures from CMS's CQL and runs CMS's MADiE decks on our ELM and on CMS's: 334/334 cases agree on every rate and stratifier, and 0 of 14,880 define values differ (each non-function define, per patient, by value). Two broken copies prove the check looks: one moves 40 cases, one changes 94 values. It is a CI step in the official-cases job and needs no VSAC credential. No dependency changed, and no runtime behaviour changed. Every place that repeated the "uncompilable" claim is corrected.

  • The docs say what WorkWell is: it assists WebChart, and Maui and TWH are two instances of one product. The README opened as "an occupational-health compliance platform", and the guide described only TWH. Across the README, the one-page explainer, the guide, the landing copy and the OpenAPI description:

    • WebChart calculates and submits the reported rates. Where it does, WorkWell's rate is an estimate.
    • CMS's FHIR artifacts are drafts, not "published".
    • Every measure count now names its instance.
    • WebChart has no CDS Hooks client (checked 2026-10-02).
    • The compliance API is a kept surface, not the integration contract (§4A.4).
    • The CY2027 PFS rule sought comment on FHIR reporting.
    • The audit rule has open exceptions (#598).
    • CMS138 is refused by the router, like CMS68.

2026-10-02

  • The measure page's top label is never a QDM version neither engine ran. Checked live: TWH's cms125 page showed "CMS125V14" above totals that fold in the scale tenant's authored counts. The label now shows the executed artifact on an official run, a non-CMS policy reference (an OSHA citation) as before, and otherwise nothing.

  • The QRDA I importer reads what the Cypress decks carry. It now takes text results, Physical Exams (one Observation per reading), Medication Orders (as orders, never active medications), an Assessment Not Performed with its reason, and encounter diagnoses. The batch merge keeps references intact and merges a repeat of the same source entry across a person's duplicate documents, so a copied visit is not counted twice. scripts/cvu/bundle-agreement.ts compares per patient against a Cypress bundle.

    • 2027 deck (bundle 2026.1.0), vendored drafts, as production runs them: CMS122 64/64, CMS125 155/155, CMS130 269/269, CMS137 36/36 (both rates), CMS2 375/379.
    • CMS165 is not reachable through the QRDA I route yet: it alone runs with profile tagging required, and imported resources carry no QI-Core profiles. Its draft also reads a blood pressure only as a US Core panel, and pairing the two QDM readings into one would supply a code the document never states, so they stay two readings; the question returns with profile stamping.
    • The rest is logic, not import: CMS2's draft lacks the active-medication follow-up (2) and the 2027 next-day follow-up (2).
  • The screens name the logic that runs, and call the rates estimates. The routed measures show CMS125FHIR v1.0.000 (a CMS draft, derived from CMS125v14), not CMS125v14/v1.0/"published" (§4.3). On the patient profile every rate screen says WebChart submits the reported rate, and a run scored with last year's logic says so. Old descriptions are rewritten, audited first.

  • The pilot stack has its own password. The pilot accounts shared the demo password, which is printed in this public repository. The Maui deploy and self-heal now require WORKWELL_PILOT_PASSWORD_HASH_MAUI and pass it to the backend; the pilot accounts sign in only with that password. Local runs and CI keep the demo password. A malformed secret, or a missing one on the deployed stack, disables pilot sign-in rather than falling back to the public password, and a test pins that both workflows ship it.

    • The public password is refused for any account with its own hash, however that hash was salted.
    • Refresh tokens carry a credential version (a digest of the account's hash). A password change, and this rollout, ends every older session within one 15-minute access token: a stale or missing version cannot refresh. Every user signs in once after the deploy.
  • A failed evaluation no longer opens care gaps. When the engine threw (one subject, or a whole official batch), the outcome was stored as MISSING_DATA, and the case logic then treated that as a finding: it opened a case for a patient with none, reopened a resolved one, and rolled an old cycle's case over as if the patient had been evaluated. Now the failure is still recorded, the run is still PARTIAL_FAILURE, and the patient's cases are left exactly as they were. A failed rerun-to-verify now does the same: it records the attempt (CASE_RERUN_FAILED) instead of reopening a closed case.

  • Ingest no longer invents a visit for CMS125. On a live WebChart run the enrollment roster stamped a CPT 99213 office visit on every listed patient, which put them in CMS125's initial population whether or not a visit happened. That was a made-up clinical fact on the live path, so it is gone, and cms125 is off the roster list. A test pins that no measure with an official artifact can be roster-eligible.

    • Only the staging stack and the dev CLIs reached it; Maui and TWH run synthetic data.
    • The dev-DB sample carries no encounters, so official CMS125 now admits nobody there. The tests that probe sex mapping and mammography supply the visit as test data instead.
  • The WebChart dev-DB tools read the result history, not a cache. The shim, the generated compliance SQL, the fixture export and the YAML ingest all read observations_current. That table is WebChart's latest-per-code cache: NULL values on every LOINC row, and dates that are cache-refresh times (patient 8's HbA1c read 2015 instead of its real 2011).

    • They now read observations: 3,385 rows (the mapping doc had called it empty), dated by observed_datetime. Only a final result (obs_status '' or F, all the seed holds besides DELETED) is read; the other statuses are a question for MIE (OPEN_QUESTIONS §4.7).
    • Ingest writes there with status F, and rollback also clears rows an older ingest left in the cache.
    • The full history is what surfaced #749. Live parity (SQL against CQL, per patient, both dates, with and without the ingest fixtures) passes.
  • "Most recent" no longer depends on the order results arrive in (#749). Every authored measure sorted its results by a FHIR element the engine cannot compare, so the sort reversed its input and Last() returned the first result listed: the oldest, for data listed oldest-first. Same three BP readings, two answers (OVERDUE oldest-first, COMPLIANT newest-first). The sort now keys on the CQL date (.value) in every authored measure and the CQL generator. Maui's six run CMS's own CQL and the TWH corpus emits one event per person, so neither showed it; the WebChart dev DB's full history did. The seed rewrites, audited, any stored CQL that is exactly the pre-fix text (the MAT export, audit packet and MCP tools hand that text out); an edited text is left alone.

    • Each rewrite is recorded as its own event, MEASURE_SEED_CQL_REFRESHED, before the write and every time it happens (#753).
    • A guard against duplicates could not tell a retry of a failed write from a later save of the old text, and would have made that second rewrite silent. So a retried write is recorded twice: an over-claim, never a silent change.
  • What WebChart can and cannot do, checked rather than assumed. The teatea trial (RC202509), the docs and MIE's dev database were checked read-only. Findings:

    • WebChart has no CDS Hooks client.
    • Bulk export works, per partition, with _since.
    • Appointments, tasks and slots are not in FHIR.
    • FHIR writes only Patient, Condition and Claim.
    • The PCP and the payer type are not exposed.
    • Most observations are uncoded.
    • The API terms forbid persistent storage and cap calls at 15,000 a day.

    The facts are in WEBCHART_API_ASSUMPTIONS_2026-07.md (two rows corrected: _count is refused, _since works); six questions for MIE are in OPEN_QUESTIONS.md §4.

2026-10-01

  • A run no longer undoes a person's closure (#747). An EXCLUDED outcome rewrote a staff-closed case as a system exclusion and cleared who closed it, so it left the closed-by-staff views and the next gap reopened it as open work. A person's closure now stands against an exclusion, as it already did against COMPLIANT and a gap; active cases and system closures still move to EXCLUDED.
  • The Cases "Excluded" tab says what it counts (#655 checklist). Programs' Excluded chip counts the patients the latest run excludes (1,327); the tab lists every case closed as excluded, by a run or a staff rerun, across every year, and keeps one after the patient becomes compliant (1,519). The tab now says so; both numbers are right.
  • Headings that describe the rows under them (#661, #655 checklist). The work list's closed-by-staff view said "patients with open gaps", headed its column "Open gaps" and ranked closed cases "1 High"; it now says closed and counts them. The Panels tab no longer carries the patient list's subtitle, and says "each PCP's" (it read "pcp's"); its "Actions" column, reported empty, is a screen-reader header whose cell holds Unassign once a panel has an owner (none does yet), so it stays. The header's date range shows only where it filters (work list, Cases, Runs) and says which date ("Opened: last 7 days", "Started: …"); it sat on Compliance, People and case pages changing nothing, and the sidebar badge no longer applies it. A simulated outreach's chip reads "Recorded, not sent", not "Simulated".
  • The work-list search filters as you type (#658, #655 checklist). It applied only on Enter, with nothing saying so. It now filters 400 ms after the typing settles; Enter still applies at once, clearing the box removes the filter, and back/forward or a link updates the box.
  • One answer per patient, on the page and in search (#655 checklist). Global search showed each patient's newest outcome of any measure as the stored bucket: on the pilot every result read "MISSING DATA", including a patient compliant on CMS2 and outside the other five populations (pat-00491, checked live). It now shows the patient's open gaps ("2 open gaps", nothing for none), the rows of the Open Cases table on the page it opens, read in one query for all results instead of one per result. The patient page's summary read the newest outcome from any run while its table read each measure's winning population run, so a one-patient rerun, or the nightly while it ran, gave the page two answers; the summary now reads the winning run too.
  • The work list is the case manager's daily screen (#698, owner decision). Three screens listed patients and gaps and nothing said which to use; the Programs chips opened Compliance and "Open cases" opened Cases. For a case manager the gap chips (Overdue, Due Soon, Missing Data) and "Open cases" now open the whole practice's work list filtered to the measure and status (panel=all, or a staff member who owns a panel saw their panel's share of the number); Compliant and Excluded stay on Compliance, and other roles keep their links. Worklist sits above Cases in the sidebar; Cases says it is one row per gap (it called itself the daily worklist); each screen links to the others keeping the measure and status; the sidebar badge says it counts open gaps not yet contacted, not patients. A gap chip can read more than the list it opens: it counts gaps staff closed, which the list does not.

2026-09-30

  • Maui's database costs less: weekday nightlies and 90-day outcome retention. September's Neon bill ($6.44: 51.6 compute hours, 2.6 GB-month) is almost all the nightly recompute of 20,000 synthetic patients; nothing polls the database while idle. WORKWELL_SCHEDULER_DAYS (cron day-of-week, unset = every day) now runs Maui's nightly Monday to Friday, and retention drops from 400 to 90 days so storage stops growing (nothing is old enough to delete until December). Since #705 the nightly takes 40-50 minutes instead of 80-98. The Programs freshness banner reads the schedule (GET /api/runs/schedule), so a weekend is not reported as a missed update.
  • CMS137's card shows both rates (#697, owner decision). It showed one number, 12.9%, which reads as Engagement, and nothing said Initiation (37.4%) existed or could be worked. Engagement requires Initiation, so every patient who missed Initiation already has a case, and each case says which rate was missed. The card now lists each rate under the headline and says so; the measure page's CMS rate box says the same. No chip or list per rate: it would re-list a subset of the same cases.
  • A measure's trend is warm in every time zone (#615). The measure page asks for its trend in the browser's zone, and the trend memo was keyed by it, so the nightly warm (no zone) never served that page: on Maui the first visitor after a run waited 19 s for cms130's trend, and 0.2 s without the zone. The memo now holds the zone-free per-run points and each request collapses them to one point per day in its own zone; a zone that merges the memo's runs into fewer than ten days reads further back. Live after the deploy, four of the six were still 6-7 s in US Eastern: warmed in UTC, a late-evening manual run sat on its own UTC day but shared a local day with a nightly. The warm now asks in the deployment's practice zone (practiceTimeZone, US Eastern on Maui).
  • A deploy no longer signs out whoever's access token lapses during it. #688 kept the login itself across a restart, but the browser still treated any failed refresh as a logout: a 502 or a dropped connection while the backend restarted sent the page to /login, and a failed request called logout(), which ends the login on the server. The audit log showed it on Maui on 2026-09-30: a sign-in 17 minutes before a deploy, a new sign-in during its boot, and no logout or token-reuse event between them. Only a 401 from the refresh route now ends the session; anything else is retried for about two minutes (lib/api/session-refresh.ts), with a "Reconnecting" notice and a "Sign in again" link. Left open: a refresh whose response is lost after the server rotated the cookie still trips reuse detection; that needs a short server-side grace window.
  • Monthly quality snapshots leave out-of-population patients out of the denominator (#676). They were counted in, so every official measure's monthly rate read low and the screens refused the whole series. A new not_in_population column (owner-approved) records how many a row left out, NULL on an old row; the screens now refuse per row. pnpm rebuild:quality-snapshots replays each month's runs to rebuild the history. It replays only each month's runs that are some measure's newest (Maui's database is metered), and a manual workflow runs it against Maui with the deployment's own settings.

2026-09-29

  • The steward's MADiE patients end to end (#727). The gate proved the calculation in memory; the pilot-trust defects all sat after it. A new test runs the six Maui decks (334 patients) through the nightly's run, both stores and the read APIs (report, roster, cases, programs overview, reconciliation, and an idempotent second run) against an oracle written from the population table. All 334 agree on SQLite and Postgres; cms165's trustMetaProfile and preparedForQiCore changed nothing. Runs in the official-cases job, which now has a Postgres service. Evidence: docs/evidence/MADIE_END_TO_END_2026-09.md.
  • An exception no longer excuses a patient who met the numerator (owner decision). The rate already counted them as met (the QI-Core IG's Denominator Membership) while the bucket said Excluded, so one patient could be a met numerator in the rate and Excluded on the roster. outcomeFromPopulations and the multi-rate wording now read an exception only outside the numerator, and met-or-missed now honours a numerator exclusion as the rate does. No steward deck holds either shape, so no current result moves.
  • A failing backend test failed nothing in CI. The sharded test step piped into tee under GitHub's default bash -e (no pipefail), so the step took tee's status: run 36158859048 logged fail 1 and went green. shell: bash restores pipefail; it was the only such step.
  • No page scrolls sideways on a phone or tablet (#700, part 1 of 3). At 375px Programs slid sideways by 506px (121 at 768), Campaigns 316, Hierarchy 300, Runs 211, the patient page 113, Cases 80: chart tables hidden with sr-only still sized to their content, rows that did not wrap, selects as wide as their longest option. Wide tables now scroll inside a labelled ScrollRegion with edge shadows; content caps at 1600px. A Playwright spec checks every main route at 375 and 768 (e2e ran only at 1280).
  • Phone and tablet workflows (#700, part 2). The case page is one layout at every width: its phone accordion drew Preview into the hidden desktop panel, sent without a preview, and had no Mark Resolved, scheduling or upload. Work list rows become cards by container query (one table, same cells) and the filters fold behind "Filters · N active"; roster and Cases cards take selection, so a phone can assign.
  • The shell on a phone or tablet (#700, part 3). The closed nav drawer kept its twelve links in the tab order and ignored Escape; it is now inert when closed, focus moves to the current page on open and back to the menu button on close, and the current page carries aria-current. Search shows on phones (16px, so iOS does not zoom); header filters start at xl, where 1024–1279px no longer overflowed; the menu button, nav rows, Log out and the password toggle are 44px targets. A WebKit (Safari engine) sweep on iPhone SE/15 and iPad profiles found the case page's Assignee row and the Runs filters still ran past a 320px screen; both fixed, and the responsive spec now measures 320 too.

2026-09-28

  • No invented vaccination history (#628). The immunization forecast made up each person's doses from a hash of their id; on TWH a Td/Tdap case CQL scored Overdue showed a 2022 dose and "Up To Date" beside it, and a configured ICE was fed the same invented history. No history source is connected to the forecaster yet, so the forecast is empty and the case page says so. ICE waits for WebChart immunizations (E12).
  • Admin integration tiles are status only (#627). Manual Sync contacted nothing and reported "Manual sync completed", and every "Last sync" was the container's boot time. The button is gone; the tiles are built from configuration, a WebChart tile says Maui is not connected, and a time appears only where one is real (the newest run's WebChart fetch).
  • People page (#655 items). Same-named patients now show id, birth date and clinic. A person's history keeps only finished runs and the measures the deployment runs; each row shows its time and run (nightly, manual, one patient, rerun to verify, synthetic seed), and reads "Not in population" as the patient page does. Its runs come from one read (RunStore.getRunsByIds), not one per run.
  • Programs has no date range (#699). The range scoped each card's "Open cases" but not the rate or chips beside it ("Overdue 1,643" by "Open cases (364)"). Rates are measurement-year figures, so Programs and the hierarchy ignore the range and the header hides it there. "Open cases" now carries the site, so Cases shows the card's count for a chosen clinic.
  • The hierarchy shows what its rate is made of (#643). In population replaces Evaluated, and each row's rate is the Programs card's (displayRate); one lower-is-better measure reads Poor control, as its card does (CMS122 showed Compliance 53.9% beside a card reading Poor control 46.1%).
  • One name per measure (#648). Every measure picker (Cases, Orders, Campaigns, the hierarchy, groups, Admin waivers), the campaign history, a person's history and the roster's "Scoped to" line use the card's label, MIPS 113 · CMS130 · Colorectal Cancer Screening (the roster printed the id twice). Work list gap chips carry the short form MIPS 113 · CMS130, the full label as tooltip and accessible name. Patient links are /patients/<id> on a patient deployment; /employees/<id> still opens.
  • Run History checked live after #719: 4.9 s on the first visit a minute after a deploy, 0.2 s after (was 48.8 s).

2026-09-26

  • Run History opens warm. The list counts each run's outcomes. A finished run's counts were kept for only ten minutes, so nearly every visit was a cold one: 48.8 s for the first page on the pilot, against 0.48 s kept. They are now kept until compaction resets them, or a restart; two loads asking at once (the grid and the app-wide run-status check) share one read; and the read-model warm (boot, and after every run) counts the first page, so after a nightly only the new run is read. #655 checklist item.

2026-09-25

  • Run History no longer has a second scrollbar into empty space. The outcomes grid's hidden "Skip to table" link, absolutely positioned with no positioned ancestor, was placed against the document, escaped the scrolling content area and stretched the page to 3,099px in a 900px window. The grid wrapper is now a positioned box; checked in the browser (3,099px to 900px). Admin and Measures use the same grid and are covered. The grid's loading overlay had the same fault and dimmed the top of the page rather than the grid; it now covers the grid. Its tooltips are portals and are unaffected.
  • #650: no window where a measure has none. A CMS130 case's evidence read "Window (days): 365", the authored default, though CMS130 qualifies on FIT yearly, FIT-DNA at 3 years or a colonoscopy at 10. The same number reached the clinician's CDS card ("Compliance window: 365 days"), the outcomes CSV and the AI fallback explanation. An outcome with official evidence now carries no window, and all four say nothing about one. The case and patient pages show the "Why flagged" summary and the exclusion status, without the window, days-overdue and "Last result date: None" rows (official evidence records no result date, so "None" contradicted a patient flagged by a reading). Checked live first on the desktop layout.
  • #617: no forecast where there is nothing to forecast. The measure page's "Risk outlook (next 90 days)" projects from a last-exam date and a window. Official evidence has neither, so on all six Maui measures it showed "Upcoming due soon: 0" and a "Predicted 90d" column equal to "Current". The outlook now reports forecastable. For such a measure the panel says why there is no 90-day forecast (the official result records whether a patient met the measure, not when the qualifying test was done) and keeps only the real per-site current rate. The case the issue missed: with nobody compliant yet (early January), the evidence was never looked at, so the zeros read as a forecast there too. One row is now peeked either way, named by subject so it stays an indexed lookup. Deriving real expiries from the measure logic stays the follow-up.
  • #621: the Orders page names the measures it cannot propose for. The patient-page simulation already named the four measures it cannot replay (#671). The order catalog still dropped them silently: CMS2, CMS130, CMS165 and CMS137 have no catalog order, so their at-risk patients got no proposal, and filtering to one showed "No order proposals", which reads as nobody at risk. The route now returns measuresWithoutOrder, and the page names those measures and says their gaps are tracked as cases on the work list. No order codes were invented for them: which gaps may be ordered by protocol is the open question in #640.
  • #616: Order Proposals no longer invents standing orders. With no order source connected, the default made up a standing order for about one patient in five from a hash of the ID. When it matched a patient's own gap, their proposal was hidden under "standing order on file" and left out of the FHIR bundle. That hid 46 of 2,509 at-risk patients on the pilot, all CMS122 or CMS125, the only two routed measures with an order. Nothing is invented now, and every at-risk patient is proposed. Because WorkWell cannot see orders already placed in WebChart, the page says a proposal may repeat one; the API reports this as standingOrdersChecked. The deduplication itself stays, for when a WebChart order source is connected.
  • #623: Maui's failure alerts now reach the owner by email. The webhook URL is a secret that both the deploy and the self-heal pass to the container (a test holds the two together). It points at a small Google Apps Script that emails the alert's one-line summary. TWH still alerts nobody. Review found the request carried the whole alert, including a single-patient run's "Patient: " label and raw error text, although only the summary is shown. It is now limited to fields that cannot name a patient. A webhook that answers with an error is now logged as a failed delivery. A value that is not an https URL turns the webhook off and is never printed into the log.

2026-09-24

  • #623: the dashboard says when its numbers are stale. A failed nightly leaves the previous results in place, which is right, and said nothing. /programs now shows a banner when the latest overnight update did not finish (naming the last complete one), finished with errors for some patients, or has not run for 36 hours. The failure alert's webhook body now carries a one-line summary that Slack, Teams and Discord accept as it is, built only from fields that cannot name a patient; which channel it goes to is still to be chosen.

  • #644: Run History no longer takes every database connection. Loading the list counted every listed run's results at once (38 runs, each a count over up to 120,000 rows): 9.3 s cold, and during an all-programs run it used up all ten connections, so the page said "No database connection was available in time". A finished run's counts cannot change, so they are now kept (reset by outcome compaction, and expired after 10 minutes in case a maintenance script changed them from another process); the rest are read three at a time. A running run's duration shows its elapsed time instead of "0s".

  • The worker pool, measured live: an all-programs run (the nightly's 120,000 evaluations) took 48.7 minutes, against 88 for the in-process nightly, with no stall over 1.5 s.

  • #663: a frozen API leaves its evidence behind. The 22 September hang could never be explained, because the self-heal deleted the container and its logs. The watchdog thread, which keeps running while the server is stuck, now also writes its report (the requests in flight, how long, memory, the build) to a file on the container's disk once a freeze passes 30 s; the next boot shows it on the admin runtime view. And both self-heals now restart the container in place first (the platform's restart keeps the disk), recreating only if that fails. Recovery still waits for GitHub's slow schedule; a faster restart depends on whether the platform restarts an app whose process exits (asked of MIE).

  • WORKWELL_AUTH_ACCESS_TTL_SECONDS removed from the five deploy/reconcile workflows. It set a 30-day access token for MCP, but only the Java backend read it; the TypeScript port dropped the reader, so tokens have lasted 15 minutes since. Kept that way (a 30-day access token cannot be revoked, and #688 makes logout a real revocation). MCP.md now says 15 minutes and how to re-mint.

  • #615: every dashboard cache warm is now recorded. The first Programs load after a quiet stretch still timed out on the pilot (the overview ran 33 to 60 s), and the nightly's warm had not filled the caches it exists to fill, but nothing could say whether it had run. /health now shows the last warm (lastWarm: boot, nightly or after a run, how long, whether it worked) and /api/admin/runtime the last ten, with the error. Measured on a Neon branch of the pilot's database with its cache emptied: the warm itself failed, because the overview's "does this run hold this measure?" check took 26.6 s and the overview passed the 30 s limit, and the pass then gave up before warming any measure page. It now warms each measure's panels even when the overview fails. The outcomes (run_id, measure_id) index turns that check into a lookup (the cold warm then succeeds, and a measure's drivers load in 0.04 s instead of 16 s); it is added (owner DDL, built at boot, about 6 s on the pilot's data). Retention is not the cause (3.5 to 6 s a night). Deployed: the index is live. The boot that built it timed out on both warm attempts (the panels warmed anyway); the next boot (#688) warmed in 80 s with ok: true. Tonight's nightly record is the next evidence.

  • #688: a deploy no longer signs everyone out. Each login's current refresh-token id was kept in an in-memory store that every deploy or restart emptied, so the next refresh was refused and the user was sent to the login page. It is now a small database table (auth_refresh_families, owner DDL), and rotation, reuse detection and logout work as before, across a restart too. Login, logout and a replayed token are now audit events (AUTH_LOGIN, AUTH_LOGOUT, AUTH_REFRESH_REUSE_DETECTED, written before the store change); the 15-minute rotation inside a login is not (owner decision). Found on the way: the 30-day WORKWELL_AUTH_ACCESS_TTL_SECONDS the workflows set is read by nothing (tokens last 15 minutes); removed above (#703).

  • #604: the nightly no longer freezes the server. Each 500-patient chunk of an official measure was one synchronous fqm-execution call (~21 s), so the server could answer nothing for 20 to 25 s at a time through the ~80-minute nightly (a sign-in failed this morning). The calculation now runs in one worker thread and the main thread keeps serving; results are identical (checked on the Maui corpus for every routed measure, CMS137's rates and strata included). WORKWELL_FQM_WORKER=off puts it back in-process. /api/admin/runtime reports the container's cores and memory limit, which decide whether a pool could also shorten the run and how to bound the worker. Known limit: a single-patient official read during the nightly (/simulate, a rerun) still waits behind the chunk in flight, no worse than before. Verified live the same evening: a manual CMS125 run over 20,000 patients (22:44 to 22:56 UTC) caused no event-loop stall at all (the only one since the deploy was the 1 s at boot), and the warm after it succeeded.

  • A pool of calculation workers. The container has 4 cores, and a chunk's six measures were still calculated one after another. They now go side by side to a pool of 2 workers (WORKWELL_FQM_WORKERS, capped at the cores minus one): measured locally on 500 corpus patients, a chunk went from 27.5 s to 17.9 s (1.5 to 1.7x), so the ~80-minute nightly should take roughly 45 to 55. Each worker holds about 500 MB while busy and is released after 5 idle minutes; a third worker bought nothing measurable.

2026-09-23

  • The work is now one GitHub milestone, "Ready for January" (24 issues): the Maui sandbox's numbers agree on every screen, it stays up, nothing simulated is shown on Maui screens, the ACO's attributed-list report can be demonstrated, and PY2027 is ready. Asks blocked on MIE, the practice or the ACO carry the waiting label; minor findings are one checklist in #655. Open issues went from 108 to 52.
  • Docs trimmed. docs/archive/, finished plans, proposals, the July trial runbook and the stale DATA_MODEL.md were deleted (git history keeps them). ARCHITECTURE.md, DEPLOY.md and DATA_MODEL_CONTRACTS.md were condensed. The ADRs were retired: DECISIONS.md and ADR_INDEX.md are deleted, the three rules that governed and lived nowhere else moved to CLAUDE.md, AI_GUARDRAILS §7 and LOCKED_DECISIONS §4A.7, and an old ADR-0NN id resolves with git show fd243d34:docs/DECISIONS.md.
  • #677 (#642) was failing CI because VSAC intermittently answers 401 for a valid key and the vendoring script treated that as final. 401 and 429 are now retried.
  • #637 decided: no prior-year history. The corpus stays current-year only; 1 January starts near zero, as a real year-to-date report does. The work became "January readiness": no rate (not 0.0%) when nobody is counted, a year line on the programs page, trends and "from previous" kept within one measurement year (read from the run's own record, so a January rerun of the old year is labelled the old year), and a "based on N patients so far" note under 20 (CMS's case minimum).
  • Programs cards streamlined: one rate, the chips (no "Not in population"), the trend and the worklist link. The CMS measure rate and the staff-closed link moved to the measure page; the drivers were already there. The attributed-list report keeps its "Not measured" column.
  • CI made faster and quieter. About ten oversized backend tests evaluated far more patients than their assertions needed, or recomputed the same result per test; they now use a handful or compute once, with every check kept (the slowest file went from ~346 s to ~120 s alone). CI runs once per push (the duplicate pull_request run is gone), a hung VSAC request is abandoned after 90 s instead of 300, and docs-only merges no longer redeploy both stacks. The Maui Playwright suite now runs on every push. Deleted: the stale TWH Playwright suite (it targeted a July staging build), the weekly stub-engine scale job, the never-run redirect workflow, and the always-failing frontend Dependabot entry.
  • Security cleanup. Next.js 16.3.0 -> 16.3.6 closes two critical advisories (remote code execution via the AVIF image optimizer; the other applies only to Windows hosts), and patched versions of sharp, dompurify, undici, js-yaml, brace-expansion, browserslist, Babel, Vitest, immutable and the Hono server adapter close most of the rest (68 open alerts -> ~14). Left on purpose: hono and vite (installed as peers, which pnpm will not move; their advisories are in features we do not use), and csv-parse and uuid (major bumps the pinned engine libraries do not allow).
  • #618: the pilot's case managers can escalate and rerun a case. Rerun to Verify, Escalate and the next-step "Rerun to verify" were behind an admin-only gate, so both pilot accounts lost them and the next-step panel went blank once outreach was sent. They now show wherever the case actions do; only the simulated delivery-state controls stay admin-only. The next step follows the delivery state: verify after a send, retry a failed one, nothing while queued. Escalating a closed, resolved or excluded case is refused (it used to reopen it silently) and not offered.
  • Two tabs no longer sign the user out. A refresh rotates the login cookie and the server ends the login on a replayed one; two tabs refreshing at once (the work list in one, a patient in another) did exactly that. Every refresh now takes a cross-tab lock (Web Locks API), so the second tab sends the new cookie.
  • Case managers see findings, not engineering detail. From a live walk-through of the pilot: the case page no longer shows a case manager raw JSON, value-set OIDs or a why_flagged heading; the patient page drops the FHIR id and its "Recalculate", which started a whole-practice run of every measure; "View hierarchy" and a CLI hint on the measure page are gone. Admins keep all of it.
  • The AI surfaces now ask gpt-6-luna first and gpt-5.4-nano second. The client sent max_tokens and a temperature, both rejected by GPT-5-era models, so the old primary most likely failed on every call and gpt-4o-mini answered. It now turns reasoning off and sends max_completion_tokens. The audit records the model that answered, not the one configured.
  • #644 (first part): "Run This Measure" asks first. One click used to start a whole-practice run that slowed every page for about 15 minutes. A confirmation now says what it costs; the nightly run already covers it. The polling bursts in #644 remain.
  • #671: the patient page agrees with itself. Its posture and Measure Details now read an outcome the way its table does (out of population is "Not in population", not "Missing Data"), list only the measures the deployment runs (no old Hypertension on Maui), and name the official-only measures instead of cms130. The simulation runs only when asked, and names the four measures it cannot replay.
  • #668: Run History says what a run is and what its numbers mean.
    • A measure run is titled by its measure. It used to say "All Programs" for the four official-only measures, and for site and patient runs too.
    • The two rates are labelled: compliant of everyone evaluated, and the CMS measure rate of the measure's population.
    • Patients outside the population are counted on their own line and shown that way row by row, where they used to read "Missing Data".
    • The outcomes table says "Showing 5,000 of 20,000", and runs over an hour show their duration.
    • A failed load, and filters that exclude every run, no longer read "No runs yet", and a run the filters remove no longer leaves its detail behind.
    • The global site filter no longer empties the list; the list says it isn't filtered by site.
  • #659, #660: the work list names the measure and the patient. Four of the six measures read as raw ids (cms130) on the work list and case page, and in the cases CSV, two MCP tools and the People page; every one now uses the catalog name. Each work-list row shows the patient's ID beside the name, since two patients can share a name, clinic and provider.
  • Links, labels and numbers say what they mean (live walk-through): "Open Worklist" links that went to /cases now say "Open cases"; the patient page links back to the work list; the ACO list report and the roster's assign line name the measure (no cms122, no id twice); search results show the patient ID; /cases says "Loading cases…" instead of "0 cases loaded"; a case with no result shows a dash, not "0 days overdue"; the measure trend line is neutral, since green read as good news on CMS122.