Chapter 8

Chapter 8: Conformance Testing, Certification and the Road Ahead

A claim that a system “implements WIA-BIO-004” is only worth something if someone else can check it. This chapter turns the standard’s requirements into a checklist, builds golden and negative test vectors with exact expected values, runs them against the v1.0 SDK and CLI, and records what passes, what fails and what the next revision should fix.

8.0 From a Specification to a Checkable Claim

The previous seven chapters described what WIA-BIO-004 asks for: a vocabulary of biomarker types and clinical roles (Chapter 1), a fixed set of diagnostic statistics (Chapter 2), JSON records for measurements, reference ranges and validation studies (Chapter 3), an SDK and CLI (Chapter 4), analytical and clinical validation protocols (Chapters 5 and 6), and a privacy-aware integration architecture (Chapter 7). None of that helps a laboratory director, a sponsor’s data manager or a regulator who has to decide whether a particular product actually behaves as the standard says. For that you need a conformance claim: a statement of which requirements a system meets, backed by evidence that a third party can re-run.

Conformance testing for a data standard of this kind has an unusual property. Most of what WIA-BIO-004 specifies is arithmetic, and arithmetic has right answers. Sensitivity at a given cutoff is not a matter of opinion; neither is the trapezoidal area under a set of ROC points. That makes it possible to write test vectors whose expected outputs are known to four decimal places, and to find real defects with them. We do exactly that in this chapter, and several of the results are uncomfortable: the v1.0 reference SDK does not type-check as shipped, under-reports AUC on tied or sparse threshold grids, and the v1.0 CLI prints fixed demonstration numbers regardless of its input. Recording such findings honestly is the point of conformance work, not an embarrassment to be hidden.

All SDK observations below were made on a scratch copy of the v1.0 TypeScript sources (api/typescript/src/index.ts and types.ts), compiled with TypeScript 5.9 and run under Node.js 22. Where we had to change the source to get it to compile, we say so.

8.1 What “Conformant” Means in v1.0

8.1.1 The four sources of requirements

WIA-BIO-004 v1.0 does not contain a single section titled “conformance”. The requirements that a conformance claim can be tested against are spread over four places, and a checklist has to gather them:

  1. Required components (§8.1). “Any WIA-BIO-004 compliant system must include” data collection, quality control, statistical analysis, clinical correlation and reporting. These are functional capabilities, not code modules; a laboratory information system plus a statistics service can satisfy them together.
  2. Data-format records (§7.1, §7.2, §3.3.2, §5.3.1). The measurement, validation-study, reference-range and performance-report JSON structures described in Chapter 3. A conformant producer emits them with the "standard": "WIA-BIO-004" envelope and a declared version; a conformant consumer accepts them.
  3. Error codes (§8.3). The spec defines six codes, B001 (insufficient sample size) to B006 (low statistical power); the SDK extends the set to B010. A system that reports failures must use these codes with their stated meanings.
  4. Validation requirements (README). Seven requirements for any biomarker validated under the standard: minimum sample size, statistical power of at least 80 %, validation in at least two independent populations, blinded measurement, comparison with a reference standard, reproducibility (CV below 15 % for quantitative biomarkers, as stated in the README) and documented pre-analytical stability.

The first three are properties of software and data; the fourth is a property of a biomarker study. They must be kept apart in a claim. A vendor’s statistics engine can be fully conformant while the biomarker a customer validates with it fails requirement 3 because only one cohort was ever recruited. Conversely, a well-designed study run on non-conformant software may still produce correct numbers; it simply cannot prove that it did.

8.1.2 What v1.0 does not define

Equally important is what the documents do not say. v1.0 defines no numbered conformance levels, no tiers and no pass-rate thresholds. Tiered schemes of the “bronze / silver / gold” kind, familiar from other certification programmes, have no basis in v1.0, and this book does not invent one. The README links a certification site, but the v1.0 documents do not describe a certification procedure, an accredited assessor or a test suite. The phase annexes describe a non-normative idea of an evidence package and a profile manifest (§8.4 below), and refer to vector directories that are not present in the repository.

