The data in this run is synthetic. This is a validation of our detectors — evidence that the harness fires on failures we planted, and stays quiet where we planted none. It is not a claim about any real diagnostic device, vendor, hospital or patient population, and no number on this page should be quoted as one.
This is our first evidence run on the core track. Everything we had published before this — three adversarial batteries and a repeat-run variance check — was on the secondary LLM track. This run evaluates a model across the four dimensions that decide whether a diagnostic model is safe to deploy somewhere it was not built: transferability (does it survive a new site), calibration (do its probabilities mean what they say), subgroup performance (who does it work worse for) and drift (does it still work in six months). Each is mapped item by item to TRIPOD+AI, STARD-AI, DECIDE-AI, CLAIM and the DCB0129/DCB0160 clinical risk management standards.
Until this run, the D1–D7 scorecard was rendered from the developer-set metrics alone. On the reference corpus that produced a table reading calibration Pass (ECE 0.0262) and subgroup equity Pass (gap 0.0309) — while the deployment site those same probes had already measured sat at ECE 0.0682 and a subgroup gap of 0.065, and while the engine verdict immediately underneath said recommend against deployment.
A clean developer scorecard was hiding a deployment failure. That is precisely the failure mode our methodology exists to catch, and it was sitting in our own reporting code. It is fixed: both reads are now printed and the worse one sets the result, with a regression test pinning it. We are publishing it because an assurance company that only reports the bugs it finds in other people's systems is not doing assurance.
| Dimension | Measured | Risk | Verdict |
|---|---|---|---|
| D1 Discrimination & transferability | AUROC 0.8818 → external 0.7574 (drop 0.1244) | Moderate | Conditional |
| D2 Calibration | dev ECE 0.0262, Brier 0.1346; deployment ECE 0.0682, slope 0.6762 | Minor | Pass |
| D3 Shortcut learning / leakage | perturbation AUROC drop 0.0821; conditional site-gap 0.1245 | — | Measured, not scored |
| D4 Subgroup / equity | dev max group AUROC gap 0.0309; deployment gap 0.065 | Moderate | Conditional |
| D5 Operating point @ deployed threshold | dev sens 0.719, spec 0.8587, PPV 0.7623 @thr 0.5; deployment sens 0.4486, spec 0.8974, PPV 0.6358 | Severe | Fail |
| D6 Automation bias / over-reliance | needs human-factors / in-situ evaluation with real clinicians (DECIDE-AI) | — | Not assessed |
| D7 Post-deployment drift | worst PSI 0.2659 (M6); worst AUROC decay 0.1187 (M5); 5 of 6 periods adequately powered | Major | Fail |
Against a fixed sign-off reference, cumulative AUROC decay reaches 0.1187 by month 5, while the largest single month-on-month step is 0.0211. A monitor comparing each month to the previous one would have waved every one of those comparisons through. That is why the reference is fixed at sign-off rather than rolling.
Month 6 is the row that justifies the power guard. At n=40 it reports an AUROC of 0.869 — better than any powered period, and close to sign-off. A monitor without a power guard would have read that as a recovery and closed the alert. It is excluded from the decay statistic and marked underpowered, while its PSI (0.2659, the worst in the sequence) is still used, because population-stability statistics need no outcome labels and a 40-case month is perfectly adequate to show the score distribution has moved. Label-free statistics on every period; performance claims only on powered ones.
| Dimension | Measured | Verdict |
|---|---|---|
| D1 transferability | AUROC 0.9397 → 0.8929 (drop 0.0468, 95% CI −0.025 to 0.110 — crosses zero) | Pass |
| D2 calibration | dev ECE 0.1652; deployment ECE 0.2108, slope 0.2427 | Fail |
| D3 shortcut | perturbation AUROC drop −0.0038; conditional site-gap 0.2059 | Measured, not scored |
| D4 subgroup / equity | dev gap 0.2978 (White 0.9948 vs Black 0.6970); deployment gap 0.1432. Three dev groups excluded as underpowered — see below | Fail |
| D5 operating point | dev sens 0.7551, spec 0.9909; deployment sens 0.9383, spec 0.6714, PPV 0.623 | Conditional |
| D7 drift | worst PSI 1.1743 (2026-07); worst AUROC decay 0.0416; 5 of 6 periods powered; prevalence 0.339 → 0.133 across the sequence | Fail |
Two things worth stating plainly. First, calibration slope 0.2427 is severe over-confidence — the model's probabilities are far more extreme than the outcomes justify — and it is completely invisible in the AUROC, which is a healthy 0.8929. That is the pattern the whole methodology is built around.
Second, one of our two shortcut probes did not work. The perturbation probe returned a negative AUROC drop: removing the planted acquisition-token shortcut did not degrade ranking at all. The conditional site-gap probe on the same data returned 0.2059. AUROC is rank-based, so a shortcut that inflates scores without reordering cases is invisible to it. Two probes for one failure mode turns out not to be redundancy — one of them does not work on its own.
Until this run every metric we produced was a bare point estimate. On a 159-case cohort that is not a small omission: it is the difference between “sensitivity is 0.76” and “sensitivity is somewhere between 0.64 and 0.88, and you cannot tell those apart”. Every headline metric now carries a seeded 95% bootstrap confidence interval. Differences use a paired bootstrap, because the interval for a difference has to come from the distribution of the difference, not from subtracting two independent endpoints.
It immediately changed a result. Run B's transferability drop is 0.0468 — which we would have reported as a drop. Its 95% interval is −0.025 to 0.110: it crosses zero. On this cohort that difference is not distinguishable from no difference, and the honest reading is “no evidence of a transferability drop”. A point estimate could not tell those two apart.
| Run A metric | Developer site | Deployment site |
|---|---|---|
| AUROC | 0.8818 (0.8635–0.8987) | 0.7574 (0.7292–0.7877) |
| ECE | 0.0262 (0.0216–0.0493) | 0.0682 (0.0498–0.0878) |
| Sensitivity @0.50 | 0.7190 (0.6812–0.7554) | 0.4486 (0.4036–0.4988) |
| Subgroup AUROC gap | 0.0309 (0.0026–0.0638) | 0.0650 (0.0109–0.1217) |
Read the last row carefully. The developer-site equity gap only just excludes zero, so on its own it is weak evidence; the deployment-site gap is the one that carries weight. The point estimates alone made both look equally real.
An earlier version of this run reported three ethnic groups in Run B at an AUROC of 1.0, with no qualification. Those groups had 13, 14 and 20 cases. A subgroup too small to estimate a score cannot support a fairness claim in either direction, and a table that reports it as a perfect score reads as reassurance precisely because the cells are too small to fail.
| Group (Run B, developer site) | n | Positives | AUROC | Scored? |
|---|---|---|---|---|
| White | 80 | 24 | 0.9948 | yes |
| Black | 32 | 11 | 0.6970 | yes |
| Mixed | 20 | 4 | 1.0000 | no — underpowered |
| Other | 14 | 5 | 1.0000 | no — underpowered |
| Asian | 13 | 5 | 1.0000 | no — underpowered |
Groups below the floor (n≥30 and ≥5 of each class) are now excluded from the scored gap and named on the scorecard row with their n — excluded is not deleted. What remains is the real disparity: 0.9948 against 0.6970. The finding got stronger and more honest at the same time. Where no group is adequately powered the row reads “fairness is UNTESTED on this cohort, not passed”, which must never look the same as having no demographic labels at all.
PSI smooths empty histogram bins so the logarithm stays finite, and we floored them at a
fixed 1e-6. For an empty current bin the resulting term is large and
positive: the smoothing that looks conservative manufactures drift, and does
so worst on the thinnest periods — exactly where the evidence is weakest. It scored a
45-case monitoring period at PSI 4.08 against a KS of 0.43.
With sample-size-aware add-half smoothing that same period is 1.17, in line with the 260-case period beside it. Empty-bin counts are now printed so a reader can see when the smoothing did any work at all. We are reporting this for the same reason as the scorecard defect above: a statistic whose value is driven by a constant we picked, rather than by the data, is not evidence.
TRIPOD+AI, STARD-AI and CLAIM are reporting standards addressed to the people who built the model. A third-party evaluator cannot “pass” or “comply with” them, and neither can an evaluation run. What a run does is supply measured evidence toward specific items, and test the claims a developer made under them. DCB0129 is the manufacturer's duty and DCB0160 the deploying organisation's; we are neither, and our findings are inputs to their hazard log and safety case, never a substitute for either.
| Standard | Items mapped | Evidenced by this run | With findings |
|---|---|---|---|
| TRIPOD+AI | 6 | 5 | 3 |
| STARD-AI | 3 | 2 | 1 |
| CLAIM | 3 | 2 | 1 |
| DECIDE-AI | 3 | 1 | 1 |
| DCB0129 | 3 | 1 | 1 |
| DCB0160 | 3 | 2 | 2 |
| Total | 21 | 13 | 9 |
The eight items not evidenced are not gaps in the run: they are things no prediction file of any size can answer — whether a named Clinical Safety Officer exists, whether the reference standard was pre-specified, whether clinicians over-defer to the model in the real workflow. They need a documentation audit or an in-situ clinical study. (Two further items, the TRIPOD+AI and CLAIM uncertainty requirements, were marked not implemented in the first version of this run; they are now evidenced, which is what moved coverage from 11 to 13.) A mapping that showed full coverage would be a mapping that was lying.
Both configurations were executed three times. After removing the wall-clock field — the only value permitted to vary — all three repeats of each hashed identically. The pipeline is Python standard library only: no numpy, no scikit-learn, no torch. Every statistic here (AUROC by Mann–Whitney U, ECE, Brier, calibration slope, PSI, two-sample KS) is a few auditable lines of code.
This is a stronger reproducibility claim than we could make on the LLM track, for a
structural reason: our 2026-07-30/31 variance check found that temperature=0,
seed=42 is not a determinism guarantee for local LLMs, with individual cases
flipping between repeats. That finding does not carry over here, because there is no
sampling anywhere in this pipeline — the diagnostic track is seeded arithmetic over a
frozen array. Our standing rule against claiming byte-identical results as a general
property still applies to the LLM track and is not weakened by this.
The data is synthetic, so this validates detectors, not devices. Genuine transferability and fairness evidence needs credentialed real datasets or a partner cohort — and in our beachhead domain no public blood-film dataset carries patient demographics at all, which makes subgroup testing on public image data effectively impossible without a clinical partner.
Also, explicitly:
Full limitations, methodology, standards mapping, reproduction commands and artefact hashes are in the full report: Diagnostic-ML Evidence Run v1, 2026-08-03 — available on request.