Self-directed build · Interactive simulation

Two records that look identical until one of them isn't.

A non-clinical care coordinator works a post-discharge outreach queue. Two members surface the same number of concerning signals. Escalating is the right call for one and the wrong call for the other, and nothing in the interface says which is which.

Role

Instructional design + interaction design

Client

Fictional — Bellwether Health Plan

Built with

Claude · v0 · Figma · Relume

Year

2026

About this piece. Decision Lab is a self-directed portfolio build, not a paid client engagement. The client, the member records, and every detail in the scenario below are fictional and illustrative. Every source cited in the evidence base was individually opened and verified before use — the survivors and the discard ledger both ship with it. The record-access model was confirmed with a health-plan subject-matter expert rather than assumed.

01 · The Problem

Nothing on the record was wrong.

"I read the whole record. Everything on it was normal. He was back in the hospital eleven days later."

— a care coordinator, describing the call she thinks about most

Every item on that record cleared its own threshold. No alert fired. The pattern only existed if someone put the pieces next to each other — which is a skill a checklist can't teach, because a checklist is a list of individual items and the entire problem is that individual items pass.

Decision Lab puts a learner at a desk with six records inside one care management member profile: a care plan, a contact log, read-only claims and pharmacy feeds, a prior-auth history, and one assessment that has to be retrieved from an archive. They flag what reads as a signal, spend a real time budget opening records to find out, and commit to one of four next steps. Then they do it again with a second member whose file looks almost identical and resolves the opposite way.

The Decision Lab workspace: a member identity rail beside six record tabs, with a claims feed open, all inside one care-management shell.
Six records, four read-only feeds, one screen. The shell reports "4 feeds, none reconciled"; the open panel is stamped with where its data came from and when it last synced. Nothing is flagged and no alert has fired — which is the whole problem. Everything a coordinator needs is already in front of them, and nothing joins it up.

02 · The Move

Two cases, and why that was harder than it sounds.

The first build had a real bug, caught in testing: the second case was a patch on the first — five fields overridden, everything else inherited. That included the signal map and the feedback tree, so the second case was being scored against the first case's answer key. A learner who got case two right was told they'd gotten it wrong, while the system quietly recorded a success.

Before

Case B was five overridden fields on top of Case A. Everything not overridden — including the signal map and the whole feedback tree — was inherited.

After

Two fully independent case files, same shape, sharing nothing but a schema. Records, signals, decisions, feedback and debrief are stated separately for each member — deliberately duplicated, not inherited.

The fix wasn't a patch on the patch. It was structural, because inheritance is what let the bug happen twice. If the two cases could contaminate each other's scoring, the claim that this measures discrimination rather than suspicion wouldn't be true. Adding a third case now means adding a file, not touching the engine.

A debrief screen badged 'Missed it', headed 'You spent an RN on a member who has support at home', ending with a card that reads the learner's own run back to them.
The failure mode most training never scores: escalating the member who was fine. Every surface signal had its explanation sitting in the same record set. The referral comes back with nothing to do, and it is paid for by the person who actually needed the RN. Escalating both members is not vigilance — it is declining to read.

03 · What It Asks of a Learner

Four feeds, one screen, a budget that never shows its hand.

All six records live on one screen, inside the care management profile a non-clinical coordinator actually works in. Claims and pharmacy data arrive there as read-only feeds on their own overnight schedules, each stamped with where it came from, when it last synced, and what it reconciles against: nothing.

The panels look different because a claims extract doesn't look like a care plan note, even inside one product. But nobody had to go anywhere to see all of it. It was already in front of them.

5 signals each

Both members surface the same number of concerning items — and the right answer is opposite

1 record decides it

The deepest and most expensive to open, and the only place the answer is written down

Opening a record for the first time costs minutes from a 105-minute shift budget, scaled to depth — and that cost is never shown as a hint. Anything can be flagged, whether or not it turns out to be a signal; a false flag counts against the read, not just a missed one.

The workspace with the desk-time bar at zero and a red banner reading 'Your desk time is gone.'
Assessment mode puts more material against the clock than the budget covers. Here the budget is spent and two records were never opened — the decision gets made without them. Practice mode removes the budget entirely, and it is the default, because a timed task is a barrier.