The practical consequence is that a conformance claim under v1.0 is a self-declaration with evidence. The strongest form of it lists every requirement, marks each one as supported, partially supported or excluded, and attaches reproducible test results. The rest of this chapter shows how to build that.

8.1.3 A conformance checklist

The following table is the checklist used throughout the chapter. The identifiers (C-01 …) are this book’s, introduced so that test results and manifests can refer to a row; they are not numbered in the standard.

IDRequirement (source)EvidenceHow tested
C-01Standardised measurement capture (§8.1-1, §7.1)Sample §7.1 records produced by the systemSchema validation; field-by-field comparison with a known input
C-02Automated QC checks (§8.1-2)QC rule configuration; audit of flagged samplesFeed records with qualityFlags.hemolyzed = true; confirm exclusion or annotation
C-03ROC, sensitivity, specificity (§8.1-3, §4)Output of the statistics engineGolden vectors G-01 to G-07 (§8.2)
C-04Clinical correlation with outcomes (§8.1-4, §6)Correlation / survival analysis outputsReference data set with a known Spearman ρ or hazard ratio computed by an independent package
C-05Standardised performance report (§8.1-5, §5.3.1)Generated reportsInternal consistency: PPV/NPV must follow from Sens, Spec and the stated prevalence
C-06Record envelope and version (§7.1, §7.2)Every emitted recordstandard equals "WIA-BIO-004"; version is a semantic version
C-07Unit consistency (§7.1, Chapter 3)Validator behaviourNegative vector N-05 (value unit differs from reference-range unit)
C-08Temporal consistency (Chapter 3)Validator behaviourNegative vector N-06 (dateMeasured earlier than dateCollected)
C-09Error codes B001–B006 (§8.3)Error responsesNegative vectors N-01 to N-04 must return the documented codes
C-10Sample-size requirement (README 1)Study record: population.cases, controlsCheck against the stated minimum; note the README / SDK difference (§8.3)
C-11Independent cohorts (README 3)validation.validationCohorts[]At least two independent populations, each validation cohort listed with its own AUC (see §6.8)
C-12Blinding and reference standard (README 4–5)referenceStandard.method, protocol documentDocument review; cannot be verified from data alone
C-13Reproducibility and stability (README 6–7)Precision and stability reports (Chapter 5)Recompute CV from raw replicate data; compare with the acceptance limit the claim names
C-14Privacy controls (§9)De-identification and encryption configurationInspection; re-identification spot checks (Chapter 7)

Rows C-12 to C-14 are not automatable in the sense that C-03 is. They still belong in the checklist, because a claim that silently leaves them out overstates what was checked.

8.2 Building Test Vectors

A test vector is an input together with its expected output and a tolerance. Golden vectors exercise normal behaviour and must produce specific numbers; negative vectors are inputs that a conformant system must reject, with a specific error. Both kinds are needed. A statistics engine that returns plausible numbers for every input, including invalid ones, passes every golden vector and is still dangerous.

8.2.1 Golden vectors from the standard’s own examples

The cheapest golden vectors are the worked examples already printed in the specification, because anyone can check them by hand. Two need care. The §5.3.1 report example quotes PPV 86.0 % and NPV 84.5 %, which do not follow from its own sensitivity (82 %), specificity (88 %) and prevalence (0.50); the vector below uses the recomputed values. And Appendix A.1 gives only the counts at one threshold, not the underlying measurements, so to test ROC behaviour we need a full data set, which §8.2.2 supplies.

