Every score, rate, projection and settlement figure mdvin puts on a screen — the question it answers, the logic behind the way it is built, the formula, a worked example in real numbers, and how to check it without taking our word for anything.
A platform that ranks people for clinical outreach and estimates money owed under a contract is making claims about the world. Those claims are worth exactly what the arithmetic behind them is worth, so this document states all of it: not a summary of the approach, but the formula behind each output with the function that computes it named beside it.
Every calculation here is held to three commitments, and they are the reason the engines look the way they do rather than the way a modelling exercise would look:
The worked examples are not illustrations. Each is the engine's own output on the
demonstration population, printed by
Docs/evidence/2026-09/2026-09-24_worked_examples.py and quoted here unchanged.
These are not style preferences. Each was taken for a reason specific to running clinical outreach under a regulated contract, and each costs something — which is why they are stated rather than assumed.
The obvious instinct for risk stratification is gradient boosting. It is the wrong first move here, for three reasons. The output format already demands reasons — the member panel renders each risk factor with its provenance, which a rule engine produces natively and a tree ensemble only ever reconstructs afterwards. A risk tier drives outreach, and outreach is regulated — a score that affects what care somebody is offered must be explainable, disclosed and defensible in discovery. And there is no training data: a synthetic cohort is not a training set, and fitting to it would produce a model whose version string meant nothing. When real longitudinal data exists these rules become the baseline a learned model has to beat, and the factor format they already emit is what makes that comparison possible.
No score in this paper has a column in the database. A persisted priority is stale the moment a clock moves, and it would need its own recompute job that can drift from what the screen shows. The cost is that every page load does the arithmetic; the benefit is that the number on screen is always the number the current facts produce. Risk scores are the deliberate exception: each run is appended as history, so what a score meant last quarter is still recoverable.
Measure specifications, targets, contract terms and SLA lengths are versioned records rather than constants in a source file, so changing one is a data change with an audit row behind it and a measure's numerator can be restated without a release. What stays in code is the logic that must not vary by tenant: what it means for a gap to be open, who may write to a member's record, when an authorization blocks scheduling.
Every recommendation names the endpoint a human would call and calls none of them. The server applies its own guards regardless of what the screen suggested — scheduling stays blocked while a linked authorization is pending, and the refusal is visible rather than pre-empted. The platform connects the workflow; the clinician provides the judgment.
Of the people this plan is responsible for, who is most likely to have an expensive, avoidable year — and why, in terms a clinician can argue with?
Risk is additive across independent signals, each drawn from a different source system, because that is how a clinician reasons about it: a diagnosis list, recent labs, social circumstances, what the hospital sent over, whether the member is actually taking the medication. Each rule that fires contributes its points and records what it saw and where it came from, so the score is never more than the sum of statements you can check one at a time. Only the most severe band per lab fires, so one bad result is counted once rather than twice.
score = the sum of the points of every rule that fires tier = HIGH if score ≥ 60 · RISING if ≥ 35 · MEDIUM if ≥ 15 · else LOW
| Active condition | Points |
|---|---|
Heart failure I50.9 | 14 |
Chronic kidney disease stage 3 N18.3 | 12 |
Coronary artery disease I25.10 | 10 |
Diabetes with hyperglycemia E11.65 | 10 |
Type 2 diabetes E11.9 | 8 |
Hypertension I10 | 5 |
Obstructive sleep apnea G47.33 | 4 |
Hyperlipidemia E78.5 | 4 |
| Three or more chronic conditions | +8 |
| Measured value — latest, 730-day lookback | Points |
|---|---|
HbA1c above 9.0 4548-4 | 18 |
| HbA1c above 8.0 | 10 |
Systolic BP above 160 8480-6 | 14 |
| Systolic BP above 140 | 9 |
Creatinine above 1.5 2160-0 | 10 |
Total cholesterol above 240 2093-3 | 6 |
Only the most severe band per lab fires: an HbA1c of 9.4 scores 18, not 28.
| Social risk — the newest screening answer per Z-code, counted when it is not negative (0, false, negative, no, no risk, none) and not blank; once per Z-code; no lookback | Points |
|---|---|
Homelessness Z59.0 | 10 |
Food insecurity Z59.4 | 8 |
Transportation insecurity Z59.82 | 7 |
Social isolation Z60.2 | 5 |
| Utilisation (365-day lookback) and adherence (current PDC, no lookback) | Points |
|---|---|
| ED visits, 6 each | capped 18 |
| Inpatient admissions, 10 each | capped 20 |
| Each active medication below 80% PDC | 7 |
| Points | Factor that fired | Source |
|---|---|---|
| 12 | Chronic kidney disease stage 3 (N18.3) | problem_list |
| 10 | Diabetes with hyperglycemia (E11.65) | problem_list |
| 10 | HbA1c above 8% (was 8.9%) | lab |
| 10 | Creatinine above 1.5 (was 1.8 mg/dL) | lab |
| 9 | Systolic BP above 140 (was 148.0 mmHg) | ehr |
| 8 | 3 chronic conditions | problem_list |
| 8 | Food insecurity (Z59.4) | survey |
| 7 | Atorvastatin 40mg adherence 62% (below 80%) | pharmacy |
| 6 | 1 ED visit in 12 months | adt |
| 5 | Hypertension (I10) | problem_list |
| 85 | → tier HIGH (60 or above) | 10 factors, 6 source systems |
For this measure, is this member counted — and if they are counted, have they had the care it asks for?
Four buckets, mutually exclusive, so no member is ever both excluded and counted. The distinction that matters most is between not in the denominator (the measure does not apply to them) and excluded (it would apply, but a clinical exclusion releases them): conflating those two is the error that quietly inflates a rate. Denominator, numerator, exclusions and target all come from the versioned measure package rather than from code, so a specification change is a data change with a version attached.
| Bucket | Rule |
|---|---|
| Not in denominator | Age today outside the package's minimum or maximum, or the package names condition codes and the member has no active condition among them |
| Excluded | In the denominator, but holds an active condition listed in the package's exclusions |
| Compliant | In the denominator, not excluded, and the numerator is met |
| Open gap | In the denominator, not excluded, and the numerator is not met |
Always inside the package's lookback window, 365 days by default:
How is the plan performing on this measure, and how much of that performance is still recoverable this year?
D = members in the denominator and not excluded (denominator) N = of those, the compliant (numerator) X = the excluded (reported, outside D) G = D − N (open gaps) rate% = round(100 × N / D, 1) undefined, shown as a dash, when D = 0
| Measure | D | N | X | Open | Rate | Target |
|---|---|---|---|---|---|---|
| Diabetes: Eye Exam Performed | 6 | 0 | 0 | 6 | 0.0% | 70 |
| Diabetes: HbA1c Control (<8.0%) | 6 | 2 | 0 | 4 | 33.3% | 75 |
| Statin Adherence (PDC ≥ 80%) | 8 | 2 | 0 | 6 | 25.0% | 80 |
| Annual Wellness Visit | 8 | 2 | 0 | 6 | 25.0% | 60 |
| Controlling High Blood Pressure | 10 | 4 | 0 | 6 | 40.0% | 75 |
Eye exam: 100 × 0 / 6 = 0.0%. Blood pressure:
100 × 4 / 10 = 40.0%. In every row N + open = D — the numerator and the open
gaps account for the whole denominator, with the excluded reported separately. The rows count
the members attributed for the current period — the contract's population, the
same one PMPM and settlement divide by. Every member is evaluated, so the demo's unattributed
duplicate record (“Jon Doe”) still has his wellness-visit gap opened and worked,
but he is not in the wellness visit's D of 8.
One number for quality, because the contract settles on one number.
composite% = round( mean( rate% of every rated measure ), 1 )
(0.0 + 33.3 + 25.0 + 25.0 + 40.0) / 5 = 123.3 / 5 = 24.66, shown as 24.7% — the composite the contract is judged against.
Unweighted, because that is the demonstration contract's stated scoring method. A programme that weights its measures would change every number in the opportunity table below, and the weighting would live in the contract terms rather than in code.
There are twenty-eight open gaps and a team that can work a handful this month. Which ones move the number the contract settles on?
“Opportunity” is defined here rather than left to intuition: it is the number of composite points the score would gain if every open gap in that measure closed. That is a real, checkable quantity, and it is what makes the ranking arguable — a measure twenty points below target with two eligible members matters less than one five points below target with thirty. Ranking by “most gaps” or “furthest below target” gets both of those backwards.
With n = the number of rated measures and target = the package's own target percentage:
| Column on screen | Formula |
|---|---|
| Performance | rate% |
| Target | from the versioned measure package; “no target set” when absent |
| Gap to target, in points | round(target − rate%, 1) — positive means short |
| “N members would close it” | max( ceil(D × target / 100) − N , 0 ) |
| Open gaps | G |
| Composite points per gap | round(100 / D / n, 2) |
| Composite points available | round(100 / D / n × G, 2) — from the unrounded points per gap, so this figure and the scenario engine cannot drift apart |
| Opportunity band | Low if at or above target · else High if available ≥ 3.0 · Medium if ≥ 1.0 · else Low |
Why 100 / D / n — two separate facts, one after the other:
closing one gap raises that measure's rate by 100 / D points,
and the composite is the unweighted mean of n measures,
so the composite moves by 1/n of that.
| Measure | D | Open | 100 / D | ÷ n = 5 | On screen | Available |
|---|---|---|---|---|---|---|
| Diabetes: Eye Exam Performed | 6 | 6 | 16.67 | 3.3333 | 3.33 | 20.0 |
| Diabetes: HbA1c Control | 6 | 4 | 16.67 | 3.3333 | 3.33 | 13.33 |
| Statin Adherence | 8 | 6 | 12.50 | 2.5000 | 2.5 | 15.0 |
| Annual Wellness Visit | 8 | 6 | 12.50 | 2.5000 | 2.5 | 15.0 |
| Controlling High Blood Pressure | 10 | 6 | 10.00 | 2.0000 | 2.0 | 12.0 |
Five different numbers from one rule, with no exceptions. A formula that explains only the row being questioned is a rationalisation; one that explains all five is the method. Note also that the ranking is not by open gaps — the wellness visit, statin adherence and blood pressure each have six open gaps, as many as the eye exam, yet are worth 15.0, 15.0 and 12.0 against the eye exam's 20.0. Equal points rank by title, so the wellness visit is second.
D = open ÷ (1 − rate). Eye exam: 6 ÷ (1 − 0) = 6. Wellness
visit: 6 ÷ (1 − 0.25) = 8. Then 100 ÷ D ÷ 5 reproduces the
points per gap. Nothing else is needed, and nothing has to be taken on trust.The contract pays nothing on savings unless quality clears a gate. What is the cheapest route from where we are to where we must be, counted in gaps closed?
Greedy — take the measure with the highest points per gap first, then the next. Greedy is not an approximation here, it is optimal: every gap within one measure is worth exactly the same number of composite points, so taking the highest-value measure first can never cost a cheaper route later. That is a property of the unweighted mean, and it would stop being true under a weighted contract — at which point this function would have to change, and this paper would say so.
gate = contract.minimum_quality_pct shortfall = round(gate − composite, 1) (0 when already met) then, measures sorted by points_per_gap descending: need = min( G , ceil( (shortfall − gained) / points_per_gap ) ) gained += need × points_per_gap rate_after% = round(100 × (N + need) / D, 1) until gained ≥ shortfall projected% = round(composite + gained, 1) achievable = shortfall ≤ 0 or projected ≥ gate gaps_to_close = the sum of need
Current 24.7%, gate 60.0%, shortfall 35.3 points.
| Step | Measure | Close | Of open | Rate after |
|---|---|---|---|---|
| 1 | Diabetes: Eye Exam Performed (3.33 per gap — the richest) | 6 | 6 | 100.0% |
| 2 | Diabetes: HbA1c Control (3.33 per gap) | 4 | 4 | 100.0% |
| 3 | Annual Wellness Visit (2.5 per gap, tied with statin adherence and first by title — stops as soon as the gate is cleared) | 1 | 6 | 37.5% |
| Projected composite 60.5% — gate cleared | 11 |
Eleven gaps, not twenty-eight, and the third measure is only partly worked — the plan stops the moment the gate is cleared rather than finishing a measure for tidiness.
If the team commits to closing a specific number of specific gaps, where does that leave the composite, the gate, and the money?
for each measure with k chosen closures, capped at G: rate_after% = round(100 × (N + k) / D, 1) composite_after% = round( mean( rate_after% of every rated measure ), 1 ) gate_met_after = composite_after ≥ gate settlement_after = gross_savings × shared_savings_rate × (1 − quality_withhold_pct) if the gate is met after and gross_savings > 0, else 0 settlement_delta = settlement_after − settlement_before
| Closures | Measure rate | Composite | Gate met after? |
|---|---|---|---|
| Close 1 | 0.0% → 16.7% | 24.7% → 28.0% | No |
| Close all 6 | 0.0% → 100.0% | 24.7% → 44.7% | No |
One closure is worth 3.33 composite points. The screen reports the composite exactly as the live score is computed — each measure's rate rounded to one decimal, then their mean rounded to one decimal — so the eye exam's 16.7% gives (16.7 + 25.0 + 25.0 + 33.3 + 40.0) ÷ 5 = 28.0, and the sentence under the result reads “Composite moves +3.3 points”: the difference of the two displayed figures. Six closures move it 20.0, matching the row's stated points available exactly. And even perfect performance on one measure does not clear a 60% gate: that is the honest answer, and it is the one the screen gives.
Against the benchmark this contract set, are we spending less — and does any of that saving actually become money?
Two independent conditions, and the second is the one that surprises people. Savings are real only if spending came in under benchmark, and they are only payable if quality clears the gate. A programme can generate savings and receive nothing. That is why the quality composite is presented as a financial number on the contract screen rather than a clinical one, and why the gate path above exists at all.
member_months = from the cost summary for the current period
actual_pmpm = allowed cost per member per month
variance_pmpm = round(benchmark_pmpm − actual_pmpm, 2)
gross_savings = round(variance_pmpm × member_months, 2)
gate_met = composite ≥ contract.minimum_quality_pct
settlement = round(gross_savings × shared_savings_rate × (1 − quality_withhold_pct), 2)
when gross_savings > 0 and the gate is met; otherwise 0, and the note says which
| Benchmark PMPM | 1,750.00 |
| Actual PMPM | 1,554.32 |
| Variance per member per month | 195.68 |
| Member months in the period | 105.12 |
| Gross savings — 195.68 × 105.12 | 20,569.88 |
| Quality composite against the gate | 24.7% vs 60.0% |
| Estimated settlement — gate not met | 0.00 |
Twenty thousand dollars of real savings, and nothing payable. This is the most useful number on the contract screen: it converts a quality problem into a financial one, and it is why the platform ranks gaps by composite points rather than by clinical severity alone.
A care manager opens the day with thirty-two open tasks and time for eight. Which eight?
The authored priority label is an input, not the ranking. A manual “Critical” is a snapshot of what somebody believed when they created the task; the clinical signals move afterwards. A member discharged yesterday with a contact window closing today outranks a task somebody marked Critical last month — and the earlier weighting, which put the human label at 40 points against 15 for clinical risk, could not express that. The label still counts, because discarding a clinician's judgment would be its own failure, but it can now be outscored by what is actually happening to the member.
The score is the sum of every component that fires, and each component is returned with the sentence that justifies it.
| Component | Points |
|---|---|
| Authored priority — Critical / High / Medium / Low | 20 / 12 / 6 / 2 |
| Member risk tier — HIGH / RISING / MEDIUM / LOW | 22 / 15 / 6 / 0 |
| Worst open gap's priority — Critical / High / Medium / Low | 18 / 12 / 6 / 2 |
| Post-discharge 48-hour contact window still open | 28 |
| Contact window missed, visit window still open (7 days) | 20 |
| Outreach stalled — no reply for 21 days (the tenant's engagement.stalled wait), or the member declined or could not be reached | 14 |
| Ageing — from day 3, points = days open (capped 21) | 3–21 |
| Due date and authorization clock | Points |
|---|---|
| Overdue — 37 at 1 day late, +2 per further day, capped at 10 days late | 37–55 |
| Due today | 25 |
| Due in 1–2 days | 18 |
| Due in 3–7 days | 10 |
| Due in 8 or more days | 3 |
| Authorization past its decision deadline | 30 |
| Decision due within 24h — expedited / standard | 25 / 20 |
| Decision due within 72h — expedited / standard | 18 / 12 |
| On the SLA clock, beyond 72h | 5 |
| Points | Component | Why |
|---|---|---|
| 12 | priority | marked High |
| 55 | due | overdue by 60 days |
| 22 | patient_risk | high-risk patient |
| 18 | gap_priority | critical-priority gap: HbA1c Control — Glycemic Assessment, 5 open in total |
| 107 | Total | Complete the health risk assessment |
The care manager sees the composed sentence and the score: “overdue by 60 days + high risk + HbA1c Control — Glycemic Assessment” in the Reason column, and score 107 under the priority badge, whose tooltip lists every line above. Note that the authored label contributed 12 of 107 — the queue is being driven by the member's situation, which is the whole point of the design.
Which people need me today — including the ones nobody has raised a task for?
A care manager thinks in members, not tickets. Ranking tasks splits one member with three gaps into three small jobs, so the person who needs the most attention can look like three minor items. Worse: a member who needs attention but has no task is not in the queue at all — high risk, several gaps open, nobody raised anything, invisible. This ranks the panel instead, so a member with no task can outrank a member with a stale one. The same engine scores a cohort list, so two screens cannot disagree about how much a member needs attention.
| Signal | Points | Why it carries weight |
|---|---|---|
| Risk tier HIGH / RISING / MEDIUM / LOW | 30 / 20 / 8 / 0 | The strongest single predictor the plan holds |
| Each open gap nobody is working | 8 | Unworked, not merely open |
| Each gap already in flight | 2 | Still present, much quieter |
| Discharge follow-up owed: the 7-day visit window is open and no follow-up visit has been attended | 25 | It closes whether or not anybody acts |
| Plus, from day 3: the 48-hour contact window was missed (added to the visit-window points) | 15 | The visit window may still be saved |
| No active coverage while a gap is open | 18 | As hard a block as a pending authorization, and quieter: nothing is pending, so nothing prompts anyone to look |
| Outreach stalled: no reply for 21 days, or the member declined or could not be reached | 14 | Needs a different approach, and nothing else surfaces that |
| Authorization on the clock | 12 | A deadline the plan does not control |
| Medication adherence below 80% PDC | 10 | Precedes the admission it causes |
| High risk and in no programme | 10 | Eligible and unenrolled is a decision nobody made |
| Points | Signal | Why |
|---|---|---|
| 40 | unworked_gaps | 5 open gaps with nothing running × 8 |
| 30 | risk | high risk |
| 10 | adherence | Atorvastatin 40mg adherence 62%, below the 80% threshold |
| 80 | Total — John Doe | next action: start an intervention on the HbA1c gap |
The same member scores 85 on clinical risk and 80 on panel attention — two different questions with two different answers. Risk asks how likely a bad year is; the panel asks how much of that is unattended right now. A high-risk member with everything already in flight scores lower here, and should.
For this population: which member, which action, why them, why now, and who should do it?
Ranked by expected value — how much the gap matters, weighted by the member's risk, discounted by whether anything can actually be done today. A recommendation nobody can act on is noise, and the fastest way to lose a work queue is to fill it with noise. But the score itself never reaches the screen. The customer review's words were that numeric scoring is difficult for users to interpret, and that is not a cosmetic complaint: a recommendation justified by “32.0” is one nobody can argue with and — the part that matters — nobody can catch being wrong. So the arithmetic orders the list, and what a person sees is a band, the reasons with their sources, and the single next step.
impact = 10 × risk_weight × gap_priority_weight risk_weight HIGH 1.6 · RISING 1.3 · MEDIUM 1.0 · LOW 0.8 gap_priority_weight Critical 2.0 · High 1.5 · Medium 1.0 · Low 0.6 score = impact × 1.0 actionable today = impact × 0.9 previous outreach ended unreachable or declined = impact × 0.5 blocked — an authorization is pending order = band first (Critical, High, Medium), then score, then member
The band is derived from the facts, never from a cut point on the score, so the band and the reasons beneath it can never disagree:
Record HbA1c Control — band Critical, because a critical-priority gap on a high-risk member.
impact = 10 × 1.6 (HIGH risk) × 2.0 (Critical gap) = 32.0
score = 32.0 × 1.0 (actionable now) = 32.0 sort key only, never displayed
The eleven reasons the engine returned, each naming the system that asserted it — this is what the user actually reads:
| Open quality gap — HbA1c Control | Care Gap |
| Overdue 97 days | Care Gap |
| Critical priority on the measure package | Care Gap |
| High-risk member | Risk |
| Impacts Diabetes: HbA1c Control (<8.0%) — a tracked VBC quality measure with a 75% target | Quality |
| 1 ED visit in the last 6 months | Utilization |
| Coverage not verified — check eligibility before booking | Eligibility |
| No outreach attempted for this gap yet | Outreach |
| Food insecurity recorded and unaddressed — may prevent attendance | SDOH |
| Enrolled in Complex Care Management | Program/Contract |
| Actionable now | Workflow |
Next step: “No prior authorization is required — this can be scheduled as soon as the member is reached.” That sentence is the answer from the authorization engine, not a guess made on this screen, which is why the two can never contradict each other.
Somebody came out of hospital. How long do we have, and for what?
This is the highest-yield window in value-based care and the one most often missed, because the trigger arrives from outside — an ADT message, not a person's decision. Every window is measured from the discharge date, not from the day the notification arrived: a feed that runs a day late shortens the window, it does not extend it.
| Window | Length | Measured from |
|---|---|---|
| Contact after discharge | 48 hours | the discharge date, not the day the notification arrived |
| Follow-up visit | 7 days | the discharge date |
| Readmission counted in | 30 days | the discharge date |
| A discharge still treated as actionable | 120 days | lookback horizon |
Discharged 2026-09-22, two days ago: contact due by 2026-09-24 — window still open — and the follow-up visit due by 2026-09-29. While that contact window is open it contributes 28 points to every related task in the queue and 25 to the member's panel score, which is how a discharge reorders somebody's morning without anyone re-prioritising anything by hand.
When is this decision due, and what does the platform do as that moment approaches?
decision_due = submitted_at + 72 hours when urgency is expedited = submitted_at + 7 days standard
PA-2026-40188, standard urgency, submitted 2026-09-20 12:29 → decision due 2026-09-27 12:29. Exactly seven days, to the minute, from submission — not from receipt, and not from the start of the next business day.
Configurable per deployment and modelled on CMS-0057-F. Every queue in the product reads this same field, which is why an authorization nearing its deadline lifts its member's task without anybody re-prioritising anything. The API also refuses a denial that does not carry a specific reason code and text.
How do we reach somebody who has not replied — without becoming the plan that harasses its members?
A ladder starts on the member's preferred channel when they have named one, then walks the other channels in the order below, least intrusive first, skipping any that cannot be used (no address on file, opted out, and so on). Each rung waits long enough for a real person to have replied. With no preference on file the ladder opens on SMS and ends on Voice; a member who prefers Voice is contacted that way first. Consent is checked by a single authority that every channel calls, and a refusal is written to the audit chain before it is raised — so a member who was not contacted still leaves a record of why they were not contacted.
| Rung, when no channel is preferred | Wait before the next |
|---|---|
| 1 · SMS | 2 days |
| 2 · Email | 3 days |
| 3 · Portal | 7 days |
| 4 · Voice | 2 days |
| Limit | Value |
|---|---|
| Contacts allowed per member (default) | 4 |
| within a rolling window of | 7 days |
| Consent disclosure validity | 365 days |
Who should be offered care management, and what is the evidence for saying so?
Four named criteria, each recording its own evidence. The function returns the reasons, never a bare boolean — “eligible” with nothing behind it cannot answer an auditor, a medical director or the member. Before this existed, eligibility was a condition inline in the ranking engine that wrote nothing down, which meant eligible was a state no member could actually be in.
| Criterion | Test | Why the plan considers it |
|---|---|---|
| High-risk member | risk tier is HIGH | The strongest predictor the plan has of the next twelve months |
| Rising risk with open gaps | tier RISING and three or more open care gaps | Rising risk is where care management is cheapest and most effective — before the admission, not after it |
| Recent acute event | an admission or ED visit within 180 days | The strongest short-term predictor of the next admission is the last one |
| Unmet social barriers | two or more unaddressed social needs | No clinical intervention reaches a member who cannot execute the plan |
| Member | Criterion | Evidence recorded |
|---|---|---|
| John Doe | high_risk | risk tier HIGH |
| John Doe | recent_acute | 1 acute encounter(s) since today − 180 days |
| Maria Gonzales | high_risk | risk tier HIGH |
| Maria Gonzales | recent_acute | 1 acute encounter(s) since today − 180 days |
| Robert Kim | recent_acute | 2 acute encounter(s) since today − 180 days |
| Sam Okafor | recent_acute | 2 acute encounter(s) since today − 180 days |
| Linda Tran | rising_with_gaps | 4 open gaps, rising risk |
| Fatima Hassan | rising_with_gaps | 4 open gaps, rising risk |
Eight criteria met by six members; two members qualify on two criteria each (John Doe and Maria Gonzales: high risk and a recent admission or ED visit). Note that Linda Tran and Fatima Hassan are not high risk — they are the rising-risk members the programme exists to reach early — and Robert Kim and Sam Okafor qualify on a recent acute event alone. A HIGH-only rule would have missed all four.
We send gap-closure opportunities to attributed providers. Which of them engage, and how fast?
acceptance_rate% = round(100 × accepted / responded)
disputed_rate% = round(100 × disputed / responded)
evidence_returned% = round(100 × with_evidence / all opportunities)
response_median_days = the median of the response times in days: the middle value,
or the mean of the two middle values for an even count, rounded once to 1 decimal
overdue = an answer is still owed (NEW or VIEWED) and the due date is past
(by more than the chase rule's grace; the clock stops at the first answer)
Dr. P. Osei currently holds 2 opportunities and has answered neither, so the Acceptance column prints the words “no responses yet”, not 0%. That distinction is the formula doing its job: zero would be a statement about this provider's judgment, and the honest statement is that there is nothing to divide by yet.
We sent this member to somebody else. When should we have heard back, and who finds out if we have not?
due_on = the due date typed on the referral (a real date, or the request is refused),
else today + sla_days(queue)
sla_days = the tenant's setting for that queue, else its default, else 3 days
Per queue, because one queue's standard is not another's — hard-coding three days for all of them is the assumption a customer's operations team overturns on day one. A redirect restarts the clock for the new queue and does not settle the referral; unable to contact does settle it, because a team that tried and could not reach somebody has produced a result worth keeping rather than an absence. The person who raised the referral (when one is recorded) is notified when it is received, accepted, completed or outcome confirmed, and when it ends declined or unable to contact; routing, work starting, a redirect and a cancellation do not notify. A confirmed outcome is appended to the linked intervention's own trail, so the loop visibly closes where it started.
On the current trajectory, where does this metric land at the end of the performance period?
Ordinary least squares through the captured points — deliberately the simplest method that can be checked by hand, because a projection is arithmetic and a forecast is a claim, and this produces the first. What makes it trustworthy is not the fit but the refusals: the function would rather return a sentence than a number it cannot stand behind.
method = ordinary least squares through the captured points slope = Σ(x − x̄)(y − ȳ) / Σ(x − x̄)² intercept = ȳ − slope × x̄ R² = 1 − ss_res / ss_tot projected = slope × (days to the horizon) + intercept
And the three refusals, each returned with its reason in words rather than a number nobody should trust:
| Metric | R² | Projected at 2026-12-31 |
|---|---|---|
| Quality composite | 0.977 | 51.16% |
| Cost PMPM (allowed) | 0.976 | 1,361.69 |
| Open care gaps | 0.961 | 9.68 |
| Measure: HbA1c control | 0.978 | 63.21% |
| Measure: retinal exam | 1.0 | 0.0% |
| High and rising-risk members | 0.893 | 0.93 |
| Attributed members | — | refused: only 1 captured point |
Two rows worth reading together. The retinal measure fits perfectly (R² = 1.0) and projects 0.0% — a flat line through a measure where nothing has ever closed is an excellent fit and a useless forecast, which is why a high R² is a licence to draw the line, not evidence that the future will follow it. And attributed members refuses outright on a single point rather than drawing a line through it.
| Quantity | Rounding |
|---|---|
| Rates and composites | 1 decimal |
| Composite points | 2 decimals |
| Money | 2 decimals |
| Members needed to reach a target | whole, rounded up |
| Percentages of a provider's responses | whole |
Part I explains the calculations that drive the product. This part is the dictionary: 457 values across 25 screens, each with the formula in plain words, why it is computed that way, the function that computes it, and its value in the demonstration tenant. Those demo values are not typed by a person: they are read from the product itself, on a freshly reset demo tenant, by signing in as the persona who sees the screen and calling the same endpoint the screen calls. The demo tenant is reset every day, and this page's values are regenerated on the server from that day's reset, so they match what the demo shows today: values as of 2026-10-12.
The VBC manager, the executive and the platform administrator open this screen as their Dashboard (the care manager's Dashboard is a different screen). It shows six population counts, each linking to the list behind it, and today's priorities: groups of members who share a reason to act now.
Formula
Patient row in the tenant
no attribution filter, no status filter, no panel filterWhy this wayIt answers 'how many people are on file', not 'how many we are responsible for'. That costs a mismatch with Contract Health, whose rows are built over the members attributed in the current contract period, so the two screens can show different member totals on the same day. For a panelled login (a provider) every card counts that login's panel only.
In the demo13 members
Check it yourselfClick the card: it opens Members, and that list has the same number of rows.
app/services/command_center.py · kpis()app/static/app.js · rCommandCenter()Formula
Patient rows whose stored risk_tier, upper-cased, is HIGH or RISING
over every member in the tenantWhy this wayIt reads the tier stored on the member record, which Part I (Clinical risk score and tier) calls a convenience: the score history is the authority, and the tier bands (HIGH 60 or more, RISING 35 or more) are defined there. It is not re-scored at read time. Contract Health's 'High / rising risk members (attributed)' uses the same two tiers over attributed members only and says so in its label; the care manager's Dashboard card of the same name matches the tier without upper-casing. The card is marked amber when the count is above zero, and shows the hint 'monitor'.
In the demo6 members
Check it yourselfOpen Members and count the HIGH and RISING tier badges; the total matches the card.
app/services/command_center.py · kpis()app/services/risk.py · score_patient()Formula
CareGap rows where gap_is_open(status)
open = status not in CLOSED, EXCLUDED, NOT_APPLICABLE, GAP_CLOSED, RESOLVED, DUPLICATE
the database refuses any status outside the open and closed lists (and a blank one)Why this wayGaps being worked (IN_PROGRESS, SCHEDULED, AWAITING_AUTH, SERVICE_COMPLETED, REOPENED) still count, because a gap is open until it is closed, not until someone touches it. It counts gap rows, not members, over every member and every measure. Contract Health's 'Open care gaps (attributed)' counts only attributed members' gaps and names that population in its label, so it can be lower (28 against 29 in the demo). The care manager's Dashboard counts gaps whose status is in the open list; because the database accepts only the known states, that and this card's rule always agree.
In the demo29 gaps
Check it yourselfClick the card: Care Gaps opens with the same total in its header, computed by the same gap_is_open rule.
Formula
Why this wayOne definition of a discharge: the card is the last 30 days of the very list it opens (inpatient stays, never a future date), so the card and the list behind it cannot disagree. It counts discharges, not members. For a provider login it counts the provider's panel only.
In the demo1 discharges
Check it yourselfClick the card: Transitions lists the inpatient discharges; count the rows discharged in the last 30 days.
app/services/command_center.py · kpis()app/services/transitions.py · recent_discharges()Formula
Intervention rows whose status is not CANCELLED, COMPLETED, GAP_CLOSED, DECLINED or UNREACHABLEWhy this wayDeclined and unreachable interventions are treated as finished, because nobody is working them. The Interventions list the card opens uses the same five states, so card and list agree. The care manager's Dashboard shows a card with the same label that excludes only GAP_CLOSED, CANCELLED and COMPLETED, so it counts declined and unreachable interventions as active and can be higher.
In the demo1 interventions
Check it yourselfClick the card: Interventions opens with the same total in its header.
app/services/command_center.py · kpis()app/services/worklists.py · interventions()Formula
WorkTask rows whose status is open (any case) and whose due date is before today
today = the US business date, not the server's date; every owner in the tenant, assigned or notWhy this wayA task due today is not overdue until tomorrow. The count is tenant-wide, so the card opens My Work on its 'Team' tab, which lists the same open tasks; the rows marked OVERDUE there are the ones counted. The care manager's Dashboard 'Overdue tasks' card uses the same date rule.
In the demo8 tasks
Check it yourselfClick the card: My Work opens on 'Team'; count the rows marked OVERDUE.
app/services/command_center.py · kpis()app/core/clock.py · business_today()Formula
red → 'needs attention' (Tasks overdue, when above 0); amber → 'monitor' (High / rising risk, when above 0)Why this wayOnly a breached commitment (work already past due) earns the warning; a count of risk earns 'monitor', because risk is to be watched rather than a deadline missed. Every tone the server can send has a hint on the card.
In the demo('High / rising risk', 'amber'), ('Tasks overdue', 'red')
Check it yourself'needs attention' sits under Tasks overdue and 'monitor' under High / rising risk; each disappears when its count is 0.
app/services/command_center.py · kpis()app/static/app.js · rCommandCenter()Formula
Why this wayAn empty row would read as a metric; its absence reads as 'nothing to do'. A detector that raises an error is not treated as empty: it is named above the table ('N checks could not run'), and the 'Nothing needs attention' all-clear is never shown while any check has failed.
In the demo4 clusters
Check it yourselfCount the table rows; it matches the number beside the title.
app/services/command_center.py · priorities()app/core/isolate.py · run_isolated()app/static/app.js · rCommandCenter()Formula
CRITICAL = post-discharge follow-up, assessments overdue · HIGH = measure below target, vendor referrals overdue · MEDIUM = duplicate outreach, members we cannot reach
colour: CRITICAL red, HIGH amber, anything else greyWhy this wayThe priority says which kind of clock is running: a window that cannot be reopened (Critical), a commitment already breached (High), or waste that breaches nothing (Medium). It does not grow with the size of the cluster; one member in a closing window outranks a hundred with a missed target.
In the demoHealth risk assessments overdue CRITICAL, Post-discharge follow-up CRITICAL, Diabetic Retinal Exam HIGH, Duplicate outreach MEDIUM
app/services/command_center.py · priorities()app/static/app.js · rCommandCenter()Formula
Critical 0, High 1, Medium 2), then by member count, largest firstWhy this wayThe kind of deadline decides first; size only breaks ties within the same priority. It is not the Work-queue score in Part I (Work-queue ranking): no member-level signal enters it.
In the demoHealth risk assessments overdue, Post-discharge follow-up, Diabetic Retinal Exam, Duplicate outreach
Check it yourselfRead the Priority and Members columns: priorities never go up as you read down, and within one priority the member counts never go up.
app/services/command_center.py · priorities()Formula
Why this wayThis counts people. The 'Why' text beside it sometimes counts something else (discharges, referrals, outreach ladders), so the two numbers on one row can differ; where the sentence counts members (Duplicate outreach, Health risk assessments overdue) it is this number. For a panelled login every detector reads only the panel's rows, so the count, the example names and the sentence all describe the provider's own members.
In the demoHealth risk assessments overdue 2, Post-discharge follow-up 1, Diabetic Retinal Exam 6, Duplicate outreach 3 members
Check it yourselfClick the row's action button; the list it opens contains these members, among others.
app/services/command_center.py · priorities()app/api/population.py · command_center()Formula
… is added when the cluster has more than 3 membersWhy this wayThe names are the cluster's members who most need attention, by the one 'needs attention' ranking the Risk panel uses (its score is computed at request time), so for a large cluster they are its most urgent three rather than an arbitrary three. The demo value below reports how many names are shown rather than which.
In the demoHealth risk assessments overdue 2 name(s), Post-discharge follow-up 1 name(s), Diabetic Retinal Exam 3 name(s) + …, Duplicate outreach 3 name(s)
app/services/command_center.py · priorities()app/services/panel.py · panel()app/static/app.js · rCommandCenter()Formula
transitions.windows(tenant): the tenant's transition.contact / transition.visit rule params (48 hours and 7 days by default)
rows = the Transitions list's discharges (inpatient stays discharged 0 to 120 days ago) whose visit window is open: discharged visit_days days ago or less
N = those whose contact window is open: discharged contact_days days ago or less (the contact hours rounded up to whole days: 48 h = 2)
M = the rest, discharged 3 to 7 days ago by default · members = distinct members in rowsWhy this wayThe windows and the definition of a discharge are the Transitions module's (Part I, Transition-of-care windows), read through the same functions as the Transitions screen, the work queue and My Panel, so tuning the rule moves all of them and this row, the Discharges card and the Transitions list count the same discharges: an ED visit is not a transition, and a discharge dated in the future is not one yet. N and M count discharges, not members, and together they are every discharge still inside the visit window. For a provider login only the panel's discharges count.
In the demo1 discharge(s) inside the 48-hour contact window, 0 more discharged 3-7 days ago, still inside the 7-day visit window
Check it yourselfClick View transitions: the contact window is still open on each discharge counted in N.
app/services/command_center.py · _post_discharge()app/services/transitions.py · recent_discharges()app/services/transitions.py · windows()Formula
quality.active_measures): rate = the measure's own rate from the measure engine; short = target − rate
the measure with the largest positive short, among those with at least one open gap in the login's scope, is shown
members = members in scope with an OPEN gap on that measureWhy this wayOne measure is reported, not all of them, so the row is a decision rather than a report. The rate is the measure rate in Part I (A measure across a population), compliant members over eligible members, read from the same engine as the Quality screen, so the two show the same rate. The target and title are the active version's; a retired version is never quoted. For a provider login the rate is still the plan's (the sentence says so) while the members are the provider's own.
In the demoDiabetic Retinal Exam: 0% against a 70% target — 70.0 points short
Check it yourselfOpen Quality and find the same measure: its rate is the one this row quotes.
app/services/command_center.py · _measure_below_target()app/services/quality.py · active_measures()app/services/quality.py · evaluate_tenant()Formula
COMPLETED, REFUSED or UNREACHABLE and is past its due date
P = round( 100 × members whose governing HRA was completed on or before its due date ÷ D ), D = enrolled membersWhy this wayIt is the HRA completion KPI itself, over the denominator the plan does not choose: every enrolled member, so an HRA nobody attempted counts as much as one the member refused. Only HRAs count; a reassessment is a different obligation with its own clock. Missing HRAs are raised before the row is read, so a member enrolled since the last sweep is counted. The action opens the Assessments worklist, whose headline is the same KPI.
In the demo2 enrolled member(s) past their contractual HRA window
app/services/command_center.py · _assessments_overdue()app/services/assessments.py · hra_standing()app/services/assessments.py · kpis()app/services/assessments.py · ensure_due()Formula
VendorReferral rows whose due date is before today (the date it is held to (vendors.due_on, A-058): the vendor's contracted SLA (referral date + the vendor's SLA days, 7 when none) until the referral is SCHEDULED; once SCHEDULED with an appointment date, that appointment; nothing once DELIVERED or settled)
N = number of late referrals · D = the largest today - due date in days, with that referral's vendorWhy this wayIt calls the same is_overdue rule as the vendor scorecard, so this row and Vendor Performance cannot disagree about which referral is late. N counts referrals; the Members column counts members. The row is absent when nothing is late.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNothing is late in the freshly reset demo tenant, so the row does not appear. When it does, click Vendor performance: the breaches there are the same referrals.
app/services/command_center.py · _vendor_sla()app/services/vendors.py · is_overdue()Formula
OutreachSequence whose status is not COMPLETED, EXHAUSTED or STOPPED
N = members with 2 or more live ladders · K = the most live ladders any one member has
reasons = the purposes of that member's ladders (at most 3 named)Why this wayIt flags members being contacted about several things at once, which the member experiences as being pestered. A ladder records its PURPOSE (a care gap, an intervention, a transition, a task), and the sentence names those purposes. Two live ladders for the same purpose cannot exist: the store holds one per member per purpose (Part I, Engagement ladder, describes one ladder at a time).
In the demo3 member(s) have more than one live outreach ladder — one has 3
Check it yourselfOpen any listed member's Engagement tab: more than one ladder is live.
app/services/command_center.py · _duplicate_outreach()Formula
OutreachSequence rows with status EXHAUSTED, with no time limit
members = distinct members among themWhy this wayA ladder is exhausted when every usable channel has been tried (Part I, Engagement ladder, wait times and the contact ceiling). N counts ladders; the Members column counts members. Because there is no time limit, a ladder that ran out months ago still counts. This is a different signal from the 'outreach stalled' ranking factor, which reads intervention status.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNo ladder is exhausted in the freshly reset demo tenant, so the row does not appear. It appears once a ladder has used every channel.
app/services/command_center.py · _unreachable()The Care Manager's "what needs me today" screen (payer personas — VBC Manager, Executive, Platform Admin — get the Command Center under the same menu item instead): eight operational counts, the top of the ranked member panel, three progress strips and the population totals.
Formula
open whose due date (first 10 characters, YYYY-MM-DD) equals today's US business date. Tenant-wide: every owner, not only mineWhy this wayRead against the US business calendar (business_today()), not the server's time zone, so a task due today does not jump to tomorrow at 19:00 Eastern. It counts tasks, not members: one member with three tasks due today adds three. It is a subset of Population "Open tasks".
In the demo15 tasks
Check it yourselfMy Work, Team: sort by Due and count the open tasks dated today.
app/api/vbc.py · summary()Formula
open that have a due date and whose due date (due[:10]) is earlier than today's US business date, compared as ISO text. Tenant-wideWhy this wayA task due today is not overdue until tomorrow. The Command Center's "Tasks overdue" card computes the same idea in a second function (command_center.py · kpis()): it lower-cases the status and parses the date, so a task stored as Open or with a malformed due value is counted on one screen and not the other. On the demo data both show the same number. See Part I, Prioritisation, for how overdue work is ranked.
In the demo8 tasks
Check it yourselfMy Work, Team: sort by Due and count the open tasks dated before today; as VBC Manager, the Command Center's "Tasks overdue" card shows the same number on the demo.
app/api/vbc.py · summary()app/services/command_center.py · kpis()Formula
HIGH or RISING. Tenant-wide, every member row including unresolved duplicatesWhy this wayThe tier is the one stored by the last rescore (see Part I, Population health), not recomputed on read. It equals the High plus Rising bars of the Risk distribution strip on this page. The Command Center card of the same name upper-cases the tier first, so it would also count a tier stored as high; the demo stores upper case and both screens agree.
In the demo6 members
Check it yourselfAdd the High and Rising numbers in the Risk distribution strip lower on the page.
app/api/vbc.py · summary()app/services/risk.py · score_patient()Formula
visit_days days before today (the tenant's transition.visit window, 7 by default, via transitions.windows) whose follow-up is still owed: no appointment on or after the discharge date has been attended (transitions.open_followup). Counts discharges, not distinct membersWhy this wayAnchored to the discharge date, so a discharge we learned about late has less window left, not a fresh one (see Part I, Clocks and windows). A discharge whose follow-up visit has already been attended drops off: the same test the transition plan uses for its 'visit completed' step, so the card and the plan agree. The Transitions screen's "Visit window open" count is the window alone and can be higher. The Command Center's "Discharges (30d)" is a different number: 30 days of the Transitions list.
In the demo1 discharges
Check it yourselfOpen Transitions: the "Visit window open" card shows the same number.
app/api/vbc.py · summary()app/services/transitions.py · recent_discharges()Formula
SUBMITTED, IN_REVIEW or PENDED) or with at least one open gap and no current coverage (services/coverage: a coverage that is active with effective_from ≤ today ≤ effective_to) — each member counted onceWhy this wayBoth causes stop the same service, so they share one card; the note under Care gap progress splits them, and gives the authorization count separately. A member with two pending authorizations and no coverage is one member blocked, not three. Pending is the one definition in models.pa_is_pending, read from the status, which My Panel's 'authorization on the clock' signal also uses.
In the demo1
Check it yourselfIt equals the sum of the four numbers in the "Blocking care" note under Care gap progress.
app/api/vbc.py · summary()Formula
IDENTIFIED or OUTREACH whose last event is at least the tenant's engagement.stalled wait old (stalled_days, 21 by default), or one that ended DECLINED or UNREACHABLE (models.iv_follow_up_why). Counts interventions, not membersWhy this wayThe same test as the "Outreach stalled" signal in the work queue and on My Panel, and the same wait as the rule that raises 'Outreach with no movement', so an outreach sent this morning is not counted until the wait has passed. A member who DECLINED or could not be reached still counts, and each reason says which it was; whether a member who declined should count at all is an owner decision.
In the demo0 interventions
Check it yourselfMy Panel, "all factors": members with an outreach_stalled factor are where these sit; one member can hold several such interventions.
app/api/vbc.py · summary()Formula
GAP_CLOSED, CANCELLED or COMPLETEDWhy this wayAnything not finished counts as active here, including DECLINED and UNREACHABLE. The Command Center card with the same label, and the Interventions worklist it opens, both leave out DECLINED and UNREACHABLE, so this card shows a larger number than those screens as soon as one outreach fails. On the fresh demo there is one intervention (AUTH_APPROVED) and all three show 1.
In the demo1 interventions
Check it yourselfAs VBC Manager, the Command Center's "Active interventions" card and its Interventions worklist show the same number while no outreach has ended declined or unreachable.
app/api/vbc.py · summary()app/services/command_center.py · kpis()app/services/worklists.py · interventions()Formula
CLOSED and a closure date (closed_on, YYYY-MM-DD) on or after the first day of the current US business monthWhy this wayOnly CLOSED counts; EXCLUDED, NOT_APPLICABLE and the other closed states do not. Gaps closed before closure dates were recorded have no date and are reported separately (see "Gaps closed without a closure date") instead of being counted as not this month, which would read as a measured zero. Reopening a gap clears its closure date.
In the demo0 gaps
Check it yourselfMember 360, Care gaps: a gap closed this month shows Closed with this month's closure date; the fresh demo has none closed yet.
app/api/vbc.py · summary()Formula
Why this wayA plain non-zero test with no threshold: the five badged cards are things to do, the other three are things to know. Red is kept for work already past its date.
In the demoTasks due today, Overdue tasks, Discharges to follow up, PA / eligibility blocking care
Check it yourselfEvery badged card shows a number above 0; the unbadged ones never carry the badge.
app/static/app.js · rDash()Formula
limit=8) and the number of members scoring above 0Why this wayThe same ranking as My Panel (see Part I, Prioritisation, My Panel), cut at 8 so the first screenful stays short. My Panel asks for 25, so the two screens show different row counts from one ranking.
In the demo8 members
Check it yourselfCount the table rows; the first 8 rows of My Panel are the same members in the same order.
app/static/app.js · rDash()app/api/population.py · my_panel()app/services/panel.py · panel()Formula
Why this wayVisible means the whole tenant for a care manager and a provider's attributed members for a provider. It is not "my" members: the ranking function's description says it narrows to the members a care manager owns work for, but the code only uses the viewer to set a mine flag, so every care manager sees the same N.
In the demo12 members
Check it yourselfMy Panel lists the same members (up to 25 rows); for the demo's 12 the whole list is on one page.
app/services/panel.py · panel()app/core/scope.py · visible_ids()Formula
open (any owner)Why this wayCounts across all scored members, not only the 8 rows shown, so it can be larger than the number of NO TASK badges visible on this page. These are the members the task queue cannot show, because nobody has raised work for them.
In the demo2 members
Check it yourselfOn My Panel, count the rows with a NO TASK badge.
app/services/panel.py · panel()Formula
Why this wayThe same computation as My Panel, from the same weight table (panel.PANEL_WEIGHTS), which Part I's table is rendered from. The 25 points apply while the member's newest discharge still owes its follow-up (the tenant's visit window, 7 days by default, open and no visit attended), and the extra 15 once the contact window has passed, so days 3 to 7 score 40 by default. The 10 for 'high risk and in no programme' reads the member's enrolment, whatever the top next-best action is. Adherence counts a PDC measured at 0% as below the threshold, as the risk score does. A gap whose only intervention was declined, went unreachable or completed without closing counts as unworked (8), not in flight (2).
In the demo80 points
Check it yourselfMy Panel, open "all factors" on John Doe: +40 + 30 + 10 = 80, Part I's worked example.
app/services/panel.py · panel()Formula
Why this wayOne line, not all of them, so the column stays readable; the full list is on My Panel. For John Doe the 40-point unworked-gaps component outranks the 30-point risk tier. Adherence percentages are rounded, the same figure Member 360 prints, and a stalled-outreach line names what happened: "no reply for N days", "not started in N days", "member declined" or "member could not be reached".
In the demo5 open gap(s) with nothing running: HbA1c Control — Glycemic Assessment, Diabetic Retinal Exam, Statin Adherence (PDC >= 80%)
Check it yourselfMy Panel, "all factors": the Reason is the text of the factor with the most points.
app/services/panel.py · panel()Formula
open, whoever owns themWhy this wayIt says only that no task exists. It says nothing about interventions: a member can carry NO TASK while a gap is being worked.
In the demoJon Doe
Check it yourselfOpen the member's 360: the Tasks list has no open task.
app/services/panel.py · panel()Formula
HIGH ≥ 60 points, RISING ≥ 35, MEDIUM ≥ 15, else LOW (see Part I, Population health)Why this wayIt is the tier as of the last rescore, not recomputed on this read. A tier can also be set by hand through the records interface; the demo's duplicate record "Jon Doe" is seeded LOW after the tenant rescore, with no factors behind it.
In the demoHIGH
Check it yourselfMember 360, Risk: the factors listed add up to a score inside that tier's band.
app/services/risk.py · score_patient()app/services/risk.py · rescore()Formula
Start intervention, Try an alternate channel, Track authorization <ref> or Offer care-management enrollmentWhy this wayPart I does not describe the enrollment action: a HIGH-tier member in no enrolled programme gets it, always in band High with score 10 × 1.6 × 0.8 = 12.8. A gap already being worked and not waiting on an authorization gets no action, and dismissed actions are dropped, so the column can show a dash.
In the demoStart intervention
Check it yourselfMy Panel shows the same action for the member; the member 360's Next best action card leads with it.
app/services/nba.py · recommend()app/services/nba.py · _recommend_for_gap()app/services/nba.py · _recommend_enrollment()Formula
; , then cut to the first 90 characters in the browserWhy this wayThree reasons, not the full list of up to eleven, because the full list lives on the member 360. The cut is a plain character count, not a word boundary: John Doe's line is 91 characters, so the screen ends "…measure packag".
In the demoOpen quality gap — HbA1c Control; Overdue 97 days; Critical priority on the measure packag
Check it yourselfMy Panel shows the same reason uncut, and the member 360's Next best action lists the reasons in the same order.
app/services/nba.py · _recommend_for_gap()app/static/app.js · rDash()Formula
no deadline when there is noneWhy this wayThree different clocks can be running, so the reason travels with the date (see Part I, Clocks and windows, for the discharge and authorization clocks). The earliest date may already be past: for John Doe it is a task 97 days overdue. The discharge clock is the member's NEWEST discharge (transitions.latest_discharge_by_member), and it only counts while the follow-up is owed (transitions.open_followup: the 7-day window is open and no appointment since the discharge has been attended) — the same test the +25 score uses, so a row never shows a closed window as its next deadline.
In the demo97 days before the reset
Check it yourselfMember 360: the earliest date among the member's open tasks, discharge window and pending authorizations is the one shown.
app/services/panel.py · _soonest_due()app/services/panel.py · panel()Formula
OPEN or REOPENED, models.GAP_NOT_STARTED); In progress = every other open state (IN_PROGRESS, SCHEDULED, AWAITING_AUTH, SERVICE_COMPLETED, models.GAP_IN_PROGRESS, derived from the open set); Closed = CLOSED onlyWhy this wayOpen and In progress partition the open states, so together they always equal the Population 'Open care gaps' card on the same page; a gap whose member attended and whose evidence is still on its way (SERVICE_COMPLETED) is in progress. EXCLUDED, NOT_APPLICABLE and the other closed states are not shown. Closed counts gap rows, not the measure numerator in Part I, Quality measurement.
In the demoOpen 28, In progress 1, Closed 0
Check it yourselfOpen + In progress equals Population "Open care gaps" whenever no gap is waiting on evidence.
app/api/vbc.py · summary()Formula
Why this wayEach bar is a share of the three states drawn, not of all gaps, so gaps in states outside the bars do not shorten them. Whole-percent rounding can make the bars total 99% or 101%.
In the demoOpen 97%, In progress 3%, Closed 0%
Check it yourselfDivide each count by the sum of the three and round.
app/static/app.js · rDash()Formula
SUBMITTED, IN_REVIEW or PENDED. Tenant-wide; counts authorizations, not membersWhy this wayAn authorization still on its clock stops the member being scheduled, so it belongs on an operational board (see Part I, Clocks and windows, Prior-authorization decision clock). Pending is read from the status alone (models.pa_is_pending), the same test My Panel and the scheduling gate use.
In the demo0 authorizations
Check it yourselfAuthorizations screen: count the rows at Submitted, In review or Pended.
app/api/vbc.py · summary()Formula
active → terminated; latest row active but ended, or not yet started → lapsedWhy this wayEach member lands in exactly one bucket, because the three causes need different fixes: a missing record is often an unmerged duplicate, terminated means the member left the plan, lapsed means the dates ran out. Members with no open gap are skipped: nothing is waiting on cover. The same function (services/coverage) drives the panel's "no active coverage" signal and every other screen that names a member's coverage.
In the demono coverage on file 1, terminated 0, lapsed 0
Check it yourselfThe demo's 1 is Jon Doe, the unmerged duplicate of John Doe: his 360 has no coverage.
app/api/vbc.py · summary()Formula
CLOSED and no closure dateWhy this wayThe closure date was added later, so older closures carry none. They are counted as closed but left out of "this month" and named here, rather than silently treated as closed in some other month.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNo gap is closed in the fresh demo, so the note does not appear. It appears once a gap with status Closed has no closure date, for example a record migrated from before closure dates existed.
app/api/vbc.py · summary()Formula
IDENTIFIED, ASSIGNED, OUTREACH, ENGAGED, AWAITING_AUTH, AUTH_APPROVED, SCHEDULED, IN_PROGRESS, COMPLETED, GAP_CLOSED, with REOPENED placed at ENGAGED; UNREACHABLE, DECLINED and CANCELLED count only as created. Outreach ≥ OUTREACH, Reached ≥ ENGAGED, Scheduled ≥ SCHEDULED, Completed ≥ COMPLETEDWhy this wayStages are cumulative, so a member already scheduled still counts as reached. Consequences: an intervention waiting on or holding an authorization counts as reached but not scheduled; one that was reached and later declined drops out of every stage; a REOPENED intervention counts as reached, the same as My Panel's follow-through 'Member reached'. It counts interventions, not members.
In the demoInterventions created 1, Outreach or beyond 1, Reached (engaged or beyond) 1, Scheduled or beyond 0, Completed or gap closed 0
Check it yourselfThe demo's one intervention is at Auth approved: it counts in Outreach and Reached only.
app/api/vbc.py · summary()app/models.py · iv_funnel()Formula
Why this wayEach stage is shown as a share of every intervention created, the same base the Analytics funnel draws against, so the two screens give the same bar for the same count.
In the demoInterventions created 100%, Outreach or beyond 100%, Reached (engaged or beyond) 100%, Scheduled or beyond 0%, Completed or gap closed 0%
Check it yourselfDivide each stage by the Interventions created number and round.
app/static/app.js · rDash()Formula
Why this wayThe tier as of the last rescore (see Part I, Population health). It includes hand-set tiers and the demo's unmerged duplicate "Jon Doe", seeded LOW with no factors, so the Low bar holds one member no rule scored.
In the demoHigh 2, Rising 4, Medium 5, Low 2
Check it yourselfThe four numbers add up to Population "Patients"; Patients screen, filter by risk.
app/api/vbc.py · summary()app/services/risk.py · score_patient()Formula
Why this wayA member with no tier shortens all four bars without appearing, so the bars can total under 100%; rounding alone can make them total 99% or 101%. The demo totals 99%.
In the demoHigh 15%, Rising 31%, Medium 38%, Low 15%
Check it yourselfDivide each tier count by Population "Patients" and round.
app/static/app.js · rDash()Formula
Why this wayNot attributed members and not de-duplicated: an unmerged duplicate (the demo's "Jon Doe") counts as a second member. The whole tenant for a care manager; a provider who opens this screen sees the count of their own attributed panel, like every other figure on it.
In the demo13 members
Check it yourselfThe Patients screen lists the same number of rows; the Command Center's "Members" card agrees.
app/api/vbc.py · summary()Formula
OPEN, IN_PROGRESS, SCHEDULED, AWAITING_AUTH, SERVICE_COMPLETED, REOPENEDWhy this wayGap rows, including gaps already scheduled or attended and waiting on evidence. Part I, Quality measurement defines open gaps per measure as denominator minus numerator and totals twenty-nine; this card shows 29 on the demo too. The Command Center card counts any status not in the closed set, which agrees for every known status.
In the demo29 gaps
Check it yourselfAs VBC Manager, the Command Center's "Open care gaps" card and the Open care gaps worklist behind it show the same number.
app/api/vbc.py · summary()Formula
open, any owner. Tenant-wideWhy this wayThe denominator the two task cards come from: Tasks due today and Overdue tasks are subsets of it (Part I, Prioritisation, opens with the same thirty-two). A started task keeps status open, so it stays in this count.
In the demo31 tasks
Check it yourselfMy Work: the Team button carries the same number.
app/api/vbc.py · summary()Care managers open My Work to see their own open tasks, ranked by what is happening to each member and with the reason each task exists. VBC managers use the Team view of the same screen to find everyone's open work, including tasks nobody owns.
Formula
rule_key). 'Open' means status == 'open' exactly, so tasks someone has started are still counted.Why this wayThe title counts what is on screen, while the Mine and Team buttons always count the whole tenant. So when the queue is filtered to one exception (an amber badge names the rule), the title can be smaller than the buttons beside it. That is intended: the filter is visible and 'all work' removes it. Neither view is narrowed by panel or attribution: Team is the whole tenant.
In the demo30 tasks
Check it yourselfcount the rows in the table; with no exception filter the title equals the Mine button in the Mine view and the Team button in the Team view
app/static/app.js · rWork()app/api/vbc.py · my_tasks()Formula
status == 'open' and owner_id equal to the signed-in userWhy this wayThe count is the same whichever view is selected, because both counts come back with every response. That lets the toggle show what is on the other side without a second request. Started (in-progress) tasks are included, because a started task is still open.
In the demo30 tasks
Check it yourselfin the Mine view with no exception filter, the table has exactly this many rows
app/api/vbc.py · my_tasks()Formula
status == 'open', whoever owns them and including tasks with no ownerWhy this wayOne number, printed in three places on this screen. It is tenant-wide, not limited to the signed-in user's panel. The empty state compares it with Mine (team > mine) to tell a care manager with a clear queue that work is waiting in the Team view.
In the demo31 tasks
Check it yourselfswitch to Team: the table has this many rows
app/api/vbc.py · my_tasks()app/static/app.js · rWork()Formula
owner_id. Count: number of open tasks in the tenant with no owner_id, from every source (rule, campaign, transitions sweep, typed by hand)Why this wayA task is unassigned when it was typed in without an owner, or when a rule found nobody to give it to. Rules assign by continuity first (the member's current owner), then to the holder of the target role with the fewest open tasks, and leave the task unowned only when nobody holds that role (see 'Owner' below). The count and the badges describe the same set, so they agree whenever the table shows the whole Team view.
In the demo1 tasks
Check it yourselfswitch to Team and count the amber UNASSIGNED badges; they equal N
app/api/vbc.py · my_tasks()app/services/task_rules.py · _assign()Formula
task_origin(task): rule for source rule or transitions, campaign for source campaign, else manual
AUTO badge: origin rule · CAMPAIGN badge: origin campaign
X = open tasks in the tenant whose origin is rule · Y = those whose origin is campaignWhy this wayThe note exists to report how much of the queue came from events rather than from typing. A campaign is launched by a person, so it is reported as its own origin rather than as rule work. The legacy transitions source is counted as rule work, because the transitions sweep now delegates to the rule engine. X and Y count the whole tenant whichever view is shown.
In the demo25 tasks
Check it yourselfswitch to Team and count the blue AUTO badges; they equal X
app/api/vbc.py · my_tasks()app/static/app.js · rWork()Formula
manual. Every task has exactly one origin, so X + Y + Z = Team N always.Why this wayReported, not asserted: the note sits beside the claim that work should come from events and rules, so a reader can see how far that is true of this queue.
In the demo6 tasks
Check it yourselfin the Team view, the rows reading 'created by hand' under the member name number Z
app/api/vbc.py · my_tasks()Formula
started_at is set. N = number of open tasks in the tenant with started_at set. Derived, never stored as a status.Why this wayIn progress is not a status. The task stays open, so it stays in the queue and in every open count, and it does not disappear from other screens that filter on open. Pressing Start sets started_at and also claims an unowned task, so two people do not work the same call.
In the demo0 tasks
Check it yourselfzero after a reset. Press Start on any row: the badge appears on that row and N goes up by one
app/api/vbc.py · my_tasks()app/static/app.js · taskStart()Formula
priority. Typed in by hand: the label chosen. Raised by a rule: the rule's own grade where it has one (abnormal lab: Critical at 30% or more outside the range, else High), otherwise the rule's configured priority. A few rules may later raise it one band (see 'Escalated priority').Why this wayThe badge prints the word the record holds, coloured red, amber, blue and green. It is one input to the ranking, worth 20 / 12 / 6 / 2 points, and not the ranking itself (see Part I, Work-queue ranking). So a Medium task can sit above a Critical one.
In the demoHigh
Check it yourselfthe top row's badge. Hover 'score N' under it: the first line is '+points marked <label>'
app/static/app.js · priorityBadge()app/services/task_rules.py · evaluate()Formula
escalate_after_days and escalate_to_role (as shipped: gap.evidence_missing and appointment.not_rebooked, 7 days, vbc_manager): when today − (due date, else the business date it was created) >= 7, the priority moves up one band (Low→Medium→High→Critical; Critical stays Critical) and [escalated to vbc_manager] is added to the reason. It happens once per task, guarded by a task.escalated audit row.Why this wayEscalation adds urgency without taking the task away from its owner: the owner is not changed. Both rules raise their tasks with no due date, so in practice the 7 days run from the day the task was created. The Exception Center's 'Escalated' card states exactly this, from the server's own sentence (ESCALATION_MEANING): no reassignment happens and nobody is notified; the role appears only as text in the reason.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnone in a fresh tenant. It appears on a task-rule sweep once an 'evidence missing' or 'missed visit not rebooked' task is 7 days old
app/services/task_rules.py · _escalate_overdue()Formula
Why this wayComputed each time the screen loads and never stored, so the order moves as the calendar does, with no recompute job. The screen shows both the composed sentence (Reason column) and this number; Part I's worked example is the team queue's top row, generated from this endpoint. In a freshly reset tenant only five of the eight components can fire; age, the authorization clock and stalled outreach have nothing to act on.
In the demo107 points (top row)
Check it yourselfhover 'score N': the +points in the tooltip add up to N
app/services/ranking.py · rank()Formula
+points why, joined with · , in a fixed order: priority, due, age, member risk, post-discharge window, authorization, gap, engagement. The 'marked <label>' line is always first, even at +0.Why this wayThe tooltip shows the evidence behind the order, so the queue explains its ranking rather than just asserting it. It is in engine order, not sorted by points. The worked example in Part I is sorted by points and shortens the gap line to 'HbA1c Control'. The screen shows the full measure name and adds ', N open in total' when the member has more than one open gap.
In the demo+12 marked High · +55 overdue by 60 days · +22 high-risk patient · +18 critical-priority gap: HbA1c Control — Glycemic Assessment, 5 open in total
Check it yourselfhover the top row's score and compare it word for word
app/static/app.js · rWork()app/services/ranking.py · rank()Formula
Critical 20, High 12, Medium 6, Low 2, any other label 0Why this wayThe human label counts, but it can be outscored by what is happening to the member (see Part I, Work-queue ranking). It used to be the heaviest component at 40.
In the demo12 points (top row)
app/services/ranking.py · rank()Formula
D = due date − today, where today is the US business date (America/New_York). D < 0: 35 + 2 × min(days late, 10), i.e. 37 to 55. D = 0: 25. D = 1–2: 18. D = 3–7: 10. D > 7: 3. No due date, or one that is not a date: no line.Why this wayLateness counts most, and stops adding after 10 days so an ancient task cannot bury today's work. Part I's range, 37–55, is generated by calling this function at 1 to 30 days late, so it cannot drift from it. The business-date clock stops the queue from calling today's tasks overdue after 20:00 Eastern, when UTC has already rolled over.
In the demo55 points (top row)
Check it yourselfa task more than 10 days late shows the +55 cap; one 1 day late shows +37, and one 4 days late shows +43
app/services/ranking.py · _due_component()Formula
min(D, 21) points.Why this wayAge breaks ties. It lets low-priority work with no due date eventually surface. Part I's row is generated from this function: from day 3, points = days open, so 3 points on day 3 and 21 from day 21.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfa reset creates every task at reset time, so no task is 3 days old yet. The line appears on any task still open 3 days later
app/services/ranking.py · _age_component()Formula
HIGH 22, RISING 15, MEDIUM 6, LOW 0 (no line)Why this wayIt applies to every task of the member, whatever the task is about. The tier itself comes from the clinical risk score (see Part I, Clinical risk score and tier). A task with no member gets only the priority, due and age components.
In the demo22 points (top row)
app/services/ranking.py · rank()Formula
contact_hours, 48 by default, rounded UP to whole days by transitions.windows: 2): 28 points, open until discharge + 2 days. Otherwise N ≤ visit days (7 by default): 20 points, visit due by discharge + 7. Otherwise: no line.Why this wayThe window closes whether or not anyone acts, so it ranks above almost everything while it is open (see Part I, Transition-of-care windows). It lifts EVERY open task of that member, not only the discharge tasks. The '48 hours' are counted as whole calendar days, so a member discharged just after midnight stays 'open' for almost 72 hours.
In the demo28 points (first row that has it)
Check it yourselfevery row for the member discharged two days before the reset carries +28, including a social-barrier task that has nothing to do with the discharge
app/services/ranking.py · rank()app/services/transitions.py · recent_discharges()Formula
decision_due. H = hours until it (elapsed time, printed whole with the fraction dropped). Past it: 30. H < 24: 25 expedited / 20 standard. H < 72: 18 / 12. Otherwise: 5.Why this wayThis is the regulatory clock the authored priority could never express (see Part I, Prior-authorization decision clock). Only the soonest authorization counts, and it lifts every task of that member, not only the task about the authorization.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnone of the demo queue's members has an undecided authorization with a deadline, so no line shows after a reset. Submit an authorization for a member with a task, and every task of that member gains the line
app/services/ranking.py · _sla_component()Formula
OPEN, IN_PROGRESS, SCHEDULED, AWAITING_AUTH, SERVICE_COMPLETED, REOPENED), the one with the highest priority: Critical 18, High 12, Medium 6, Low 2. ', K open in total' is added when K > 1.Why this wayIt uses the worst gap's priority, not the number of gaps, because three Low gaps are not one Critical gap. A gap the member has attended but that has no evidence yet (SERVICE_COMPLETED) still counts as open here.
In the demo18 points (top row)
app/services/ranking.py · rank()Formula
IDENTIFIED or OUTREACH whose last event is at least the tenant's engagement.stalled wait old (stalled_days, 21 by default), or one that ended DECLINED or UNREACHABLE (models.iv_follow_up_why). The sentence names a silent one before a member-side stop.Why this wayIt flags members whose outreach needs a different channel. It waits exactly as long as the rule 'Outreach with no movement' (engagement.stalled) does, so outreach sent today scores nothing until the wait has passed, and the queue, My Panel and the Dashboard read the same test. A member who DECLINED or could not be reached still scores 14, with a reason that says which; whether a declined member should earn queue points is an owner decision.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfno queue member has outreach at those statuses after a reset. Press 'Begin outreach' on a member's intervention, and every task of that member gains +14 at once
app/services/ranking.py · rank()Formula
Why this wayThe tie-breaks make the order stable across reloads instead of whatever the database returned (see Part I, Work-queue ranking). The order is the only output; no rank number is shown. It applies to the rows in view, Mine or Team, after any exception filter.
In the demo107, 107, 101, 94, 94 (scores of the first five rows)
Check it yourselfthe first two rows tie on score; the one due earlier is above the other
app/services/ranking.py · rank()Formula
+ . Phrases: the due/age text as-is, 'marked <label>', '<tier> risk', the gap's measure name, 'post-discharge contact window open' / 'overdue', 'authorization on the SLA clock', 'outreach stalled'.Why this wayThe headline says what is true about the member in a few words, not what somebody typed. Three phrases fit a table cell; the tooltip keeps the full list. The grey line under it is the rule's own reason text (see the rows below).
In the demooverdue by 60 days + high risk + HbA1c Control — Glycemic Assessment
Check it yourselfthe three phrases are the three largest +points in that row's tooltip
app/services/ranking.py · priority_reason()Formula
Why this wayKept as a safety net, and effectively unreachable. The authored-priority component always exists and is worth at least 2 for every standard label, so the headline is empty only for a non-standard label.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnever shown in the demo: every row has a composed headline
app/services/ranking.py · top_reason()app/static/app.js · rWork()Formula
due sort before today's date, where today is the US business date (America/New_York). A task due today is not overdue. A due that is not a date never shows the badge.Why this wayIt is the same boundary the ranking uses for its overdue points, so the badge and the '+N overdue by D days' line always agree.
In the demo8 rows badged in the Mine view
Check it yourselfcount the red OVERDUE badges; every one of those rows has an 'overdue by' line in its tooltip, and no other row does
app/api/vbc.py · my_tasks()Formula
rule_key; with no rule: 'campaign' if source == 'campaign', 'transitions sweep' if source == 'transitions', else 'created by hand'Why this wayIt names the rule in a care manager's words ('Post-discharge contact window'), not the mechanism, so the person can see why a task they did not create is in their queue.
In the demoHRA past its contractual window
app/static/app.js · rWork()app/api/vbc.py · my_tasks()Formula
ceil(48h ÷ 24)), + 3 for medication reconciliation and barriers, + 7 for the visit and the assessment. Care gap due soon: the gap's date; today if the gap says 'now', 'overdue' or 'asap'; otherwise today + 14. Abnormal lab: result date + 7. Authorization at risk: the date of decision_due. Appealable denial: decision date + 60. Coverage ending: the coverage end date. SLA breach and HRA: the missed date. Social barrier, enrolment and engagement tasks: the day raised. Evidence missing and missed visit not rebooked: no date.Why this wayDischarge windows count from the discharge date, not from the day the record arrived (see Part I, Transition-of-care windows). A later sweep never re-dates an existing task: one task per rule and trigger. The authorization date is its business date (America/New_York, core.clock.business_iso), so a late-evening Eastern deadline is due that evening, not the next day. The task rule and every screen read the contact window through transitions.windows, rounded up the same way, so a tenant that tunes the rule moves the task and the screens together.
In the demo60 days before the reset
Check it yourselfthe 'Discharged … — follow-up visit due within 7 days' row is due 7 days after the discharge date in its reason
app/services/task_rules.py · _r_transition_contact()app/services/task_rules.py · _transition_step()app/services/task_rules.py · _r_gap_due_soon()app/services/task_rules.py · _r_pa_sla_at_risk()Formula
100 × (value − upper) ÷ |upper|; below: 100 × (lower − value) ÷ |lower|; rounded to a whole percent. Only the latest abnormal result per member per test in the last 30 days. Under 10%: no task. 30% or more: Critical, else High.Why this wayThe lab's abnormal flag has no size, so the rule measures how far outside the range the result is before it interrupts anyone. A result with no reference range is not treated as normal. It still raises a High task, which says the platform could not grade it.
In the demo14 % (highest-ranked lab task)
Check it yourselfevery graded lab task in the demo is below 30% and shows High; the HbA1c with no range says it could not be graded
app/services/task_rules.py · _deviation_pct()app/services/task_rules.py · _r_lab_critical()Formula
SUBMITTED, IN_REVIEW or PENDED: H = hours until decision_due, with the fraction dropped (23.9 prints '23h'). The task is raised when H ≤ 24, and says 'overdue' once H < 0.Why this wayIt warns the UM nurse a day before the regulatory deadline (see Part I, Prior-authorization decision clock). The hours are frozen into the reason when the task is raised; the score tooltip's authorization line is recomputed on every load.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnone after a reset. Raised by a task-rule sweep within 24 hours of a pending authorization's deadline
app/services/task_rules.py · _r_pa_sla_at_risk()Formula
DENIED or PARTIALLY_APPROVED authorization: raised while appeals.is_appealable — today is on or before the window's closing business date; closes on decision time + the line of business's window (appeals.appeal_window: 180 days by default, 60 for Medicare Advantage, tenant-configurable)Why this wayIt keeps a denial in front of a care manager while an appeal is still possible. The window is the appeals engine's own, by the coverage's line of business, so the task's closing date is the date filing an appeal stops being accepted, and the member attention panel names the same window.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnone after a reset. Deny an authorization, run the task rules, and the task appears with the closing date 180 days out (60 for a Medicare Advantage member)
app/services/task_rules.py · _r_pa_denied_appealable()Formula
Why this wayA member in care management who loses coverage drops out of the programme; 30 days leaves time to confirm continuing coverage. Coverage with no end date never raises a task.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnone after a reset
app/services/task_rules.py · _r_coverage_lapsing()Formula
OUTREACH or IDENTIFIED: N = today − date of its last recorded event; raised when N ≥ 21; due the day raisedWhy this wayThe last event is the last deliberate act on the intervention. The '+14 outreach stalled' score line uses the same wait and the same last event (see that row).
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnone after a reset
app/services/task_rules.py · _r_engagement_stalled()Formula
is_past_due(due, today, grace_days) — today − the object's due date > grace_days (grace 0, so from the day AFTER the due date) — and the object is not settled: vendor vendors.due_on (the SLA until SCHEDULED, then the appointment; A-058), referral due_on, provider opportunity sla_due_on (NEW or VIEWED only), social-need follow_up_on, HRA due_on. The task's due date is that same date.Why this wayA deadline is missed the day after it, not on it. The rules and every OVERDUE badge and overdue flag (My Work, the vendor chain, referrals, social-need follow-ups, the provider portal) ask the same function (core.clock.is_past_due), so a breach task never appears while its own row still reads 'due today'. A tenant's grace_days adds whole days after the due date. The HRA reason says how late it is, always 1 day or more.
In the demo2 tasks in the Team view
Check it yourselfa vendor task says 'SLA was <date>' or, for a scheduled service, 'the appointment was <date>'; the referral's own row shows the same date
app/services/task_rules.py · _r_vendor_sla_breach()app/services/task_rules.py · _r_referral_sla_breach()app/services/task_rules.py · _r_opportunity_unanswered()app/services/task_rules.py · _r_sdoh_follow_up()app/services/task_rules.py · _r_hra_overdue()Formula
SERVICE_COMPLETED, and today − the date it reached that status ≥ 7. Missed visit: the member's most recent appointment is NO_SHOW or CANCELLED, and today − its date ≥ 5. Both are raised with no due date.Why this wayAttendance is not the outcome. A gap stuck at 'attended' stays open on the measure, and a missed visit nobody rebooks is a member falling out of care. With no due date these tasks can never show OVERDUE, and their 7-day escalation clock runs from creation (see 'Escalated priority').
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnone after a reset
app/services/task_rules.py · _r_gap_evidence_missing()app/services/task_rules.py · _r_appointment_not_rebooked()Formula
ELIGIBLE, sorts them by number of criteria met (most first), and proposes the first 5 (max_per_run). A member who already has an offer task still takes one of the 5 places.Why this wayThe rule is rationed because care management has limited capacity, and a queue with every eligible member in it gets scrolled past. The cap is in effect 'the top 5 eligible members each evaluation', not '5 new offers each run'. Ties between members with the same number of criteria keep database order. The eligibility criteria are in Part I, Care-management eligibility.
In the demo1 offer tasks in the Team view
app/services/task_rules.py · _r_enrollment_offer()Formula
Why this wayA member with three care managers effectively has none, so new work joins whoever already has the member. Continuity overrides the rule's target role: an authorization task meant for a UM nurse can go to the member's care manager. Tasks raised by a provider's response (review, dispute, more information) are assigned without a caseload count, so unless continuity applies they go to the lowest-id holder of the role, not the least-loaded one.
In the demoJane Smith
Check it yourselfreassign from the Owner drop-down; the next rule-raised task for that member goes to the new owner
app/services/task_rules.py · _assign()app/services/task_rules.py · _caseload()The Care Manager's first screen: every member who currently needs attention, ranked by a points score that explains itself, with the next best action and what is already running. Below it, a follow-through panel counts what happened to the interventions the signed-in care manager owns.
Formula
> 0, counted BEFORE the top-25 cut.
For a care manager: every member in the tenant. For a provider login: only the provider's attributed members (scope.visible_ids).Why this wayCounted before the cut so the badge says how many members need attention, not how many rows fit on the screen: the table shows at most 25 of them. The list is the whole visible scope by design (A-099): unassigned members stay visible to somebody. A MINE badge marks the members the signed-in user owns an open task or a live intervention on, and the Mine button asks the server for those alone, filtered before the top-25 cut; the badge count then counts them. A care manager therefore sees the whole tenant's attention list under All; the Dashboard's Today's priorities shows the same computation with 8 rows and the same total ('of N needing attention').
In the demo12 members
Check it yourselfCount the rows: with fewer than 25 members needing attention the table has exactly this many rows. Open Dashboard: Today's priorities says 'of N needing attention' with the same N.
app/services/panel.py · panel()app/api/population.py · my_panel()app/core/scope.py · visible_ids()Formula
> 0 that have no WorkTask with status open, whoever owns the task. Counted over all members needing attention, not only the 25 rows shown.Why this wayThis is the reason the panel exists: the task queue can only rank work somebody created, so a member who needs attention but has no task is invisible there. Any open task counts, including one owned by another care manager, so the number answers 'has nobody raised anything', not 'have I raised anything'. When more than 25 members need attention, N can be larger than the NO TASK badges visible in the table.
In the demo2 members
Check it yourselfCount the amber NO TASK badges in the Member column; with 25 or fewer rows they equal N.
app/services/panel.py · panel()Formula
WorkTask in status open (has_task is false). Owner and task subject are not considered.Why this wayThe badge is a presence test, not an ownership test: a member whose only open task belongs to someone else shows no badge, and a member whose tasks are all closed shows the badge even when an intervention is running (Derrick Boateng in the demo: an intervention at AUTH_APPROVED, no open task).
In the demoJon Doe, Derrick Boateng
Check it yourselfOpen the member's 360 and look at open tasks: a badged member has none.
app/services/panel.py · panel()app/static/app.js · rPanel()Formula
Patient.risk_tier: HIGH when the risk score is >= 60, RISING >= 35, MEDIUM >= 15, else LOW. See Part I, Population health.Why this wayThe panel reads the tier stored on the member; it does not recompute it. The stored tier is refreshed only when risk is recomputed, and a user with record-edit rights can also set it by hand: a member created by hand carries the model version 'manual entry', and an edit changes the tier without changing the version. Until the next recompute a hand-set tier can differ from the model's latest score, which is what the risk header on Member 360 shows. The tier is worth 30 / 20 / 8 / 0 points in the priority score.
In the demoHIGH (John Doe)
Check it yourselfOpen the member's 360: the risk header shows the model's tier, which matches unless the tier was set by hand since the last recompute.
app/services/risk.py · score_patient()app/services/risk.py · rescore()Formula
30 / RISING 20 / MEDIUM 8; 8 per open gap with no live intervention; 2 per gap already being worked; 25 while the post-discharge follow-up is owed (the member's newest discharge, the tenant's visit window, 7 days by default, open, no follow-up visit attended), plus 15 more once the contact window (48 hours by default) has passed (days 3–7 by default); 10 if any active medication has a measured PDC below 0.80; 12 if a prior authorization is pending (SUBMITTED, IN_REVIEW or PENDED); 14 if an intervention needs a different approach (at IDENTIFIED or OUTREACH with no event for the tenant's engagement.stalled wait, 21 days by default, or ended UNREACHABLE or DECLINED); 18 if the member has an open gap and no active coverage today; 10 if the member is HIGH risk and enrolled in no care-management programme. Members scoring 0 are not listed. Computed at request time, never stored. See Part I, My Panel.Why this wayA points table rather than a model, so a care manager can argue with each weight, and the table the paper prints is the table the scorer reads, so the two cannot drift. Each signal reads the fact itself: 'in no programme' is the member's enrolment, not which recommendation ranked first (in the demo Maria Gonzales, HIGH risk and not enrolled, scores 80 although her top recommendation is a Critical gap); a PDC measured at 0% is below the threshold and an unmeasured one flags nothing. Adherence, authorization and stalled outreach each count once per member however many medications, authorizations or interventions qualify. This is a different scale from the risk score (Part I's worked example: John Doe 85 on risk, 80 here) and from the My Work task score.
In the demo80 points (John Doe)
Check it yourselfOpen 'all factors' on the row: the + numbers add up to the Priority.
app/services/panel.py · panel()app/services/transitions.py · recent_discharges()app/services/nba.py · recommend()Formula
components.length). One per signal that fired, at most 10.Why this wayIt counts signals, not problems: five unworked gaps are ONE factor worth 40 points. A high count means the attention comes from several independent directions; it says nothing about size.
In the demo3 factors (John Doe)
Check it yourselfOpen 'all factors': it lists exactly this many lines.
app/static/app.js · rPanel()app/services/panel.py · panel()Formula
why text of the single highest-points component. Ties keep a fixed order: risk, unworked gaps, gaps in flight, post-discharge, contact overdue, adherence, authorization, stalled outreach, no coverage, no programme.Why this wayOne sentence, so the reason for the rank is readable at a glance; the rest is one click away. The sentence carries computed values: the unworked-gap sentence counts all unworked gaps but names only the first three; the adherence sentence names the lowest-PDC medication with its percentage rounded, the same figure Member 360 prints (attention.pdc_pct); the authorization sentence names the pending authorization due soonest, the same one the Due column and the auth line name; a stalled intervention reads 'reached out, nothing back' or 'member declined', whichever it is.
In the demo5 open gap(s) with nothing running: HbA1c Control — Glycemic Assessment, Diabetic Retinal Exam, Statin Adherence (PDC >= 80%)
Check it yourselfOpen 'all factors': the Why sentence is the first (largest) line.
app/services/panel.py · panel()Formula
+points factor — why.Why this wayThe score ships with its own explanation, so a member's position can be checked rather than trusted. The factor names are the internal keys (unworked_gaps, post_discharge, …), not translated labels.
In the demo+40 unworked_gaps, +30 risk, +10 adherence (John Doe)
Check it yourselfThe + numbers sum to the Priority in the same row.
app/services/panel.py · panel()app/static/app.js · rPanel()Formula
25.Why this wayA name tie-break keeps the order stable across reloads. The order moves by itself as clocks move (a discharge window opening, an authorization going pending) because nothing is stored. The Dashboard's Today's priorities is the first 8 rows of the same ranking, and a cohort's member list is ranked by the same engine restricted to the cohort, so the three cannot disagree about a member.
In the demoJohn Doe 80, Maria Gonzales 80, Linda Tran 62
Check it yourselfOpen Dashboard: Today's priorities lists the same members in the same order.
app/services/panel.py · panel()Formula
500 recommendations. Labels: 'Start intervention', 'Track authorization <ref>', 'Try an alternate channel', 'Offer care-management enrollment'. See Part I, Next best action.Why this wayOne pass for the whole tenant keeps the screen to one read of each table. Deferred or dismissed recommendations are left out. 'Nothing recommended' means every gap is already in hand (for example an intervention running) and the member is not a high-risk unenrolled member. The band that decided the order is not shown on this row.
In the demoStart intervention (John Doe)
Check it yourselfOpen the member's 360: its next-best-action list (same engine, same order) starts with the same action.
app/services/nba.py · recommend()app/services/panel.py · panel()Formula
3 reason texts of that recommendation joined with ; . For an enrollment offer, the fixed sentence 'high-risk member is not enrolled in any care-management program'.Why this wayThree reasons keep the row short; the full reasoning is on the member's 360. The Dashboard shows the same text cut to 90 characters.
In the demoOpen quality gap — HbA1c Control; Overdue 97 days; Critical priority on the measure package
Check it yourselfOpen the member's 360: the first recommendation's reasons begin with the same three.
app/services/nba.py · recommend()Formula
CANCELLED, COMPLETED, GAP_CLOSED, DECLINED or UNREACHABLE (models.iv_works_gap, the same as iv_is_live).Why this way'In flight' means somebody still has work to do on it. The same test decides whether a gap counts as 'already being worked' (2 points) or 'unworked' (8 points), and Cost Concentration's '(N unworked)' uses it too, so the two screens agree: a gap whose only intervention was declined, went unreachable, or completed without the gap closing is unworked on both.
In the demoDiabetic retinal exam AUTH_APPROVED (Derrick Boateng)
Check it yourselfOpen the member's 360, Care tab: the same interventions are listed as open.
app/services/panel.py · panel()Formula
SUBMITTED, IN_REVIEW or PENDED) whose decision is due soonest; ties on the reference.Why this wayShown because a pending authorization blocks scheduling. It is the same authorization the Due column and the 'authorization on the clock' reason name, because all three take the first of models.pending_auths. Pending is read from the status: a pended request stays pending, and a DRAFT (nothing sent to the payer) is not pending, here or on the Start button. A pending authorization not linked to the gap shows here while Start is still offered, because only an authorization on the gap's own intervention blocks booking it. See Part I, Prior-authorization decision clock.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfSubmit a prior authorization for a member on the panel; after a reload the line appears and Priority rises by 12.
app/services/panel.py · panel()Formula
BOOKED or CONFIRMED whose business-time date is today or later; the screen prints that business-time date (next_appointment.date).Why this wayOnly live bookings count, so a cancelled or no-show appointment never reads as care arranged. The filter and the printed date use the same clock, US business time (core/clock), so an appointment in the US evening, after midnight UTC, prints the day the member is expected. The server sends the time already in business time (business_iso), and the page slices no UTC timestamp.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfBook an appointment for a panel member from the 360 Care tab; after a reload its date appears here.
app/services/panel.py · _next_appt()app/static/app.js · rPanel()Formula
CLOSED, all time, with no date window.Why this wayIt tells a care manager what has already been achieved for this member. Gaps retired as not applicable or excluded are not counted, and a closed gap that was later reopened drops out. It counts the member's gaps, whoever closed them; it is not the same number as the follow-through 'Gaps closed' below, which counts the signed-in user's interventions.
In the demo0 closed (John Doe)
Check it yourselfOpen the member's 360, Care tab: count the gaps shown as closed.
app/services/panel.py · panel()Formula
Why this wayAny engagement counts, so an inbound message or a failed send updates it. 'Never contacted' means no engagement is recorded in the platform, not that nobody tried by another route.
In the demonever contacted
Check it yourselfRecord an engagement (a call or a message) for the member; after a reload this line shows today's date.
app/services/panel.py · _last_contact()Formula
vbc.write); the recommendation is actionable (no undecided authorization linked to the gap's intervention, of any status including DRAFT); and the recommendation is for a gap.Why this wayThe screen only offers what the server would accept. An enrollment offer is actionable but is not about a gap, so it never gets a Start button. Starting still runs the authorization-requirement check on the server first.
In the demo10 rows show Start (care manager)
Check it yourselfRows showing 'nothing recommended' or an enrollment offer have no Start button.
app/static/app.js · rPanel()app/services/nba.py · recommend()Formula
Why this wayThe denominator of the follow-through funnel. It is ownership, not authorship: a reassigned intervention moves to its new owner's count. There is no date window: every count on this panel is all time, and the panel's basis line says so.
In the demo0 interventions
Check it yourselfClick Start on a panel row; after a reload this card goes up by one.
app/services/panel.py · follow_through()Formula
ENGAGED in the one funnel order (models.iv_reached): ENGAGED, AWAITING_AUTH, AUTH_APPROVED, SCHEDULED, IN_PROGRESS, COMPLETED, GAP_CLOSED and REOPENED, all time.Why this way'Reached' means the member responded, so an intervention that only sent a message does not count, and a stopped one (UNREACHABLE, DECLINED, CANCELLED) never does. It always includes 'Service completed'. The Dashboard's engagement funnel 'Reached' reads the same order, so the two agree, including on a REOPENED intervention.
In the demo0 interventions
Check it yourselfMove one of your interventions to ENGAGED; this card goes up by one.
app/services/panel.py · follow_through()Formula
COMPLETED or GAP_CLOSED, all time.Why this wayThe service happened. Started ≥ Reached ≥ Completed always holds, so the three cards read as a funnel.
In the demo0 interventions
app/services/panel.py · follow_through()Formula
CLOSED, all time.Why this wayIt counts interventions, not distinct gaps. A gap closes on clinical evidence that can arrive for reasons unrelated to the outreach, which the caveat under the panel says in bold: read it as what happened to the work, not what the work caused. A gap that later reopened drops out. Different from the per-member 'N closed' in the table and from a quality measure's numerator.
In the demo0 interventions
app/services/panel.py · follow_through()Formula
IDENTIFIED or OUTREACH whose last event is at least the tenant's engagement.stalled wait old (stalled_days, 21 by default), or one that ended DECLINED or UNREACHABLE (models.iv_follow_up_why).Why this wayThese need a different channel or a caregiver contact, and nothing else surfaces them. An outreach counts only once it has been quiet for the tenant's wait, the same wait the rule 'Outreach with no movement' uses. DECLINED and UNREACHABLE are included and named as such, not described as silence. The Dashboard's 'Outreach needing follow-up' uses the same test for the whole tenant; this note counts only your own.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfIn the fresh demo tenant the care manager owns no interventions, so the note is absent. Start an intervention and move it to OUTREACH: the note appears with N = 1.
app/services/panel.py · follow_through()app/static/app.js · rPanel()The lists behind the Command Center cards and priorities (open care gaps, care plans, interventions in flight, referrals, vendors, vendor performance, open assessments). A VBC manager, executive or administrator opens them by clicking a card; each is a list you can act on, not a report.
Formula
CLOSED, EXCLUDED, NOT_APPLICABLE, GAP_CLOSED, RESOLVED, DUPLICATEWhy this wayThis is the list the Command Center's 'Open care gaps' card opens, and it uses the same rule (gap_is_open), so the card and the list agree. A gap being worked (in progress, scheduled, awaiting authorization) is still open: a gap closes on evidence, not on effort. The list shows at most 200 rows; the count is the full total.
In the demo29 open gaps
Check it yourselfOpen the Command Center, note 'Open care gaps', click it: the header count here is the same number.
app/services/worklists.py · care_gaps()app/services/workflow.py · gap_is_open()Formula
Why this wayThe same missed measure matters more on a high-risk member with a chronic condition, so priority is a lookup rather than a score: two inputs, sixteen fixed answers, nothing hidden. It is re-derived every time the measure is evaluated, so it follows the member's tier. The list is sorted Critical first, then by member name. This priority feeds both ranking engines (Part I, Work-queue ranking and Next best action).
In the demoCritical 6, High 10, Medium 12, Low 1
Check it yourselfOpen a Critical row's member: their risk tier is HIGH and the measure is a clinical one.
app/services/quality.py · gap_priority()app/services/worklists.py · care_gaps()Formula
Why this wayThe sentence is not written for display; it is the evidence the numerator test produced, so it cannot say something the engine did not decide. See Part I, One member, one measure.
In the demolatest Systolic blood pressure 148.0mmHg on 2026-09-12 is at or above the 140 threshold
Check it yourselfOpen the member and the gap: the lab or procedure history shows exactly what the sentence says.
app/services/quality.py · meets_numerator()app/services/quality.py · evaluate_patient()Formula
ACTIVE care planWhy this wayOne row per active plan. Rows are sorted with overdue reviews first, then by member name, so the plans that need a person come to the top.
In the demo1 plans
app/services/worklists.py · care_plans()Formula
MET:
a goal linked to a care gap is met when that gap is CLOSED or RESOLVED
a goal linked to a vendor referral is met when the referral is CONFIRMED
a goal with no linked work is met only when a person marked it metWhy this wayA goal's status is derived from the work beneath it, so nobody can type 'met' over an open gap; only a goal with nothing linked relies on a person's word. The cost: a goal whose gap closed for an unrelated reason also reads as met.
In the demoJohn Doe: 8 goals, 0 met
Check it yourselfOpen the member's Care plan tab: each goal's status and the gap or referral it is linked to are shown side by side.
app/services/worklists.py · care_plans()app/services/careplan.py · _goal_status()Formula
Why this wayA review date in the past is a commitment already missed, so it is flagged rather than merely shown. Dates are judged on the US business calendar, not the server's clock, so the flag does not flip at the wrong midnight.
In the demo0 overdue reviews
app/services/worklists.py · care_plans()app/core/clock.py · business_today()Formula
CANCELLED, COMPLETED, GAP_CLOSED, DECLINED or UNREACHABLEWhy this wayIn flight means somebody still has something to do. This is the same rule as the Command Center's 'Active interventions' card, which opens this list, so they agree. The care manager's Dashboard card of the same name excludes only GAP_CLOSED, CANCELLED and COMPLETED, so a declined or unreachable intervention counts there and not here.
In the demo1 interventions
app/services/worklists.py · interventions()Formula
IDENTIFIED, OUTREACH, ENGAGED, SCHEDULED, then every other state, then member nameWhy this wayEarliest stage first, because an intervention nobody has contacted yet is the one most likely to stall.
In the demoAUTH_APPROVED 1
app/services/worklists.py · interventions()Formula
Why this wayIt reads the trail in the order entries were written, which is the order things happened, rather than re-sorting by timestamp.
In the demoauthorization approved; ready to schedule
app/services/worklists.py · interventions()Formula
Why this wayAll three kinds of referral in one list, because to the member they are all 'somebody was asked to help'. Sorted overdue first, then by due date.
In the demo3 referrals
app/services/worklists.py · referrals()Formula
Why this wayEach kind keeps its own clock because each is a different promise: a follow-up call, a contract, an internal service level. A referral that has settled is never overdue, however late it settled. See Part I, Referral SLA.
In the demo0 overdue now
Check it yourselfThe header's 'N overdue' chip is the count of red OVERDUE badges in the list.
app/services/worklists.py · referrals()app/services/referrals.py · sla_days()Formula
RESOLVED + vendor CONFIRMED + internal OUTCOME_CONFIRMED
none settled → no percentage, with the reasonWhy this wayThe denominator is settled referrals, not all referrals, so a referral still in progress neither helps nor hurts the rate. Closed means the outcome was confirmed by the plan, not claimed by the vendor. Green at 80% or above. For a login limited to a panel, the list, the overdue count and this percentage are all computed over the panel's referrals; settled is judged per referral kind.
In the demo100 %
Check it yourselfIf nothing has settled yet the badge reads 'closed loop: n/a' and says why.
app/services/worklists.py · referrals()Formula
CONFIRMED, DECLINED, FAILED, CANCELLEDWhy this wayA vendor is a contracted party, so its SLA is a contract term, typed once and read everywhere. Open referrals is everything that has not reached a final state.
In the demo5 vendors; SLA days 3, 5, 14, 7, 5; 2 open referrals
app/services/worklists.py · vendors()app/services/vendors.py · ensure_vendors()Formula
CONFIRMED)Why this wayConfirmed, not delivered: 'delivered' is the vendor's claim, 'confirmed' is the plan's evidence.
In the demoMedRide NEMT 0/1, Nourish Home Meals 1/1, HouseCall Health Assessments 0/1
app/services/vendors.py · performance()Formula
CONFIRMED ÷ settled ), settled = CONFIRMED + DECLINED + FAILED + CANCELLED
no settled referrals → 'n/a' with the reasonWhy this waySame denominator rule as the referrals list: only finished referrals are judged.
In the demoNourish Home Meals 100%
app/services/vendors.py · performance()Formula
Why this wayIt measures the vendor's own turnaround (to delivery), not the plan's confirmation lag. A median, the same function every median on every screen uses, so one slow ride does not define the vendor; for an even count it is the average of the two middle values, not either one.
In the demoNourish Home Meals 4 d
app/services/vendors.py · performance()app/core/stats.py · median()Formula
Why this wayIt counts breaches that are still live, the ones a person can still chase; a referral that was late but has since been confirmed drops out. It is therefore a current-risk count, not a historical breach rate.
In the demo0 live breaches, all vendors
app/services/vendors.py · performance()app/services/vendors.py · is_overdue()Formula
Assessment rows of any kind whose status is not COMPLETED, REFUSED or UNREACHABLE
OVERDUE = due date before today (US business date)
rows sorted by due date, soonest firstWhy this wayThe list the Command Center's 'Health risk assessments overdue' row opens. It lists assessment ROWS of every kind (an HRA or a reassessment), because each is a piece of work somebody can do; the Command Center row counts MEMBERS by their governing HRA. So the header counts open assessments, not overdue members, and the two numbers are not meant to match. The list shows at most 200 rows; the count is the full total.
In the demo8 open, 2 overdue
Check it yourselfCount the rows; the header shows the same number, and the OVERDUE badges sit on the rows whose due date has passed.
app/services/worklists.py · assessments()app/static/app.js · rWlAssess()Formula
Why this wayThe HRA completion KPI, over the denominator the plan does not choose: every enrolled member, so an HRA nobody attempted counts as much as one the member refused, and one completed late is not a completion. N is the number on the Command Center's overdue-HRA row, and P is the rate the operational board's 'HRA completion' shows. Refused and unreachable are reported apart because they call for different responses.
In the demo62% of 8 enrolled; 2 overdue, 1 refused, 0 unreachable
Check it yourselfOpen the Command Center: its 'Health risk assessments overdue' row names the same N and P.
app/services/assessments.py · kpis()app/services/assessments.py · hra_standing()app/static/app.js · rWlAssess()The member roster: every staff role with patient read access (Care Manager, VBC Manager, Executive, PA Coordinator, Auditor, Administrator) sees every member of the tenant here; a Provider sees the same screen as "My Panel" in the sidebar, narrowed to the members attributed to them. It is a lookup list, not a work queue: it counts and classifies, it does not rank.
Formula
GET /vbc/patients: every Patient record in the tenant, then narrowed by scope.filter_patient_rows (a no-op for staff roles, the attributed panel for a provider).
Counted in the browser as rows.length; the search box hides rows but does not change N.Why this wayEvery member record, not only attributed members and not de-duplicated: the demo's unmerged duplicate "Jon Doe" (same date of birth and MRN as John Doe, arriving from the EHR feed) is counted as a second member until someone merges it. No row is dropped for missing coverage or a closed attribution period. The Dashboard's "Patients" card counts the same records and agrees for every staff role; it agrees for a provider too: both the Dashboard card and this list count the provider's panel. Rows are listed in the order the database returns them. The query sets no order, so the list is not ranked by risk or by anything else.
In the demo13 members
Check it yourselfCount the table rows, or download the CSV with the search box empty: same number. As Care Manager, Dashboard > Population > "Patients" shows the same N.
app/api/vbc.py · list_patients()app/core/scope.py · filter_patient_rows()app/static/app.js · rPatients()Formula
PatientAttribution row (no end date, or one after today) where provider_id = the signed-in provider, in the CURRENT period (open and containing today).
A provider login with no provider binding, or no current period, sees an empty list (never the whole tenant).Why this wayAttribution is by provider identity, not by the free-text PCP name, so the PCP column on this screen does not decide who is on the panel. In the demo every one of Dr. P. Osei's five members shows a different declared PCP (Dr. Sarah Smith, Dr. Robert Jones or Dr. Karen White); the demo seed moves these five attribution rows to Dr. Osei after they were created from the declared PCP. Those rows still read method declared_pcp and "Declared PCP on record: Dr. …", which names a different physician. That is a known inconsistency in the demo data. A member outside the panel is not merely hidden: opening them by link answers not-found rather than forbidden, so a provider cannot learn the member exists. Two things share the name here: the Care Manager's "My Panel" is a different screen (members ranked by a priority score, see the My Panel chapter) and counts something else.
In the demo5 (John Doe, Sarah Martinez, Linda Tran, Ahmed Ali, Rose Chen) members
Check it yourselfSign in as doc@demo.provider: the badge shows the number above and exactly those names. Sign in as the Care Manager: the same screen lists the whole tenant (see "Patients (N)").
app/core/scope.py · visible_ids()app/core/scope.py · _provider_panel()app/api/vbc.py · list_patients()Formula
Patient.risk_tier, printed as-is. When the risk model last scored the member: HIGH if score >= 60, RISING >= 35, MEDIUM >= 15, else LOW. See Part I, Population health.
Colour: HIGH red, RISING amber, MEDIUM blue, LOW green.Why this wayThe list reads the tier stored on the member; it does not rescore. The stored tier is refreshed in only two places: the nightly demo reset, and the recompute call POST /population/risk/recompute (VBC Manager permission). No screen offers that call. New labs, conditions or admissions do not rescore the member on arrival, so the badge can lag the clinical record. A user with record-edit rights can also overwrite the tier by hand. A hand edit does not change the model name stored on the member, so a hand-set tier still carries the model's name. A member no model has scored shows a badge too: the column defaults to LOW. In the demo, "Jon Doe" (the unmerged duplicate) shows a green LOW with no score and no factors behind it. His Member 360 says "No risk model has scored this member" and the list does not. The Dashboard's Risk distribution counts that unscored LOW as a Low member.
In the demoJohn Doe HIGH, Sarah Martinez MEDIUM, Maria Gonzales HIGH, Sam Okafor RISING, Linda Tran RISING, Ahmed Ali RISING, Rose Chen MEDIUM, Derrick Boateng MEDIUM, Elena Petrova MEDIUM, Robert Kim MEDIUM, Fatima Hassan RISING, Grace Nwosu LOW, Jon Doe LOW (never scored)
Check it yourselfOpen a member: the Member 360 risk header shows the score and the factor list, and the tier matches the badge (for a scored member the factors add up to a score inside the band). For Jon Doe the 360 says no model has scored him.
app/services/risk.py · score_patient()app/services/risk.py · rescore()app/api/records.py · update_patient()app/api/vbc.py · list_patients()app/static/app.js · statusBadge()The care manager's record of one member (VBC managers, executives, PA coordinators, providers for their own panel and auditors can open it too). This chapter covers the header, the Overview's service cases, partner activity and recent activity, the care gaps and interventions on the Care tab, the transition badge on the Utilization tab, and coverage and authorizations on the Coverage tab. Figures are shown as the care manager Jane Smith sees them.
Formula
Why this wayWorked out by the server each time the record is read, never stored, because a stored age is wrong on the member's next birthday. Known defect: when the date of birth is missing or unreadable, the age function returns -1 rather than no value, so the header prints '| -1' where it should leave the age out.
In the demo65 years
Check it yourselfThe date of birth is printed two lines below the name. Subtract it from today's date.
app/api/vbc.py · get_patient()app/services/quality.py · _age_on()Formula
male / female / other (any case) shown as Male / Female / Other; anything else is not shown
plan: payer + plan of the member's current coverage (services/coverage.current_coverage: status active and today within its dates); none → 'no active coverage on file'
not recorded: names each of sex, PCP and active coverage that is emptyWhy this wayThe header names a missing fact instead of leaving a blank, because a blank looks like a display fault. The plan line is the same coverage the summary strip's Coverage card and the Coverage tab's line name: one function answers which coverage is current.
In the demoJohn Doe: Male | UnitedHealthcare MA Choice; not recorded: nothing, Jon Doe: (sex not shown) | no active coverage on file; not recorded: sex, active coverage
Check it yourselfOpen Jon Doe (the unmerged duplicate): no sex is shown, the plan line reads 'no active coverage on file' and the header lists both under 'not recorded'.
app/api/vbc.py · get_patient()Formula
Patient.risk_tier), the same field as the Patients list badge. When the risk model sets it: HIGH ≥ 60 · RISING ≥ 35 · MEDIUM ≥ 15 · LOWWhy this waySee Part I, Clinical risk score and tier, and the Patients list row on the risk tier. Known inconsistency on this screen: the Primary risk drivers panel on the Overview prints the tier of the latest scoring run, not this stored field. Editing the tier by hand (Edit patient) changes this badge and not the panel, so the two can show different tiers for one member. They agree in the fresh demo tenant.
In the demoheader HIGH; risk drivers panel HIGH
Check it yourselfCompare the header badge with the badge on the Primary risk drivers panel on the Overview tab.
app/api/vbc.py · get_patient()app/services/member360.py · risk_drivers()app/api/records.py · update_patient()Formula
Why this waySalesforce is the system of record for cases; this panel is a copy, so a care manager does not work the member while a complaint is open without knowing about it. Open or closed depends on the closed date Salesforce sent, not on its status text, and the status is shown verbatim. A case sent with a closed-family status ('Closed', 'Resolved') but no closed date still reads 'Open', and since 2026-09-25 (A-048) it carries a red flag 'status says Closed but no closed date was sent' rather than being silently remapped. The count is only as current as the last sync (next row).
In the demo1 open: Transportation complaint (In Progress)
Check it yourselfEach case card below the count says Open or Closed; count the Open ones.
app/api/population.py · member_cases()app/static/app.js · casesPanel()Formula
Why this wayA copied status without the time of the last copy invites a stale answer to be read as current. Since 2026-09-25 (A-048) the time is shown in business time with its zone label; before that it was the UTC time with no zone, so after 8 pm Eastern (daylight time) it already showed the next day's date.
In the demomoves with the clock — read it on screen
Check it yourselfIn the demo it is the time of the nightly reset, in US Eastern time, with the zone shown.
app/api/population.py · member_cases()Formula
DELIVERED 'Vendor reports it completed', CONFIRMED 'Completion confirmed by the plan, with evidence', …)
overdue: the due date is before today (vendors.due_on: the SLA due date until the referral is SCHEDULED, then its appointment date; nothing once DELIVERED or settled = CONFIRMED, DECLINED, FAILED, CANCELLED)
SLA due date = referral date + the vendor's contracted SLA days (7 when the vendor has none)
colour: red if overdue, FAILED or DECLINED · green if CONFIRMED · grey if otherwise settled (CANCELLED) · blue otherwise
rows: newest last activity first, by full timestampWhy this wayDelivered is the vendor's claim and confirmed is the plan's evidence, so only confirmed turns green. The SLA clock starts at the referral, not when the vendor acknowledges it, so a slow acknowledgement cannot move the deadline. Part I, Referral SLA covers team referrals; vendor referrals use the vendor's own contracted days instead. A vendor with an SLA of 0 days is treated as 7.
In the demoMedRide NEMT: Service scheduled (blue), Nourish Home Meals: Completion confirmed by the plan, with evidence (green), HouseCall Health Assessments: Vendor acknowledged (blue)
Check it yourselfClick 'Closed-loop detail': the Referrals tab's Vendor referrals table shows the same referrals, with the SLA date in red when overdue.
app/services/vendors.py · member_view()app/services/vendors.py · is_overdue()app/static/app.js · p360Tab()Formula
Why this wayOne list across all areas, so the order of events is visible. Events on the same day are ordered by their time, and events at the same instant keep the order they were read in; the kind of event never decides the order. Until 2026-09-25 (A-047) the time of day was dropped and same-day events were ordered by the kind's name (tasks ahead of gaps), and an event after 8 pm Eastern was dated the next day.
In the demo2026-10-12 Care plan — active, 2026-10-12 Task raised — Review result with member, 2026-10-12 Outreach planned — Food insecurity recorded on 2026-07-14 with no ref, 2026-10-12 Outreach planned — HRA was due 2026-08-13 — 60 days overdue, 2026-10-12 Outreach planned — HbA1c Control — Glycemic Assessment — due 2026-07-, 2026-10-12 Task raised — Assign a care manager and open a care plan
Check it yourself'Full timeline' opens the Timeline tab. Its first six entries are the same six, in the same order.
app/services/member360.py · timeline()app/static/app.js · p360Tab()Formula
open); M = every care-gap record, closed and excluded included
progress = the gap orchestration bar 'X of Y steps complete' (eligibility, authorization and the clinical steps); the gap's own status is shown as its badgeWhy this wayN is the same count as the summary strip's 'Open gaps', from the same rule, and the Open outreach ladder and Send to provider buttons show for exactly those gaps (A-063). Progress has one source, the orchestration bar (see the Gap orchestration row in the Patient 360 summary-strip chapter); the old six-step 'step N of 6' line read from the status is removed (A-040), because it showed a second, disagreeing progress figure for the same gap.
In the demoJohn Doe: 5 open of 5 gaps, Derrick Boateng, Diabetic Retinal Exam: orchestration 5 of 8 steps complete
Check it yourselfOn the Care tab, the Care Gaps title's open count matches the summary strip's Open gaps; each gap shows one progress bar.
app/api/vbc.py · get_patient()app/services/workflow.py · gap_is_open()app/static/app.js · p360Tab()Formula
Why this wayThe panel is the member's whole history of interventions. The summary strip's 'Active interventions' counts only those still in progress, so the two numbers differ once one finishes or is cancelled. Only five events are shown to keep the card short; the full trail is on the Timeline tab.
In the demoJohn Doe: 0, Derrick Boateng: 1 (Diabetic retinal exam AUTH_APPROVED)
Check it yourselfJohn Doe has none until the demo starts one from a gap. After that, the count here and the strip's Active interventions both read 1.
app/api/vbc.py · get_patient()app/static/app.js · p360Tab()Formula
Why this wayThe member's own record shows how far the transition has got, not only a date. Since 2026-09-25 the member record and Transitions share one actionable window (A-039) and the badge colours the outcome, not the lifecycle (A-038): a plan that closed with missed steps is amber, not green. The Utilization panel below counts a separate, stated 180-day window. Dismissed steps count as completed.
In the demoSam Okafor: discharged 2026-10-10, 4/7 completed (amber) — 3 step(s) still outstanding: Clinical assessment, Follow-up appointment, Visit completion, Grace Nwosu: historical — outside the 120-day actionable window, discharged 2026-03-30 — closed with missed steps
Check it yourselfOpen Sam Okafor, Utilization tab: the badge matches the plan's steps counted by hand (the 30-day watch and the closing step are not counted). Open Grace Nwosu: her March discharge is labelled historical, and its outcome reads 'closed with missed steps'.
app/api/population.py · member_transition()app/services/transitions.py · latest_discharge()app/services/transitions.py · plan()app/static/app.js · p360Tab()Formula
activeWhy this wayOnly one coverage is active at a time: adding a plan terminates the previous one and sets its end date to today. Known defect: if the coverage request fails, the panel shows 'No coverage on file' and a count of 0 instead of an error. That is a false absence, and coverage is what decides whether a service can be booked.
In the demoUnitedHealthcare MA Choice (active, 2026-01-01 to 2026-12-31)
Check it yourselfCount the table's rows.
app/api/records.py · list_coverage()app/static/app.js · coveragePanelHtml()Formula
Why this wayThe status is printed next to the plan, so a terminated plan says so. It is the same coverage the header plan line and the summary strip name whenever the member has a current one; only when none is current does it fall back to the latest row, so the reader sees why.
In the demoUnitedHealthcare · MA Choice (active)
Check it yourselfCompare with the plan line in the header and the Coverage table directly above.
app/api/population.py · member_authorizations()Formula
Why this wayVerification is something done on a date, not a property of the record: 'active' on file is not the payer saying so today. This line and the Check eligibility step in each gap's orchestration chain (Care tab) read the same query since 2026-09-25 (A-041), so they name the same check and the same business date; before that this line took the UTC date with no tie-break.
In the demothe reset day
Check it yourselfOpen Derrick Boateng (verified on the reset day). John Doe reads 'never' until someone clicks Check eligibility in the header.
app/api/population.py · member_authorizations()app/services/orchestration.py · gap_chain()Formula
APPROVED; otherwise 'scheduling held' (a denial shows its reason)
green badge by the title = the newest approved authorization
colour: green APPROVED · red DENIED · amber PENDED, IN_REVIEW · blue otherwiseWhy this wayThe member's own record answers whether a service can be booked; the server applies the same rule when booking (see Part I, The method: scheduling stays blocked while a linked authorization is pending). Limit: only an approval unlocks scheduling, so a partial approval reads 'scheduling held'. Status and service names come from one registry each since 2026-09-25 (A-045); before that the endpoint title-cased the raw status ('In_Review'), kept its own five-code name list without G0439, and printed an open authorization's raw decision-due timestamp under 'Decided'.
In the demoPA-2026-40188: Retinal exam PA — Approved, scheduling unlocked
Check it yourselfOpen Derrick Boateng, Coverage tab: one authorization, approved, with the green headline badge. The Authorizations screen shows the same reference and status.
app/api/population.py · member_authorizations()app/api/population.py · _decision()app/core/terminology.py · service_label()app/static/app.js · authorizationsPanel()The care manager's single view of one member. Every figure below is shown for the demonstration member John Doe, as the care manager Jane Smith sees him.
Formula
Why this wayAccountability is a recorded assertion with its method, because the method is the argument when a payer disputes the denominator. The method says how the member came to this provider: 'declared_pcp' when their declared PCP is the provider, 'roster' when they were assigned to the provider's panel (the demo provider's five members), with the overridden PCP kept in the basis.
In the demoDr. P. Osei by roster since 2026-01-01 (basis: Assigned to Dr. P. Osei's panel (synthetic demo roster); declared PCP on record: Dr. Sarah Smith)
app/services/member360.py · summary()Formula
Why this wayThe tier is the risk engine's; see Part I, Clinical risk score. It is kept current by the record, not by whoever remembers: any change to a member's conditions, results, encounters or medications — through ingestion, the API or a feed — re-scores that member when it is saved, and a score is refreshed once a day when time alone moves it (a lab ageing out of its window). A tier set by hand is left as the person set it.
In the demoHIGH, 10 factors
app/services/member360.py · summary()app/services/risk.py · rescore()Formula
Why this wayThe same open rule as every other gap count (gap_is_open), for one member.
In the demo5 open (3 critical, 2 high), 0 closed
app/services/member360.py · summary()app/services/workflow.py · gap_is_open()Formula
Why this waySix months is long enough to show a pattern and short enough to be about now. The big number counts only acute use (inpatient and ED), which is the utilisation a care manager acts on.
In the demo1 acute (0 inpatient + 1 ED) of 2 encounters in 180 days
app/services/member360.py · summary()app/static/app.js · summaryStrip()Formula
IDENTIFIED, OUTREACH, ENGAGED, SCHEDULED, AWAITING_AUTH, AUTH_APPROVED or IN_PROGRESS; hint = those states, or the total when none is activeWhy this wayActive means the intervention is still moving toward its outcome. The worklists and Command Center use a slightly different list of 'not finished' states, so their counts can differ for an intervention in an unusual state.
In the demo0 active of 0
app/services/member360.py · summary()Formula
Why this wayThe member's own queue in one line, so the care manager sees what is due before scrolling.
In the demo7 open, 3 overdue; next: Close care gap
app/services/member360.py · summary()Formula
active and whose dates contain today (the most recently started, if several)); payer, plan and end date from itWhy this wayCoverage decides who pays and whether an authorization rule applies. The same function names the coverage on the header, My Panel, the Dashboard and the readiness check, so they cannot disagree.
In the demoUnitedHealthcare MA Choice, ends 2026-12-31
app/services/member360.py · summary()Formula
Why this wayOne list across every source, so nothing is only visible on a tab. 'In flight' stops a care manager from starting work that someone else has already started.
In the demo7 items, 4 first, 0 in flight
app/services/attention.py · attention()Formula
Why this wayEvery severity is a written threshold, and each line shows the arithmetic that fired it. Targets are population defaults, not individualised clinical targets, and the panel says so.
In the demohigh: Creatinine above target, high: Diabetic Retinal Exam, high: HbA1c above target, high: Systolic BP above target, medium: Annual Wellness Visit, medium: Social barrier identified, medium: Statin Adherence (PDC >= 80%)
app/services/attention.py · attention()app/services/attention.py · _severely_over()Formula
Why this wayA number without its direction invites the wrong conversation: 8.9% is above target but moving the right way.
In the demo8.9%; improved from 9.4%
app/services/attention.py · _value_phrase()app/services/attention.py · _latest_by_code()Formula
Why this wayThe due date is when the measure year closes, because that is when an open gap becomes a missed one. The PA note comes from the same policy lookup the authorization screens use, so the two cannot disagree.
In the demo1 overdue gap lines
app/services/quality.py · gap_due_on()app/services/workflow.py · pa_required()Formula
Why this wayPDC (proportion of days covered) is a stored pharmacy input, not computed by the platform; 80% is the standard adherence threshold.
In the demoAtorvastatin 40mg adherence 62%, below the 80% threshold
app/services/attention.py · _adherence()Formula
appeals.is_appealable: 180 days, 60 for Medicare Advantage), with the window's length and closing date
coverage: 'ends <date> (N days)' when 0–60 days awayWhy this wayEach is a clock the member is on. The discharge line uses a 30-day window, while the Transitions screen keeps a discharge actionable for 120 days.
In the demoRecently discharged from hospital: Discharged 2026-10-10 (2 day(s) ago) from Apex Demo Medical Center.
Check it yourselfShown for Sam Okafor, discharged two days before the reset.
app/services/attention.py · _transition()app/services/transitions.py · recent_discharges()app/services/attention.py · _authorizations()app/services/attention.py · _coverage()Formula
apex-risk-rules v2)Why this wayA transparent points model, not a statistical one: every point names the fact that earned it. See Part I, Clinical risk score, for every weight. The panel shows the latest stored scoring run, and that run always matches the member's current record: saving a new result re-scores the member, and the score history keeps every earlier run. For John Doe the 9.4% HbA1c band (18 points) gave way to the 8.9% band (10 points) when the September result arrived: 85 points.
In the demo85 points, HIGH, model apex-risk-rules v2
app/services/member360.py · risk_drivers()app/services/risk.py · score_patient()Formula
Why this wayA diagnosis explains the tier but is not something to close, so it gets an explanation rather than a false call to action.
In the demo5 drivers with an action button
app/services/member360.py · _action_for_driver()Formula
Why this wayAn observed change, not a claim about what caused it; the panel says so. No time window, so the whole record on file is compared.
In the demoHbA1c 9.4 → 8.9 (-0.5, improved, target 8.0), Systolic BP 162.0 → 148.0 (-14, improved, target 140.0), Creatinine 1.9 → 1.8 (-0.1, improved, target 1.3)
app/services/outcomes.py · patient_trajectory()Formula
Why this wayThe abnormal flag is the laboratory's statement, kept as sent: re-deriving it from a reference range would put words in the lab's mouth. PDC is a pharmacy input the platform stores, not a figure it computes.
In the demo3 problems, 6 of 6 lab results flagged abnormal, 2 encounters, PDC Atorvastatin 40mg 62%
app/services/ingest.py · promote()app/static/app.js · panelMeds()Formula
Why this wayA progress bar that can never reach the end is one nobody reads, so a service with no authorization requirement has 6 steps, not 8. Each step is derived, never ticked.
In the demoHbA1c Control — Glycemic Assessment: 2 of 6 steps complete, Diabetic Retinal Exam: 2 of 8 steps complete, PA required, Statin Adherence (PDC >= 80%): 2 of 6 steps complete
app/services/orchestration.py · gap_chain()Formula
Why this wayAn eligibility answer goes stale; a 30-day-old check is not evidence of coverage today.
In the demoopen: coverage on file (UnitedHealthcare MA Choice) but never verified with the payer
app/services/orchestration.py · gap_chain()Formula
Why this wayApproval of an authorization never closes a gap; only clinical evidence of the service does. The check is on the evidence supplied with the event.
In the demo0 gaps closed on evidence so far
app/services/workflow.py · evaluate_completion()Formula
Why this wayOne chronology across every system, so the member's story reads in order. Counting before filtering lets a reader see what a filter hides.
In the demo43 events; Clinical 11, Encounters 2, Care Gaps 6, Tasks 7, Referrals 12, Provider 1, Care Plan 1, Engagement 3
app/services/member360.py · timeline()Formula
Why this wayColour marks events that change what should happen next, so a long timeline can be skimmed.
In the demored 7, amber 1, green 1
app/services/member360.py · timeline()The care manager's working tabs on one member: care-management enrolment, the health risk assessment, the care plan, social barriers, appointments and documents (Care tab), team, social and vendor referrals (Referrals tab), and Partner Activity on the Overview. Every figure below is read as the care manager Jane Smith sees it, for John Doe unless another member is named.
Formula
cm-complex) enrolment: status ELIGIBLE, OFFERED, ACCEPTED or ENROLLED (other programmes are listed, never shown as this one)
badge = the live status; with nothing live: MEETS CRITERIA when at least one eligibility criterion is met today, else NOT ELIGIBLE
a stage dot is ticked when it is at or before the live status in ELIGIBLE → OFFERED → ACCEPTED → ENROLLED
'Previously declined / disenrolled' = the member's DECLINED or DISENROLLED row for cm-complex, shown only when nothing is liveWhy this wayThe badge is the recorded lifecycle; the criteria are re-evaluated on every view. The two can differ, and the panel shows both rather than choosing: an ENROLLED member who no longer meets any criterion keeps ENROLLED (Sarah Martinez shows ENROLLED above 'does not currently meet the criteria'). A member who declined is shown as declined and is not re-offered automatically; offering again is a person's decision. One limit. The Recommended action card uses a different rule (see the Next best action chapter): it offers enrolment to every HIGH-risk member not yet ENROLLED, so Maria Gonzales, already OFFERED, is still recommended an offer (ranked sixth, so it is below the three cards Patient 360 shows, but it is on the Population list), while rising-risk members who meet the criteria get no offer card.
In the demoENROLLED; stages reached: ELIGIBLE, OFFERED, ACCEPTED, ENROLLED
Check it yourselfOpen John Doe → Care. Sam Okafor shows MEETS CRITERIA with a 'Previously declined' line and an 'Offer again' button.
app/services/enrollment.py · member_view()app/static/app.js · enrollmentPanel()Formula
Why this waySee Part I, Care-management eligibility. The criteria are computed on every view and never stored as a flag, so the reasons are current. The acute test counts encounters by admission date. The social test uses the same barrier classification as this tab's Social drivers of health panel, so a declined referral still counts as unaddressed. Part I's worked example is out of date: the product today finds six members on eight criteria. Maria Gonzales also meets the recent-acute test, and Robert Kim qualifies on it; neither line is in Part I's table.
In the demoHigh-risk member (risk tier HIGH), Admission or ED visit in the last 6 months (1 acute encounter(s))
Check it yourselfOpen John Doe → Care: two lines, 'High-risk member' and 'Admission or ED visit in the last 6 months'. His Utilization tab shows the admission.
app/services/enrollment.py · evaluate()Formula
cm-complex) enrolment is ENROLLED: started when a care manager owns the member (the active care plan's owner, else the owner of the newest open task) AND an active care plan exists
otherwise the red badge shows and the missing items are listed; when started, the owner is shown as Care managerWhy this wayEnrolment can be recorded directly, for example from a payer file or a programme older than the platform. So the platform reports an enrolment with nothing behind it rather than refusing to record it. The panel's note states the rule in one sentence (enrollment.ENROLLED_RULE, which Part I quotes word for word): ENROLLED may be recorded directly, the panel flags an enrolment with no care manager or no care plan, and only the Start programme button assigns a care manager and opens a plan first. The check runs only for a Complex Care (cm-complex) enrolment: in the fresh demo tenant 3 of the 4 members ENROLLED in Complex Care have no care plan and show this badge; John Doe is the exception. (The nightly 'enrolled but not started' task rule still checks ENROLLED rows of every programme.)
In the demoNOT ACTUALLY STARTED: no active care plan
Check it yourselfOpen Sarah Martinez → Care: ENROLLED, NOT ACTUALLY STARTED, 'no active care plan'. John Doe shows Care manager Jane Smith and no red badge.
app/services/enrollment.py · delivery_state()app/static/app.js · enrollmentPanel()Formula
COMPLETED, REFUSED and UNREACHABLE are settled)
badge = the live status; else COMPLETED if any HRA was completed; else NONE DUE
PAST WINDOW, and a red Due date = due date before today (business date) and not settledWhy this wayThe window is the contract's, so an HRA nobody attempted shows as past its window as plainly as one the member refused. Two limits. A late completion is shown as COMPLETED, with nothing on this screen marking it late; the in-window test exists only in the HRA completion KPI, which no screen renders. And this panel does not know about an in-home HRA referred to a vendor. John Doe shows DUE and PAST WINDOW while his Referrals tab shows HouseCall Health Assessments working an annual in-home HRA for him; the two records are not linked.
In the demoDUE, PAST WINDOW
Check it yourselfOpen John Doe → Care: DUE and a red PAST WINDOW; the row's Due date is red.
app/services/assessments.py · member_view()app/static/app.js · hraPanel()Formula
Why this wayThe clock runs from enrolment, not from when somebody created the record: a window that starts when the plan notices is not a window. A refused or unreachable HRA starts the next annual cycle from its due date, so the member is asked again next year rather than on the next sweep. The 90 and 365 days are constants in code, not tenant settings. The missing HRA rows are created when this panel is read (the read also writes).
In the demo60 days before the reset
Check it yourselfJohn Doe enrolled 150 days before the reset; his first HRA was due 90 days later, so it is 60 days past due.
app/services/assessments.py · ensure_due()Formula
Why this wayThe count of lines on the plan, not a measure of progress. The plan's own summary also computes met and PA-blocked counts, but this panel does not show them.
In the demo8 goals
Check it yourselfCount the rows of John Doe's care plan table.
app/services/careplan.py · read()Formula
Why this wayA plan nobody revisits is a document, not a plan. The 90 days are a constant in code, not a tenant setting. 'Started' is the day the plan record was opened, not the enrolment date. John Doe's plan is opened by the demo reset, so it reads as started on the reset day although he enrolled five months earlier.
In the demo90 days after the reset
Check it yourselfClick Mark reviewed: the date moves to today + 90 days.
app/services/careplan.py · ensure_plan()app/services/careplan.py · review()app/services/careplan.py · read()Formula
Why this wayA typed status can read 'active' over a gap nobody has touched, so a linked goal takes its status from the work and cannot be edited. Hovering the badge shows the basis. MET for a vendor line means CONFIRMED, with evidence, not the vendor's own DELIVERED. A proposal and the add-goal request are one shape, so accepting a vendor proposal keeps its vendor link and the line reads its status from the referral. For a gap worked more than once, the plan reads the gap's current intervention: a live one before a finished one, then the one with the latest activity, the same pick every other screen uses.
In the demoComplete retinal screening: OPEN (from the work), Improve diabetes control: OPEN (from the work), Improve blood pressure control: OPEN (from the work), Complete annual wellness visit: OPEN (from the work), Address food insecurity: OPEN (set by hand), Improve medication adherence: OPEN (from the work), Address ride to retinal exam: SCHEDULED (from the work), Address annual in-home health risk assessment: ACTIVE (from the work)
Check it yourselfHover a status: the tooltip names the gap, intervention, authorization or vendor referral it was read from.
app/services/careplan.py · _goal_status()app/services/careplan.py · _vendor_goal_status()app/services/workflow.py · current_intervention()Formula
Why this wayA target is shown with the reading and its date, because a target met months ago on a value nobody has taken since is not met today. Three things to know. The test is inclusive while the target text prints a strict '<', so an HbA1c of exactly 8.0 shows 'at target' beside 'HbA1c <8%'. There is no lookback window, unlike What needs attention (400 days). The arrow is always drawn ↓ for improving. That is right for every target the product proposes today, all lower-is-better, but a hand-entered higher-is-better goal would show the wrong arrow. Change over time on the Overview (Part II, the Patient 360 summary-strip chapter) compares latest with first in the same way.
In the demoImprove diabetes control: now 8.9% ↓ (2 readings), Improve blood pressure control: now 148mmHg ↓ (2 readings)
Check it yourselfCompare with the HbA1c and systolic readings in the Clinical tab's Labs & vitals table.
app/services/careplan.py · _progress()app/static/app.js · planProgress()Formula
Why this wayThe platform cannot send a task to a pharmacist or a social worker it has no account for, so it says so instead of pretending to assign the work. Adherence proposals default to Pharmacist, social-barrier proposals to Social Work, vendor proposals to Vendor.
In the demo4 goals
Check it yourselfCount the rows whose Owner column says 'outside this system'.
app/services/careplan.py · read()app/services/careplan.py · add_goal()Formula
Why this wayNothing is invented: each proposal names the fact behind it, and adding it to the plan is a person's decision. Social proposals come from the attention list, which reads the same current barriers as the Social drivers of health panel (the newest answer per code, when it is positive) and proposes only those nobody is working: a barrier with a live referral, a vendor on it, or a resolved referral is not proposed, and a re-screen 'no' retires it. A proposal is added exactly as offered: the proposal and the add-goal request are one shape, and a field the request does not know is refused rather than dropped.
In the demoComplete retinal screening → completed by the gap due date, Address transportation insecurity → Barrier addressed, Complete annual wellness visit → completed by the gap due date, Improve diabetes control → HbA1c <8%
Check it yourselfOpen Sarah Martinez → Care: no plan yet, and the proposals listed here, each with its reason.
app/services/careplan.py · propose()app/services/attention.py · _social()app/services/sdoh.py · current_findings()Formula
no, none, negative, no risk, false, 0 or blank is negative, so a newer negative answer retires the barrier
each barrier: RESOLVED if its latest community referral is RESOLVED · IN PROGRESS if that referral is not settled, or a contracted vendor in the matching category (transport, meals) holds an unsettled referral · else UNADDRESSED, including a DECLINED or UNRESOLVED referral
the three counts add up to the barrier countWhy this wayA member who declined help still has the barrier, so it returns to unaddressed. A barrier a vendor is already working is not reported as ignored. The enrolment criterion and the population views use the same classification. A later 'no' on re-screening clears an earlier 'yes' here, in the enrolment criterion, the population views, the attention list and the risk score, which all read the same current findings. The Clinical tab's Social screening answers panel lists every answer instead (next value).
In the demo1 barrier(s): 1 unaddressed, 0 in progress, 0 resolved
Check it yourselfOpen John Doe → Care or Referrals: one barrier, food insecurity, not addressed.
app/services/sdoh.py · barriers()app/services/sdoh.py · barrier_state()app/services/sdoh.py · current_findings()Formula
Why this wayThis is the raw record of screening answers, newest first. It counts a different thing from the Care tab's Social drivers of health panel, which counts the barriers these answers raise now: a member screened twice, or with a 'no' answer, shows more here than there. The two panels have different titles, and this one's note says the barriers are on the Care tab.
In the demo1 screening answers
Check it yourselfCount the rows of the table; compare with the barrier count on the Care tab's Social drivers of health panel.
app/api/population.py · clinical_record()app/static/app.js · panelSdoh()Formula
Why this wayIt shows who holds the barrier, so a care manager does not start a second referral for a ride already booked, and a referral the member turned down comes back as not addressed. The badge, the referral box, the vendor box and the Start referral control each show when their work exists, so a declined referral and the vendor who later took the barrier both appear. Counting the badges gives the header's counts.
In the demoFood insecurity: NOT ADDRESSED
Check it yourselfJohn Doe's food-insecurity barrier reads NOT ADDRESSED, and the header says 1 unaddressed.
app/static/app.js · sdohStateBadge()app/static/app.js · sdohBarrier()app/services/sdoh.py · barrier_state()Formula
Why this wayEach step is read from the referral, so the chain shows how far a barrier has really got. A referral here is a record, not a transmission: a person sends it and marks it sent. Limits. 'Overdue' takes precedence over 'done', so an ACCEPTED or UNREACHABLE referral past day 14 shows Follow-up overdue beside 'member reached — accepted the referral'. A referral opened without a resource still shows Intervention chosen as done, with 'no resource chosen yet'. The 14 days are a constant and run from opening, not from sending.
In the demoFinding: done, Intervention chosen: open, Referral made: open, Follow-up: open, Resolution: open
Check it yourselfStart a referral on John Doe's barrier: Intervention chosen turns done, and 'Follow-up due' shows the date 14 days out.
app/services/sdoh.py · _chain()app/services/sdoh.py · start()Formula
Why this wayAttendance is recorded here but does not close a gap: the gap waits at SERVICE_COMPLETED for clinical evidence. Known inconsistency: if the request fails, the panel shows 0 and 'No appointments' rather than saying it could not load, unlike the other panels on this tab.
In the demo0 appointments
Check it yourself0 in the fresh tenant; after booking in Act 3 it reads 1.
app/api/integration.py · appointments()app/static/app.js · p360Tab()Formula
Why this wayA re-upload under the same name is a new version, not an overwrite, and every download re-checks the full SHA-256, so a changed file refuses to serve. Limits: two names that differ only in replaced characters share one version line; and a failed request shows 0 and 'No documents' rather than an error.
In the demoreferral: 0.2 KB, v1, checksum of 16 characters
Check it yourselfOpen Sarah Martinez → Care: one referral letter, 0.2 KB, v1.
app/api/documents.py · upload()app/api/documents.py · list_documents()app/static/app.js · docsPanelHtml()Formula
Why this wayThe member's whole history of hand-offs between teams, not only the open ones; the Due column and the LOOP CLOSED badge say which are still live.
In the demo0 referrals
Check it yourself0 in the fresh tenant; Create referral adds one.
app/services/referrals.py · member_view()Formula
referral_sla setting for that queue, else its default, else 3 days); a redirect resets it to that day + the new queue's SLA
red when due is before today and the referral is not settled (OUTCOME_CONFIRMED, DECLINED, CANCELLED, UNABLE_TO_CONTACT)Why this waySee Part I, Referral SLA. A typed due date wins over the queue's SLA, so it must be a real date: text that is not a YYYY-MM-DD date is refused when the referral is created, never stored to read later as 'no date'.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfCreate a referral to Care Management with Due blank: Due is 3 days out, and it turns red on the fourth day if still open.
app/services/referrals.py · create()app/services/referrals.py · sla_days()app/services/referrals.py · _row()app/api/fields.py · _iso_date()Formula
OUTCOME_CONFIRMED; COMPLETED (the receiving team's own report) does not show it; DECLINED, CANCELLED and UNABLE_TO_CONTACT settle the referral without itWhy this wayA loop that closes on the doer's own word measures effort, not outcome. Only a confirmed outcome with evidence closes it, and that evidence is printed in the same row.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfWalk a referral to COMPLETED: no badge. Confirm the outcome with evidence: LOOP CLOSED appears.
app/services/referrals.py · _row()Formula
Why this wayPartner Activity is a rollup of the same records, so the two numbers cannot differ. Community (social) referrals and team referrals are not included; they have their own panels.
In the demo3 vendor referrals; Partner Activity 3
Check it yourselfJohn Doe: MedRide NEMT, Nourish Home Meals and HouseCall Health Assessments on both tabs.
app/services/vendors.py · member_view()Formula
Why this wayDELIVERED is the vendor's claim and CONFIRMED is the plan's evidence, so they are separate steps. The chain is drawn in full, including the part that has not happened. A stopped referral keeps the steps it really reached: one acknowledged and then declined reads ● ● ✕ ✕ ✕ ✕. The internal referral chain on the Referrals tab reads its stops the same way.
In the demoMedRide NEMT ● ● ● ● ○ ○, HouseCall Health Assessments ● ● ○ ○ ○ ○, Nourish Home Meals ● ● ● ● ● ●
Check it yourselfHover a marker: the tooltip names the step.
app/services/vendors.py · chain()app/services/workflow.py · reached_index()Formula
SCHEDULED; SCHEDULED with an appointment date → the appointment ('appt'); DELIVERED or settled → nothing owed
red when the due date is before today
starter contracts: MedRide NEMT 3 · Nourish Home Meals 5 · HouseCall Health Assessments 14 · Regional DME Supply 7 · Bridge Behavioural Network 5Why this wayThe clock starts at the referral, not at the vendor's acknowledgement, so a vendor who is slow to acknowledge gains nothing. The SLA measures SENT → SCHEDULED (owner decision A-058): once the vendor has scheduled the service with a date, the service is due on that appointment, so John Doe's MedRide ride, booked for two days after the reset, is not overdue in between even though its 3-day SLA ended on the reset day. The day after the appointment, if it has not been delivered, it is red and Partner Activity says 'appointment passed <date> — follow up with the vendor'. Vendor SLAs are per contract. They are not the team-referral SLA in Part I (3 days per queue). Referring the member again to the same vendor for the same service returns the live referral, so its SLA does not restart.
In the demo2 days after the reset
Check it yourselfMedRide NEMT was referred 3 days before the reset with a 3-day SLA (SLA date: the reset day) and is SCHEDULED for two days after it, so its column reads 'appt' and that date, not red.
app/services/vendors.py · refer()app/services/vendors.py · is_overdue()Formula
Why this wayRead from the event trail, never from a 'last contacted' field somebody has to maintain. The date is the Eastern business date of the event's UTC timestamp, like every other date on this screen, so an event logged after 8 pm Eastern is dated that evening.
In the demo1 day before the reset
Check it yourselfThe first row, MedRide NEMT 'Service scheduled', matches the newest event in that referral's trail on the Referrals tab.
app/services/vendors.py · _last_interaction()app/services/vendors.py · member_view()app/core/clock.py · business_iso()Formula
Why this wayWhat happens next is derived from the record, so it cannot go stale. A passed due date outranks everything else, because the vendor owes a response; the due date is the SLA until the vendor schedules and the appointment after (A-058), so a booked service is not chased before its day. The consequence is described under the SLA date above. The Status column and its colour are in Part II, Patient 360: header, gaps, interventions and authorizations.
In the demoMedRide NEMT: appointment, Nourish Home Meals: 14 meals delivered over two weeks; member confirmed, HouseCall Health Assessments: awaiting: vendor reached the member
Check it yourselfHouseCall Health Assessments reads 'awaiting: vendor reached the member · due …'; Nourish Home Meals shows its recorded outcome.
app/services/vendors.py · _next_step()app/static/app.js · p360Tab()The care manager's view of who is already talking to one member and on what authority: the Coordination line in the member header, the contact lists on the Engagement tab, and the disclosure consent a vendor referral needs on the Referrals tab. Every figure below is shown for the demonstration member John Doe, as the care manager Jane Smith sees him.
Formula
COMPLETED, EXHAUSTED, STOPPED); every programme's ladders are counted together
counted in the browser as the length of the list the server returns; 0 → 'no live outreach'Why this wayOne number across programmes, because each programme could always see its own outreach and none could see the others'. A ladder counts from the moment it is planned, before anything has been sent: in the fresh demo tenant John Doe has three live ladders (all PLANNED) and no contact history yet, so a live ladder means 'somebody intends to reach this member', not 'somebody has'. The ladder rungs and waits are explained in Part I, Engagement ladder, wait times and the contact ceiling.
In the demo3 live outreach ladders
Check it yourselfopen the Engagement tab; the Outreach panel lists the same ladders. A ladder that is completed, exhausted or stopped drops out of the count.
app/services/engagement.py · who_is_engaging()app/static/app.js · coordinationLine()Formula
coordination.decided event in the audit chain for this member (any programme, any age); badge = its decision, hover = its reason, 'with X on D' = what it was combined with
the decision itself is the gate's first matching check, in order: do-not-contact → SUPPRESS; no channel usable → SUPPRESS (permanent) or DELAY (temporary); inside quiet hours → DELAY (ESCALATE if the caller marked it critical); a booked appointment or planned contact about something else within lookahead_days (default 2) → COMBINE; another live ladder about something else → COMBINE; the attributed provider already has an open opportunity for this gap → COMBINE; caller marked it urgent or critical → ESCALATE; member at the weekly contact ceiling → DELAY (automation is held at the channel check instead); otherwise PROCEED
no event → 'nothing refused'Why this wayThe header replays the gate's own record rather than recomputing it, so it cannot disagree with what the gate actually did. PROCEED is never written to the audit chain, so the badge only ever shows a decision that stopped, folded or escalated something; 'nothing refused' means no such decision has been recorded, not that the member has never been contacted. The decision is shown however old it is (its date is printed beside it). The contact ceiling it uses (4 contacts in 7 days) is in Part I, Engagement ladder, wait times and the contact ceiling; Part I does not yet describe the gate's order of checks. A campaign's fold into another campaign's task is recorded under the same event, so the badge can show COMBINE with task.
In the demonothing refused
Check it yourselfhover the badge for the reason. Try to send outreach to a member who already has a live ladder: the Outreach coordination window shows the full check chain, and after you choose, the header shows that decision.
app/services/coordination.py · latest()app/services/coordination.py · decide()app/static/app.js · coordinationLine()Formula
overridden → 'proceeded anyway' with the person's recorded reason; combined → 'folded into existing work'; skipped → 'automation skipped this member'; ANY other outcome → 'refused'Why this wayA person may override COMBINE, DELAY or ESCALATE with a reason of at least 3 characters; SUPPRESS (consent) cannot be overridden, and automated contact takes no override. Known defect: a campaign that finds another campaign's task owned by someone else creates its own task anyway and records the outcome kept_separate; the browser has no label for that outcome, so the header says 'refused' for work that in fact went ahead.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnothing to show in the fresh demo tenant: no coordination decision exists after the reset. Override a COMBINE in the Outreach coordination window with a reason; the header then reads 'proceeded anyway' followed by that reason.
app/services/coordination.py · gate()app/services/coordination.py · record_task_combined()app/static/app.js · coordinationLine()Formula
40
the panel is not drawn at all when the count is 0Why this wayOne list across SMS, email, portal and voice, so 'when did we last speak to this person' needs one screen. It reads the same contact ledger the contact ceiling and the who-is-engaging header count, so a call is listed once, as the call. The count stops at 40 without saying so: a member with 60 contacts shows 40. The Engagement panel lower on the same tab is the raw message log and counts differently (see the next rows).
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnothing to show in the fresh demo tenant: nothing has been sent yet, so the panel is hidden. Send one outreach from the ladder; the panel appears with 1. Place a simulated call to the end; the count rises by 2.
app/services/engagement.py · history()app/api/engagement.py · history()app/static/app.js · p360Tab()Formula
who == 'member'); the call's outcome is appended when it has one; status is the call's status in lower caseWhy this wayMember turns show whether the member actually engaged or the call rang out. The count is of turns, not minutes or words, and it is the same number the Voice tab's calls list shows. The call's transcript is not listed again as a separate message: it is the call's own record.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnothing to show in the fresh demo tenant. Run a simulated call from Engagement → Voice and answer twice; the line reads '… 2 member turn(s)'.
app/services/engagement.py · history()app/services/voice.py · _apply_outcome()Formula
15 message records by time, both directions; voice calls themselves are not included, but the transcript message each call writes is
no messages → 'No outreach recorded'Why this wayThe raw message log, with template and status. It is a different count from Contact history on the same tab: capped at 15 rather than 40, and without call records, so the two titles differ for any member with calls or more than 15 messages. Neither says 'of M'. The channel is printed as stored (lower case) and the timestamp without its year.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfnothing to show in the fresh demo tenant (the panel reads 'No outreach recorded'). After one send, both this panel and Contact history show 1; after one finished call, this shows 1 more and Contact history 2 more.
app/api/vbc.py · get_patient()app/static/app.js · p360Tab()Formula
sms · email · phone · portal; a channel is marked '(opted out)' only when the stored opt-in is explicitly falseWhy this wayA quick read of the stored preference. It does not distinguish 'never asked' from 'opted in': both print the bare channel name. The Consent panel at the top of the same tab does distinguish them (NEVER ASKED), so for John Doe the top panel shows phone as never asked while this line shows 'phone' exactly like the channels he agreed to. Outreach itself is enforced on the server, not from this line.
In the demosms · email · phone · portal
Check it yourselfcompare with the Consent panel at the top of the tab: a channel shown NEVER ASKED there is shown without a mark here.
app/static/app.js · p360Tab()app/services/engagement.py · consent_state()Formula
*) authorization, else none
none → never_asked; recorded as declined → refused; expiry date before today → expired (valid through the expiry date itself); expiry not a date → unreadable; otherwise granted
Send referral is enabled only for granted; the server applies the same test again when the referral is sent
a recorded agreement expires 365 days after it was recorded unless another date is givenWhy this waySharing a member's name, address and need with a vendor is disclosure to a third party, a different consent from permission to contact them, so no channel opt-in counts. A category record wins over the blanket one even when it is a refusal or has expired. The window defaults to the first vendor's category (transportation). Known limits: the heading reads 'No disclosure consent on file' for a refusal or an expired authorization as well as for 'never asked' (the reason line underneath says which); the expiry date of a valid authorization is not shown; and an expiry typed in a form is stored without being checked, which is how unreadable arises. Part I's 'Consent disclosure validity 365 days' is this default.
In the demotransportation (never_asked): No disclosure consent on file; Send referral disabled
Check it yourselfopen Create vendor referral for John Doe: the line says no consent on file and Send referral is greyed out. Click 'They agreed'; the line turns to confirmed. Maria Gonzales carries a blanket authorization from the reset and opens confirmed for every need.
app/services/engagement.py · disclosure_state()app/services/engagement.py · record_disclosure()app/api/population.py · disclosure_consent()app/static/app.js · vendorModal()Formula
Why this wayA snapshot of the authorization that let the member's details leave the plan, kept with the referral so a later withdrawal does not rewrite history. Known defect: John Doe's three demonstration vendor referrals are created by the reset without passing the disclosure test and carry the text 'member verbal consent on enrollment call', while his disclosure record is empty for every category; so on the same tab the table says consent was given and the Create vendor referral window says none is on file.
In the demomember verbal consent on enrollment call
Check it yourselfhover 'consent:' on any vendor referral, then open Create vendor referral: for John Doe the two disagree until an authorization is recorded.
app/api/population.py · vendor_refer()app/static/app.js · p360Tab()The recommendation cards on Population (for the VBC manager) and on Patient 360 (for the care manager). Advisory only: nothing happens until a person acts, and every card says why it exists in reasons a care manager can check.
Formula
Why this wayThe score only orders cards within a band and is never shown: a number a care manager cannot interpret is a justification nobody can check. The band and the reasons are what the card shows. See Part I, Next best action.
In the demoJohn Doe — Record HbA1c Control (Critical), John Doe — Complete Diabetic Retinal Exam (Critical), John Doe — Record Blood Pressure Control (Critical), Maria Gonzales — Record HbA1c Control (Critical), Maria Gonzales — Complete Diabetic Retinal Exam (Critical)
app/services/nba.py · recommend()app/services/nba.py · _recommend_for_gap()Formula
Why this wayBands are derived from facts, not cut from a score, so each one comes with the sentence that set it ('critical-priority gap on a high-risk member').
In the demoCritical: critical-priority gap on a high-risk member
app/services/nba.py · _band()app/services/nba.py · _gap_age()Formula
Why this wayEight named inputs, every one checkable on the member's record. The card lists which inputs it used, so a missing input is visible, not silent.
In the demoOpen quality gap — HbA1c Control [Care Gap], Overdue 97 days [Care Gap], Critical priority on the measure package [Care Gap], High-risk member [Risk], Impacts Diabetes: HbA1c Control (<8.0%) — a tracked VBC quality measure with a 75% target [Quality], 1 ED visit in the last 6 months [Utilization]
app/services/nba.py · _reasons()Formula
Why this wayThe next step is the same policy lookup the authorization screens use, so a card can never tell a care manager a service is free of authorization when the policy says otherwise.
In the demoRecord HbA1c Control — No prior authorization is required — this can be scheduled as soon as the member is reached.
app/services/nba.py · _recommend_for_gap()app/services/nba.py · _title()app/services/workflow.py · pa_required()Formula
Why this wayA blocked card still shows (the member still needs the care) but it sinks in the order (stage factor 0.5) and its band falls to Medium, because nobody on the care team can move it until the payer decides.
In the demo0 blocked cards in the top 8
app/services/nba.py · _recommend_for_gap()Formula
Why this wayThe button state asks the same permission system the server enforces, so a button that looks enabled always works, and a disabled one says why.
In the demoas the VBC manager: Start Intervention off (your role is read-only for care work); Launch PA off (your role is read-only for care work); Send to Provider off (your role cannot route work to a provider); Assign off (your role cannot assign w
app/services/nba.py · _actions()Formula
Why this wayWhat is being deferred is an open gap on a real member, so a deferral expires, and a material change in the case brings the card back with the reason.
In the demo0 deferred in the fresh tenant
Check it yourselfDismiss a card for 30 days, then change the member's gap priority: the card comes back with the reason.
app/services/nba.py · dismiss()app/services/nba.py · fingerprint()The care manager's outreach screens: the Engagement page (Voice, SMS, Email, Portal, History and Preferences tabs), the Outreach ladder on a member's Patient 360 Engagement tab, the coordination window that opens when outreach is held, and the HRA findings line on the Care tab. Everything is shown as the care manager Jane Smith sees it; member-level figures are for John Doe unless a row names another member. Campaigns has its own chapter; the consent table, the Coordination line and Contact history are in Patient 360: coordination; the Care management and HRA status panels are in the Patient 360 care chapter.
Formula
OutreachSequence rows, by creation time, of every status (settled ladders included), among the members this login may see — the panel is applied in the query, before the 50 are taken
the number is the length of that list; when more exist, the title adds 'newest 50 of N'Why this wayA sequence is one conversation with one member about one thing, across channels, so this counts conversations, not messages. The list stops at 50 and says so ('newest 50 of N') when there are more. Because the panel is applied before the limit, a provider gets their own newest 50, not what survives of the tenant's newest 50. In the fresh demo the provider sees 5 of the 10.
In the demo10 sequences
Check it yourselfCount the rows in the History table; they match the number in the title.
app/api/engagement.py · sequences()app/static/app.js · engSequences()Formula
can_really_send() is false
can_really_send() is true only when a real messaging provider is configured AND the deployment is not synthetic-onlyWhy this wayBoth conditions are required, so a synthetic-data deployment can never message a real person even if someone configures a provider. The demo is synthetic-only, so the badge always shows. It is one deployment-wide answer: each sequence also carries its own 'any real send' flag, which no screen renders.
In the demoshown on both
Check it yourselfThe amber badge sits beside the History title and beside the ladder title on John Doe's Engagement tab.
app/services/messaging.py · can_really_send()app/api/engagement.py · sequences()app/api/engagement.py · plan()Formula
PLANNED · a send → ATTEMPTED · moved to the next usable channel → ESCALATED · an outcome that means we spoke to the member → REACHED · a real booked appointment → SCHEDULED · REMINDED · COMPLETED (also set when the care gap closes)
STOPPED = the member opted out · EXHAUSTED = no untried channel can ever be used
when nothing is usable now: any untried channel blocked only by quiet hours or the contact ceiling → wait; otherwise → EXHAUSTEDWhy this wayThe rule separates 'not yet' from 'never'. Quiet hours and the contact ceiling are clocks, so a ladder they block waits instead of being written off. Opt-out, no address on file and do-not-contact are walls, and only walls end a ladder. An EXHAUSTED ladder goes back to PLANNED when the task-rule sweep finds a usable channel again. SCHEDULED cannot be set without a real, uncancelled appointment. When the task-rule sweep runs, a stalled ATTEMPTED or ESCALATED ladder whose wait has passed is escalated automatically. The event written when a ladder waits gives the hold's own reason (quiet hours, or the contact ceiling) and when it clears. A send, a call or an intervention's outreach made through another door is recorded on the ladder too (an attempt; a call's ending as its outcome) when it names the ladder, is about the ladder's purpose, or the member has only one live ladder.
In the demoPLANNED 8, REACHED 2
Check it yourselfHistory: count the Status badges; Maria Gonzales and Elena Petrova are REACHED, the other eight PLANNED.
app/services/engagement.py · open_sequence()app/services/engagement.py · reachability()app/services/engagement.py · escalate()app/services/engagement.py · record_outcome()app/api/engagement.py · step()app/services/task_rules.py · _r_engagement_escalate()app/services/task_rules.py · _r_engagement_exhausted()Formula
Why this wayThe ladder starts where the member asked to be reached, as Part I (Engagement ladder, wait times and the contact ceiling) states; its fixed order, SMS first and Voice last, applies only to the channels the member did not choose. Sam Okafor's two ladders start on Email because Email is his stated preference, and a member who prefers phone is called first. A preferred channel that is blocked is skipped, not waited for.
In the demoEmail 2, SMS 7, Voice 1 ladders
Check it yourselfHistory: Sam Okafor's rows read Email; Elena Petrova's reads Voice after SMS was tried.
app/services/engagement.py · channels()app/services/engagement.py · open_sequence()app/services/engagement.py · escalate()Formula
Why this wayOne conversation, several channels: a text on Monday and a call on Wednesday about the same exam are one row, not two. Escalating alone adds nothing to Tried; a channel is added when a send is recorded on it. On this deployment every send is simulated, and the ladder's event says so. A call placed from the Voice tab is not a send on any ladder (see Call status), so it never appears here.
In the demoSMS → Voice · 2 attempt(s)
Check it yourselfHistory: Elena Petrova's row; her Patient 360 ladder's Outreach step reads '2 attempt(s) across SMS, Voice'.
app/services/engagement.py · record_attempt()app/services/engagement.py · _steps()app/static/app.js · engSequences()Formula
Why this wayWaits are Part I's (Engagement ladder, wait times and the contact ceiling). Whether the demanded act has happened is read from the record that would prove it, as in the 'Member said' row. The words come from the status, not from the date: a PLANNED ladder has sent nothing, so its date is the first send, not a retry. In the fresh demo all eight PLANNED ladders read 'first send due' with the reset day. A wrong number clears the date on purpose, so the escalation rule cannot redial a bad number.
In the demoMaria Gonzales: refer to the contracted transportation vendor; Elena Petrova: book an appointment from an open slot; 8 ladder(s) show a first-send date
Check it yourselfHistory: Maria Gonzales and Elena Petrova show amber badges; open their Patient 360 ladders and the 'Member said' line names the same act.
app/api/engagement.py · sequences()app/services/engagement.py · next_trigger()app/services/engagement.py · record_attempt()app/services/engagement.py · record_outcome()Formula
MessageTemplate row in the tenant whose channel is this tab's — every version, locale and status, not only approved ones
the picker: the server's list of the distinct APPROVED keys on this channel (GET /engagement/templates?channel=), one entry per key, shown as 'key — purpose'Why this wayShowing the library per channel makes 'is there an approved text for this' answerable before a send. The picker offers only what a send can use: a draft is never offered, and a key approved in two locales (the demo's T-OUTREACH on SMS, en-US and es-US) appears once, because the version and locale are chosen per member at send — the highest approved version of that key in the member's own locale, falling back to en-US. The 'No approved … template exists' note appears when the picker is empty, so a draft cannot hide it.
In the demosms 4, email 1, portal 0 templates
Check it yourselfOpen each tab: the panel titles read 4, 1 and 0, and the Portal tab says no approved Portal template exists.
app/api/engagement.py · templates()app/static/app.js · engChannel()app/services/messaging.py · resolve_template()Formula
{first_name} = the member's name up to the first space · {org} = the tenant's name · {service} = 'a check-up' (this screen names no service)
a placeholder nothing fills is refused (422), never sentWhy this wayThe channel tabs choose the channel: a send from the Email tab for John Doe, who prefers SMS, is recorded as an email and the toast says 'Recorded on email'. A template with no approved version for that channel is refused. Consent, quiet hours and the coordination gate apply as for any other contact, on the channel sent. When the member has exactly one live ladder, the send is recorded on it as an attempt and the gate does not tell that ladder to combine with itself.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfEngagement → Email → pick John Doe → Send, and choose Proceed separately with a reason in the coordination window. The toast names sms. The message then appears in Patient 360 → Engagement with '{org}' unreplaced.
app/services/messaging.py · send_outreach()app/services/messaging.py · outreach_values()app/services/messaging.py · fill()app/api/engagement.py · send()app/static/app.js · engChannel()Formula
VoiceCall rows, by start time, among the members this login may see — the panel is applied in the query, before the 20 are taken
the number is the length of that listWhy this wayA short recent list, not a call log: it stops at 20 (the response carries the full count as total, which this screen does not print). A provider gets their own newest 20. The fresh demo tenant has no calls; each call placed during the demo adds one.
In the demo0 calls
Check it yourselfPlace a call and finish it: the title goes up by one and the call is the top row.
app/api/voice.py · calls()app/static/app.js · rVoice()Formula
IN_PROGRESS when the call starts
when a reply reaches an ending: booked, callback or declined → COMPLETED; escalated → ESCALATED; opted_out → OPTED_OUT; voicemail → VOICEMAIL; no_answer → NO_ANSWER
'Member hangs up' while the call is live → ABANDONEDWhy this wayStatus says how the call ended; Outcome says what it achieved, which is why three different results share COMPLETED. The live badge and the table badge are the same stored field. The code also treats RINGING as a live state, but nothing ever sets it. Every ending, abandoned included, writes the transcript to the member's contact history. A call is a separate record from the member's outreach ladder: booking, declining or opting out on a call does not move the ladder, and the ladder's Outreach step still reads 'not attempted'.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfStart a call for John Doe, answer 'Yes, please' then 'Mornings': the badge turns COMPLETED and the same status appears in the table.
app/services/voice.py · start()app/services/voice.py · respond()app/services/voice.py · hang_up()app/api/voice.py · calls()Formula
Why this wayThe outcome is what the workflow acts on. An opt-out sets the member's phone opt-in to 'no', so Voice then reads 'opted out' wherever consent is shown. 'booked' moves the linked intervention from OUTREACH to ENGAGED only when the call was started from an intervention. The Voice tab starts calls without one, so a booking made here books nothing and changes no intervention; the words 'care team to confirm' mean exactly that.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfEnd a call with 'Stop calling me': the outcome reads 'Member opted out of calls', and John Doe's Voice channel then shows opted out on his Engagement tab.
app/services/voice.py · respond()app/services/voice.py · _apply_outcome()app/services/voice.py · hang_up()app/static/app.js · rVoice()Formula
voice.turn_count)
the same number Patient 360's Contact history prints as 'N member turn(s)'Why this wayIt counts the member's replies, not every spoken line: a call answered twice ('Yes, please', 'Mornings') shows 2, and a call hung up before any reply shows 0. The column is headed 'Member turns'. The scripts list reports a script's size as steps (9 and 7), a different thing, and no screen shows it.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfOpen the call's Transcript: count the Agent and Member bubbles; the total equals Turns.
app/api/voice.py · calls()app/services/voice.py · turn_count()app/services/engagement.py · history()Formula
IN_PROGRESS nor RINGING → show 'Outcome: …' and Close
otherwise the buttons = the replies of the script step the call is at, in script order, plus 'Member hangs up'Why this wayThe member's side is picked from a fixed list, because a simulated call must not imply speech recognition. The server enforces the same rule the screen draws: a reply that is not on the current step's list is refused, and so is any reply after the call has ended ('call already COMPLETED'). Example: the Care gap outreach script opens with five replies: 'Yes, please', 'Not right now', 'Who is this? I want a person', 'Stop calling me', '(no answer)'.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfStart a Care gap outreach call: five reply buttons appear; answer until the call ends and they are replaced by the outcome and Close.
app/services/voice.py · options()app/services/voice.py · respond()app/static/app.js · rVoice()Formula
messaging.outreach_values): {first_name} = the member's name up to the first space · {org} = the tenant's name, or 'your care team' if it has none · {service} = the Service typed at Start call, else the linked intervention's name, else 'a check-up'
a placeholder nothing fills refuses the call (422) rather than speaking the holeWhy this wayThe words come from a versioned script a person approved (v1 of each); the system only fills in names. Only the Care gap outreach greeting uses {service}. Replies are rendered without the typed service, which changes nothing today because no later step uses it. The first-name rule is simple: 'Mary Ann Smith' is greeted as 'Mary'.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfStart a Care gap outreach call for John Doe with Service 'a diabetic eye exam': the greeting reads 'Hello John, this is an automated call from Demo Health ACO … due for a diabetic eye exam'.
app/services/messaging.py · outreach_values()app/services/messaging.py · fill()app/services/voice.py · _line()app/api/voice.py · start()Formula
Why this wayThe window is the gate's refusal, shown with its working, so a person decides and the decision is recorded. The Coordination line in the member header shows the last one. Every door on these screens sends urgency 'routine', so ESCALATE cannot come from them. The Voice and channel tabs send no purpose: when the member has exactly one live ladder, the call or message is that ladder's own next step (recorded on it, and not told to combine with it); with two or more, every live ladder counts as 'something else'. In the fresh demo, Start call for John Doe opens with COMBINE because three of his ladders are planned for today. When the thing to combine with is a ladder, it is always labelled 'Care Management', whichever programme opened it.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfEngagement → Voice → John Doe → Start call: the window shows COMBINE with 'Future contact scheduled' marked. Choose Proceed separately with a reason and the call starts.
app/services/coordination.py · decide()app/services/coordination.py · gate()app/static/app.js · coordModal()Formula
COMPLETED, EXHAUSTED or STOPPED
none → NONE RUNNING, and no progress barWhy this wayThe panel shows one conversation, the newest one still live. A member with several live ladders shows only one here, and History lists them all. A finished ladder is never shown, so a ladder that just COMPLETED makes the panel read NONE RUNNING, not a full bar.
In the demoPLANNED, one of 3 live ladders
Check it yourselfEngagement → History: John Doe has three rows; his Patient 360 ladder shows one of them.
app/api/engagement.py · plan()app/static/app.js · engagementLadder()Formula
Why this wayOne rule for every 'N of M' in the product (progress.step_progress, shared with the Transitions plan and the gap orchestration): a step nobody has to do is not outstanding work. So a first-channel success at REMINDED reads 5 of 6 and a ladder that needed escalation 6 of 7 — each one step (Completion) from done. Only live ladders are shown, and a completed ladder leaves the panel.
In the demo1 of 7 steps complete, bar 14% (red)
Check it yourselfCount the ticked steps in the chain under the bar.
app/services/engagement.py · plan()app/static/app.js · engagementLadder()Formula
Why this wayThe step after Escalation is named after what the member's answer demands, for example 'Vendor referral', and not always 'Scheduling'. Its done flag is read from the record that proves it. Response's detail uses the ladder's own label for its next date: 'not attempted — first send due <day>' while PLANNED, 'no answer yet — retry <day>' once something has been sent.
In the demoPreferred channel blocked, Outreach done, Response done, Escalation not_needed, Vendor referral open, Reminder open, Completion open
Check it yourselfMaria Gonzales, Engagement tab: Escalation shows a dash ('the member answered on the first channel') and Vendor referral is still open.
app/services/engagement.py · _steps()app/static/app.js · engagementLadder()Formula
Why this wayOne rule set for every channel, voice included; see Part I, Engagement ladder, wait times and the contact ceiling. This list asks as the person looking at it, so a member at the ceiling shows each channel as available with the ceiling's warning; automation (the task-rule sweep) is held. Every caller says whether it is a person or automation; none defaults. The count comes from one ledger that the ceiling, the who-is-engaging header and the Contact history all read, so a call counts once however it ended. The text says 'contacted N times in 7 days' and counts every programme's contacts, the care manager's own included.
In the demoSMS available, Email available, Portal available, Voice available
Check it yourselfSam Okafor's list reads 'no mobile on file' for SMS; Derrick Boateng's reads 'asked not to be contacted at all' on every channel.
app/services/engagement.py · contactability()app/services/engagement.py · contact_ledger()app/services/engagement.py · channels()app/services/voice.py · _apply_outcome()Formula
Why this wayThe ladder's own status never vouches for itself: 'appointment booked' is printed only when an appointment row exists. Every trigger is tied to this ladder: an eligibility check or authorization from last year does not mark today's 'Cost concern' or 'PA required' as done; only the act this ladder's trigger performed does.
In the demoTransportation barrier: TRIGGERS VENDOR REFERRAL
Check it yourselfMaria Gonzales: the amber badge and a button to refer her to the transportation vendor; after the referral is sent the badge turns green.
app/services/engagement.py · next_trigger()app/static/app.js · engTriggerBanner()Formula
Why this wayUnlike most values in this paper, the line is a snapshot written once at completion and never recomputed. A referral later closed or refused does not change it. A typed outcome always wins: refused and unreachable HRAs must carry a reason, and that reason is what shows here. Linda Tran's refused HRA shows 'member declined the HRA'. 'D of M' can be below M on purpose: a transport referral needs disclosure consent, and without it the finding is recorded but not actioned.
In the demoREFUSED: member declined the HRA
Check it yourselfComplete John Doe's HRA with two Yes answers: the table's last column shows the one-line summary, and the assessment's event trail carries the same line.
app/services/hra.py · summary()app/services/hra.py · _is_positive()app/api/population.py · assessment_step()app/static/app.js · hraPanel()Formula
Why this wayThe window shows what the answers set off, not only that they were saved: each positive answer opens a referral, a vendor request or a task through that module's own service. A failed action is shown with its reason and the finding stays on the record, so the social-needs rules keep chasing it. An HRA cannot be completed with no answers.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfAnswer Yes to transportation and food and complete: the window lists what started and anything that could not start (for example, a transport referral with no disclosure consent).
app/static/app.js · hraSubmit()app/services/hra.py · extract()app/services/hra.py · generate()Formula
Patient in the tenant that this login may see — the panel is applied in the query
the number is the length of that listWhy this wayThe Preferences page lists everyone this login could contact, so a provider sees their own panel and a care manager the tenant. Each row is the member's consent record exactly as their Patient 360 consent table shows it — one authority, consent_state(), so the list and the member cannot disagree.
In the demo13 members
Check it yourselfCount the rows of the Members table; they match the card and the title.
app/api/engagement.py · preferences()app/static/app.js · engPreferences()app/services/engagement.py · consent_state()Formula
do_not_contact = trueWhy this wayDo not contact is a wall on every channel at once, whatever each channel's own opt-in says, so it is counted apart from the per-channel figures. The member's row carries the DO NOT CONTACT badge.
In the demo1 members
Check it yourselfCount the DO NOT CONTACT badges in the Members table; they match the card.
app/api/engagement.py · preferences()app/static/app.js · engPreferences()app/services/engagement.py · consent_state()Formula
preferred_channel is not empty, shown 'of' the member countWhy this wayThe stored preference is counted even when that channel is currently blocked: 'they asked for SMS and we cannot use it' is a different fact from 'they have no preference'.
In the demo12 members
Check it yourselfCount the rows whose Preferred column is not a dash; they match the card.
app/api/engagement.py · preferences()app/static/app.js · engPreferences()app/services/engagement.py · consent_state()Formula
Why this wayNever asked is neither yes nor no, so it is its own column rather than folded into either (A-184). These are what is RECORDED, not whether outreach would go out right now: quiet hours and the contact ceiling change by the hour and are shown on each member's Patient 360, where 'Usable right now' is asked of the same authority outreach uses.
In the demoSMS {'opted_in': 9, 'opted_out': 0, 'never_asked': 4, 'no_address': 4}, Email {'opted_in': 12, 'opted_out': 0, 'never_asked': 1, 'no_address': 2}, Portal {'opted_in': 12, 'opted_out': 0, 'never_asked': 1, 'no_address': 2}, Voice {'opted_in': 0, 'opted_out': 2, 'never_asked': 11, 'no_address': 3} members
Check it yourselfFor any channel, count the OPTED IN, OPTED OUT and NEVER ASKED badges in that column of the Members table; they match the row, and the three add up to the member count.
app/api/engagement.py · preferences()app/static/app.js · engPreferences()app/services/engagement.py · consent_state()The care manager's list of recent hospital discharges, each with its follow-up clocks (contact within 48 hours, a visit within 7 days, readmission watch for 30 days) and a transition plan whose every step is read from the work beneath it.
Formula
Why this way120 days is how long a discharge stays actionable (Part I, Transition-of-care windows). Only inpatient stays count, and the Command Center's 'Discharges (30d)' card counts this same list's last 30 days. For a provider login the list and the cards above it both count the provider's panel only.
In the demo1 discharges
app/services/transitions.py · recent_discharges()app/services/transitions.py · summary()Formula
Why this wayBoth clocks run from the DISCHARGE date, not from when the record reached the platform, so a late feed cannot buy time. A missed window is shown as missed, not as late success: a 48-hour call made on day three is a missed measure.
In the democontact open 1, visit open 1, missed 0
app/services/transitions.py · recent_discharges()Formula
contact_hours, 48 by default, rounded up: 2) · visit by = discharge date + visit_days (7 by default); both from transitions.windowsWhy this wayCalendar-day deadlines a person can act on. These columns, the KPI cards and each plan step's deadline all read the tenant's own task-rule settings, so a tenant that changes its rules changes all of them together.
In the demodischarged 2 days ago: contact by +2 d, visit by +7 d
app/services/transitions.py · recent_discharges()Formula
Why this wayA per-discharge flag and a count over the 120-day list; it can still rise for a discharge whose 30 days have not passed. This is a different figure from Finance's 30-day readmission RATE, which judges only discharges whose window has fully elapsed.
In the demo0 readmitted
app/services/transitions.py · recent_discharges()Formula
Why this wayThe card that says whether anything has already fallen through, as opposed to what is still due.
In the demo0 plans
app/static/app.js · rTransitions()Formula
Why this wayProgress leaves out the steps nobody can act on (the 30-day watch, the close, a step not needed). A dismissed step counts as settled because someone recorded a decision on it. In the demo: discharge, contact, medication reconciliation and barriers are done, 4 of 7.
In the demo4 of 7 actions completed
app/services/transitions.py · plan()app/static/app.js · transitionPlan()Formula
Why this wayNo step is ticked by hand: each is read from the task, authorization or appointment it depends on, so the plan cannot claim more than has happened.
In the demoDischarge done, Contact done, Medication reconciliation done, Clinical assessment open, Follow-up appointment open, Barriers / transport done, Eligibility / PA not_needed, Visit completion open, Readmission monitoring watching, Transition closed open
app/services/transitions.py · plan()app/services/transitions.py · _step_deadline()Formula
Why this wayTwo different failures: overdue can still be done, missed cannot.
In the demomissed 0, overdue 0
app/services/transitions.py · plan()Formula
Why this wayA transition is not closed at the follow-up visit; it is closed when the member has stayed out of hospital for 30 days, which is what the plan is for.
In the demo28 days left in the window
app/services/transitions.py · plan()The VBC manager's population view: every measure's denominator and numerator, saved cohorts, attribution (who the contract holds you responsible for), where cost and opportunity concentrate, and how each provider group performs.
Formula
Why this wayEligibility is read from the clinical record at request time, never from a stored flag, so a new diagnosis puts a member into the denominator on the next read. Every member is evaluated (so every member's gaps open and close), but the rate counts the members attributed for the current period — the contract's population, the same one PMPM and settlement divide by. See Part I, One member, one measure.
In the demoDiabetes: HbA1c Control (<8.0%) 6, Diabetes: Eye Exam Performed 6, Statin Adherence (PDC >= 80%) 8, Controlling High Blood Pressure (<140 systolic) 10, Annual Wellness Visit 8
app/services/quality.py · evaluate_tenant()app/services/quality.py · in_denominator()Formula
max_exclusive: HbA1c <8.0, systolic <140), at or below for a '<=' one
procedure measures: a qualifying procedure within the look-back
medication measures: the best active qualifying medication's adherence (PDC) ≥ 80%Why this wayThree kinds of evidence, one rule each, all read from the record. The comparison is data on the versioned package, not a guess in code: a result EXACTLY at the threshold (HbA1c 8.0, systolic 140) is NOT controlled under a '<8.0%' or '<140' title. The outcomes 'at target' test, the attention panel and care-plan goals use the same comparison (quality.within_limit).
In the demoDiabetes: HbA1c Control (<8.0%) 2, Diabetes: Eye Exam Performed 0, Statin Adherence (PDC >= 80%) 2, Controlling High Blood Pressure (<140 systolic) 4, Annual Wellness Visit 2
app/services/quality.py · meets_numerator()Formula
Why this wayExcluded members leave the denominator entirely rather than counting as failures, as measure specifications require.
In the demo0 excluded across all measures
app/services/quality.py · excluded()Formula
Why this wayThe same rate as the Quality screen, from the same function, coloured by the same rule against the same target (the measure package's), so a rate is one colour everywhere it appears.
In the demoDiabetes: HbA1c Control (<8.0%): 4 open, 33.3%, Diabetes: Eye Exam Performed: 6 open, 0.0%, Statin Adherence (PDC >= 80%): 6 open, 25.0%, Controlling High Blood Pressure (<140 systolic): 6 open, 40.0%, Annual Wellness Visit: 6 open, 25.0%
app/services/quality.py · evaluate_tenant()app/static/app.js · rPopulation()Formula
Why this wayA cohort stores its definition, not its members, so its size is re-evaluated on every read and a member enters or leaves as their record changes.
In the demoRising and high risk 6, Diabetes population 6, Diabetes with open gaps 6, Uncontrolled hypertension 6
app/services/population.py · cohort_members()Formula
panel.panel with the cohort's members as patient_ids): risk tier + unworked and in-flight gaps + discharge windows + adherence + authorization on the clock + stalled outreach + no active coverage + high risk with no programme (see Part I, My Panel); '—' for a member who scores 0
rows sorted by that score, highest firstWhy this wayThe same engine as My Panel, on purpose: a member cannot rank high on one screen and low on the other. Scoring 0 on My Panel means 'not listed'; here the member is still a cohort member, so the row stays and shows '—'.
In the demo('John Doe', 80), ('Maria Gonzales', 80), ('Linda Tran', 62)
Check it yourselfOpen the same member on My Panel: the Priority there is the same number.
app/api/population.py · cohort_members()app/static/app.js · cohortMembersPanel()app/services/panel.py · panel()Formula
has_owner false)Why this wayIt says how much of the cohort is still nobody's work. 'Unworked' here is about the MEMBER: any owned open task counts, whatever it is for. It is not My Panel's 'unworked gap', which is about a gap with no live intervention, so a member can be 'assigned' here and still carry unworked gaps there.
In the demo0 of 6
Check it yourselfCount the amber 'unworked' badges in the Owner column: they are U.
app/api/population.py · cohort_members()app/static/app.js · cohortMembersPanel()Formula
status open) with an owner_id, of any rule, campaign or hand; else unworkedWhy this wayOwnership, not activity: a task owned by somebody who has not touched it still reads 'assigned'. Read with My Work's Team view for what that owner holds.
In the demo6 assigned
Check it yourselfAssigned + unworked badges = S, the cohort size in the badge above.
app/api/population.py · cohort_members()app/static/app.js · cohortMembersPanel()Formula
Why this wayAttribution decides accountability, so each member's method and rationale are shown beside the count. The count is the period's LIVE attributions (no end date, or one after today) — the same number as the Attribution summary's 'Total attributed' (population.attributions). An ended row stays in the table, marked 'ended'.
In the demo12 members
app/services/population.py · current_period()Formula
Why this wayRemoved is a MEASURED zero, stated as such: no de-attribution path writes an end date yet. 'Added this period' counts members attributed AFTER the opening roster: the period's first attribution run is the roster and is dated from the period start (member-months accrue from it); a member added by a later run is dated the business day it ran.
In the demototal 12, added 0, removed 0; Dr. P. Osei 5, Dr. Robert Jones 3, Dr. Sarah Smith 2, Dr. Karen White 2
app/services/vbc_manager.py · attribution_summary()Formula
Why this wayThe members nobody is accountable for are the ones most likely to be missed, so they are listed by name with the reason.
In the demoJon Doe (PCP on record but not attributed)
app/services/vbc_manager.py · attribution_summary()Formula
Why this waySpend alone ranks members nobody can help; open gaps alone ignore cost. Multiplying them puts first the members where money is being spent AND something can still be done. A member with no unworked gaps still ranks, on cost alone.
In the demoJohn Doe: $19262.5, Professional / office, 5 unworked, score 173.4, Maria Gonzales: $19262.5, Professional / office, 5 unworked, score 173.4, Linda Tran: $10707.5, Professional / office, 4 unworked, score 66.4, Fatima Hassan: $10707.5, Professional / office, 4 unworked, score 66.4
Check it yourselfJohn Doe: 17,135 ÷ 1000 × (1 + 5 × 1.6) = 154.2.
app/services/vbc_manager.py · concentration()Formula
Why this wayBuilt from the same three inputs as the ranking, so the sentence is the ranking's own reason.
In the demo$19,262 spend, 5 gap(s) with no intervention running, high risk
app/services/vbc_manager.py · _concentration_why()Formula
Why this wayThe same PMPM arithmetic as the contract, narrowed to one group's members, so group PMPMs are comparable with the contract's. Closed gaps are counted all-time, so a new period does not reset closure credit.
In the demoDr. P. Osei: 5 attributed, 3 high/rising, 16 open, 0.0% closed, $1158.62 PMPM, Dr. Robert Jones: 3 attributed, 2 high/rising, 6 open, 0.0% closed, $1579.52 PMPM, Dr. Sarah Smith: 2 attributed, 1 high/rising, 5 open, 0.0% closed, $2917.76 PMPM, Dr. Karen White: 2 attributed, 0 high/rising, 1 open, 0.0% closed, $1296.42 PMPM
app/services/vbc_manager.py · provider_performance()The VBC manager's quality screen: every measure's rate against its target, how many composite points each open gap is worth, and a what-if model of closing more gaps before committing a team to the work.
Formula
Why this wayEvery active measure is listed, including one with no eligible members yet, so nothing disappears from the table because it is empty.
In the demo5 measures
app/services/vbc_manager.py · quality_opportunity()Formula
Why this wayThe rate is recomputed from the members every time the screen is read, never stored, so it cannot go stale. See Part I, A measure across a population. The 15-point amber band is a display convention only; it changes no number.
In the demoDiabetes: Eye Exam Performed 0.0% (0 of 6), Annual Wellness Visit 25.0% (2 of 8), Statin Adherence (PDC >= 80%) 25.0% (2 of 8), Diabetes: HbA1c Control (<8.0%) 33.3% (2 of 6), Controlling High Blood Pressure (<140 systolic) 40.0% (4 of 10)
Check it yourselfClick 'Members' on a row: the drill lists the D members and marks the N who meet it.
app/services/quality.py · evaluate_tenant()app/static/app.js · rateBarHtml()Formula
Why this wayTargets come from the versioned measure package, not from this report. A measure with no target still shows its rate and gaps, but no shortfall: comparing a rate with a target nobody agreed would manufacture one.
In the demoDiabetes: Eye Exam Performed 70.0%, Annual Wellness Visit 60.0%, Statin Adherence (PDC >= 80%) 80.0%, Diabetes: HbA1c Control (<8.0%) 75.0%, Controlling High Blood Pressure (<140 systolic) 75.0%
app/services/vbc_manager.py · quality_opportunity()Formula
Why this wayPoints here are percentage points of THAT measure's rate, not composite points; the next column converts to composite points.
In the demoDiabetes: Eye Exam Performed 70.0 pts short, Annual Wellness Visit 35.0 pts short, Statin Adherence (PDC >= 80%) 55.0 pts short, Diabetes: HbA1c Control (<8.0%) 41.7 pts short, Controlling High Blood Pressure (<140 systolic) 35.0 pts short
app/services/vbc_manager.py · quality_opportunity()Formula
Why this wayThe smallest whole number of additional members who must meet the measure for the rate to reach the target. Rounded UP, because a fraction of a member cannot close a gap.
In the demoDiabetes: Eye Exam Performed 5, Annual Wellness Visit 3, Statin Adherence (PDC >= 80%) 5, Diabetes: HbA1c Control (<8.0%) 3, Controlling High Blood Pressure (<140 systolic) 4
Check it yourselfEye exam: D = 6 and target 70%: ⌈6 × 0.70⌉ = 5 members; N = 0, so 5.
app/services/vbc_manager.py · quality_opportunity()Formula
Why this wayCounted by the measure engine from members, not from stored gap records, so it is what the rate implies. The drill behind 'Members' lists the same members, and a guard fails if the two ever disagree.
In the demoDiabetes: Eye Exam Performed 6, Annual Wellness Visit 6, Statin Adherence (PDC >= 80%) 6, Diabetes: HbA1c Control (<8.0%) 4, Controlling High Blood Pressure (<140 systolic) 6
app/services/quality.py · evaluate_tenant()Formula
Why this wayTwo facts in sequence: closing one gap moves that measure's own rate by 100 ÷ D points, and the composite is the plain average of n measures, so it moves by that ÷ n. Small denominators move the composite fastest per gap, which is a property of the contract's scoring method, not a statement about which members matter most. See Part I, Quality opportunity and points per gap.
In the demoDiabetes: Eye Exam Performed 3.33, Annual Wellness Visit 2.5, Statin Adherence (PDC >= 80%) 2.5, Diabetes: HbA1c Control (<8.0%) 3.33, Controlling High Blood Pressure (<140 systolic) 2.0
Check it yourselfEye exam: 100 ÷ 6 ÷ 5 = 3.33.
app/services/vbc_manager.py · quality_opportunity()Formula
Why this wayThe composite points the score would gain if every open gap in that measure closed. Rounded once at the end, so it equals what the scenario engine answers for closing the same gaps (a guard compares the two).
In the demoDiabetes: Eye Exam Performed 20.0, Annual Wellness Visit 15.0, Statin Adherence (PDC >= 80%) 15.0, Diabetes: HbA1c Control (<8.0%) 13.33, Controlling High Blood Pressure (<140 systolic) 12.0
Check it yourselfEye exam: 100 ÷ 6 ÷ 5 × 6 = 20.0 (not 3.33 × 6 = 19.98).
app/services/vbc_manager.py · quality_opportunity()Formula
Why this wayStated thresholds rather than a feeling: a band is a reading aid over the points-available number, and says nothing the number does not.
In the demoDiabetes: Eye Exam Performed High, Annual Wellness Visit High, Statin Adherence (PDC >= 80%) High, Diabetes: HbA1c Control (<8.0%) High, Controlling High Blood Pressure (<140 systolic) High
app/services/vbc_manager.py · _opportunity_band()Formula
Why this wayThe single number the contract settles on: an unweighted mean of the rated measures, computed in one place (quality.composite) and read by every screen that shows it. A measure with no eligible members has no rate and is left out of the mean rather than counted as zero. See Part I, The quality composite.
In the demo24.7 %
app/services/quality.py · composite()app/services/vbc_manager.py · scenario()Formula
Why this wayThe contract engine's own arithmetic with the numerators moved; nothing is stored and nothing is predicted. Denominators do not move and closures are capped at the gaps actually open. The demo value models closing every open gap in the top-opportunity measure.
In the democlose all 6 Diabetes: Eye Exam Performed gaps: 24.7% → 44.7%
Check it yourselfEnter the same closures in the panel and press Model this.
app/services/vbc_manager.py · scenario()Formula
Why this wayIt is the difference of the two cards above it, so it always matches them, even where it differs by 0.1 from (points per gap × closures) because each side was rounded first.
In the democlose one Diabetes: Eye Exam Performed gap: +3.3 points
app/static/app.js · scenarioResult()Formula
Why this wayThe gate is read from the contract, never set on this screen. Clearing it is what turns earned savings into a payment; see Part I, Contract performance and settlement.
In the demogate 60.0%; the gate plan (11 closures) gives 60.5%, clears: yes
app/services/vbc_manager.py · scenario()app/services/finance.py · contract_performance()Formula
Why this wayIt models quality only: the savings already generated are held constant and the question is whether the gate now releases them. It is an estimate; the payer reconciles against run-out claims months later.
In the demowith the gate plan: $0.0 → $9069.84 (change $9069.84)
app/services/vbc_manager.py · scenario()app/services/vbc_manager.py · _rate()Formula
Why this wayYou cannot close more gaps than are open, so a larger request is capped and says so rather than producing a rate above 100%.
In the demoDiabetes: Eye Exam Performed: 6 modelled (capped), 0.0% → 100.0%
app/services/vbc_manager.py · scenario()Formula
Why this wayGreedy is optimal here, not a heuristic: every gap in a measure is worth the same number of composite points, so the highest-yield gaps first is the fewest closures that clear the gate. See Part I, Path to the quality gate.
In the demoDiabetes: Eye Exam Performed 6, Diabetes: HbA1c Control (<8.0%) 4, Annual Wellness Visit 1
app/services/vbc_manager.py · gate_path()The VBC manager's (and executive's) view of the active value-based contract: is it earning, what is binding it, the path to the quality gate, how each measure is trending, and today's priorities.
Formula
Why this wayA shared-savings contract pays on the members attributed to it, so cost and savings are computed over this population. The Command Center's 'Members' card counts everyone on file, so the two numbers differ by design.
In the demo12 members
app/services/vbc_manager.py · contract_health()app/services/population.py · attributed_patients()Formula
Why this wayA leader's first question is 'which of the two gates is stopping us'. The sentence answers it from the same numbers as the cards below it, so it can never contradict them.
In the demoCost performance is favourable — $19,094 of savings generated — but none of it is earned, because quality is 24.7% against a 60.0% gate. Quality is the binding constraint, not cost.
app/services/vbc_manager.py · _headline()Formula
Why this wayAllowed dollars, not paid, because the contract benchmark is set on allowed cost. Member-months counts partial months, so a member attributed mid-period costs only their share of the denominator. See Part I, Contract performance and settlement.
In the demo$1,580.00 against a $1,750.00 benchmark — under by $170.00
Check it yourselfFinance → Cost: the same PMPM and member-months are shown with the claims behind them.
app/services/finance.py · cost_summary()app/services/finance.py · contract_performance()Formula
Why this waySavings the population generated against the benchmark, before the contract decides how much of it is earned. The per-member variance is rounded to cents before it is multiplied, exactly as it is shown, so anyone can reproduce it from the card: 195.68 × 105.12.
In the demo$19094.4 (Variance against benchmark across 112.32 member-months)
app/services/finance.py · contract_performance()Formula
Why this wayOne definition read by every screen (quality.composite). The measure rates count the same population as the cost cards on this screen: the members attributed for the contract's period (population.scoring_population; owner decision, 2026-09-24). An unattributed member's gaps are still evaluated and worked, but are not in the score the contract settles on. See Part I, The quality composite.
In the demo24.7% against a 60.0% minimum to earn any savings
app/services/quality.py · composite()app/services/finance.py · contract_performance()Formula
Why this wayThe gate is binary: below it, savings generated earn nothing. That is why the demo shows about $20,570 generated and $0 settled. It is an estimate; the payer reconciles against run-out claims after the period closes.
In the demo$0.0 — Savings generated but the quality gate is not met (24.7% against a 60.0% minimum), so none of it is earned.
app/services/finance.py · contract_performance()Formula
OPEN or REOPENED (no work has moved them on)Why this wayCounted over the contract's attributed members, and labelled '(attributed)' so it is not read as the Command Center's 'Open care gaps', which counts every member's gaps (28 against 29 in the demo). 'No intervention started' is read from the gap's own state: a gap an intervention has touched has moved to in progress, scheduled or awaiting authorization.
In the demo28 open; 27 of 28 have no intervention started
app/services/vbc_manager.py · contract_health()Formula
HIGH or RISINGWhy this wayThe members most likely to drive next quarter's cost. Always 'monitor': a count of risk is not by itself good or bad. Counted over attributed members and labelled so, unlike the Command Center's 'High / rising risk', which counts every member. Tier bands are in Part I, Clinical risk score.
In the demo2 high, 4 rising, of 12 attributed
app/services/vbc_manager.py · contract_health()Formula
Why this wayEach chip is a stated test on the card's own value, not a judgement typed by a person.
In the demoActual PMPM favorable, Gross savings positive, Quality score at_risk, Estimated settlement at_risk, Open care gaps (attributed) attention, High / rising risk members (attributed) monitor
app/services/vbc_manager.py · contract_health()Formula
Why this wayThe distance to the only threshold that decides whether savings are paid. See Part I, Path to the quality gate.
In the democurrent 24.7%, required 60.0%, shortfall 35.3 pts
app/services/vbc_manager.py · gate_path()Formula
Why this wayGreedy is the fewest closures here because every gap in a measure is worth the same number of points. The arithmetic uses the exact per-gap value and rounds once at the end, so closing all six eye-exam gaps is worth +20.0 here and 20.0 on the Quality screen: one quantity, one number (a guard compares the two).
In the demoDiabetes: Eye Exam Performed: close 6 of 6 → 100.0%, +20.0, Diabetes: HbA1c Control (<8.0%): close 4 of 4 → 100.0%, +13.33, Annual Wellness Visit: close 1 of 6 → 37.5%, +2.5
app/services/vbc_manager.py · gate_path()Formula
Why this wayWhat the arithmetic gives if exactly those gaps close and nothing else moves: a projection, not a forecast. 'Model this' on the Quality screen, run with the same plan, gives the same answer to within 0.1 point (the scenario engine re-computes each rate instead of adding points).
In the demo11 gaps → 60.5% against a 60.0% gate
app/services/vbc_manager.py · gate_path()Formula
Why this wayA trend is only as honest as its points, so today's is captured from the live engines rather than typed. The early points in the demo are a seeded synthetic backfill and the screen says so.
In the demoAttributed members 12.0 → 12.0, PMPM (allowed) 1664.0 → 1580.0, Open care gaps 36.0 → 28.0, Annual Wellness Visit 12.4 → 25.0, Controlling High Blood Pressure (<140 systolic) 27.4 → 40.0, Diabetes: HbA1c Control (<8.0%) 20.7 → 33.3
app/services/trends.py · capture()app/services/trends.py · catalogue()Formula
Why this wayDirection is judged against each metric's own sense of good: open gaps falling is improvement. Colouring every rise green would teach people to stop reading the column.
In the demoAttributed members flat, PMPM (allowed) improving, Open care gaps improving, Annual Wellness Visit improving, Controlling High Blood Pressure (<140 systolic) improving, Diabetes: HbA1c Control (<8.0%) improving, Diabetes: Eye Exam Performed flat, Statin Adherence (PDC >= 80%) improving, Quality composite improving, High or rising risk members improving
app/services/trends.py · catalogue()Formula
Why this wayShown so a reader can tell a trend from a single reading: a metric with one point has no trend, and the projection engine refuses to project it (Part I, Trends and projection).
In the demoAttributed members 1 over 0 d, PMPM (allowed) 7 over 42 d, Open care gaps 7 over 42 d, Annual Wellness Visit 7 over 42 d, Controlling High Blood Pressure (<140 systolic) 7 over 42 d, Diabetes: HbA1c Control (<8.0%) 7 over 42 d, Diabetes: Eye Exam Performed 7 over 42 d, Statin Adherence (PDC >= 80%) 7 over 42 d, Quality composite 7 over 42 d, High or rising risk members 7 over 42 d
app/services/trends.py · catalogue()Formula
Why this waySeverity is a fixed, published scale rather than a hidden score: the gate outranks everything because below it no savings are paid at all. Each row's 'why' quotes the numbers that raised it.
In the demoQuality gate is not met (100), Diabetes: Eye Exam Performed below target (80.0), Annual Wellness Visit below target (75.0)
app/services/vbc_manager.py · priorities()app/services/vbc_manager.py · _stalled_campaigns()The VBC manager's and executive's money view: cost per member per month and what drives it, utilisation, the contract's settlement arithmetic, the outcomes the contract is judged on, and observed clinical change.
Formula
Why this wayCost is only meaningful over the population a contract holds you responsible for, so every cost figure on this screen divides by this population's member-months.
In the demo12 members
app/services/finance.py · cost_summary()app/services/population.py · attributed_patients()Formula
Why this wayIt counts months ELAPSED to date, so a cost-to-date is divided by the months it was incurred over, not by the year. A part month counts as its share (30.44 = the average days in a month). Known inconsistency: this period total does not stop a member at an attribution end date, while the monthly PMPM chart below does.
In the demo112.32 member-months
app/services/finance.py · member_months()app/services/finance.py · months_between()Formula
allowed, paid and member_liability over claims of attributed members with a service date between the period start and min(period end, today)Why this wayAllowed = what the plan recognises for the service (paid + member liability). Cost performance is judged on allowed, because that is what the benchmark is set on; paid alone would reward shifting cost to the member.
In the demoallowed $177466.0 = paid $140628.88 + member liability $36837.12
app/services/finance.py · cost_summary()Formula
Why this wayThe single cost figure the contract is scored on, computed in one place and read by Finance, Contract Health and the trend. See Part I, Contract performance and settlement.
In the demo$1580.0 = 177466.0 ÷ 112.32
app/services/finance.py · cost_summary()Formula
Why this wayPer 1,000 members per year is the unit payer benchmarks are published in, so the figure can be compared with a benchmark without conversion. The same two figures appear in the Utilisation panel, computed by a second function with the same rule.
In the demoED visits: 3 → 320.5 per 1,000/yr, Inpatient admissions: 4 → 427.4 per 1,000/yr
app/services/finance.py · cost_summary()Formula
Why this wayThe number actual cost is measured against. It is a negotiated term, so it is read from the contract, never derived.
In the demo1750 $ PMPM
app/services/finance.py · contract_performance()Formula
Why this waySign convention chosen so a positive variance is money saved. Known limit: every active contract card shows the tenant's current-period PMPM; the contract's own period is printed but does not narrow the cost.
In the demo1750.0 − 1580.0 = 170.0
app/services/finance.py · contract_performance()Formula
Why this wayThe variance is the displayed, cent-rounded figure, so the product can be checked on a calculator from the card itself.
In the demo170.0 × 112.32 = $19094.4
app/services/finance.py · contract_performance()Formula
Why this wayRead from the one composite function every screen uses; the minimum is the contract's term.
In the demo24.7% against 60.0% → NOT met
app/services/finance.py · contract_performance()app/services/quality.py · composite()Formula
Why this wayThe gate is binary: savings below it are generated but not earned. Labelled an estimate, because the payer reconciles against run-out claims after the period.
In the demo$0.0 — Savings generated but the quality gate is not met (24.7% against a 60.0% minimum), so none of it is earned.
app/services/finance.py · contract_performance()Formula
Why this wayEvery category divides by the same member-months, so the category PMPMs add up to the total PMPM: a driver is a slice of the one number, not a separate average.
In the demoProfessional / office: 72 claims, $646.37 PMPM, 40.9%, Inpatient: 4 claims, $505.7 PMPM, 32.0%, Pharmacy: 48 claims, $370.51 PMPM, 23.5%, Emergency department: 3 claims, $57.43 PMPM, 3.6%, Outpatient: 0 claims, $0.0 PMPM, 0.0%
Check it yourselfThe category PMPMs add up to the total PMPM on the Cost cards (to the cent, allowing for rounding).
app/services/finance.py · cost_drivers()Formula
Why this waySame sign convention as the contract: positive is under benchmark. A category with no contracted benchmark is not judged at all rather than compared with an invented one.
In the demoProfessional / office +53.63, Inpatient +34.3, Pharmacy +9.49, Emergency department +32.57, Outpatient +40.0
app/services/finance.py · cost_drivers()Formula
Why this wayA month-to-date figure read as a full month nearly always looks like an improvement, so the partial month is flagged every time.
In the demoProfessional / office 16.23 → 42.82 (+26.59), Inpatient 0.0 → 3287.04 (+3287.04), Pharmacy 0.0 → 0.0 (+0.00), Emergency department 188.6 → 0.0 (-188.60), Outpatient 0.0 → 0.0 (+0.00)
app/services/finance.py · cost_drivers()app/services/finance.py · member_months_window()Formula
Why this wayTells a reader whether a category is a few expensive members or a broad pattern, which calls for a different action.
In the demoProfessional / office 12, Inpatient 3, Pharmacy 12, Emergency department 3, Outpatient 0
app/services/finance.py · cost_drivers()Formula
Why this wayOne sentence answering 'what should I look at first'. Under 100 members a single admission moves PMPM visibly, so the screen warns before anyone reads a trend into it.
In the demoProfessional / office is the largest cost driver at 40.9% of allowed dollars. No category benchmark is configured on the contract, so this is the largest driver, not a measured opportunity.
app/services/finance.py · cost_drivers()Formula
Why this wayThe payer-standard unit. Ambulatory counts virtual visits too, because a telehealth primary-care visit is the utilisation a value-based contract wants more of.
In the demoInpatient admissions 427.4 (n=4), ED visits 320.5 (n=3), Ambulatory / PCP visits 1068.4 (n=10)
app/services/finance.py · utilization()Formula
Why this wayStays still in progress are left out, because a stay counted before it ends would make length of stay look shorter than it is.
In the demo4 days
app/services/finance.py · utilization()Formula
Why this wayA discharge whose window has not closed is excluded rather than counted as 'not readmitted', which would flatter the rate. Over the same population and window as the other utilisation rows: attributed members' index discharges inside the period to date (population.scoring_population). A readmission is looked for in any later stay of the same member.
In the demo33.3% = 1 of 3 index discharges; 1 still inside the window
app/services/outcomes.py · readmission_rate()Formula
Why this wayThe same windowing rule as readmissions: only discharges whose 7 days have passed are judged. Like readmissions it is computed over attributed members' discharges inside the period.
In the demo0.0%
app/services/finance.py · post_discharge_followup()Formula
Why this wayEach month is divided by its own member-months, so months are comparable. The current month is flagged partial because its PMPM is month-to-date.
In the demo2026-05 $3255.89, 2026-06 $2496.58, 2026-07 $1119.02, 2026-08 $1428.11, 2026-09 $204.82, 2026-10 $3329.86 (partial)
app/services/finance.py · pmpm_by_month()app/services/finance.py · member_months_window()Formula
Why this wayEach row re-uses the engine that owns the number, so this table cannot disagree with the panels above it. A value the engine cannot compute yet (for example, no discharge whose 30-day window has closed) shows as not available with the reason, never as 0.0, and is not frozen into the trend: zero is a score somebody earned.
In the demoImprove quality (composite measure rate) 24.7 %, Close care gaps 26.3 %, Reduce avoidable utilization (ED visits) 320.5 per 1,000/yr, Lower total cost of care 1580.0 $ PMPM, Reduce avoidable utilization (30-day readmissions) 33.3 %, Patient outcomes (members whose tracked values improved) 71.4 %
app/services/finance.py · compute_outcomes()Formula
Why this wayThere is no survey (CAHPS) or grievance source in the platform, so these are declared unmeasured rather than estimated.
In the demoImprove member experience, Succeed in value-based contracts
app/services/finance.py · compute_outcomes()Formula
Why this wayAn observed change between readings, not a claim that any intervention caused it. Computed over every member with readings, with no attribution or date window, and a member with a single reading is counted as not evaluable instead of unchanged.
In the demoHbA1c: 3 of 5 improved (60.0%), mean -0.24 %, Systolic BP: 4 of 5 improved (80.0%), mean -6.4 mmHg, Total cholesterol: 2 of 3 improved (66.7%), mean +1 mg/dL, Creatinine: 1 of 1 improved (100.0%), mean -0.1 mg/dL
app/services/outcomes.py · population_change()app/services/outcomes.py · patient_trajectory()The VBC manager's (and the executive's) Actions & Outcomes screen: the operational KPI board, population-level next best actions, how providers are answering the opportunities sent to them, how each campaign's members are progressing, and what the care team has delivered.
Formula
Why this waySix KPIs (average case resolution, first contact resolution, CAHPS, grievances, member satisfaction, research time) have no source in this platform and are always shown with the system that owns them, never as 0 and never dropped. A rate over an empty denominator also shows as 'not measured here', so N can move as data arrives. The panel shows no targets by design: none has been agreed.
In the demo10 of 16
Check it yourselfCount the cards that show a number: they equal N; the rest say 'not measured here'.
app/services/opskpi.py · board()app/static/app.js · opsKpiPanel()Formula
OUTCOME_CONFIRMED + vendor referrals at CONFIRMED
N = settled referrals: internal OUTCOME_CONFIRMED, DECLINED, CANCELLED, UNABLE_TO_CONTACT + vendor CONFIRMED, DECLINED, FAILED, CANCELLED
rate = round( 100 × closed ÷ N, 1 ); 'not measured here' when N = 0Why this wayIn-flight referrals are left out of the denominator rather than counted as failures. Internal COMPLETED ('team reports it done') is the doer's claim, not a settled state, so it is in neither the numerator nor N until the outcome is confirmed. Closed is counted inside settled, so the rate cannot pass 100%, and it is the same calculation (referrals.loop_closed) as the per-person 'Referrals (loop closed)' column further down this screen. Tenant-wide, all time. See Part I, Referral SLA, for what settles a referral.
In the demo100% over 1
Check it yourselfIn the demo the one settled referral is a confirmed vendor referral: 1 ÷ 1 = 100%.
app/services/opskpi.py · board()app/services/referrals.py · loop_closed()Formula
Why this wayA count, so 0 is shown as 0 (a measurement), not as 'not measured'. Vendor referrals are not counted. The card also prints 'over N' where N is the same count, which adds nothing.
In the demo0 handoffs
app/services/opskpi.py · board()Formula
closed_on date; 'over N' = all mirrored cases
'not measured here' when no case has been mirrored at allWhy this waySalesforce holds the real backlog; this counts only the cases this platform has been told about, and the card says so. With nothing mirrored the card refuses rather than printing 0, because 'no view' is not 'empty'.
In the demo1 over 1
app/services/opskpi.py · board()Formula
case_type, lower-cased, is complaint; 'over N' = all mirrored cases
'not measured here' when no case has been mirroredWhy this waySame limit as the backlog: only complaints mirrored from Salesforce. CAHPS, grievances and member satisfaction beside it are never computed here and say which system owns them.
In the demo1 over 1
app/services/opskpi.py · board()Formula
REACHED or beyond
rate = round( 100 × numerator ÷ N, 1 )Why this wayCounted per ladder, not per message. 'No answer', 'Voicemail left' and 'Wrong number' are outcomes of an attempt nobody answered, so they are not responses. The numerator is drawn from the attempted ladders it is divided by, so the rate is bounded by 100%. The care-team 'Response' column on this screen is a different figure (members with an inbound message, per person).
In the demo100% over 2
Check it yourselfIn the demo both attempted ladders are at REACHED (needs appointment; transportation barrier): 2 ÷ 2.
app/services/opskpi.py · board()app/services/engagement.py · responded()Formula
COMPLETED on or before its due date ÷ N )Why this wayThe same figure GET /population/kpis/assessments serves, from the one function (A-060). An HRA completed late is a completed assessment and a missed obligation, so it does not count; one nobody attempted counts against the plan exactly as much as one the member refused, so the plan cannot choose a friendlier denominator.
In the demo62% over 8
app/services/assessments.py · kpis()app/services/opskpi.py · board()Formula
OFFERED, ACCEPTED, ENROLLED or DECLINED
rate = round( 100 × those at ENROLLED ÷ N )Why this wayEnrolled over everyone offered, not over enrolments, so declines count against it; an offer the member accepted is an offer. The same figure GET /population/kpis/assessments serves, from the one function (A-060). Known limit: an enrolment that later moved to DISENROLLED (or is still ELIGIBLE) is in neither count, so a member who enrolled and left drops out of the denominator.
In the demo67% over 12
app/services/assessments.py · kpis()app/services/opskpi.py · board()Formula
Why this wayThe card's basis reads 'members this platform has attempted to reach', but an inbound-only message also counts, so it is 'any two-way contact recorded'. N is the whole tenant, not the attributed population.
In the demo15.4% over 13
app/services/opskpi.py · board()Formula
Why this wayA duplicate that never happens looks like nothing happening unless it is counted. The same function feeds the Duplicate activity prevented screen. No denominator is shown: there is no meaningful 'out of'.
In the demo0 prevented
app/services/prevention.py · summary()app/services/opskpi.py · board()Formula
done owned by one of those N ÷ N, 1 )Why this wayA workload figure, not a score, and not ranked per person. The numerator is drawn from the same people as N, so a completed task nobody owned is no coordinator's work and does not inflate it. All time.
In the demo3 over 1
Check it yourselfDemo: 3 done tasks, all owned by Jane Smith, 1 owner: 3 ÷ 1 = 3.0.
app/services/opskpi.py · board()Formula
Why this wayAt most 4 quality pushes plus one each for transitions, adherence, cost and enrolment can be raised, which is 8, so the limit never drops a row today. The whole panel is advisory: nothing is created until a person launches it.
In the demo7 recommendations
app/services/vbc_manager.py · population_actions()Formula
Why this wayThe score is an ordering rule, not a measurement, and it mixes units (composite points against dollars), so the exchange rate between them is a policy choice. A cost row needs more than $50 PMPM over benchmark to outrank adherence, and more than $100 plus $10 per discharge due to outrank the post-discharge queue; a quality push outranks that queue once it is worth more than 6 composite points (plus 0.1 per discharge due). In the demo all four quality pushes (13.33 to 20 points) rank first. The score is not shown.
In the demoquality_push 200.0, quality_push 150.0, quality_push 150.0, quality_push 133.3, transitions 61.0, adherence 55.0, enrollment 48.0
app/services/vbc_manager.py · population_actions()Formula
Why this wayOnly the top 4 are offered, so a measure ranked fifth (in the demo, blood pressure control) is not proposed even with open gaps. Open gaps equal members because a member has at most one open gap per measure. Every figure is the Quality screen's own; see Part I, Quality opportunity and points per gap.
In the demoEED-RETINAL: 6 members, AWV: 6 members, SUPD-STATIN: 6 members, CDC-HBA1C: 4 members
Check it yourselfThe same four measures lead the Quality screen with the same open gaps and composite points; 'Open' lists exactly that many members.
app/services/vbc_manager.py · population_actions()app/services/vbc_manager.py · quality_opportunity()Formula
Why this wayThe window closes whether or not anyone acts, so it is the one event-driven row. Members, as the column says: a member with two owed discharges counts once. The count is the length of the list its Open button shows (vbc_manager.action_members), each member with the discharge and the date the visit is due by. See Part I, Transition-of-care windows.
In the demo1 discharges
Check it yourselfThe Transitions screen's 'Visit window open' card shows the same number.
app/services/vbc_manager.py · population_actions()app/services/vbc_manager.py · action_members()app/services/transitions.py · recent_discharges()Formula
Why this wayCounts the whole tenant, not only attributed members. It uses the same test as the member's own risk and attention panels and My Panel, so a member recorded at 0% (never filled) is counted everywhere. The Open button lists exactly these members, each with the medications below threshold (vbc_manager.action_members).
In the demo3 members
app/services/vbc_manager.py · population_actions()app/services/vbc_manager.py · action_members()Formula
Why this wayA financial action, not a clinical one, and raised only when the contract sets a benchmark for the category; with none there is nothing to be over. The figures are the Financial screen's cost-driver row for that category.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfFinancial → Cost drivers: the same category shows the same variance and Members; 'Open' lists that many members.
app/services/vbc_manager.py · population_actions()app/services/finance.py · cost_drivers()Formula
Why this wayThe members behind the dollar figure. The row count equals the recommendation's Members. The allowed total is summed per member and rounded as it goes, while the category row rounds once at the end, so the two can differ by a cent if a claim amount carries sub-cent precision.
In the demo— nothing to show in the fresh demo tenant
app/services/finance.py · category_members()Formula
Why this wayA high-risk member outside every programme is surfaced by no standing measure, so it is raised here. It reads the fact directly rather than counting 'offer enrolment' items in a ranked list, so no list limit can cut it, and a member whose offer was deferred on their own page is still a member outside every programme. The Open button lists exactly these members (vbc_manager.action_members). See Part I, Next best action.
In the demo1 members
app/services/vbc_manager.py · population_actions()app/services/vbc_manager.py · action_members()app/services/nba.py · unenrolled_high_risk()Formula
Why this wayA count that cannot open to the members inside it is a poster. The list and the count come from one function, so they cannot disagree, and each row says why the member is on it.
In the demotransitions: 1 members, first Sam Okafor (discharged 2026-10-10; follow-up visit due by 2026-10-17)
Check it yourselfClick Open on the post-discharge, adherence or enrolment row: the modal lists as many members as the row's Members column.
app/services/vbc_manager.py · action_members()app/api/population.py · population_action_members()Formula
OPEN, IN_PROGRESS, SCHEDULED, AWAITING_AUTH, SERVICE_COMPLETED, REOPENED)
sorted HIGH, RISING, MEDIUM, LOW, then name · In flight = the gap's current intervention, else 'nothing started', and beneath it the intervention's pending authorization ('auth <ref> · <status>') when one is blocking · Recommended action = that gap's member-level next best actionWhy this wayThe recommendation is the same one the member's care manager sees, so the two screens cannot disagree. Barrier is the measure engine's own reason the gap is open. The list is the attributed members with a live gap — the population the measure's rate and its open-gap count are over (services/population.scoring_population). A blocking authorization is found through the intervention that raised it, so it is shown beside the intervention's status rather than instead of it.
In the demoEED-RETINAL: 6 members, first John Doe (HIGH)
Check it yourselfThe number of rows equals the row's Members (6 for the eye exam in the demo).
app/services/vbc_manager.py · measure_members()app/static/app.js · drillBody()Formula
Why this wayIt is the sum of the Sent column. Opportunities whose member has left the provider's panel are counted apart in a line under the table ('no longer on their provider's panel'), each provider's with its own drill. The panel is hidden entirely when no opportunity has been sent.
In the demo2 opportunities
Check it yourselfAdd up the Sent column.
app/services/opportunities.py · summary()Formula
Why this wayThe denominator for the Evidence returned rate (the y% in that column). Settled and withdrawn opportunities stay in it. See Part I, Provider response metrics.
In the demoDr. P. Osei 2
Check it yourself'view' on the row lists exactly this many opportunities.
app/services/opportunities.py · summary()Formula
answer (NEW or VIEWED, models.OPP_STAGE); MORE_INFO_NEEDED is the care team's turn and is returned apart as waiting_on_care_teamWhy this wayWork waiting on the provider — the same stage the provider's own 'Need your answer' card counts.
In the demoDr. P. Osei 2
app/services/opportunities.py · summary()Formula
ACCEPTED / DISPUTED, whatever their status now
x = disputed_rate = round( 100 × disputed ÷ responded ); 'no responses yet' when responded = 0
Evidence returned = count at EVIDENCE_RETURNED or CLOSED with an evidence reference; y = round( 100 × evidence returned ÷ sent )Why this wayThe recorded answer, not the current status, so an accepted opportunity later withdrawn still counts as accepted. The two rates are Part I's Provider response metrics, printed beside their counts (A-149): disputed is over what was RESPONDED (silence is not a dispute), evidence returned over everything SENT. A rate with no denominator says why rather than printing 0%.
In the demoDr. P. Osei 0 accepted, 0 disputed, 0 evidence returned (0%)
app/services/opportunities.py · summary()Formula
Why this wayJudged only against answers: silence is not a refusal. See Part I, Provider response metrics, whose worked example quotes the same words. The responded count is not on screen, so the % cannot be rebuilt from the visible columns. Rounding is half-to-even (1 of 8 = 12.5 shows 12).
In the demoDr. P. Osei no responses yet
app/services/opportunities.py · summary()Formula
Why this wayA true median — the product's one median (app/core/stats.py), the same function every median on every screen uses, the care-team medians on this screen included: response times 1 and 3 show 2. A median rather than a mean so one opportunity left over a holiday cannot dominate. See Part I, Provider response metrics.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfOnce a provider answers, 'view' lists each opportunity; the business-day gap between creation and response gives the list to sort.
app/services/opportunities.py · summary()app/services/opportunities.py · business_date_of()app/core/stats.py · median()Formula
provider.response_days, default 7), fixed at creation
overdue = the provider still owes an answer (NEW or VIEWED) and today is past Due by more than the 'Provider has not responded' rule's grace (grace_days, 0 by default)
rows sorted by Overdue, then Sent, highest first; the title badge is the tenant total, hidden at 0Why this wayOne answer, opportunities.deadline_state, shared with the provider's screen and with the rule that raises the chase task, so the badge and the task agree on the same day. The response clock stops at the first answer.
In the demo0 overdue
Check it yourselfIn the demo both of Dr. P. Osei's opportunities were created on the reset day and fall due 7 days later, so none is overdue; 'view' shows the Due date on each.
app/services/opportunities.py · row()app/services/opportunities.py · response_days()app/services/opportunities.py · summary()app/services/task_rules.py · _r_opportunity_unanswered()Formula
Why this wayRe-running a push tops up the live campaign for that target instead of creating a second one, so this counts distinct pushes. With none the panel says 'No campaigns yet'.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfLaunch a push from a quality row (or the Quality screen); the count goes to 1, and launching the same measure again leaves it at 1.
app/services/campaigns.py · performance()Formula
Why this wayStored rather than recomputed, so the denominator cannot drift under the result. The cost: a member whose gap has since closed stays in it, and the denominator grows when a manager re-runs the push.
In the demo— nothing to show in the fresh demo tenant
app/services/campaigns.py · launch()app/services/campaigns.py · funnel()Formula
Why this wayA TASK count, not a member count, and the funnel says so: the stage carries the unit 'tasks' and the modal prints it beside the count. Members already holding this campaign's open work are skipped, and a member folded into another campaign's open task gets no new task, so it can be below Eligible; a re-run after a task is done can add a second task for the same member, so it can also exceed it.
In the demo— nothing to show in the fresh demo tenant
app/services/campaigns.py · funnel()Formula
intervention.create audit row is dated at or after the campaign's creation), any status, any measureWhy this wayA campaign creates tasks, never interventions, so there is no link to count by; the launch date bounds it instead. Every creation path writes the dated create row, and an intervention with no dated start cannot be claimed for the campaign period. The cost: an intervention for another measure, or one later cancelled, still counts as started.
In the demo— nothing to show in the fresh demo tenant
app/services/campaigns.py · funnel()Formula
ENGAGED or later in the one funnel order (AWAITING_AUTH, AUTH_APPROVED, SCHEDULED, IN_PROGRESS, COMPLETED, GAP_CLOSED, and REOPENED), or with an inbound message logged since the launchWhy this wayA sent message is not a reached member; a reply or an engaged intervention is. Unreachable, declined and cancelled interventions never count, and nothing from before the launch counts.
In the demo— nothing to show in the fresh demo tenant
app/services/campaigns.py · funnel()app/models.py · iv_reached_at_least()Formula
COMPLETED or GAP_CLOSEDWhy this wayAttendance, not authorisation: the service happened. Any measure; only work started in the campaign period.
In the demo— nothing to show in the fresh demo tenant
app/services/campaigns.py · funnel()Formula
CLOSED on the campaign's measure (any measure for a cohort campaign), closed on or after the launch's business dateWhy this wayOnly the measure engine closes a gap, on clinical evidence. A closure from before the launch is not the campaign's, and the screen's own caveat applies: observed progression, not attributed effect.
In the demo— nothing to show in the fresh demo tenant
app/services/campaigns.py · funnel()Formula
Why this wayBoth rates are the Quality screen's rate (see Part I, A measure across a population) over the whole population, so the change is a before-and-after, not a campaign effect; the screen says so under the table.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfC equals the measure's Performance on the Quality screen.
app/services/campaigns.py · launch()app/services/campaigns.py · performance()app/services/quality.py · evaluate_tenant()Formula
OUTREACH or later, or an outbound message logged since the launch
Appointment scheduled = those at SCHEDULED or later, or with an appointment created since the launch at BOOKED, CONFIRMED, ARRIVED or COMPLETED
each stage carries its unit: Work generated counts tasks, every other stage members (the other six stages are the table columns above)Why this wayEight stages from eligible to closed so the drop between them is visible; the drop is the finding. Every stage is bounded to the campaign period: interventions started, messages logged, appointments made and gaps closed on or after the launch. Cancelled and no-show appointments do not count as scheduled (models.APPT_BOOKED_STATES).
In the demo— nothing to show in the fresh demo tenant
Check it yourselfLaunch a push and click 'Funnel': eight stages, each with its count; Eligible, Work generated, Started, Reached, Completed and Gaps closed equal the table row.
app/services/campaigns.py · funnel()Formula
Why this wayEvery member stage against the stored population.
In the demo— nothing to show in the fresh demo tenant
app/services/campaigns.py · funnel()Formula
Why this wayShows where members stop moving. Intervention created is compared with Eligible population, not with the task count between them, and a stage after an empty stage reads '—': there is nothing to be a share of.
In the demo— nothing to show in the fresh demo tenant
app/services/campaigns.py · funnel()Formula
Why this wayThe one sentence a manager acts on. Work generated (a task count) is left out of the comparison, so the drop is always a number of members, and the note names its unit.
In the demo— nothing to show in the fresh demo tenant
app/services/campaigns.py · _biggest_drop()Formula
UNREACHABLE, DECLINED or CANCELLED (a member with two stopped interventions counts once)Why this wayCounted apart from 'not yet reached' because a channel that is not working should not hide inside a queue. Members, as the sentence says, and only interventions started in the campaign period.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfAppears only when an eligible member has an intervention marked unreachable, declined or cancelled.
app/services/campaigns.py · funnel()Formula
Why this wayLaunching creates tasks (or provider opportunities) and nothing else: no message, no intervention, no gap change. A first launch's message states no counts; a top-up states new and skipped; combined, kept-separate and provider counts are added when non-zero, so a smaller task count is not read as a smaller reach.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfLaunch any quality push, then launch it again: the second message reads 0 new task(s) and N already had open work.
app/services/campaigns.py · launch()app/static/app.js · campaignCreate()Formula
Why this wayDeliberately no composite score and no ranking: the customer asked for progress toward outcomes, and a leaderboard over a handful of people is a statistic about nothing. Outcome columns come first; activity is labelled as context.
In the demoJane Smith
app/services/productivity.py · by_owner()Formula
CLOSEDWhy this wayCredited to the intervention owner, not caused by them: the measure engine closes gaps on clinical evidence that may arrive for other reasons, and the panel says so. It counts interventions, so two interventions on one closed gap count 2, and a cancelled intervention on a gap that closed still counts. All time.
In the demoJane Smith 0
app/services/productivity.py · by_owner()Formula
COMPLETED or GAP_CLOSED · Y = every intervention they own, any status
Z = round( 100 × X ÷ Y, 1 ), omitted when Y = 0Why this wayY includes interventions still in flight, so a new owner's rate starts low; the referral rate beside it uses only settled referrals. All time.
In the demoJane Smith 0 of 0
app/services/productivity.py · by_owner()Formula
OUTCOME_CONFIRMED, DECLINED, CANCELLED, UNABLE_TO_CONTACT; vendor CONFIRMED, DECLINED, FAILED, CANCELLED)
X = internal OUTCOME_CONFIRMED + vendor CONFIRMED · Z = round( 100 × X ÷ Y, 1 ); 'none settled' when Y = 0Why this wayCredited to whoever RAISED the handoff: the question is whether the referral a coordinator started produced an outcome for the member. In-flight referrals are neither success nor failure. Computed by the same function as the Operational KPIs 'Referral completion' card (referrals.loop_closed), so the two agree over the same referrals. See Part I, Referral SLA.
In the demoJane Smith 100% · 1 of 1 settled · 3 raised
app/services/productivity.py · by_owner()app/services/referrals.py · loop_closed()Formula
open · M = every task they own, any status (open, done, dismissed), all timeWhy this wayM is a lifetime total, not current workload. Completed and dismissed counts are computed but not shown.
In the demoJane Smith 30 of 33
app/services/productivity.py · by_owner()Formula
open, has a due date, and that date is before today (business date)Why this wayA task due today is not overdue and a task with no due date never is. The drill applies the same rule row by row, so the badges in it add up to the column.
In the demoJane Smith: 8 (drill badges: 8)
Check it yourselfOpen 'Work' for the person and count the red OVERDUE badges.
app/services/productivity.py · by_owner()app/services/productivity.py · owner_work()Formula
intervention.create audit event to the first later intervention.* audit event written by a member of staff (dropped unless strictly later)
table value = median of the exact durations, rounded once to 1 decimal; '—' when none · the drill shows each duration to 2 decimalsWhy this wayMeasured from the append-only audit trail, not a mutable column, and a median so one item left over a weekend cannot dominate. Every way an intervention is created (a care manager starting one, a provider accepting an opportunity) writes the intervention.create row, so every intervention is anchored. A touch is a staff action: a member's own reply and system writes move the intervention but are not somebody picking the work up.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfStart an intervention and move it once; the drill shows its hours and the table the median.
app/services/productivity.py · by_owner()app/services/productivity.py · _intervention_timings()app/services/productivity.py · owner_work()app/services/workflow.py · create_intervention()Formula
task.done audit event: hours from the task's creation to its FIRST task.done
value = median of the exact durations, rounded once to 1 decimal; the line is hidden when there is noneWhy this wayFor a task the first action is also the last (open → done), so this is the task's 'time to first action' under its accurate name. Dismissed tasks, open tasks, and tasks closed without an audit row (e.g. seeded as done) are left out. Rounded once, at the end, like first touch.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfIn the demo Jane Smith's 3 done tasks were seeded done, with no task.done event, so the line is hidden until a task is closed on screen.
app/services/productivity.py · by_owner()app/services/productivity.py · _task_close_times()Formula
Why this wayContext, never the headline. Known limit: 'replied' has no timing or thread check, so an inbound message sent before the contact, or answering a colleague, counts, and two people who contacted the same member both get the reply. The Operational KPIs 'Outreach response' is a different figure (per ladder).
In the demoJane Smith no contacts made
app/services/productivity.py · by_owner()Formula
BOOKED, CONFIRMED, ARRIVED or COMPLETED of members on whom the person owns an intervention · X = those at ARRIVED or COMPLETED
'no appointments booked' when Y = 0Why this wayNot 'appointments this person booked': it follows the members they own work for. Cancelled and no-show appointments are not booked, by the same definition (models.APPT_BOOKED_STATES) as the funnel's 'Appointment scheduled' on this screen.
In the demoJane Smith no appointments booked
app/services/productivity.py · by_owner()Formula
Why this wayWork owned by nobody appears in no one's numbers, so it is surfaced. Only work still to be done counts: done, dismissed, completed and cancelled items are not 'going unworked'. N is the same set (worklists.unowned_open) as My Work's unassigned count.
In the demo1 task(s), 1 intervention(s)
app/services/productivity.py · by_owner()app/services/worklists.py · unowned_open()The VBC manager's and executive's Analytics screen (prior-authorization volume, approval, turnaround, SLA and appeals; care gaps; the intervention funnel and risk mix), plus three panels any authorized role opens over other screens: the documentation readiness check, the eligibility (271) response and the Ask mdvin answers about authorizations.
Formula
Why this wayA volume count, not a workload count: finished and withdrawn requests stay in it. It equals the sum of the PA Pipeline table beside it and the length of the Authorizations list for the same role, because all three read the same rows at request time.
In the demo1 authorizations
Check it yourselfAdd up the Count column of the PA Pipeline table; it is the same number.
app/api/analytics.py · pa_analytics()Formula
approve or deny in the request's decision trace (its review records)
the card's second line: 'A approved of A + D determinations (D denied)'Why this wayCounted on what the plan determined, not where each request stands now: a later appeal outcome, a closure or an expiry does not change the first determination, so an approved request that is later closed stays an approval. Pending, pended and withdrawn requests have no determination and are in neither A nor D. Nothing to divide by reads as a dash, like the three cards beside it.
In the demo100%
Check it yourselfIn the PA Pipeline table: Approved ÷ (Approved + Denied + Closed).
app/api/analytics.py · pa_analytics()app/services/workflow.py · determinations()Formula
submitted_at and a determination (the first approve or deny in the decision trace) of ( determination time − submitted_at ), in hours, rounded to 2 decimals; the card's second line says how many were determined
'—' when there are noneWhy this wayMeasured from submission, the same start as the decision clock (see Part I, Prior-authorization decision clock), to the plan's determination. A pend asks for information and determines nothing; a withdrawal ends the request without a determination; an appeal outcome is the appeal's decision. None of them moves this figure, because it reads the decision trace, which nothing rewrites, rather than the request's last-reviewer-action timestamp.
In the demo72h
Check it yourselfThe demo request PA-2026-40188 was submitted 4 days before the reset and decided 1 day before it: 3 days = 72 hours.
app/api/analytics.py · pa_analytics()app/services/workflow.py · determinations()app/services/workflow.py · decide_pa()Formula
submitted_at, a determination (first approve or deny in the decision trace) and a decision_due
M = those of N determined at or before decision_due
SLA % = round( 100 × M ÷ N, 1 ); '—' when N is 0Why this wayOnly determined requests are judged, so a request that is late and still undecided is not in N yet; it shows on the Authorizations list's red SLA chip instead. The determination is compared with the request's own decision due; a later appeal decision does not turn an on-time determination into a miss. A pend or a withdrawal is not a determination and is not judged.
In the demo100% (met on 1 of 1 measured)
Check it yourselfThe note under PA Pipeline prints M and N; M ÷ N is the card.
app/api/analytics.py · pa_analytics()Formula
UPHELD, OVERTURNED or PARTIALLY_OVERTURNED
overturned = decided appeals that are not UPHELD
overturn % = round( 100 × overturned ÷ decided, 1 ); '—' when none is decidedWhy this wayA partial overturn counts as overturned, because the member got some of the service after the plan first said no. Level 1 and Level 2 appeals are counted alike. As the screen says, a high rate usually points to documentation missing at submission rather than to reviewer error; the readiness check further down this chapter exists to catch that before submission.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNo appeal exists in the fresh demo, so the card shows '—'. File and decide one on the Authorizations screen and the card becomes overturned ÷ decided.
app/api/analytics.py · pa_analytics()Formula
Why this wayFiled minus decided is the number of appeals still waiting for a decision.
In the demo0 appeals
app/api/analytics.py · pa_analytics()Formula
CLOSED, all timeWhy this wayOnly a gap closed by evidence counts. EXCLUDED and NOT_APPLICABLE also take a gap off the open list but are not closures, so they are left out. Other screens show a same-named number computed differently: the Dashboard's 'Gaps closed this month' counts only closures dated this month, My Panel's 'Gaps closed' counts only gaps behind the signed-in care manager's own interventions, and Actions & Outcomes counts members, not gaps. The intervention state GAP_CLOSED in the funnel below is a different record and can differ from this count.
In the demo0 gaps
Check it yourselfEquals the 'closed' entry in the 'care gaps … total (…)' line at the foot of the screen (absent when 0).
app/api/analytics.py · vbc_analytics()Formula
Why this wayA snapshot of where requests stand now, not how many ever passed through a state. The rows add up to PA Volume.
In the demoAPPROVED 1
Check it yourselfFilter the Authorizations list by status; each count matches.
app/api/analytics.py · pa_analytics()app/static/app.js · rAnalytics()Formula
expedited, any statusWhy this wayExpedited requests run on the 72-hour clock rather than 7 days (see Part I, Prior-authorization decision clock). This counts all of them, open and decided.
In the demo0 requests
app/api/analytics.py · pa_analytics()Formula
Why this wayThe code is kept when a denial is overturned on appeal, so an overturned request still counts under its original reason: the panel answers 'why did we deny', not 'why are requests denied now'. A withdrawal clears the code.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNo request is denied in the fresh demo. Deny one as the medical director (a reason code is mandatory) and it appears here under that code.
app/api/analytics.py · pa_analytics()Formula
Why this wayThe top of the funnel and the 100% width every other bar is drawn against. The stages, their labels and their order are one table (models.IV_FUNNEL_STAGES), which the Dashboard's Engagement funnel draws too.
In the demo1 interventions
app/api/analytics.py · vbc_analytics()app/models.py · iv_funnel()Formula
OUTREACH in the one funnel order (models.IV_STAGE): OUTREACH, ENGAGED, AWAITING_AUTH, AUTH_APPROVED, SCHEDULED, IN_PROGRESS, COMPLETED, GAP_CLOSED, and REOPENED (which re-enters at ENGAGED)Why this wayCurrent state, not 'ever reached': an intervention that went UNREACHABLE, DECLINED or CANCELLED drops out even if outreach happened. A REOPENED intervention had already reached the member, so it counts. The name overstates it slightly: a member who phoned in can go straight from identified to engaged, or to awaiting authorization, without any outreach, and still counts here. The Dashboard's Engagement funnel draws the same stage from the same table (models.iv_funnel), and the campaign funnel reads the same order.
In the demo1 interventions
app/api/analytics.py · vbc_analytics()Formula
ENGAGED in the funnel order: ENGAGED, AWAITING_AUTH, AUTH_APPROVED, SCHEDULED, IN_PROGRESS, COMPLETED, GAP_CLOSED or REOPENEDWhy this wayThe Outreach set without OUTREACH itself: the member responded. The same current-state limit applies: a DECLINED member leaves the count. REOPENED counts, because the member was reached before the service had to happen again. The demo's one intervention (Derrick Boateng's retinal exam, authorization approved) counts here although it was created directly at AUTH_APPROVED. The Dashboard labels this stage the same way.
In the demo1 interventions
app/api/analytics.py · vbc_analytics()Formula
SCHEDULED, IN_PROGRESS, COMPLETED or GAP_CLOSEDWhy this wayAWAITING_AUTH and AUTH_APPROVED are left out because they come before booking: an approved authorization unlocks scheduling. The count can go DOWN: a no-show sends a scheduled intervention back to outreach, and a completed one can be reopened.
In the demo0 interventions
app/api/analytics.py · vbc_analytics()Formula
COMPLETED or GAP_CLOSEDWhy this wayCOMPLETED means the service happened; GAP_CLOSED means the evidence also closed the care gap. This is an intervention count, so it can differ from the Gaps Closed card, which counts care-gap records.
In the demo0 interventions
app/api/analytics.py · vbc_analytics()Formula
Why this wayEach bar is that stage's share of ALL interventions created, not the conversion from the stage before it, and it is rounded to a whole percent. The Dashboard's Engagement funnel draws the same stages against the same first stage, so the same counts give the same bar lengths on both screens.
In the demoInterventions created 100%, Outreach or beyond 100%, Reached (engaged or beyond) 100%, Scheduled or beyond 0%, Completed or gap closed 0%
app/static/app.js · rAnalytics()Formula
Why this wayIt counts the tier stored on each member record, the same one the Patient 360 summary and the Dashboard's 'High / rising risk' card read, not a fresh score. See Part I, Clinical risk score and tier. A tier typed in by hand is counted as typed. Tiers with no members are not listed.
In the demo13 members; HIGH 2, MEDIUM 5, RISING 4, LOW 2
Check it yourselfThe four numbers add up to the member count on the Patients list (13 in the demo).
app/api/analytics.py · vbc_analytics()app/services/risk.py · rescore()Formula
Why this wayA count of stored gap records. It is not the Quality screen's 'open gaps' (D − N per measure), which the measure engine recomputes from members; the two agree in the fresh demo (29) because every stored gap is still open, and drift apart as gaps close, are excluded, or when a member has two records for one measure.
In the demo29 gaps
app/api/analytics.py · vbc_analytics()Formula
Why this wayRaw statuses, not grouped into open and closed. Open side: OPEN, IN_PROGRESS, SCHEDULED, AWAITING_AUTH, SERVICE_COMPLETED, REOPENED. Closed side: CLOSED, EXCLUDED, NOT_APPLICABLE (and three statuses nothing writes today). Only CLOSED feeds the Gaps Closed card.
In the demoopen 28, in progress 1
app/api/analytics.py · vbc_analytics()app/static/app.js · rAnalytics()Formula
Why this wayDeterministic: it points at records and never asserts a clinical fact. What satisfies a criterion is written on the policy itself, so a record of the right TYPE is not enough: on PA-2026-40188, C3 'Elevated HbA1c or insulin-dependent' is satisfied only by an HbA1c above 8.0 in the last year or a long-term insulin code, never by a cholesterol result (it used to be). Structured evidence outranks documents, so a criterion can be Evidenced with nothing attached to the request; the cited item says which record it is. Publishing a policy refuses a malformed matcher, or a criterion that neither a matcher nor any document type could ever evidence.
In the demoC1 SATISFIED (Type 2 diabetes mellitus without complications (E11.9)), C2 SATISFIED (Diabetic Retinal Exam: open (no qualifying procedure in the last 365 days)), C3 MISSING (nothing in the record), C4 MISSING (nothing in the record)
Check it yourselfEach Evidenced line names the record it used and its source system; open the member's Clinical tab and find it.
app/services/readiness.py · assess()app/services/readiness.py · _match_criterion()app/services/readiness.py · _match_document()app/services/readiness.py · validate_criteria()app/static/app.js · readinessHtml()Formula
Why this wayAn unattached document is named before a missing one because it is the cheaper fix: one click, versus obtaining new documentation. 'All N criteria are evidenced' means every criterion's own matcher found its record or a document of an allowed type is attached.
In the demonot ready: 2 requirements (C3, C4) cannot be evidenced from the record. Obtain and attach the documentation, or expect the payer to request more information.
app/services/readiness.py · assess()app/static/app.js · readinessHtml()Formula
Why this wayTicked by default: the documents are already in the chart, so attaching them at submission avoids a pend for missing information. Only documents the check found are attached; nothing is generated.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNo seeded chart holds an unattached document of a required type, so the checkbox does not appear in the fresh demo. Upload a clinical note for a member, then launch a retinal-exam request for them: the note appears as 'In the chart, not attached' and N counts it.
app/services/readiness.py · assess()app/static/app.js · readinessHtml()app/static/app.js · loadReadiness()Formula
pa_required false → not required · otherwise requiredWhy this waySilence is never read as 'no': a 'not required' answer must come from a policy that says so (the demo loads one for routine services). 'Cannot say' costs a phone call; a wrong 'no' costs a denied claim. One resolver (services/policy) answers for this panel, the booking gate, next best action and the care-gap chain, with one pin rule: a request is judged under the version it was submitted under, even after that version is retired; a question with no request reads the newest active version.
In the demoDerrick Boateng 92250: required · John Doe 99213: not required · Ahmed Ali 92250: cannot say
Check it yourselfAhmed Ali's plan (BCBS Texas, commercial) has no policy on file in the demo, so the panel says it cannot tell and asks you to confirm with the payer.
app/services/readiness.py · assess()app/services/policy.py · resolve_policy()app/services/policy.py · policy_answer()Formula
active and whose dates contain today (the most recently started, if several); with none current, the member's latest coverage row (largest effective_to), whose status is then reported as it is; the check is saved with a new ELG-… transaction numberWhy this wayReports what the benefit record on file says; it quotes no price. Every screen that names 'the' coverage asks the same function — the header, the summary strip, the readiness check, the care-gap chain, the Dashboard and My Panel — so a terminated plan that ends later no longer wins over the active one. With no current coverage, the modal shows the latest row with its status (terminated or lapsed) rather than nothing.
In the demoACTIVE · UnitedHealthcare MA Choice · 2026-01-01 to 2026-12-31
Check it yourselfThe modal's transaction number is saved; the Coverage tab's 'Last verified with the payer' shows it afterwards.
app/services/eligibility.py · verify()app/api/eligibility.py · check()app/services/coverage.py · current_coverage()Formula
Why this wayBoth dollar figures are read from the coverage's benefit record as filed; only the bar is computed, in the browser, and capped at 100%. A figure nobody entered is stored as null and shown as 'not on file', never as '$null' or a derived number.
In the demo$875 of $1500 → 58%
Check it yourselfJohn Doe: 875 ÷ 1,500 = 58.3%, drawn as 58%.
app/static/app.js · eligibilityModal()app/services/eligibility.py · verify()Formula
Why this waySame arithmetic as the deductible bar. The 'Add coverage' form has fields for the deductible and the amount met only, and it sends only what was typed: the server stores every other benefit figure as null (services/coverage.normalize_benefits), so for coverage added there the out-of-pocket line and the copays read 'not on file' rather than figures the payer never filed. Seeded coverage carries benefit figures. Coverage added before this rule keeps whatever it stored.
In the demo$2200 of $5000 → 44%
Check it yourselfJohn Doe: 2,200 ÷ 5,000 = 44%.
app/static/app.js · eligibilityModal()app/static/app.js · coverageSave()app/services/coverage.py · normalize_benefits()Formula
active and pa_required true; one line per policy with its service codesWhy this wayThe list of services this plan says need authorization, so nobody drafts a request that eligibility would have refused. The payer match is an exact text match, and the 'Add coverage' form takes the payer as free text: coverage typed as 'United Healthcare' matches no policy filed as 'UnitedHealthcare', and the box silently disappears.
In the demoUHC-MA-EYE-014 v3: 92250
Check it yourselfSign in as the policy administrator: the Policy Packages list shows the same active, authorization-required package for UnitedHealthcare Medicare Advantage.
app/services/eligibility.py · verify()app/api/eligibility.py · check()Formula
APPROVED; 'to' = the authorization's valid_to
a new approval, or an overturned appeal, is valid from the decision's business date to that date + 180 daysWhy this wayValidity is not re-checked here. An authorization past its end date is moved to EXPIRED by the automation sweep (every 15 minutes by default), and until then it still lists. PARTIALLY_APPROVED requests receive no authorization number and never list. Known inconsistency in the demo: the seeded authorization PA-2026-40188 was written with a 90-day window, not the 180 days every real approval receives.
In the demo179 days after the reset
Check it yourselfCheck eligibility on Derrick Boateng, the member with the demo's approved authorization: the box lists PA-2026-40188 · CPT 92250 · AUTH-88213417, valid from the day before the reset to the date shown here.
app/services/eligibility.py · verify()app/services/workflow.py · grant_authorization()app/services/workflow.py · expire_authorizations()Formula
SUBMITTED, IN_REVIEW or PENDED, the one definition in models.pa_is_pending) with a decision_due, soonest first
over = live with due < now · soon = the rest due within 48 hours
facts: over + soon (or all live when both are empty), at most 6; 'in 2d 3h' / '5h' / '40m' / '1d 4h overdue', whole units, never rounded upWhy this way48 hours is the assistant's own cut; the SLA chip on the Authorizations list goes red under 24 hours and amber under 72, so the two can describe the same request differently. Pending is read from the status, so a pended or resumed request stays on this answer while its clock keeps running.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfIn the fresh demo the only request is already decided, so the answer is 'No authorization has a clock running right now.' Submit a request and ask again: it is listed with its time to deadline.
app/services/assistant.py · _h_at_risk()app/services/assistant.py · _rel()Formula
Why this wayThe same readiness engine as the panel above, so the two cannot disagree about a request. Known limit: with no request or member named, 'highest reference' is not 'newest', because reference numbers are random; and it answers even for a request that is already decided, as the demo shows.
In the demoPA-2026-40188: 2 requirement(s) that nothing in the record satisfies
app/services/assistant.py · _h_pa_blocked()app/services/assistant.py · _newest_pa()app/services/readiness.py · assess()Formula
decision_due, in whole units as above
'past the deadline' when due < now and no decision is recorded
Urgency label = '72 hours' when expedited, else '7 days'Why this wayThe due time is the decision clock of Part I (see Prior-authorization decision clock). Two limits: the urgency label is fixed text, so a deployment that configures a different clock would see the old numbers here; and the 'Decision due' fact is shown even after the decision is made.
In the demoPA-2026-40188 · APPROVED · standard (7 days)
Check it yourselfThe decision-due fact moves with the clock; compare it with the SLA due date on the Authorizations list.
app/services/assistant.py · _h_pa_status()app/services/assistant.py · _rel()Formula
SUBMITTED, IN_REVIEW, PENDED or DRAFT; closed out = total − open
facts: one per status, largest firstWhy this wayTotal equals PA Volume on the Analytics screen. 'Open' here is every undecided request, so it includes DRAFT, which the Dashboard's 'PA blocking care' and the at-risk answer leave out (nothing has been sent to the payer). An APPEALED request is still undecided but counts as closed out. Phrasing matters: 'How many authorizations are there?' is answered with one request's status instead.
In the demo1 in total, 0 still open and 1 closed out
app/services/assistant.py · _h_counts()The attributed clinician's home screen (sidebar: My Opportunities), seen by a provider login such as the demo's Dr. P. Osei: the gap-closure opportunities the payer has routed to this provider, grouped by what they need, plus the size of the provider's attributed panel.
Formula
answer — status NEW or VIEWED (models.OPP_STAGE, the one table)
counted in the browser from the stage field of GET /population/opportunitiesWhy this wayAn opportunity needs the provider while nobody has answered it (NEW, VIEWED). MORE_INFO_NEEDED is the provider's own question waiting on the care team: it is the care team's turn, so it is listed in its own 'Waiting on the care team' section and is not counted here. It comes back as VIEWED when the care manager answers. The VBC manager's Provider responses panel counts the same stage as 'Awaiting'. The list is only this provider's opportunities (see 'Which opportunities are listed' below).
In the demo2 opportunities
Check it yourselfThe section below shows the same count and that many rows. As the VBC manager, Actions & Outcomes > Provider responses > Awaiting for Dr. P. Osei shows the same number.
app/static/app.js · rProvider()app/api/population.py · list_opportunities()Formula
work — status ACCEPTED, IN_PROGRESS, COMPLETED or EVIDENCE_RETURNED (models.OPP_STAGE)Why this wayIN_PROGRESS means the provider accepted and the intervention was created in their name; COMPLETED means they marked the service done and the evidence is still to come. EVIDENCE_RETURNED is listed but never stays: both paths that reach it (already completed with evidence, return evidence) move the opportunity on to CLOSED in the same request, and evidence that does not match the measure is refused before it gets there. In practice this card is IN_PROGRESS + COMPLETED.
In the demo0 opportunities
Check it yourselfAccept an opportunity in 'Need your answer': it moves to this section, and both counts change by one.
app/static/app.js · rProvider()app/services/opportunities.py · return_evidence()Formula
answer: NEW or VIEWED) and today is past its due date by more than the grace of the 'Provider has not responded' rule (grace_days, 0 by default)
KPI = number of listed rows flagged overdueWhy this wayComputed by the server on every read, never stored; with no grace the due day itself is not overdue. One answer, opportunities.deadline_state, read by this screen, the VBC manager's Provider responses panel and the rule that raises the chase task, so a badge and a task cannot disagree about the same opportunity. The response clock stops at the first answer: an accepted opportunity, one marked done, or one waiting on the care team for information is not 'unanswered past the response deadline'. See Part I, Provider response metrics.
In the demo0 opportunities
Check it yourselfThe KPI equals the number of red badges in the three sections. The VBC manager's Provider responses > Overdue for Dr. P. Osei shows the same. A freshly reset tenant has none, because the seeded opportunities are due 7 days after the reset.
app/services/opportunities.py · row()app/static/app.js · _oppTable()app/services/task_rules.py · _r_opportunity_unanswered()Formula
GET /vbc/patients returns for this login: members with a live PatientAttribution (no end date, or one after today) to this provider (provider_id, not the name) in the CURRENT period (open and containing today)
the request failed → '—' with 'could not load the panel', never 0Why this wayThe same list and count as the provider's My Panel screen (see the Patients chapter, 'My Panel (N), provider sign-in'). 'This period' is the current period — open and containing today — the same rule as the Patient 360 attribution line and the recipient of a provider opportunity (population.provider_panel / current_attribution); a period left open after it ended counts for nobody. A failed request is said as a failure (getOr): a 0 here is a real empty panel.
In the demo5 members
Check it yourselfClick My Panel in the sidebar: its count badge shows the same number.
app/core/scope.py · _provider_panel()app/api/vbc.py · list_patients()app/static/app.js · rProvider()Formula
settled: CLOSED, WITHDRAWN, DISPUTED or NOT_CLINICALLY_APPROPRIATEWhy this waySettled means the provider's part is on the record, not that the gap is resolved. DISPUTED and NOT_CLINICALLY_APPROPRIATE land here, but each one raises a High-priority task for the VBC manager that is still open, and the gap itself is not changed: settled for the provider, unresolved for the payer. WITHDRAWN is the payer taking an opportunity back. The sections (Need your answer, Waiting on the care team, In progress, Settled) split the list by the server's stage with no overlap and no gaps: every status belongs to exactly one of them.
In the demo0 opportunities
Check it yourselfDispute an opportunity with a reason: it moves from 'Need your answer' to 'Settled', and a High-priority 'Reconcile the disputed gap' task appears in the VBC manager's queue.
app/static/app.js · rProvider()app/services/opportunities.py · row()Formula
response_days calendar days
response_days = tenant setting provider.response_days, default 7; a negative value counts as 0, and a value that is not a number falls back to 7
set once at creation, never movedWhy this wayThe deadline counts calendar days, not business days, so it can fall on a weekend. It is not restarted when the care team answers a question, so a provider can get back an opportunity that is already past due. Sending a gap again while this provider already has a live opportunity on it returns the existing opportunity, with its original deadline.
In the demo7 days after the reset
Check it yourselfSend a gap to the provider as the care manager: the new row's Due date is the reset day plus 7.
app/services/opportunities.py · create()app/services/opportunities.py · response_days()Formula
provider_id is the signed-in provider's AND whose member is on the provider's current panel (the My panel rule)
sorted: unsettled first, then by due date, earliest first (no due date last)Why this wayThe provider comes from the login's binding, never from a query parameter. A provider login bound to no provider sees nothing, not the whole tenant. When a member leaves the panel, that member's opportunities leave this list — and leave the VBC manager's per-provider counts with it, because both read the same set; the VBC manager sees them counted apart as 'no longer on their provider's panel', with a drill to withdraw or reassign them, so nothing disappears silently. Staff logins get the whole tenant from the same endpoint.
In the demoJohn Doe, CDC-HBA1C, Sarah Martinez, EED-RETINAL
Check it yourselfAs the VBC manager, Provider responses > Sent for Dr. P. Osei equals the total number of rows in the three sections here.
app/api/population.py · list_opportunities()app/services/opportunities.py · list_rows()app/core/scope.py · filter_patient_rows()Formula
NEW or VIEWED → Accept · Already completed · Need info · Not appropriate · Dispute
IN_PROGRESS → Mark service done · COMPLETED → Return evidence
MORE_INFO_NEEDED → 'waiting on the care team' · settled → 'settled' · anything else → '—'Why this wayThe screen only draws the actions the server accepts at that status, and the server still checks each one: 'Already completed' needs evidence that matches the measure, and 'Not appropriate' and 'Dispute' need a reason. One difference from the server: it also accepts evidence while an opportunity is IN_PROGRESS, but the screen offers Return evidence only at COMPLETED. Opening the member from a NEW opportunity (the member button) records that the provider viewed it: NEW becomes VIEWED, and 'Viewed' means the provider opened it.
In the demoJohn Doe: Accept, Already completed, Need info, Not appropriate, Dispute, Sarah Martinez: Accept, Already completed, Need info, Not appropriate, Dispute
Check it yourselfClick Already completed and leave the evidence empty: nothing is sent. Enter evidence that does not match the measure: the server refuses it and the gap stays open.
app/static/app.js · _oppActions()app/services/opportunities.py · respond()The VBC manager's (and executive's) view of what went wrong: open rule-raised work that is an exception rather than routine, grouped by kind with its age, escalation and lateness, plus the two integration failures that are not per-member work. Providers and auditors cannot open it (the API answers 403). A care manager opens My Exceptions instead: the same computation narrowed to the exception tasks they own (D-F), with no integration panel.
Formula
exceptions.summary(), over the open exception tasks whose owner is the signed-in user
no integration panel: dead letters and failed batches are platform work, not a member'sWhy this wayOne computation, two audiences (D-F): the manager's count of a category and a care manager's share of it cannot be computed two ways. The page is gated by task.read, the permission its endpoint enforces; the team view stays analytics.read. A task owned by nobody is in the team view only.
In the demo5
Check it yourselfSign in as the care manager and open My Exceptions; open queue on a category lands on My Work (Mine) filtered to that exception, with the same rows.
app/services/exceptions.py · summary()app/api/population.py · my_exceptions()Formula
status open) raised by the rule engine (source = 'rule') whose rule declares kind: exception in the rule catalogue (EXCEPTION_RULES is derived from it)
card = the sum of the Open column over every categoryWhy this wayThe Exception Center is a view over tasks the rule engine already raised, not a separate store, so an exception is resolved exactly when its task is done or dismissed. Which rules count is declared by each rule itself: every catalogue entry must say kind: exception or kind: routine, and a test fails on an entry that says neither, so no rule is routine by omission. As shipped 15 rules are exceptions (pa.sla_at_risk, pa.denied_appealable, vendor.sla_breach, referral.sla_breach, referral.awaiting_acceptance, engagement.exhausted, engagement.stalled, hra.overdue, coverage.lapsing, sdoh.barrier_unaddressed, opportunity.unanswered, gap.evidence_missing, appointment.not_rebooked, opportunity.disputed, opportunity.review). 'Abnormal result with no follow-up', 'Enrolled member with no care manager or no care plan' and 'Referral follow-up due' are declared routine, so their tasks sit in the team queue and are not counted here; whether they should be exceptions is an owner decision. The integration failures below are not included in this card.
In the demo5 = 2 HRA past its contractual window + 3 Social barrier with nothing done about it
Check it yourselfthe Open column adds up to the card. Click 'open queue' on a row: the team queue opens filtered to that rule and lists the same number of tasks.
app/services/exceptions.py · summary()app/static/app.js · rExceptions()Formula
priority is CriticalWhy this wayThe one per-category severity signal. It counts the task's priority as it stands now, so a task escalated from High counts here after its escalation. It does not change the row order, which is Escalated, then Overdue, then Open.
In the demo0
Check it yourselfclick 'open queue' on a row: that many of its rows carry a Critical priority badge.
app/services/exceptions.py · summary()Formula
business today − waiting_since(task) ≥ 7 calendar days (AGED_DAYS = 7)
waiting_since = the task's due date, else the business date it was created · card = the sum of the columnWhy this wayAged counts from the same date escalation counts from (task_rules.waiting_since), so the two cannot disagree about how long a task has waited. A task that carries a due date ages from that date, so an HRA task whose assessment was due 60 days ago is aged on the day it is raised, although the nightly demo reset re-creates every task that day. A task with no due date ages from the day it was raised. The 7 days is a constant in the code, not a tenant setting; the card caption is read from the same constant (aged_days).
In the demo2 tasks
Check it yourselfa category whose 'Waiting since' date is 7 or more days ago has at least one aged task; in the fresh demo tenant the overdue HRA tasks are aged from their due dates.
app/services/exceptions.py · summary()Formula
task.escalated audit row exists for it
card = the sum of the columnWhy this wayEscalation is read from the audit trail, never stored on the task, so it cannot be set by hand. The rule that writes it: an open rule task whose rule sets escalate_after_days and escalate_to_role, once today − waiting_since(task) ≥ escalate_after_days (its due date, else the business date it was created), is raised one priority band and gets '[escalated to <role>]' added to its reason, once; the task.escalated audit row is what makes it once. As shipped only gap.evidence_missing and appointment.not_rebooked set these (7 days, vbc_manager), so the other 13 categories show 0 unless a tenant adds the parameters. The card caption is the server's own statement of what escalation does (ESCALATION_MEANING): priority raised one band and flagged for the escalation role; the owner keeps the task. Nothing is reassigned and nobody is notified. See the My Work chapter, 'Escalated priority'.
In the demo0 tasks
Check it yourselfnone in a fresh tenant. After a rule sweep, an 'evidence missing' or 'missed visit not rebooked' task 7 days old is counted here, and in the queue its priority is one band higher and its reason ends '[escalated to vbc_manager]'.
app/services/exceptions.py · summary()app/services/task_rules.py · _escalate_overdue()Formula
Why this wayLateness is computed from the task on every read, never stored. Due today is not overdue. This counts exception tasks only; the Command Center's 'Tasks overdue' card uses the same test over EVERY open task, so it is normally the larger number. The due date is the one the rule copied from the triggering record (for 'HRA past its contractual window', the assessment's own due date).
In the demo2 (2 HRA past its contractual window)
Check it yourselfclick 'open queue' on a category with a red badge: that many of its rows carry the red OVERDUE badge (the queue applies the same test to the same ten characters of the due date).
app/services/exceptions.py · summary()Formula
rule_key values with at least one open exception task
each category's title and grey 'why' line come from the rule catalogue (the 'why' cut to 120 characters)Why this wayCategories are rules, not a separate taxonomy, so each one maps to exactly one filter on the team queue. A rule with no open task is not listed at all (there are no zero rows); when no category is open the table is replaced by 'No open exceptions', while the four cards still show 0.
In the demo2 categories
app/services/exceptions.py · summary()Formula
Why this wayEscalation is the strongest signal because a rule already decided the task had waited too long; lateness comes next; volume last, so a large pile of on-time work does not bury one late item. The order is the only output; no rank is shown.
In the demoHRA past its contractual window (esc 0, overdue 2, open 2), Social barrier with nothing done about it (esc 0, overdue 0, open 3)
Check it yourselfthe first row has the most escalated tasks; among equal escalated counts, the most overdue.
app/services/exceptions.py · summary()Formula
waiting_since date among the category's open exception tasks (the due date, else the business date the task was created), as an ISO date
'—' when none of its tasks has eitherWhy this wayIt is the same clock as Aged and as escalation, so a category whose date is 7 or more days ago has at least one aged task. Where a task carries a due date copied from its record (an HRA's due date), this is when the obligation fell due, not when the task was raised.
In the demo60 days before the reset
Check it yourselfa date 7 or more days before today means the Aged column of that row is at least 1.
app/services/exceptions.py · summary()Formula
MAX_ATTEMPTS = 8 delivery attempts
all-time: no date windowWhy this wayAfter MAX_ATTEMPTS failed attempts the relay stops retrying an event and leaves it unpublished, so it stays in this count for as long as it stays unpublished; there is no age limit and no dismissed state. The test is one function in the relay (relay.dead_letters), which the administrator's Webhooks screen also calls, so the two screens always show the same events; the Webhooks screen reads the attempt limit from the server rather than typing it.
In the demo0 events
Check it yourselfzero in the fresh tenant (the demo relay is off). The administrator's Webhooks screen shows the same number.
app/services/exceptions.py · summary()app/services/relay.py · drain()Formula
rows_failed > 0 that nothing has resolved (ingest.unresolved_failed)
resolved = a LATER batch of the same source and file name loaded with 0 failed rowsWhy this wayA failure stays here until the corrected file is re-sent and loads clean, so the card can return to 0 instead of counting every failure ever. The list behind it is GET /ingest/batches?failed_only=1, the same function with no row cap, so the card always equals that list. Known limit: a failure that will never be re-sent (the rows were fixed by hand, or the file was obsolete) has no way to be marked handled yet; an explicit acknowledge-with-a-note is an owner decision.
In the demo0 batches
Check it yourselfzero in the fresh tenant. Load a file with a bad row: the card reads 1; re-send the corrected file under the same name: it returns to 0.
app/services/exceptions.py · summary()app/services/ingest.py · unresolved_failed()The VBC manager's (and the care manager's) check on the task rules: which rules are on, how much work each has raised, what happened to that work, and what the rules would raise if they ran now. Figures are shown as the VBC manager Alex sees them; the care manager sees the same numbers.
Formula
enabled = true
Y = number of rules in the product catalogue (CATALOGUE), on or off
the Rules table has one row per catalogue rule, so its count is also YWhy this wayThe catalogue is fixed in the product: a tenant can turn a rule off or change its priority, owner role and thresholds, but cannot add a rule with no implementation behind it. Every rule starts on. Known limit: three rules (opportunity.review, opportunity.disputed, opportunity.more_info) are raised by a provider's response, not by the sweep. They count as 'on' and 'watching for work' although the sweep never proposes anything for them.
In the demo29/29 (table: 29 rules)
Check it yourselfCount the rows in the Rules table: there are Y. As the VBC manager, each row's button says 'Turn off' when the rule is on; the care manager sees an ON badge instead.
app/services/task_rules.py · outcomes()app/services/task_rules.py · ensure_rules()Formula
rule_key, in any status (open, done or dismissed), all time
card = sum of the Raised column over the catalogue rulesWhy this wayAll time, with no date window, so the number only grows. Each rule raises at most one task per triggering record (the store enforces one task per tenant, rule and trigger), so a rule's Raised is the number of distinct records that ever triggered it. Known limit: two features write rule-keyed tasks under keys that are not in the catalogue (hra.<finding> from an HRA finding, engagement.evidence_retrieval from an Engagement outcome). They appear in no row and in no card on this screen, the Unassigned card included (every card is the sum of its column). There are none in the fresh demo tenant.
In the demo28 (transition.contact 1, transition.visit 1, transition.med_rec 1, transition.assessment 1, transition.barriers 1, gap.due_soon 1, lab.critical_result 6, sdoh.barrier_unaddressed 3, hra.overdue 2, enrollment.offer 1, enrollment.started_without_owner 8, engagement.outcome_untriggered 2) tasks
Check it yourselfAdd up the Raised column: it equals the card.
app/services/task_rules.py · outcomes()Formula
rule_key whose status is exactly open (a started task is still open)
card = sum of the Open columnWhy this wayOpen means not yet settled, whether or not someone has started it. It can differ from My Work's 'raised by a rule' count, which is based on the task's origin (rule or the legacy transitions sweep), not on its rule key. In the demo the two agree because every rule-origin open task was raised by a catalogue rule. Known limit: the status is compared case-sensitively here, while the sweep lower-cases it; a task stored as 'Open' would count in Raised and in no other column. None exist in the demo tenant.
In the demo25 tasks
Check it yourselfAdd up the Open column: it equals the card. My Work, Team view, shows the same tasks with an AUTO badge.
app/services/task_rules.py · outcomes()Formula
due date (first 10 characters, read as a date) is before today's business date (America/New_York)
per rule badge: shown only when N > 0 · card subtitle = sum over rulesWhy this wayThe same boundary as My Work's OVERDUE badge: a task due today is not overdue. Two rules (gap.evidence_missing, appointment.not_rebooked) raise tasks with no due date, so their tasks can never be overdue here; those two rules escalate by age instead (see My Work, 'Escalated priority').
In the demo7 (gap.due_soon 1, lab.critical_result 4, hra.overdue 2) tasks
Check it yourselfThe red badges in the Open column add up to the card subtitle.
app/services/task_rules.py · outcomes()app/static/app.js · rAutomation()Formula
done, all timeWhy this wayA task is done when a person completes it, or when the platform completes it because the care gap it was raised for closed on evidence. The Rules table's 'Completed %' column is a percentage of settled work (next row), not this count.
In the demo3 tasks
Check it yourselfIn the demo the three done tasks are Sam Okafor's post-discharge contact, medication reconciliation and barriers steps: those three rules show 100% in the Completed column, and the Transitions screen shows the same steps done.
app/services/task_rules.py · outcomes()Formula
Why this wayThe denominator is settled work only. Counting open tasks as failures would make a rule look worse the more recently it fired. The cost: a rule with one settled task shows 100% or 0% on that one task, so the percentage says nothing about volume; read it with Raised. Rounding is Python's, half to even, so 1 of 8 (12.5) prints 12 where a spreadsheet prints 13.
In the demotransition.contact 100%, transition.med_rec 100%, transition.barriers 100%
Check it yourselftransition.contact: 1 done, 0 dismissed, so 100% and a green badge. A rule with nothing settled shows '—' in grey.
app/services/task_rules.py · outcomes()app/static/app.js · rAutomation()Formula
Why this wayThe screen reads a high dismissal rate as a rule raising work the team does not believe in, and only a person's dismissal counts here. For seven rules (gap.due_soon, sdoh.barrier_unaddressed, engagement.exhausted, engagement.escalate, coverage.lapsing, pa.sla_at_risk, pa.denied_appealable), an open task nobody has started is WITHDRAWN when its trigger stops being true, for example because the authorization was decided, coverage was renewed or a referral for the social barrier started. It is stored as its own status (withdrawn), shown in the Withdrawn column, and counted in neither rate, so a rule whose work mostly resolves itself does not show red. Its reason still ends '— withdrawn <date>: no longer applies'. (A care gap closed on evidence is different: its tasks are marked done.)
In the demotransition.contact 0%, transition.med_rec 0%, transition.barriers 0%
Check it yourselfNo task is dismissed in the fresh tenant, so every settled rule shows 0%. Dismiss a rule-raised task in My Work and reload: that rule's Dismissed column moves and its Completed column falls by the same amount.
app/services/task_rules.py · outcomes()app/services/task_rules.py · retire_stale()Formula
rule_key whose status is withdrawn, all timeWhy this wayThe platform retracting its own task because the trigger stopped being true (see Dismissed %). It is not a person's judgment, so it is kept out of both rates; a rule with many withdrawals is one whose work often resolves itself before anybody acts. Tasks retracted before this column existed were moved over from 'dismissed' by a data migration that recognises the '— withdrawn' the engine wrote on their reason.
In the demo0
Check it yourselfDecide an authorization that has an open 'approaching its decision deadline' task and press Run now: that rule's Withdrawn goes up by one and its Dismissed % does not move.
app/services/task_rules.py · outcomes()app/services/task_rules.py · retire_stale()Formula
open and that have no owner_id
card = the sum of that per-rule count over the catalogue rulesWhy this wayA rule gives a task to the member's current owner first, then to the holder of the rule's role with the fewest open tasks, and leaves it unowned only when nobody holds that role. Like every card on this screen it is limited to the catalogue rules, so it can never exceed Open. It differs from My Work's 'unassigned', which counts every open task with no owner, typed-in and event-raised ones included. In the demo tenant this card reads 0 while My Work reads 1, because the one unowned task was typed in by hand.
In the demo0 (My Work unassigned: 1) tasks
Check it yourselfMy Work, Team view: count the UNASSIGNED badges on rows with an AUTO badge and a rule; that is this card.
app/services/task_rules.py · outcomes()app/services/task_rules.py · _assign()Formula
Why this wayIt shows the size of a change to somebody else's queue before it happens. Three things to know. (1) A trigger whose task was completed or dismissed counts as 'already raised' and is never raised again, so a recurring problem on the same record does not come back as new work. (2) 'Run now' repeats the pass up to 4 times so rules that feed each other settle, so a run can create more than this card predicted. (3) The preview evaluates the rules and then rolls back everything the evaluation did (task_rules.preview): some rules act as they evaluate (a stalled ladder moves to its next channel), and opening this screen must not do that. 'Already raised' can be lower than Raised: in the demo the trigger of one 'enrolled but no active care plan' task (enrollment.started_without_owner) no longer holds, and that rule does not withdraw its tasks, so one raised task is no longer live.
In the demo0 would raise, 27 already raised
Check it yourselfPress Run now as a user who may run it: the toast's 'N task(s) created' is at least the card's number, and the card then reads 0.
app/api/population.py · task_rule_preview()app/services/task_rules.py · evaluate()Formula
Why this wayA sample, not the list: the panel's count is the full number, and only 10 rows are shown. Because the order is evaluation order, the most urgent candidate is not guaranteed to be among the 10.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNothing would be raised in the fresh tenant, so the panel is hidden. It appears when a rule finds a trigger with no task yet, for example after a rule is turned back on or new data is loaded, and it lists what Run now would create.
app/api/population.py · task_rule_preview()app/services/task_rules.py · evaluate()app/static/app.js · rAutomation()Formula
withdrawn), and overdue tasks of rules with an escalation setting move up one priority bandWhy this wayRunning repeatedly is safe: each trigger raises at most one task, so a second press says 'Nothing new'. The run result also reports how many tasks were withdrawn and escalated and whether the passes settled, but the toast shows only N. The same run happens on a timer and on every ingestion, so the button is for watching it happen.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfProduced only by pressing Run now, which writes. The button is shown only to a user holding the permission its endpoint requires (vbc.admin: the VBC manager in the demo); the page reads that permission from one table (ACTION_NEEDS) that a test compares with the endpoint.
The UM nurse's and medical director's Review Queue, and the Authorizations screen that the care manager, PA coordinator, provider (own panel only), VBC manager, auditor and administrator open from the same menu item: every prior-authorization request with its status, decision clock, policy and authorization number, the Review and Detail windows behind each row, and the Appeals panel. This chapter also covers the messages the Launch PA flow on Patient 360 prints, and the notifications bell those decisions feed. The documentation readiness panel, the eligibility window and the Ask panel have their own rows in the Analytics chapter.
Formula
Why this wayThis is the whole register, not open work. A reviewer's 'Review Queue' count includes approved, denied, withdrawn, closed and expired requests, and only rows in IN_REVIEW get a Review button; all the others get Detail. In the fresh demo the medical director's Review Queue shows 1 and has nothing to review: the one seeded request is already approved. The screen also counts the appeals still in review, and never shows that count.
In the demo1 requests
Check it yourselfCount the table rows: they match the heading, whatever their status. For work actually waiting on a reviewer, count only the rows with a Review button.
app/api/pa.py · list_pas()app/static/app.js · rPAs()Formula
IN_REVIEW (it passes through SUBMITTED inside the same save) · approve → APPROVED · deny → DENIED · pend → PENDED · provider responds to a pend → IN_REVIEW · withdraw → WITHDRAWN
appeal filed → APPEALED; appeal overturned or partially overturned → APPROVED, upheld → CLOSED
APPROVED whose valid-to date is before today (US Eastern business date) → EXPIREDWhy this wayMoves follow one transition table, and the decision paths refuse a move it does not allow. Two paths, the provider's response and the appeal decision, set the status directly, but only to moves the table allows. Only a medical director with an MD credential can deny, and only with a reason code and text. Expiry is applied by the automation sweep (every 15 minutes by default), so an authorization can read APPROVED for up to one sweep after its last valid day. PARTIALLY_APPROVED is a legal state that nothing writes: a partially overturned appeal leaves the request APPROVED, with a full authorization number.
In the demoPA-2026-40188 APPROVED
Check it yourselfApprove a request in the Review window: the toast and the badge both read APPROVED. The Audit Trail has one event for each move.
app/services/workflow.py · submit_pa()app/services/workflow.py · decide_pa()app/services/workflow.py · respond_to_pend()app/services/workflow.py · withdraw_pa()app/services/appeals.py · file_appeal()app/services/appeals.py · decide_appeal()app/services/workflow.py · expire_authorizations()Formula
IN_REVIEW; every other status shows '—'Why this wayThe clock keeps running while a request is PENDED: answering the pend does not restart it, and the column shows '—' for the whole pend (My Work's at-risk task does watch PENDED requests; see My Work, 'decision due in Hh'). Known defect: the list and the Review window receive the due time without a time zone, and a browser reads a time with no zone as its own local time. The countdown is off by the viewer's offset from UTC: a viewer on US Eastern time sees about 4 hours more than really remains, and a viewer in Pakistan about 5 hours less. The message after Submit PA receives the time with its zone, so it is correct, and for the same request it can disagree with the column by that offset. The 72 h and 7 days come from deployment settings; the labels 'Expedited — 72h SLA' and '7 calendar days' are fixed text.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfThe one seeded request is approved, so the fresh demo shows '—'. Launch a PA for John Doe's retinal exam: for a viewer on UTC the new row's chip reads 'in 6d 23h' (standard), and on US Eastern time about 'in 7d 3h' because of the defect above. Hover the chip to see the due time: it is UTC, printed without a zone.
app/services/workflow.py · submit_pa()app/api/pa.py · list_pas()app/api/pa.py · get_pa()app/static/app.js · relTime()app/static/app.js · slaChip()app/static/app.js · submitPA()Formula
Why this wayFixed thresholds in the browser, the same for request and appeal clocks. An expedited clock is 72 hours long, so it is amber from the moment it starts. These bands only colour the chip. They are separate from the points an approaching deadline adds to a task's rank on My Work (see Part I, Work-queue ranking). The same time-zone defect as the clock applies, so for a viewer far from UTC a chip can turn red hours early or late.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNothing is on a clock in the fresh demo. Submit an expedited request: its chip is amber at once, while a standard request's chip is green.
app/static/app.js · relTime()Formula
Why this wayFrozen so that publishing a new policy version never rewrites the rules an open case is judged under; the decision trace records the same version. When nothing matched, the column prints a bare 'v' with no key: the request was still accepted, with no policy to review it against.
In the demoPA-2026-40188: UHC-MA-EYE-014 v3
Check it yourselfSign in as the policy administrator: Policy Packages lists UHC-MA-EYE-014 v3 as active for UnitedHealthcare Medicare Advantage, with 92250 among its codes.
app/services/policy.py · resolve_policy()app/api/pa.py · create_pa()Formula
Why this wayAn identifier, not a sequence. A gap between numbers is not a missing case, and the numbers carry no order or count. '2026' is fixed text, not the year of submission. Known limit: the space is 90,000 numbers shared by every tenant, and a repeated number fails the submission with a server error instead of drawing again.
In the demoPA-2026-40188
app/api/pa.py · create_pa()Formula
AUTH- + 8 random upper-case hex characters
valid from = the decision's business date (US Eastern); valid to = the business date 180 days later
'—' when the request was never approvedWhy this wayOne function mints both the number and the window, so a first approval and an overturned appeal cannot disagree on how long an authorization lasts. An overturn starts a new 180 days from the overturn date. The Detail window shows the number but not the dates, although the server returns them; they appear in the approval notification and the eligibility window. Known inconsistency in the demo: the seeded authorization was written directly, not through an approval, so its window is 90 days (valid from the day before the reset to 89 days after it) and not the 180 every real approval receives.
In the demoPA-2026-40188: AUTH-88213417
Check it yourselfApprove a request: the message ends '· AUTH-…', and the care manager's bell shows 'valid to' a date 180 days after today.
app/services/workflow.py · grant_authorization()app/services/workflow.py · decide_pa()app/services/appeals.py · decide_appeal()Formula
MET; in the chart but not attached, or not evidenced → UNKNOWN; when the check returns no criteria (no policy found), the browser's fixed C1–C3 MET are stored instead
model label = the fixed text apex-um v1.8
note stored = the text the browser sends, and the readiness narrative only when it sends noneWhy this wayNo model is involved: the advisory is the deterministic readiness check (see the Analytics chapter's readiness rows), which is why it can cite evidence and never invent it. It records a trace row and cannot change the case. Read the box with care. The label names a model version that does not exist. The browser always sends the same fixed sentence, '3/3 MET on available evidence. Approval probability high', so that sentence is what is stored, even for a four-criterion policy and even when the check found criteria it could not evidence. No approval probability is computed anywhere. On screen the note line is blank, because the window's data does not include the stored note.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNo advisory exists in the fresh demo. As the UM nurse, open Review on a request and click Run criteria analysis: the box appears with the label apex-um v1.8, and the Detail window's trace gains an 'ai_advisory' row.
app/api/pa.py · ai_advisory()app/services/readiness.py · to_outcomes()app/static/app.js · runAI()app/static/app.js · reviewPA()Formula
APPROVED, deny → DENIED, pend → PENDED; the authorization number is added only on approval
criterion outcomes recorded = one of two fixed templates chosen in the window: 'ALL MET' (C1 C2 C3 all MET) or 'C2 UNKNOWN' (C1 C3 MET, C2 UNKNOWN)Why this wayThe status and number come back from the server, so the message cannot claim what was not saved. The outcomes do not come from the policy. The window offers two fixed three-criterion templates, whatever the policy says. The demo's retinal-exam policy has four criteria (C1 to C4), so a human decision records C4 as neither met nor unknown: it is absent. Read the trace's outcomes as the reviewer's selection, not as an evaluation of each criterion.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfAppears only after a decision. In the demo, sign in as md@demo.payer, Review the care manager's new request and Approve it. The message reads '<ref> → APPROVED · AUTH-…'.
app/services/workflow.py · decide_pa()app/api/pa.py · decision()app/static/app.js · decide()Formula
human), ai_advisory, peer_to_peer and provider_responseWhy this wayA count of recorded events, not of decisions: an advisory followed by an approval counts 2, and a peer-to-peer request counts as well. Known defect, visible in the fresh demo: the seeded approved authorization PA-2026-40188 shows a trace of 0 and the word 'none'. It was written into the database directly, not through the approval path that records the reviewer, so an APPROVED case has no recorded decision behind it.
In the demo1 rows
Check it yourselfOpen Detail on PA-2026-40188: 'Decision trace 0', then 'none'. Approve a new request and its Detail window shows 1 human row.
app/api/pa.py · get_pa()app/static/app.js · viewPA()Formula
approve deny pend withdrawn info_provided p2p_requested appeal_…; 'advisory' when none) · criteria v = the request's frozen policy version, 0 when it has noneWhy this wayThe criteria version is copied from the request, not looked up again, so the trace proves which version of the rules the case was judged under even after the policy moves on. The credential is the reviewer's own, from their login, which is how an adverse decision shows it came from a physician.
In the demohuman · approve · criteria v3
Check it yourselfEmpty in the fresh demo (see the count above). After an approval, the row reads 'human · MD-… | approve · criteria v3'.
app/services/workflow.py · decide_pa()app/api/pa.py · ai_advisory()app/api/pa.py · peer_to_peer()app/static/app.js · viewPA()Formula
Why this wayOpen and decided appeals are counted together, like the request count above. The screen also computes how many are still in review, and never shows that figure. Level-2 external reviews are counted when they exist, but no screen can create one: the endpoint has no button.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNo appeal exists in the fresh demo, so the panel is hidden. Deny a request (as the medical director, with a reason), then file an appeal from its Detail window: the panel appears with 1.
app/api/appeals.py · list_appeals()app/static/app.js · rPAs()Formula
IN_REVIEW · decided: upheld → UPHELD, overturned → OVERTURNED, partially overturned → PARTIALLY_OVERTURNED
effect on the request: overturned or partially overturned → APPROVED with a new authorization number · upheld → CLOSEDWhy this wayOnly a denied request can be appealed, and only one appeal at a time. The decision is refused unless the reviewer is a medical director with an MD credential who did not issue the original denial, and an uphold needs a reasoned note of at least 10 characters. A partial overturn is treated exactly like a full one: the request becomes APPROVED, not partially approved.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNeeds a denied, appealed request (see the appeals count). Decide it as a different medical director: the message reads 'Appeal overturned — PA APPROVED · AUTH-…'.
app/services/appeals.py · file_appeal()app/services/appeals.py · decide_appeal()app/static/app.js · decideAppeal()Formula
Why this wayThe '72h' is fixed text in the browser, and so is the '(72h)' in the medical director's notification. The clock actually applied is the tenant's appeal rule (72 hours unless the tenant changes it), so a tenant that sets 24 hours still sees '72h'. A Level-2 external review is never expedited.
In the demo— nothing to show in the fresh demo tenant
app/static/app.js · rPAs()app/services/appeals.py · appeal_rules()Formula
IN_REVIEWWhy this wayAppeal clocks are a tenant rule, while the request clock is a deployment setting: the two are configured in different places. The review window shows the chip only for expedited appeals. The same time-zone defect as the request clock applies to the panel. The message printed right after filing receives the time with its zone and is correct, so the two can differ for the same appeal by the viewer's offset from UTC.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfFile a standard appeal: the message reads 'independent review due in 29d 23h' or close to it. The panel's chip shows the same due time, shifted by the viewer's offset from UTC.
app/services/appeals.py · file_appeal()app/api/appeals.py · escalate_external()app/api/appeals.py · list_appeals()app/static/app.js · submitAppeal()app/static/app.js · appealReviewModal()Formula
Why this wayLine-of-business rules, not one universal clock, as the panel notes say. The deadline appears here only once it has passed. My Work's denied-authorization task and the member attention panel read this same window (appeals.appeal_window), so they name the same closing date. One known limit: a tenant that sets its own per-line-of-business table replaces the default table entirely, so unless it restates Medicare Advantage's 60 days, Medicare Advantage falls back to the tenant's default window.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfThe demo has no denial, so the window never shows. A Medicare Advantage denial decided on 1 July (US Eastern) can be appealed until the same time of day on 30 August; after that, filing is refused with 'Appeal window closed 2026-08-30 (60 days for this line of business)'.
app/services/appeals.py · appeal_rules()app/services/appeals.py · file_appeal()Formula
Why this way'Not required' has to come from a policy that says so. A code that no policy on file lists is 'we do not know', never 'no': an unknown shown as unknown costs a phone call, and an unknown shown as 'no' costs a denied claim. The readiness panel answers the same question with the same resolver (see the Analytics chapter, 'Prior authorization required?').
In the demoPA REQUIRED under UHC-MA-EYE-014 v3
Check it yourselfThe screen asks through POST /pa/requirement-check, which writes an audit row, so this value is read through the readiness pre-check, which uses the same resolver. On John Doe's retinal exam (92250) the demo message reads 'PA REQUIRED under UHC-MA-EYE-014 v3' (runbook step 10).
app/api/pa.py · policy_answer()app/api/pa.py · requirement_check()app/static/app.js · startIntervention()Formula
Why this wayAttaching runs after the request is saved, one document at a time, so a failure to attach never loses the request; the message says how many made it. A document already attached counts as attached, because a repeat submission under the same idempotency key finds the request already bound to it, which is the intended end state.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfNo seeded chart holds an unattached document of a required type, so the fresh demo prints no 'N of M'. Upload a clinical note for the member first, then Submit PA: the message ends '1 of 1 found document attached'.
app/static/app.js · submitPA()Formula
Why this wayEmitted in the same save as the event, so a notification can never announce something that did not happen. Known limits. Read is one flag on the notification, not per person, so when one care manager opens a role-wide notification it disappears for every care manager. Unread items older than the newest 30 are never counted. A decision notifies the care manager role only, so the PA coordinator or provider who submitted the request is not told.
In the demo— nothing to show in the fresh demo tenant
Check it yourselfThe fresh demo has no notifications. After the medical director approves the care manager's request (runbook step 13), the care manager's bell shows 1: '<ref> approved', 'Auth AUTH-… · valid to <date>'.
app/api/appeals.py · my_notifications()app/api/appeals.py · mark_read()app/services/appeals.py · notify()app/static/app.js · refreshBell()The member's own view, signed in as the member (the demonstration member John Doe): what care is due, their visits, their prior authorizations, their messages and how they want to be contacted. It deliberately shows no risk tier, cost, contract or internal review detail.
Formula
active and whose dates contain today (the most recently started, if several — a mid-year change not yet closed out); payer, plan, effective from and to, then 'verified with the plan on <date>' or 'not yet verified with the plan' (eligibility.coverage_state)
no current coverage → the line is not shownWhy this wayThe member sees the plan the care team's screens name, by the same function, and the same verification caveat the care team's authorizations panel prints (eligibility.coverage_state): the record saying 'active' is not the payer saying so today, and the member is told which it is.
In the demoUnitedHealthcare MA Choice, 2026-01-01 to 2026-12-31, not yet verified
Check it yourselfThe care team's Patient 360 for the same member shows the same payer and plan.
app/api/member.py · profile()Formula
OPEN, IN_PROGRESS, SCHEDULED, AWAITING_AUTH or REOPENED
(the open-gap states GAP_OPEN_STATES minus SERVICE_COMPLETED); count = number of rows, no limit
sorted by due date, soonest first (no date last); Due = the gap's due date, with 'overdue' when the business day is past it (clock.is_past_due, the care team's lateness test)
'Why' = the reason the gap engine recorded when it opened the gapWhy this wayDeliberately narrower than every staff gap count: a gap in SERVICE_COMPLETED means the member has attended and the evidence is still on its way, so there is nothing left for the member to do and listing it would ask them to go again. The cost: this count can be LOWER than 'Open gaps' on the care team's Patient 360 summary strip for the same person, by the number of gaps awaiting evidence. The due date is shown, so an overdue item does not look the same as one due at year end.
In the demo5 due: HbA1c Control — Glycemic Assessment; Annual Wellness Visit; Blood Pressure Control; Diabetic Retinal Exam; Statin Adherence (PDC >= 80%)
Check it yourselfCount the table rows; open John Doe's Patient 360 as the care manager and compare with the Open gaps card (they differ only by gaps in SERVICE COMPLETED).
app/api/member.py · care()app/static/app.js · rMember()Formula
BOOKED or CONFIRMED on today's business date or later (scheduling.split_upcoming, the definition My Panel's next appointment uses), soonest first; title count = upcoming
past and cancelled visits (every other appointment) are listed under them, newest first, and not countedWhy this wayThe title promises booked visits, so it counts only visits the member is still expected at: a cancelled or missed visit, or one already attended, is history and is shown as such. The fresh demo member has no booked visit, so the panel shows 'No upcoming appointments'.
In the demo0 appointments
Check it yourselfWhen the care team books a visit for John Doe, sign in as the member: the count rises by one; cancelling the visit returns the count to where it was, and the visit moves to 'Past and cancelled visits'.
app/api/member.py · appointments()Formula
Why this wayThe member sees status, dates and the specific reason they are entitled to for an adverse determination (CMS-0057-F), and nothing from the review itself: no reviewer notes, criteria evaluation or AI advisory, because exposing those would make the portal a discovery surface. The server also returns the submitted and decided dates and the authorization number, which the table does not show. Empty in the fresh demo tenant.
In the demo0 authorizations
Check it yourselfSubmit a prior authorization for John Doe as the provider or coordinator; it appears here with the same reference and status the PA queue shows.
app/api/member.py · authorizations()Formula
25 are listed; count = every such row (total), with 'Showing the newest 25 of N' when there are more · '(you)' = the row's direction is inboundWhy this wayOne list across SMS, email, portal and voice, so a reply the member typed sits next to the message it answers, and the same rows the care team's contact history lists — a finished call is one row with its transcript. A message that never reached the member is not shown to them as received.
In the demo0 messages
Check it yourselfSend an outreach message to John Doe as the care manager, then sign in as the member: it appears at the top. Replying from the portal adds a row marked '(you)'.
app/api/member.py · messages()app/services/engagement.py · history()Formula
Why this wayWhat is ticked is what is recorded: the profile carries the consent record the care team's consent panel reads. 'Never asked' is neither a yes nor a no, so it is shown unticked and a Save that does not touch it leaves it never asked; the server accepts only ladder channels and yes/no values. The consent rules themselves are the one consent authority described in Part I, Engagement ladder, wait times and the contact ceiling; the care team's consent table is in the Patient 360: coordination chapter.
In the demoSMS opted in, Email opted in, Portal opted in, Voice never asked
Check it yourselfUntick a ticked channel, Save, reload: it shows 'opted out'. The care manager's Patient 360 Engagement tab shows the same consent per channel.
app/static/app.js · rMember()app/api/member.py · profile()app/services/engagement.py · consent_state()The platform administrator's and auditor's screens: the tamper-evident audit trail and its verification, inbound data batches, payer policy packages, users and webhooks. Mostly records rather than calculations; the values below are the ones the product computes.
Formula
Why this wayEach event commits to the one before it, so changing, deleting or inserting any past event breaks every hash after it and the check names the first break. Nothing is trusted from storage: every hash is recomputed on each verification.
In the demointact
Check it yourselfPress Verify on the Audit screen: it recomputes the whole chain and reports the count checked.
app/core/audit.py · verify_chain()app/core/audit.py · _digest()Formula
total from /audit, not the 50 rows one page lists; per-action counts = events grouped by actionWhy this wayThe audit trail is append-only; these are plain counts of what it holds, so an auditor can see what kinds of activity exist before filtering to them.
In the demomoves with the clock — read it on screen
app/api/audit.py · list_events()app/api/audit.py · action_vocabulary()Formula
Why this wayEvery inbound row is accounted for, one outcome each, with the reason and the raw payload kept for a failed row: in the demo file one row names a member we do not hold and one carries the value 'pending' where a number is required. Known defect being fixed: the batch header's stored promoted/failed counters can lag the rows' own statuses (the header reads 1 promoted, 0 failed for this file); the row list is the authority.
In the demoquest_labs_2026-10-09.csv: 4 rows — 2 promoted, 1 rejected, 1 unmatched
app/services/ingest.py · promote()app/api/integration.py · batches()Formula
Why this wayPolicy is data, not code: the requirement check, the readiness criteria and the authorization screens all read these rows, so changing a policy changes every screen at once and the version is recorded on each decision.
In the demo3 packages, 2 require authorization
app/static/app.js · rAdmin()Formula
Why this wayA plain count of accounts; permissions come from the role (see the manual's permission matrix), never from the user row.
In the demo12 users, 0 with MFA
app/static/app.js · rAdmin()Formula
Why this wayOnly emitted events are offered, so a subscription can never be to a topic nothing publishes.
In the demo0 subscriptions, 25 topics
app/static/app.js · rAdmin()