Then the learner commits, and the system tells them what happened — including a "three weeks later" consequence beat that plays out narratively, before any coaching voice. A consequence folded into feedback reads as a grade. A consequence that arrives as a story reads as what it actually is.

A retrieved archive document stamped 'ARCHIVED READ ONLY', headed 'Health risk assessment', completed 12/2024.
Twenty months old, sitting behind a retrieval request, and the only place the transportation loss is written down at all. It costs the most to open, and it is the record that explains the other five. Most coordinators never open it — which is a records-design problem at least as much as a training one.

04 · How It's Built

Accessibility is a gate here, not a polish pass.

The design brief names specific failure modes rather than a generic "make it accessible": a tab pattern that declares ARIA roles but binds only to a mouse click is worse than not declaring the pattern at all. A countdown that updates without an announcement leaves a screen-reader user guessing. A time budget not explicitly labeled as a resource — not a wall clock — invites a WCAG objection on its own.

None of these are hypothetical. They are documented defects from an earlier pass at this build, kept in the project notes on purpose so they don't ship a second time. The record tabs now carry a full roving-tabindex pattern with arrow keys; the budget announces through a dedicated live region; and every color pairing in the build was checked by script rather than by eye.

Every cited fact in the evidence base was opened and individually verified before use, after an earlier draft's citations didn't hold up under checking. The evidence base ships with its discard ledger next to the surviving sources, not hidden.

Each tool did one job.

Claude — scenario logic, records, decision tree, signal map v0 — the React interface, built against that data as a fixed contract Figma — the user flow and decision tree Relume — page layout Veracity LRS — xAPI record store, reporting

Each stage handed the next a written brief instead of a conversation — which is why a defect like the shared answer key gets caught before it reaches the next stage instead of after.

A decision tree: two cases branch into four decisions each, and the correct decision in each case splits again depending on whether the learner flagged the decisive signal.
The decision tree. Every leaf carries the exact feedback line the build will show, pulled from the case files rather than retyped. Eight paths, two of which fork on whether the decisive signal was flagged — ten endpoints, not the sixteen a "2 × 4 × 2" description implies. Only the correct decision in each case splits; the other three are flat.
A flow diagram of the eight screens, from cold open through pre-brief, the shift loop, decision, escalation note, consequence, debrief and knowledge check.
The user flow. All eight screens, including the two mode branches at the pre-brief and the forced-decision path taken when the time budget runs out mid-shift. Replay routes back through the pre-brief rather than straight into the shift, so the second case starts with its own framing instead of inheriting the first one's.

05 · How It's Measured

Measured, not marked complete.

A completion flag would say a learner reached the end. It would say nothing about whether they opened the record that mattered, or flagged noise as signal. So the build reports the decision itself.

Every run emits xAPI statements to a learning record store, in the order the work happens: initialized, inspected for each record opened, noticed for each flag raised, decided, terminated. The decision statement carries the instrument — how much of the evidence was opened, how many flags matched a real signal, how many did not, and whether the one record that would have changed the answer was ever read.

5

verbs per run — initialized, inspected, noticed, decided, terminated

7

result measures on every decision, including evidence completeness and flag precision

What one decision looks like in the store.

"actor": { "objectType": "Agent",
  "account": { "homePage": "https://jtlearningstudio.com/decision-lab/",
               "name": "1b397706-1941-496c-99cd-75fbf6e3f48c" } }
"verb":   { "id": ".../verbs/decided" }
"object": { "id": ".../activities/decision-point/next-step" }
"result": { "success": true, "response": "followup",
  "extensions": {
    ".../decision-class":          "followup",
    ".../time-to-decision-ms":     3012,
    ".../evidence-completeness":   0.667,
    ".../false-positives":         2,
    ".../flag-precision":          0,
    ".../opened-decisive-record":  true,
    ".../minutes-spent":           22 } }

A real statement from the store, abbreviated. A correct next step on the benign case, reached with two flags that were not signals — a right answer on imprecise evidence, which is the distinction a completion flag cannot make.

The store never receives a name.

Statements leave the browser for a same-origin proxy that holds the credential; the client carries no key. The proxy replaces the learner's identity with a pseudonymous account before forwarding, so the store holds decisions, not people — the account.name above is an identifier, not a person.