VectorInputExpected outputSource
G-01TP 75, FN 10Sens = 88.2 %§4.1.1
G-02TN 95, FP 5Spec = 95.0 %§4.1.2
G-03TP 42, FN 8, TN 45, FP 5Sens 84.0 %, Spec 90.0 %, PPV 89.4 %, NPV 84.9 %, Acc 87.0 %, LR+ 8.40, LR− 0.178, J 0.74Appendix A.1
G-04Sens 84 %, Spec 90 %, prevalence 0.10PPV 48.3 %, NPV 98.1 %§4.1.3 (Bayes form)
G-05Sens 82 %, Spec 88 %, prevalence 0.50PPV 87.2 %, NPV 83.0 %, LR+ 6.83, LR− 0.20§5.3.1, corrected
G-06AUC 0.87, n₁ = n₀ = 150Hanley–McNeil SE 0.0210; 95 % CI 0.829–0.911§10.3
G-07Synthetic set S-100 (§8.2.2), cutoff 75Confusion matrix 42 / 8 / 45 / 5; exact AUC 0.9358; optimum J = 0.74This book

G-06 deserves a comment. The §7.2 validation-study example reports AUC 0.87 with a 95 % CI of 0.82–0.92 for 150 cases and 150 controls. The Hanley–McNeil formula printed in §10.3 gives 0.829–0.911 for the same inputs. The difference does not mean the example is wrong, since a bootstrap or DeLong interval can legitimately be wider, but it does mean a validator should not reject a CI merely because it differs from one formula. What a validator should require is that the record states how the interval was obtained, which v1.0 records have no field for. That gap reappears in §8.7.

8.2.2 A reproducible synthetic data set

To test ROC code we need individual values, not counts. The following set, called S-100 here, is constructed so that the cutoff “value ≥ 75 is positive” reproduces Appendix A.1’s counts exactly. It is entirely synthetic and describes no real assay; its virtue is that anyone can regenerate it in one line of any language.

S-100  (integers, inclusive ranges; label 1 = case, 0 = control)

cases    (50):  76, 77, …, 117   → 42 values, all ≥ 75  (TP at cutoff 75)
                60, 61, …, 67    →  8 values, all < 75  (FN)
controls (50):  30, 31, …, 74    → 45 values, all < 75  (TN)
                80, 81, …, 84    →  5 values, all ≥ 75  (FP)

Cutoff 75      : TP 42  FN 8  TN 45  FP 5      (= Appendix A.1)
Ties           : case values 80–84 equal control values 80–84 (5 tied pairs)
Exact AUC      : P(case > control) + ½·P(tie) over 2,500 pairs = 0.9358
Youden optimum : J = 0.84 + 0.90 − 1 = 0.74, first reached at observed value 76
Hanley–McNeil  : SE(0.9358; 50, 50) = 0.0258 → 95 % CI 0.885–0.986

Two properties make S-100 useful beyond G-03. It contains ties between groups, which is where ROC implementations most often go wrong, and its cutoff of 75 is not one of the observed values, so it also tests how an implementation labels the optimal threshold. An engine that reports the optimum as 76 (the smallest observed value that yields the same classification) and one that reports 75 are both correct, provided the reported sensitivity and specificity are 0.84 and 0.90. A test should therefore assert on the operating point, not on the threshold label.

8.2.3 Negative vectors

Negative vectors pin down the error behaviour promised in §8.3 and the record-level rules from Chapter 3. The “expected” column is what a conformant implementation should do; §8.3 reports what v1.0 actually does.

VectorInputExpected behaviour
N-01S-100 with the last value of predictions removed (label/value length mismatch)Reject with B002 (invalid measurement values)
N-02S-100 with every label set to 1Reject with B002: both classes are needed
N-03First 99 values of S-100Reject with B001 (insufficient sample size)
N-04Validation study with no referenceStandardReject with B003 (missing reference standard)
N-05§7.1 CRP record with unit: "mg/dL" but referenceRange.unit: "mg/L"Reject or normalise explicitly via UCUM; never compare raw numbers
N-06§7.1 record with dateMeasured earlier than dateCollectedReject as a data-quality error
N-07Measurements whose diseaseStatus is the string "healthy" instead of a booleanReject; strings must never be coerced to “diseased”
N-08Request for a 90 % confidence intervalUse z = 1.645, or reject the unsupported level; never silently use another z

