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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions backend-ts/scripts/generate-webchart-import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*
* Emits MIE's documented Data-Migration CSV formats (verified from the `mieweb/docs` sources,
* 2026-07-16 — see the generated README for source URLs and upload order):
* 01-patients.csv Chart Data CSV API (demographics; `patients.*` + `@patient_mrns.MR` headers)
* 01-patients.csv Chart Data CSV API (demographics; `patients.*` + `@patient_mrns.MR`; zip_code required)
* 02-encounters.csv Clinical Encounter CSV API (office visits — the eCQM qualifying-visit IPP gate)
* 03-observations.csv Observation Import (18 fixed columns; labs + vitals)
* 04-injections.csv Injections CSV API (immunizations; CVX in `injections.inject_code`)
Expand Down Expand Up @@ -192,10 +192,19 @@ function isValidDate(s: string): boolean {
// emitters
// ---------------------------------------------------------------------------

// A single synthetic demo ZIP for the whole cohort (Elkhart, IN — MIE's home area; clearly demo, never
// PHI). The teatea trial's Chart Data CSV API validates `patients.zip_code` against a `12345`/`12345-6789`
// regex on EVERY row and rejects the header when it is absent — verified live 2026-07-16 (the exact error
// was "Patient zip code must be formatted either 12345-6789 or 12345"; `@patient_mrns.MR` and all other
// `patients.*` columns validated). See docs/WEBCHART_TEATEA_RUNBOOK_2026-07-16.md §4.
const DEMO_ZIP = "46514";

function patientsCsv(people: Person[]): string {
const header = "@patient_mrns.MR,patients.first_name,patients.last_name,patients.birth_date,patients.sex,patients.email";
const header =
"@patient_mrns.MR,patients.first_name,patients.last_name,patients.birth_date,patients.sex," +
"patients.zip_code,patients.email";
const lines = people.map((p) =>
row([p.mrn, p.first, p.last, `${p.birthDate} 00:00:00`, p.sex, `${p.mrn.toLowerCase()}@workwell-demo.dev`]),
row([p.mrn, p.first, p.last, `${p.birthDate} 00:00:00`, p.sex, DEMO_ZIP, `${p.mrn.toLowerCase()}@workwell-demo.dev`]),
);
return [header, ...lines].join("\n") + "\n";
}
Expand Down Expand Up @@ -308,11 +317,12 @@ Generated by \`pnpm generate:webchart-import\` (WorkWell). **Synthetic data —
Tick **Verbose** on the upload form the first time; a failed-rows file can be downloaded, fixed, and
re-uploaded. **Test with 2–3 rows first** (MIE's own best-practice note).

## Caveats (verify on first upload; formats sourced from the mieweb/docs repo 2026-07-16)
## Caveats (verified live against teatea 2026-07-16 via the Data-Import "Validate File" dry-run)

- \`patients.sex\` follows the documented \`patients.<db_column>\` header pattern but its exact header
string was not byte-verified — if the importer rejects it, check the "Sample Demographics File"
tab of MIE's Chart Data CSV spec sheet and rename.
- **\`patients.zip_code\` is REQUIRED** and validated \`12345\`/\`12345-6789\` on every row — omitting it
fails the header ("does not match full list") AND every data row. The generator now emits a synthetic
demo ZIP. \`@patient_mrns.MR\` (partition \`MR\` — confirmed present in teatea's partition list) and all
other \`patients.*\` columns (incl. \`first_name\`/\`last_name\`/\`birth_date\`/\`sex\`/\`email\`) validated.
- \`part:MR\` as \`pat_id_type\` (encounters/injections) is inferred from the documented \`part:\` id-type
prefix. If rejected, re-run the failed rows with \`id:pat_id\` after looking up the chart ids, or
add \`patients.extern_id1\` to 01 and use \`id:ext_id\`.
Expand Down
26 changes: 26 additions & 0 deletions docs/JOURNAL.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Journal

## 2026-07-16 — teatea runbook executed live: registration is MIE-gated, seed contract verified

Ran the teatea trial runbook (`WEBCHART_TEATEA_RUNBOOK_2026-07-16.md`) end-to-end as System Owner.
Two decisive outcomes:

**§§2–3 (client registration + auth probe) are blocked on MIE — proven, not assumed.** The SMART
Backend Services client registry is the `login_trusts` table, reachable only via the **superuser**-gated
JWT / Login-Trusts screen. WebChart "superuser" is **not** the SuperUser security role and **not** the
`Manage Login Trusts` ACL — both were set and the screen still returned "Super user access required";
superuser is an MIE-issued session unlock (master password) held by MIE's internal accounts. **RFC 7591
is off** (`/register` + `/oauth/register` return the login HTML; no `registration_endpoint`), and the
Application-Entities editor is DICOM, not an OAuth registry. A second independent research pass over the
public `mieweb/docs` sources reached the same conclusion. The sharpened MIE ask (register `workwell-backend`
with our hosted JWKS, or grant superuser + the FHIR App Editor) is now recorded in the runbook §3 and the
#254 answer log. §5 (live evaluation) is blocked behind this — nothing to read until a client exists.

**§4 (synthetic seeding) contract verified live and the generator fixed.** teatea's Data Import →
Chart Data CSV "Validate File" dry-run established the real contract: **`patients.zip_code` is required**
(`12345`/`12345-6789`) on every row (the sole cause of the first run's 451 validation issues), partition
`MR` is valid, and all `patients.*` demographic columns validate. `scripts/generate-webchart-import.ts`
now emits a synthetic demo ZIP; typecheck green. The seed is ready to upload, but its payoff — a live FHIR
read-back — still waits on the registration above, so the actual upload + `pnpm evaluate:webchart-live`
run are deferred until MIE unblocks auth (no point seeding a population we can't yet read over FHIR).
Keypair (§1) generated and kept in `~\.workwell\` (never committed). Docs-only change here; no runtime,
schema, dependency, PHI, or Outcome Status behavior touched (ADR-008).

## 2026-07-16 — MIE manager outage: bounded, method-safe deploy retries

The WebChart stack's final deploy exposed a pre-existing failure mode in
Expand Down
21 changes: 21 additions & 0 deletions docs/MIE_INTEGRATION_QUESTIONS_2026-07-09.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,27 @@ what does its contract look like?
confirm backend-services support on the sandbox. Details: `INTEGRATION_RESEARCH_2026-07-13.md`
§1.1a. Meanwhile the transport itself is already rebuilt to the verified contract (E12 PR-2c
branch; ADR-028) — only credentials stand between us and a live sandbox evaluation.
- **2026-07-16 (teatea trial — live owner walkthrough; runbook §§1–5 executed).** Ran the teatea
runbook end-to-end as System Owner. Findings:
Comment on lines +230 to +231

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark only the runbook sections that actually ran

The answer log says §§1–5 were executed end-to-end, but the same entry states that §3's auth probe and §5's live evaluation are blocked, while the journal says the actual upload was also deferred. Readers using this log as the integration source of truth could therefore treat pagination, FHIR representation, and evaluation as live-validated when they remain pending; describe this as a walkthrough with only §1 and §4 validation completed.

Useful? React with 👍 / 👎.

- **A3 residual is confirmed MIE-gated (not self-serviceable on the trial).** Client registration
lives in the `login_trusts` table, reachable only via the **superuser**-gated JWT / Login-Trusts
screen. WebChart "superuser" is **not** the SuperUser security role and **not** the
`Manage Login Trusts` ACL — both were set and the screen still returned "Super user access
required." Superuser is an **MIE-issued session unlock (master password)**, held by MIE's internal
accounts only. **RFC 7591 is OFF** (`/register` + `/oauth/register` return the login HTML; no
`registration_endpoint` advertised). The Application-Entities editor is DICOM, not an OAuth
registry. **⇒ The sharpened A3 ask stands and is now unavoidable:** *MIE must either register the
`workwell-backend` backend-services client (JWKS we host) or grant our System Owner the superuser
unlock + FHIR App Editor.* Exact ask text: `WEBCHART_TEATEA_RUNBOOK_2026-07-16.md` §3 "MIE ask."
§3 auth probe + §5 live evaluation are **blocked on this** (nothing to probe/read until a client
exists). **A2/A4/A5 (pagination / rate-limit / observation representation) remain pending the live
read** — they'll be recorded the moment MIE unblocks registration.
- **B12 / seeding contract — verified live (unblocks §4 independently of auth).** teatea's Data
Import → **Chart Data CSV API** requires a well-formed **`patients.zip_code`** (`12345`/`12345-6789`)
on every row; partition **`MR`** is valid; all `patients.*` demographic columns validate. The import
generator (`pnpm generate:webchart-import`) was corrected to emit the ZIP. Observation Import keys on
observation **name**, not LOINC (as documented). So synthetic seeding is ready to run — but its payoff
(a live FHIR read-back) still waits on the A3 registration above.
- **2026-07-16 (Doug meeting 2026-07-15 + teatea trial probe):** **A1 CONFIRMED** (FHIR R4 is the
surface), **A3 CONFIRMED** (SMART; registration is manual + self-serviceable on the trial via
`webchart.cgi?f=admin&s=jwt`), **C13 ANSWERED** (trial instance `teatea.webchartnow.com`, System
Expand Down
57 changes: 57 additions & 0 deletions docs/WEBCHART_TEATEA_RUNBOOK_2026-07-16.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,25 @@ Verified facts this runbook builds on (probed 2026-07-16):
- Client registration is manual, at the admin **JWT screen**: `…/webchart.cgi?f=admin&s=jwt`
(it is the smart-configuration's `management_endpoint`)

> **⚠ Live-execution finding (2026-07-16) — §§2–3 are blocked on MIE; do NOT expect to self-serve.**
> A full browser walkthrough as System Owner established that client registration is **MIE-controlled**,
> not owner-serviceable on this trial (independently corroborated by a second research pass over the
> public `mieweb/docs` sources). Concretely, all of these were tried and failed:
> - `?f=admin&s=jwt` and `?f=admin&subfunc=login_trusts` → **"Super user access required."**
> - WebChart **"superuser" is NOT the "SuperUser" security role** and NOT the `Manage Login Trusts`
> ACL. Setting the account to the SuperUser role (security_role_id 12) and granting the ACL still
> gated. Per MIE docs, the Login Trusts editor lives under **"Control Panel → SuperUser"**, and
> superuser is a **session elevation requiring an MIE-issued master/unlock password** — held by
> MIE's internal accounts only (in the seeded dev DB the superuser accounts are `mie`/`cronjobs`,
> not any customer role).
> - **RFC 7591 dynamic registration is OFF**: `POST …/webchart.cgi/register` and `…/oauth/register`
> both return the HTML login shell (not JSON); smart-configuration advertises no `registration_endpoint`.
> - The **Application Entities editor is DICOM** (AE-titles), not an OAuth/FHIR client registry — dead end.
>
> **⇒ The `login_trusts` table is the only client registry, and writing it needs superuser. Steps 2–3
> below cannot complete without MIE.** Send the sharpened ask in **"MIE ask"** (end of §3), then resume
> at §3 once MIE either registers `workwell-backend` or grants superuser + the FHIR App Editor.

---

## 1. Generate the RS384 keypair (once, OUTSIDE the repo)
Expand Down Expand Up @@ -87,6 +106,30 @@ never prints key/assertion/token material. Three steps: discovery → the RS384
- **invalid_client / invalid_scope** → the probe prints a targeted hint (registration/JWKS
mismatch vs scope form); adjust and re-run.

### MIE ask (send this — §2 is MIE-gated, see the finding box up top)

Because self-registration is blocked (superuser is MIE-controlled; RFC 7591 off), send MIE this
request. It resolves the §254 A3 residual either way (they register us, or they grant superuser +
the FHIR App Editor so we self-serve):

> I am the verified System Owner of the WebChartNow trial `https://teatea.webchartnow.com/`. My
> account has the SuperUser security role and the Manage Login Trusts permission, but both
> client-registration routes return "Super user access required" (`?f=admin&s=jwt` and
> `?f=admin&subfunc=login_trusts`). Please either **(1)** grant my System Owner account SMART/FHIR
> app-registration access — the MIE-issued Super User unlock password + the FHIR App Editor — or
> **(2)** register this SMART **Backend Services** client for me:
Comment on lines +118 to +120

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the request for MIE's superuser unlock password

If the owner sends this template as directed, it asks MIE to disclose a privileged master/unlock credential that this same runbook says is reserved for MIE's internal accounts. That grants substantially broader access than the required SMART client registration and creates an avoidable credential-sharing risk; request vendor-side registration or a supported least-privilege delegated app-registration role instead.

Useful? React with 👍 / 👎.

> - Client name: `WorkWell Measure Studio` · preferred client id: `workwell-backend`
> - Grant type: `client_credentials` · token-endpoint auth: `private_key_jwt` · alg: **RS384**
> - JWKS URL: *(I will host the public JWKS at an HTTPS URL — the `webchart-teatea.pub.jwk.json` from §1)*
> - Redirect/launch URI: **none** (backend service, no browser flow)
> - Scopes: `system/*.read` (and `patient/*.rs` if supported for a backend context)
>
> Please confirm the assigned `client_id`, the pre-authorized scopes, that the token endpoint accepts
> `client_credentials` + RS384 `private_key_jwt`, and whether smart-configuration will advertise
> `client_credentials`. I will never share the private key — only the public JWKS URL.

(I keep the private key in `~\.workwell\`; MIE only ever needs the **public** JWKS.)

## 4. Seed the synthetic population (~30 patients)

FHIR on teatea is read-scoped, so data enters through WebChart's own import tooling / UI.
Expand All @@ -99,6 +142,20 @@ cd backend-ts
pnpm generate:webchart-import --patients 30 --out ..\webchart-import
```

> **✓ Live-verified 2026-07-16 (Data Import → "Validate File" dry-run on teatea).** The first validation
> pass taught us the instance's exact Chart Data contract and the generator was corrected accordingly:
> - **`patients.zip_code` is REQUIRED** and validated `12345`/`12345-6789` on **every** row — omitting it
> fails both the header ("does not match full list") and every data row. The generator now emits a
> synthetic demo ZIP (`46514`). *This was the whole cause of the first run's 451 validation issues.*
> - `@patient_mrns.MR` is correct — **`MR` is a real partition** on teatea (confirmed in the partition
> list) — and `patients.first_name/last_name/birth_date/sex/email` all validated.
> - The instance's own **Sample** (Data Import → Sample → `Sample_Chart_Data.csv`) is the authoritative
> header if a column is ever rejected; open it and match names.
>
> Always **Validate File** (non-destructive) before **Upload File** — it returns a per-row issue log and a
> "Download Failed Test Data File" button. The Data Import modal is heavy; if it fails to open, reload the
> page and reselect the row.

This emits MIE's documented Data-Migration CSV formats (verified against the `mieweb/docs` repo
sources, 2026-07-16 — the rendered docs live under
`docs.enterprisehealth.com/functions/system-administration/data-migration/`):
Expand Down
Loading