One rule is asserted on every build rather than trusted: what the screen says and what the statement records must agree. If feedback fires a miss, the statement cannot report success. An earlier version scored one case against the other's answer key, and the telemetry recorded success while the learner was told they were wrong.

Twelve demonstration runs, labeled as such.

To exercise the reporting end to end, twelve runs were driven through the live build — six per case, all four decisions, one to five records opened, none to eight flags. Every statement is tagged as demonstration data in the store. They show that the pipeline works and what a report looks like. They are not learner outcomes, and nothing here treats them as one.

Statements per actor by verb, ten most active pseudonymous actors Horizontal stacked bars. 9cf3ad13… 18 statements (inspected 6, noticed 9, decided 1); 924aa098… 18 statements (inspected 6, noticed 9, decided 1); 31dfabe5… 15 statements (inspected 5, noticed 7, decided 1); 0d3b4aab… 14 statements (inspected 6, noticed 5, decided 1); 4af097eb… 13 statements (inspected 4, noticed 6, decided 1); cad34ec8… 11 statements (inspected 4, noticed 4, decided 1); b82ac150… 11 statements (inspected 3, noticed 5, decided 1); 29592420… 11 statements (inspected 4, noticed 4, decided 1); bc71614b… 10 statements (inspected 4, noticed 3, decided 1); 1b397706… 10 statements (inspected 4, noticed 3, decided 1). Every actor also has one initialized and one terminated statement. initialized / terminated inspected noticed decided 5 10 15 statements in the store 9cf3ad13… 18 924aa098… 18 31dfabe5… 15 0d3b4aab… 14 4af097eb… 13 cad34ec8… 11 b82ac150… 11 29592420… 11 bc71614b… 10 1b397706… 10
What the store can answer that a completion flag cannot: how much each learner looked before deciding. Ten most active actors from the Veracity LRS "Actor Behavior" aggregate, September 16, 2026 — re-plotted from the LRS figures for legibility. Actor labels are the proxy-issued pseudonyms, truncated. Store-wide: 218 statements — 90 inspected, 68 noticed, 28 initialized, 16 decided, 16 terminated.

06 · Where It Stands

What's verified, and what isn't yet.

Said plainly, because a portfolio piece that only shows the finished parts isn't a credible one.

  1. 01

    The scoring bug is fixed and verified directly against both case files — each carries its own complete signal map, feedback and debrief, asserted on every build.

  2. 02

    The build reproduces from source, so what ships matches what is on disk, and the screenshots on this page are scripted captures of the real build rather than hand-shot stills.

  3. 03

    Contrast is checked by script, not by eye. A structural border token was failing the non-text contrast requirement at 2.01:1; it was found by computing every token against every surface it sits on, and the check now runs on every build so it cannot regress.

  4. 04

    The record-access model was confirmed by a health-plan SME — what a non-clinical coordinator can see, and where the boundary sits between reading a determination and disputing one.

  5. 05

    Telemetry is verified end to end — statements leave the live build, pass the proxy, and land in the record store with the full result payload; the count in the store rose by exactly the number sent.

  6. 06

    Not yet done: a screen-reader pass. The keyboard path has been walked and the ARIA is right on inspection, but no screen-reader user has tested it, and inspection is not the same evidence.

  7. 07

    Not yet done: usability testing. No learner outside the studio has run this end to end. Any claim about what it teaches is a design argument, not a finding.

  8. 08

    A cohort label now reaches the store. The proxy passes only what its manifest allows, and a cohort was not on that list — so for a day, reports could not filter by cohort. The manifest is regenerated from the emitter, and the proxy now keeps one validated cohort grouping beside the fixed program grouping. Verified by sending one labeled statement and finding the cohort in the store's grouping chart. Runs made before the change carry no label; that is left as it is rather than back-filled.

Have a decision your people make on incomplete records?

That is the shape of most high-stakes work — a screen that looks routine, and a call that has to be made anyway. If you want practice that measures the decision instead of the click, this is what it looks like built.

Read the rest, or run it yourself.

The build takes about fifteen minutes to walk end to end, both cases.