N-04 has no counterpart in the SDK, whose inputs contain no reference-standard field; it applies to systems that accept §7.2 records. N-05 and N-06 are checks that belong in a record validator in front of the statistics engine rather than in the engine itself.

8.2.4 Tolerances and rounding

Expected values need tolerances, and the tolerances should reflect how the numbers are printed, not how generous the tester feels. The standard prints percentages to one decimal and likelihood ratios to two, so a sensible rule is: counts must match exactly; percentages within ±0.05 percentage points of the unrounded value; ratios and AUC within ±0.0005. A tolerance is not an allowance for a different formula. If an implementation uses a different CI method, the correct response is to record the method, not to widen the tolerance until the vector passes.

8.3 Testing the SDK against the Specification

We ran the vectors above against the v1.0 SDK. The results fall into three groups: things that pass, behaviours that are correct but easy to misuse (and must be documented in any claim), and outright defects.

8.3.1 Building the package

The first test of any SDK is whether it builds. The v1.0 sources do not type-check with TypeScript 5.9: types.ts ends with an export type { … } block and an export { BIOMARKER_CONSTANTS, BiomarkerErrorCode, BiomarkerError } line that re-export names already exported at their declarations, and index.ts repeats export { BiomarkerDataSDK } for a class declared with export class. The compiler reports TS2323 (“cannot redeclare exported variable”) and TS2484 (“export declaration conflicts with exported declaration”). Removing the redundant blocks, which changes no behaviour, lets the package compile. Everything below was measured on that corrected copy. The package also declares a Jest configuration but ships no test files, so there is no in-repository evidence of the kind this chapter builds.

8.3.2 What passes

With a fixed cutoff of 75 and the default prevalence of 0.5, validateBiomarker on S-100 returns the confusion matrix 42 / 45 / 5 / 8, sensitivity 84, specificity 90, accuracy 87, PPV 89.36, NPV 84.91, LR+ 8.40 and LR− 0.178, which matches G-03. N-01 and N-02 throw BiomarkerError with code B002; N-03 throws B001. The Hanley–McNeil standard error is implemented exactly as printed in §10.3. The Youden optimum on the automatic grid is reported at value 76 with sensitivity 0.84, specificity 0.90 and J = 0.74, the operating point G-07 expects.

8.3.3 Correct but easy to misuse

Two scales. DiagnosticPerformance reports sensitivity, specificity, PPV, NPV and accuracy in percent, while every ROCPoint and optimalThreshold reports sensitivity and specificity as fractions from 0 to 1. A test that asserts 84 against optimalThreshold.sensitivity fails; a dashboard that mixes the two shows 0.84 % sensitivity. Conformance tests should assert the scale explicitly.

Prevalence-based predictive values. The SDK never computes PPV as TP/(TP + FP). It always applies the Bayes form with the prevalence argument, default 0.5. On S-100 the two agree only because the sample is balanced. With prevalence: 0.1 the same data give PPV 48.3 % and NPV 98.1 % (G-04), while the count-based values remain 89.4 % and 84.9 %. Both are legitimate numbers answering different questions; a report must say which one it contains and at what prevalence.

The 100-sample floor. The README asks for “100 cases and 100 controls”; the SDK constant MIN_VALIDATION_SAMPLES is 100 in total. A study with 60 cases and 40 controls passes the SDK and fails the README. Also, because validateBiomarker always calls calculateROC internally, a 99-sample input throws B001 instead of returning a result with errors[] filled in; callers expecting the errors array never see it.

8.3.4 Defects

AUC under-estimation with ties in false-positive rate. calculateAUCTrapezoidal sorts ROC points by FPR only. When several points share an FPR, they stay in threshold order, which means descending TPR, and the trapezoid joins the wrong corners of each vertical run. On S-100 the SDK returns AUC 0.9294 where the exact value is 0.9358. Adding TPR as a secondary sort key (ascending) restores 0.9358. The error is not caused by the tied values alone: after removing the ties (shifting the five control values to 80.5–84.5), the SDK still reports 0.9284 against an exact 0.9348.

User thresholds without end points. When the caller supplies thresholds, the SDK evaluates only those, and does not add the (0, 0) and (1, 1) corners. With thresholds: [70, 75, 80] on S-100 the reported AUC is 0.08, not an approximation of 0.94 but a meaningless number, because the only area integrated is the thin strip between three points. The README’s examples pass short threshold lists exactly like this. A conformant engine must either always add the corners or refuse to report an AUC from a partial grid.

Confidence level. Any confidenceLevel other than 0.95 silently uses z = 2.576. A request for a 90 % interval on S-100 therefore returns 0.860–0.999, which is a 99 % interval and wider than the correct 90 % interval of 0.885–0.974 at the SDK’s own AUC. The source comment calls the method a “DeLong approximation”; it is the Hanley–McNeil formula, and a claim must not describe it as DeLong.

No unit check. validateBiomarker accepted S-100 with half of the measurements labelled ug/L and the other half ng/mL without a warning. The two happen to be the same quantity, but the SDK would have done the same for mg/L. Unit consistency (C-07) must therefore be enforced by a record validator before data reach the SDK.

String disease status. The SDK splits groups with strict comparisons (=== true, === false) but builds its labels with a truthiness test, so any non-empty string counts as diseased. If every status is a string, both groups come out empty and the call ends with B002, which is safe for the wrong reason. If the input mixes booleans and strings, the result is silently wrong: marking ten of the S-100 controls "healthy" instead of false turns them into cases, the confusion matrix becomes 42 / 35 / 5 / 18, sensitivity drops to 70 %, and the call still returns isValid: true with an empty errors array.

8.3.5 The CLI

The v1.0 CLI parses its options (for calc-roc it also checks that the data file exists and counts its rows), then prints fixed demonstration output. Given a one-line CSV file, calc-roc reports “Total samples: 1” followed by a confusion matrix of 75 / 85 / 15 / 25 and an AUC of 0.85. The CLI has no --version flag, although the README’s quick-start uses it; the command is wia-bio-004 version. Until the CLI reads its input, it cannot be part of a conformance claim, and no number it prints should appear in evidence.

8.3.6 Recording the results

FindingVectorExpectedv1.0 resultStatus
F-01 Build—CompilesTS2323 / TS2484Fail (fixed in scratch copy)
F-02 Confusion-matrix metricsG-0384 / 90 / 89.4 / 84.9SamePass
F-03 Exact AUCG-070.93580.9294Fail
F-04 AUC from user gridG-07 with [70, 75, 80]Corners added, or refused0.08Fail
F-05 90 % CIN-08z = 1.645z = 2.576Fail
F-06 Error codesN-01 to N-03B002 / B002 / B001Same (thrown)Pass
F-07 Unit checkN-05RejectAccepted silentlyFail (engine); validator needed
F-08 String statusN-07Reject explicitlyAll strings: B002; mixed: accepted and miscountedFail
F-09 CLI outputanyDerived from inputFixed numbersFail

A test suite that encodes these findings can be short. The sketch below uses Jest, which the package already configures; the helper codeOf returns the error code thrown by a call, so that assertions are about codes rather than message text.

import { BiomarkerDataSDK, BiomarkerErrorCode } from '@wia/bio-004';

const range = (a: number, b: number) =>
  Array.from({ length: b - a + 1 }, (_, i) => a + i);

// Synthetic set S-100 (section 8.2.2)
const cases = [...range(76, 117), ...range(60, 67)];
const controls = [...range(30, 74), ...range(80, 84)];
const values = [...cases, ...controls];
const labels = [...cases.map(() => 1), ...controls.map(() => 0)];

const codeOf = (fn: () => unknown): string | undefined => {
  try { fn(); return undefined; } catch (e: any) { return e.code; }
};

const sdk = new BiomarkerDataSDK();
const measurements = values.map((v, i) => ({
  value: v, unit: 'ng/mL', diseaseStatus: labels[i] === 1,
}));

describe('golden vector G-03 / G-07', () => {
  const res = sdk.validateBiomarker({
    biomarkerType: 'protein',
    measurements,
    referenceRange: { min: 0, max: 75, unit: 'ng/mL' },
    cutoffThreshold: 75,
    prevalence: 0.5,
  });

  test('confusion matrix', () => {
    expect(res.performance.confusionMatrix).toEqual({ tp: 42, tn: 45, fp: 5, fn: 8 });
  });
  test('percent scale', () => {
    expect(res.performance.sensitivity).toBeCloseTo(84.0, 1);
    expect(res.performance.npv).toBeCloseTo(84.9, 1);
  });
  test('operating point uses fractions', () => {
    expect(res.rocAnalysis.optimalThreshold.sensitivity).toBeCloseTo(0.84, 4);
    expect(res.rocAnalysis.optimalThreshold.youdenIndex).toBeCloseTo(0.74, 4);
  });
  test('exact AUC (fails on v1.0: 0.9294)', () => {
    expect(res.rocAnalysis.auc).toBeCloseTo(0.9358, 3);
  });
});

describe('negative vectors', () => {
  test('N-01 length mismatch', () => {
    expect(codeOf(() => sdk.calculateROC({ trueLabels: labels, predictions: values.slice(1) })))
      .toBe(BiomarkerErrorCode.INVALID_MEASUREMENTS);
  });
  test('N-03 fewer than 100 samples', () => {
    expect(codeOf(() => sdk.calculateROC({
      trueLabels: labels.slice(0, 99), predictions: values.slice(0, 99),
    }))).toBe(BiomarkerErrorCode.INSUFFICIENT_SAMPLES);
  });
});

Note that the test for F-03 is written to state the correct value and to fail on v1.0. A suite that encodes the current wrong value “so the build stays green” would turn a defect into a specification.

8.4 Evidence Package and Profile Manifest

The phase annexes sketch, in non-normative terms, what a conformance claim should be delivered as: an evidence package and a profile manifest. The idea is sound and independent of the annexes’ boilerplate details, so we describe it generically.

The evidence package is a single archive that lets an assessor re-run the claim without contacting the vendor. It contains a software bill of materials (SBOM) in a recognised format such as SPDX or CycloneDX, so that the exact dependency versions are known; the API description of any network interface; the test vectors used, with their expected values; the pass/fail matrix produced by running them, including skipped tests and the reason for each skip; and a manifest listing every file with its hash, signed by the vendor. Negative results belong in the package with the same prominence as positive ones. A skipped test with no recorded reason should be read as a failure.

The profile manifest states which requirements are claimed. Each checklist row gets one of three statuses: supported, partial (with a reason) or excluded (with a justification). This avoids the most common way conformance claims mislead, which is by implying full coverage when only the easy parts were tested. The example below uses the checklist identifiers from §8.1.3 and the findings from §8.3; the field names are this book’s suggestion, not a schema defined by v1.0.

{
  "standard": "WIA-BIO-004",
  "standardVersion": "1.0.0",
  "implementation": { "name": "example-biomarker-engine", "version": "2.3.1" },
  "requirements": [
    { "id": "C-01", "status": "supported" },
    { "id": "C-03", "status": "partial",
      "reason": "AUC uses a corrected trapezoid sort; CI method is Hanley-McNeil only" },
    { "id": "C-07", "status": "supported",
      "reason": "Enforced by record validator ahead of statistics engine" },
    { "id": "C-12", "status": "excluded",
      "justification": "Study-level requirement; outside software scope" }
  ],
  "evidence": {
    "sbom": "sbom.spdx.json",
    "vectors": "vectors/",
    "results": "results/matrix.json"
  },
  "signature": "<detached signature over the manifest hash>"
}

Everything in this example is illustrative: the implementation name, version and statuses describe a hypothetical engine that has fixed F-03, not any real product.

8.5 Versioning

Every WIA-BIO-004 record carries "version": "1.0.0", and the phase annexes commit the standard to Semantic Versioning 2.0.0: a major increment for backward-incompatible changes to normative requirements, a minor increment for new requirements that do not break existing implementations, and a patch increment for editorial changes. Applied to biomarker records, that rule has concrete consequences, summarised below. The examples are drawn from inconsistencies discussed in Chapter 3 and are hypothetical future changes, not announced ones.

ChangeExampleIncrement
New optional fieldAdding aucCIMethod to rocAnalysisMinor
New value in an open vocabularyAdding a sampleType such as dried blood spotMinor
Renaming or retyping a fieldUnifying min/max with lowerLimit/upperLimitMajor
Removing an enum valueRetiring diagnostic_validation in favour of the SDK’s studyType setMajor
Changing a default that alters resultsChanging the PPV default from prevalence-based to count-basedMajor
Correcting an example or typoFixing the §5.3.1 PPV/NPV example valuesPatch

Three version numbers are in play and must not be confused: the version of the standard a record conforms to (the record’s version field), the version of the SDK that produced a result (getVersion(), also copied into BiomarkerReport.metadata.version), and the version of the implementation making the claim. In v1.0 all three read “1.0.0”, which hides the distinction. A consumer should key its parsing on the record’s version, not on the SDK version, and a manifest should list all three.

8.6 Regulatory Alignment

§10.2 lists five regulatory and laboratory-standards documents. Conformance to WIA-BIO-004 is not compliance with any of them, and none of them certifies WIA-BIO-004. What the standard can do is produce records that make the evidence those documents ask for easier to assemble and to audit. The table corrects two details in §10.2: the EMA guideline has been replaced by ICH M10, and C28-A3 was published in 2008, with the 2010 date belonging to its corrected reprint C28-A3c.

DocumentScopeWIA-BIO-004 evidence that feeds itCaution
FDA, Bioanalytical Method Validation (2018)Validation of methods measuring drugs and metabolites in study samplesPrecision, accuracy, LOD/LOQ and stability records (Chapter 5)Written for drug assays; biomarker assays call for a fit-for-purpose approach tied to the context of use
EMA, Guideline on bioanalytical method validation (2011)As above, European UnionAs aboveSuperseded by ICH M10 (2022), whose scope is drug and metabolite concentrations, not biomarkers
CLSI EP09c (2018)Measurement procedure comparison and bias estimation with patient samplesMethod-comparison data; measurementMethod and bias resultsNeeds raw paired results, not only summary statistics
CLSI EP15-A3 (2014)User verification of precision and estimation of biasReplicate data and CV recordsA verification in the user’s laboratory, not a full validation
CLSI C28-A3 (2008; C28-A3c 2010)Defining, establishing and verifying reference intervals§3.3.2 reference-range records and their partitionsRecord the number of reference individuals and the method (parametric or non-parametric)

Two other frameworks are not in §10.2 but are what assessors usually ask about. For the diagnostic-accuracy study itself, the STARD reporting guideline lists what a publication must disclose: participant flow, the reference standard, blinding, and the handling of indeterminate results. Most of these map onto §7.2 fields, and the remainder, such as indeterminate results, have no field in v1.0. For the laboratory that runs the assay, ISO 15189 sets requirements for quality and competence, including internal QC and external quality assessment, the processes that §8.1’s “quality control” component and Chapter 5’s Westgard rules implement. The applicable in-vitro diagnostic regulation, for example the EU IVDR or the US CLIA framework, depends on jurisdiction and intended use, and is outside what any data standard can decide.

8.7 Open Issues and Future Directions

8.7.1 Housekeeping for the next revision

Writing this book surfaced a set of internal inconsistencies. None of them is hard to fix, and each makes conformance testing harder until it is fixed:

8.7.2 Where biomarker data are heading

Beyond repairs, several developments will test the standard’s model of “one analyte, one value, one cutoff”.

Multi-omics composite scores. Many modern biomarkers are weighted combinations of proteins, transcripts and metabolites. The multi-analyte definition in Chapter 3 already has analytes[] and an algorithm block, but conformance testing needs more: the weights, the software version that computed the score, and a locked model so that the validated score is the one deployed. Chapter 6’s warnings about over-fitting apply with extra force.

Liquid biopsy and circulating tumour DNA. Results such as variant allele fraction from cell-free DNA sit between the genetic and quantitative worlds. They need the VCF-level provenance of Chapter 3 together with the pre-analytical controls of Chapter 5, since collection tubes and processing delay affect results, and their limits of detection are expressed differently from those of an immunoassay.

Digital biomarkers. Measures derived from wearables, smartphones or voice recordings are continuous streams processed by algorithms. The standard’s measurementMethod and laboratoryID fields do not describe a sensor, a firmware version or a feature-extraction pipeline, and analytical validation concepts such as LOD translate only partially.

Machine-learning-derived biomarkers. When the “value” is a model output, discrimination (AUC) is not enough. Calibration, meaning whether a predicted 30 % risk corresponds to an observed 30 % event rate, must be reported, together with the training population and a plan for monitoring drift. Reporting guidance for clinical prediction models, such as TRIPOD and its extensions for AI, is the natural companion here.

FHIR Genomics. Chapter 3 mapped quantitative results to FHIR Observation. For genetic biomarkers the HL7 FHIR Genomics Reporting implementation guide defines richer profiles for variants and their interpretation. Aligning WIA-BIO-004’s genetic records with those profiles would spare integrators a second mapping.

Each of these directions raises the same question this chapter started with: how would someone else check that it was done right? A standard that answers that question with shared vectors, disclosed methods and honest manifests will be useful long after the particular numbers in v1.0 have been revised.

Chapter Summary

Conformance to WIA-BIO-004 v1.0 is assembled from four sources: the five required components, the data records, the error codes and the seven validation requirements. The first three can be tested mechanically, and the arithmetic core of the standard makes exact test vectors possible. Running such vectors against the reference SDK confirms its confusion-matrix arithmetic and error codes, and exposes defects in ROC integration, partial threshold grids, confidence-level handling and unit checking, as well as a build error and a CLI that does not yet read data. An evidence package with a signed profile manifest turns those results into a claim that others can verify. Semantic versioning, alignment with the listed regulatory and laboratory guidances, and a short list of housekeeping repairs complete the path to the next revision. Future biomarkers, whether composite, circulating, digital or learned, will stretch the standard’s data model, but the discipline of checkable claims carries over unchanged.

Review Questions

  1. Which of the fourteen checklist rows in §8.1.3 can a software vendor claim on its own, and which require evidence from the biomarker study? Justify the split for C-10 and C-11.
  2. Regenerate S-100 and compute the exact AUC by counting case–control pairs. Then compute the trapezoidal AUC after sorting ROC points by FPR only, and after sorting by FPR and TPR. Explain the difference.
  3. A report states PPV 89.4 % for a biomarker intended for population screening at an expected prevalence of 10 %. Using G-04, explain what is wrong and what the report must disclose.
  4. Write a negative vector for B003 in terms of a §7.2 record, and explain why it cannot be tested against the v1.0 SDK directly.
  5. Classify each of the following as a major, minor or patch change: adding aucCIMethod; renaming referenceRange.min; correcting the §5.3.1 example.
  6. ICH M10 is written for drug and metabolite assays, not biomarker assays. Which WIA-BIO-004 analytical-validation records would you still present to an assessor, and how would you justify their acceptance limits?