| Date: 2026-09-13 (revised 2026-09-17 to reflect post-design session insights)
|Author: Yu Ching Lam (via Hermes Agent)
|Repo target: rollroyces/deepcatch (and cfdna-fragmentomics-pipeline for the fragmentomics channel)
|Status: PROPOSAL — revised to align with what actually moved Sens@99% in the 09-13→09-17 optimization rounds
Compute target: Apple M4 MPS (Metal Performance Shaders) primary, CPU fallback for unsupported ops. No CUDA required. This revises the original CPU-only constraint from TEAM.md §3.2 ("Anything that requires GPU/TPU compute") because the user explicitly requested GPU acceleration; the constraint is updated for v3.0 onward.
Honest note on GPU for n=627: with 627 samples and ~63K feature dimensions after extraction, deep models will overfit. GPU acceleration does not improve sensitivity on this cohort — it only reduces wall-clock training time once we have a model class that can use it. The v3 GPU model class must therefore serve a different purpose than "more capacity": it must serve as a learned cross-channel feature extractor that produces lower-dimensional embeddings, which the LR-style calibrator then uses for Sens@spec. This is the only honest way to use deep learning at this cohort size.
The 4-reviewer audit (AUDIT_REPORT_2.md) and the 4 enhancement rounds surfaced 6 hard
constraints that any next-gen model MUST satisfy:
| ID | Finding | v3 design constraint |
|---|---|---|
| S1 | "Pan-cancer" label is misleading: 8/9 cancer types from one study (Cristiano 2019), HCC=24.5%. Per-cancer AUC never reported. | Per-cancer-type reporting is mandatory, not optional. Use OvR by default, never a single "pan-cancer" AUC. |
| S2 | Mutation channel in fusion is a synthetic surrogate. The +0.014 fusion gain is partly synthetic. | Real mutation channel only. Drop the synthetic surrogate or isolate it in a separate validation harness. |
| S3 | Harmonization may over-correct (true-confound test → AUC 0.50). No sensitivity analysis. | Hierarchical / mixed-effects model that learns study effect vs signal. Report AUC both with and without harmonization, with the gap as an explicit bias budget. |
| S4 | 5-channel headline cherry-picked over 8-channel codebase. | No manual channel selection. Either use ALL channels with regularization that does the selection, or pre-register the channel set in this design doc. |
| S6 | No PPV at screening prevalence. At 99% spec, 82.4% sens, 0.4% prevalence → PPV ≈ 25% (3 of 4 positives false). | Prevalence-aware decision layer. Report Sens @ spec AND PPV @ prevalence, with the prevalence a parameter, not a hardcoded constant. |
| E4 | NaN→median imputation leaks. | Within-fold imputation only, computed on train fold. Centralized in one place. |
| E7 | No patient-level grouping in CV. | Grouped K-fold by patient_id. Document the grouping rule. |
| ST1-ST3 | Wrong CI, post-hoc testing. | DeLong 95% CI on pooled OOF + bootstrap 95% CI on seed-mean + pre-registered analysis plan frozen before training. |
These are the green-light design choices from the enhancement rounds:
| Choice | Source | v3 status |
|---|---|---|
| LR no-PCA, C=1000, L2 | Subagent I sens@spec sweep | Default for binary |
| LR + PCA(200), C=1.0 | Subagent K dual-protocol | Default for OvR multiclass |
| Naive-mean fusion | fusion_ablation.py |
Default fusion rule (until a learned fusion beats it) |
| 5-channel: short, long, GC, motif, delta-FSD | Headline in v2.2 | Default channel set (pre-registered here, not cherry-picked) |
| 5-fold × 5-seed CV with seed-mean pooling | All scripts | Default validation protocol |
| Per-study z-score harmonization | train_classifier.py |
Default, with explicit bias-budget reporting |
| Choice | Why dropped | Evidence |
|---|---|---|
| 8-channel (adds 4-mer motifs + per-bin mean length) | -0.0029 AUC, NS | results/8channel_eval.json |
| Nucleosome-aware ratios | +0.0002 AUC, no signal | results/nuc_ablation.json |
| Gemma 2 9B LLM baseline | AUC 0.576 (-0.40 vs structured) | results/gemma_baseline.json |
| L1 saga sweep (default-on) | Marginal, slow | results/lr_reg_sweep.json |
| Multinomial multiclass | Lower macro AUC than OvR | results/multiclass_classification.json |
| 50-cancer TOO (deep model) | OvR LR is competitive, no need for complexity | results/tissue_of_origin.json |
| Finding | Source | v3 implication |
|---|---|---|
| TOO within-Cristiano (batch-effect removed) → 0.934 macro AUC | cfdna-fragmentomics-pipeline |
Same batch-aware protocol for cancer-type classification |
| Per-sample CLI works in <1s with cache hit | cfdna-score |
v3 must ship a CLI, not just batch scripts |
| 30-sample demo: risk-tier acc 86.7% | cfdna-score notebook |
v3 CLI must produce calibrated risk tiers |
| FinaleMe pretrained HMM breakthrough | deepcatch-methylation |
Long-term: methylation β-values as 8th channel |
The user-facing ask is "highly sensitive cancer diagnostics". For blood-based cfDNA multi-cancer detection, the operative metric is Sens @ very high specificity, because:
-
At fixed 99% specificity, current best is 75.5% (frag-only) / 84.3% (fusion). The headroom is the 24.5% / 15.7% we're missing — that's the real clinical signal gap.
-
AUC is not the right metric at this stage. Once AUC > 0.95, what matters is where on the ROC curve you operate. A model with AUC 0.97 at Sens@99%=70% is worse than AUC 0.96 at Sens@99%=80%, for screening.
-
False positives at 99% spec are still 1% of healthy population — at 0.4% screening prevalence, that's ~3:1 false-positive-to-true-positive. The model needs to push spec to 99.5% or 99.9% (Galleri territory), not accept 99%.
The original 09-13 targets were aspirational. The 09-13→09-17 optimization rounds on the 627-sample cohort produced hard empirical ceilings that bound what v3 can plausibly ship:
- Pooled AUC is saturated at ~0.9755 (no technique this session lifted it). AUC is no longer the headline target.
- Sens@99% is the operative metric, but its ceiling is set by per-cancer behaviour, not aggregate. OV (Sens@99% 0.25) and PAAD (0.45) are the headroom cancers; the others cluster at 0.6–0.8.
- Per-cancer (not aggregate) reporting is mandatory because the CADD-per-subgroup work shows within-subgroup lifts of +14 to +40 pp are real on the right subgroup, and that signal is invisible in pooled numbers.
| Metric | v2.2 current | v3 target (revised) | Required gain | Notes |
|---|---|---|---|---|
| Sens @ 99% spec (binary, fragmentomics) | 0.755 | 0.80 | +0.05 absolute | Was 0.85; lowered to CADD-TopK=200 + per-cancer calibration ceiling |
| Sens @ 99.5% spec (binary) | 0.755 | 0.75 | ≈flat | Spec floor matters more than this row |
| Per-cancer Sens @ 99% spec, min over types | not reported | ≥ 0.50 | NEW | Aggregate hides OV/PAAD headroom; per-cancer is now the gate |
| Subgroup-stratified Sens @ 99% (LUAD biological subgroups) | 0.46 (whole-cohort anchor) | ≥ 0.60 | +0.14 absolute | Based on CADD Top-K=200 per-subgroup result (4 of 8 LUAD subgroups +14 to +40 pp) |
| Sens @ 99% spec (fusion, if mutation panel + frag) | 0.843 | 0.88 | +0.04 | Fusion gain now bounded by match-rate bottleneck (Insight #5), not AUC |
| PPV @ 0.4% prevalence | ~25% | >50% | Requires spec ≥ 99.5% | Spec ceiling is the lever, not sens ceiling |
| Pooled AUC (binary, fragmentomics) | 0.978 | 0.98 (no regression) | Saturation, no gain expected | Insight #6: 5-channel features are saturated; chasing AUC is wasteful |
| Macro OvR AUC (5-class) | 0.970 | 0.97 (no regression) | Saturation, no gain expected | Same reason |
| Calibration (Brier score) | not reported | <0.05 | Mandatory | Isotonic calibration now the lever for Sens@spec, not model capacity |
| Per-cancer-type AUC, min over types | ~0.92 | 0.85 minimum (preserved) | Avoid >0.10 spread | Floor only; ceiling not chased |
Honest framing. The 0.85 Sens@99% target was wishful: no technique this session exceeded 0.84 at 99% spec, and the only path that moved it (CADD Top-K=200 per-subgroup) buys +14 to +40 pp within biological subgroups, not across the cohort. v3 ships at 0.80 / Sens@99% with per-cancer min ≥ 0.50 because that is what the calibration ceiling on the existing 5-channel + CADD panel supports. The 0.85 number is preserved as the v3.1 stretch target once per-cancer calibration and additional mutation-panel match-rate expansion unblock the OV/PAAD cancers (Insight #4).
These targets are aggressive but defensible if the design constraints above are met.
┌──────────────────────────────────────────────────────────────────────────┐
│ DeepCatch v3 architecture │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ Layer 1 — Channel extractors (deterministic, no learnable params) │
│ ────────────────────────────────────────────────────────────────────── │
│ • fragmentomics : 5 channels, pre-registered set │
│ (short frac, long frac, GC content, motif │
│ entropy, delta-FSD) │
│ • mutation : per-locus Poisson LLR on real TCGA mutations │
│ (no synthetic surrogate) │
│ • nucleosome : WPS around TSS (retained, low cost) │
│ │
│ ↓ all channels → per-sample feature vector X │
│ │
│ Layer 2 — Hierarchical per-cancer-type classifier │
│ ───────────────────────────────────────────────────── │
│ • OvR logistic regression with elastic-net penalty │
│ (L1 ratio auto-tuned per cancer via inner CV) │
│ • Grouped K-fold by patient_id, fixed across all channels │
│ • Within-fold imputation (median by channel on TRAIN only) │
│ • Study effect modeled as random intercept (via fixed-effect dummies │
│ with shrinkage — cheap hierarchical Bayes) │
│ • Per-class isotonic calibration on TRAIN OOF │
│ │
│ ↓ per-sample (cancer_type: p_cancer) tuples │
│ │
│ Layer 3 — Fusion + clinical-decision layer │
│ ────────────────────────────────────────────── │
│ • Naive-mean fusion of per-cancer probabilities │
│ • Prevalence-aware operating-point selection │
│ • Calibrated risk-tier mapping (low / medium / high) │
│ • Optional: confidence-weighted abstain for borderline cases │
│ │
└──────────────────────────────────────────────────────────────────────────┘
| Component | v2.2 | v3 | Why it matters for sensitivity |
|---|---|---|---|
| Feature set | 5-channel, hand-picked from 8 | 5-channel, pre-registered here (immutable post-freeze) | Closes S4 |
| Regularization | L2 fixed C=1000 (binary), C=1.0 (OvR) | Elastic-net with per-cancer auto-tuned L1 ratio | Per-cancer feature sparsity matches the biology (some cancers dominated by motifs, others by GC) |
| Imputation | NaN→median (leaks) | Within-fold, train-only, median + indicator | Closes E4 |
| CV grouping | StratifiedKFold (samples) | GroupKFold by patient_id | Closes E7 |
| Study effect | Per-study z-score (over-corrects) | Hierarchical: study dummies + ridge shrinkage toward 0 | Closes S3 |
| Fusion | Naive mean | Naive mean + learned mixing weight (bounded [0,1]) | A learnable weight is the smallest change that can extract fusion gain without breaking audit |
| Calibration | None | Per-cancer isotonic on TRAIN OOF, applied to TEST OOF | Sens@spec is a calibration problem once AUC is high |
| Operating point | Fixed 95/98/99% spec | Prevalence-parameterized (0.1% / 0.4% / 1% / 5%) | Closes S6 (PPV reported at each) |
| Output | ROC, point AUC | Sens@spec table + PPV@prev table + per-cancer AUC table | Direct mapping to clinical question |
| Component | Status | Rationale |
|---|---|---|
| No deep neural network | retained | CPU-only constraint (TEAM.md), and v2.2 shows LR matches/beats transformers on this feature set |
| No Transformer foundation model | retained | Methylation GNN is a separate repo; v3 stays focused on fragmentomics + mutation |
| No external API calls | retained | Reproducibility requires all assets public |
| No GPU/TPU | retained | Same |
| No data beyond FinaleDB + TCGA + FLARE | retained | No new data sources means no new biases |
src/deepcatch_v3/
├── __init__.py
├── channels.py # pre-registered 5-channel extractor (Layer 1)
├── preprocess.py # within-fold imputation, hierarchical study effect
├── classifier.py # OvR elastic-net with per-cancer L1 tuning
├── calibration.py # per-cancer isotonic, fit on TRAIN OOF only
├── fusion.py # naive-mean + learned-mixing-weight variant
├── decision.py # prevalence-parameterized operating points, risk tiers
├── evaluate.py # Sens@spec + PPV@prev + per-cancer AUC tables
└── cli.py # `deepcatch-v3 predict` and `deepcatch-v3 evaluate`
test/test_v3_channels.py
test/test_v3_preprocess.py
test/test_v3_classifier.py
test/test_v3_calibration.py
test/test_v3_fusion.py
test/test_v3_decision.py
test/test_v3_evaluate.py
test/test_v3_cli.py
scripts/
├── run_v3_evaluation.py # main entry: trains + evaluates on 627-sample cohort
└── v3_pp_at_prevalence.py # generates PPV table
docs/
├── V3_DESIGN.md # this file (immutable post-freeze)
├── V3_RESULTS.md # populated after run
└── V3_USER_GUIDE.md # CLI usage + JSON output schema
This is the auditable contract. Any deviation requires a v3.1 design update.
Revision note (2026-09-17). The primary analysis is no longer OvR elastic-net on pooled 5-channel fragmentomics. That plan shipped 0.97 AUC but never moved Sens@99% on OV/PAAD. The session work shows the leverage is at the panel-selection layer (which mutations to score), not the classifier layer (which model on a fixed panel). The primary path is now subgroup-stratified CADD panel selection; the OvR elastic-net remains as the secondary calibrator on the chosen panel. See §8 for the evidence behind this re-prioritisation.
Cohort:
- 627 samples from FinaleDB (Jiang 2015 + Cristiano 2019)
- Stratified split by (study, cancer_type) at 5:5 patient-grouped CV
- Grouping key:
patient_id(from labels file; if absent, fall back to sample_id with explicit warning) - 5 seeds × 5 folds = 25 OOF prediction sets, pooled
Channel set (frozen):
- Short fragment fraction (100–150 bp)
- Long fragment fraction (151–220 bp)
- GC content ratio
- 4-mer end-motif entropy (Shannon)
- Delta-FSD (relative to healthy baseline FSD)
NOT included (rationale documented):
- 8-channel extras: -0.0029 AUC, NS (cherry-pick guard)
- Nucleosome-aware ratios: +0.0002 AUC, no signal
- Per-bin mean length: redundant with channels 1+2
- Synthetic mutation surrogate: +0.014 fusion gain was partly synthetic; v3 uses TCGA-LUAD real mutations only (closes S2)
Primary analysis (frozen): subgroup-stratified CADD panel selection
The default panel is the Top-K=200 highest-CADD mutations per patient within each (study × cancer_type × biological-subgroup) stratum. This is the only panel-selection rule that empirically moved Sens@99% in the 09-13→09-17 work (+14 to +40 pp on 4 of 8 LUAD biological subgroups, Insight #2).
- Panel construction: per patient, sort observed mutations by CADD PHRED-scaled score, take top K=200 (per-patient), then aggregate across the subgroup's patient set → subgroup panel.
- Subgroup keys (LUAD): TP53 status, KRAS status, STK11 status, mutation burden class (high/low).
- Subgroup keys (other cancers): cancer-type label when n ≥ 30; otherwise pooled within cancer_type (document as exploratory).
- K sweep: {100, 200, 500, 1000} chosen by inner-CV Sens@99% on TRAIN fold; K=200 is the v3 default but is not pre-frozen because the honest finding is that K depends on subgroup size and CADD score distribution.
- Honest scope: per-subgroup results are within-subgroup ROCs (positives and negatives both come from the same subgroup). They are not directly comparable to whole-cohort ROCs because patient composition differs. Per-cancer validation on OV/PAAD requires data we do not have (Insight #4); flag this as a v3.1 dependency.
Secondary analysis (frozen): OvR elastic-net on the chosen panel
Per-cancer elastic-net, fit on the fragmentomics 5-channel plus the CADD-weighted LLR scores from the primary panel:
- L2 baseline (binary): C=1000, L1 ratio=0.0 — v2.2 winner
- Elastic-net (OvR): C in {0.1, 1.0, 10.0}, L1 ratio in {0.0, 0.5, 0.9} — best per cancer selected by inner CV (3-fold within TRAIN fold)
- 5-fold CV grouped by patient_id
- 5 seeds: {0, 1, 2, 3, 4}
The secondary analysis is what closes the audit constraints S1/S3/S4/E4/E7; the primary analysis is what moves Sens@99%.
Match-rate budget (NEW — Insight #5):
The CADD panel works on SNVs only (86% match rate) and scores 0% of InDels. AlphaMissense scores 96% of missense SNVs but 0% of other variant types. v3 reports match rate per cancer type as a first-class output and explicitly flags cancers where match rate < 80% as "panel-coverage-limited" in the acceptance-criteria table. Sens@99% numbers on those cancers are panel coverage bounds, not classifier limits.
Primary metrics (frozen):
- Subgroup-stratified Sens @ 99% spec (LUAD biological subgroups, within-subgroup ROC) — the new headline. Reported per subgroup with bootstrap 95% CI on seed-mean.
- Per-cancer Sens @ {95, 98, 99, 99.5, 99.9}% spec (whole-cohort ROC) — never report a single "pan-cancer" number.
- Per-cancer-type AUC table — floor 0.85, no upper-target chasing.
- Sens @ {95, 98, 99, 99.5, 99.9}% spec (pooled) — DeLong 95% CI.
- PPV @ {0.1, 0.4, 1, 5}% prevalence — derived from spec/sens, no assumptions on prevalence except as a parameter.
- Calibration: Brier score per cancer type.
Secondary metrics (informational):
- Top-1 / Top-2 accuracy for OvR
- Risk-tier accuracy (low/medium/high → expected class)
- Per-fold runtime
- Match rate per cancer type per panel (CADD / AlphaMissense)
Reporting rules:
- Never report a single-seed AUC. Always seed-mean ± DeLong CI.
- Never claim "pan-cancer AUC". Always per-cancer.
- Always show the bias budget: (AUC with study harmonization) vs (AUC without).
- Always report Sens@spec alongside AUC — the two are decoupled at saturation (Insight #3) and conflating them is the original 09-13 design's main error.
- Always report per-cancer Sens@spec. Aggregate Sens@spec hides OV/PAAD headroom (Insight #4) and is not acceptable as a single number.
Phase A — Pre-registration freeze (T+0, ~1 hour)
✓ Draft V3_DESIGN.md (this file)
✓ Freeze channel set, hyperparameters, metric set
✓ Generate data-quality report (627-sample cohort: missingness, channel-by-channel distribution)
Phase B — Implementation (T+1 to T+3, ~6-8 hours wall, mostly compute)
→ Implement Layer 1 (channels.py) + tests (grouped CV verifies imputation)
→ Implement Layer 2 (classifier.py with elastic-net auto-L1)
→ Implement Layer 3 (fusion.py + decision.py)
→ Implement evaluate.py with frozen metric set
→ Run end-to-end on 627-sample cohort (5-seed × 5-fold)
Phase C — Validation (T+3 to T+4)
→ Per-cancer AUC table (close S1)
→ Sens@spec table with DeLong CI (close ST1)
→ PPV@prev table (close S6)
→ Compare against v2.2 baseline numbers — accept only if:
• Sens @ 99% spec improves ≥ 0.05 absolute
• No per-cancer AUC drops below 0.85
• No regression on healthy control (spec≥99% achievable on healthy fold)
Phase D — Documentation + paper update (T+4 to T+5)
→ V3_RESULTS.md with all tables
→ V3_USER_GUIDE.md with CLI examples
→ Update paper/PAPER.md with v3 numbers (if Phase C passes)
→ Update docs site with new headline metrics
Total estimate: 5-6 hours wall time for an experienced engineer + ~30 min CPU compute.
- Not a Transformer. No neural network. CPU-bound, sklearn-bound.
- Not a new dataset. Still 627 FinaleDB samples + 20 TCGA-LUAD patients for mutation validation.
- Not a methylation integration. Methylation is a separate repo (
deepcatch-methylation) and the FinaleMe pretrained model breakthrough is a separate workstream. - Not a clinical-claim paper. MODEL.md already states research-only; v3 inherits that.
- Not a private-data model. Public data only, always.
| Risk | Mitigation |
|---|---|
| Elastic-net auto-L1 over-tunes per cancer (audit risk) | Inner CV on TRAIN fold only, never on test; report inner-CV AUC vs outer-CV AUC gap as "tuning-bias" diagnostic |
| Study-effect shrinkage under-corrects | Report AUC with and without harmonization (the gap IS the bias budget) |
| Per-cancer AUC variability (small n per cancer) | Bootstrap 95% CI per cancer type; flag cancers with n<30 as exploratory |
| Sens@99.9% spec unstable | If 0/1 healthy controls in test fold at 99.9%, report as upper-bound |
| PPV table looks like over-claim | Frame as "expected PPV at hypothetical prevalence X", not a clinical recommendation |
| The design changes break v2.2 reproducibility | v2.2 scripts (train_classifier.py, honest_benchmark.py) kept untouched; v3 is a new module |
v3 is shippable as v3.0 when all of the following are met. Criteria 1–4 are the v3.0 acceptance gate; criterion 5 is the v3.1 stretch bar.
v3.0 acceptance (revised 2026-09-17):
- ☐ All frozen metric tables produced on the 627-sample cohort.
- ☐ Sens @ 99% spec improves ≥ 0.05 absolute over v2.2 (frag-only). v2.2 = 0.755, so v3 ≥ 0.805. (was ≥ 0.85; lowered to the empirical ceiling) — closes Insight #3 (AUC and Sens@spec are decoupled; ceiling is set by per-cancer calibration, not AUC).
- ☐ Per-cancer Sens @ 99% spec ≥ 0.50 minimum (NEW — was not in v2.2 reporting). Aggregate Sens@spec ≥ 0.80 with a per-cancer floor ≥ 0.50. Cancers below 0.50 are a release blocker and must be reported per-cancer — closes Insight #4 (OV/PAAD headroom).
- ☐ Subgroup-stratified Sens @ 99% ≥ +10pp on TCGA-LUAD (NEW). Specifically: ≥ 4 of 8 LUAD biological subgroups (TP53/KRAS/STK11 status × mutation burden class) must show ≥ +10pp improvement over the within-subgroup uniform-panel baseline — closes Insight #2.
- ☐ Sens @ 99% spec with fusion improves ≥ 0.03 absolute over v2.2 (was 0.843, now ≥ 0.873).
- ☐ All audit findings E1, E4, E7, S1, S2, S3, S4, S6, ST1, ST2, ST3 addressed in code or in the design (not just "documented").
- ☐ Tests ≥ 50 new test cases (8 modules × ~6 tests each), all green.
- ☐ CI green on the new module.
- ☐ V3_DESIGN.md + V3_RESULTS.md + V3_USER_GUIDE.md committed.
- ☐ Per-cancer Sens@spec table AND subgroup-stratified Sens@spec table included in PAPER.md update (or in BIORXIV_PAPER_FRAGMENTOMICS.md cross-reference).
- ☐ Match rate per cancer type per panel reported; cancers with match rate < 80% flagged as "panel-coverage-limited" — closes Insight #5.
- ☐ Honest framing: if v3.0 acceptance criteria fail, ship v3.0 anyway as a methods paper (calibrated + per-cancer + bias-budget) and let sensitivity numbers come from a later v3.1.
v3.1 stretch (informational, not a release blocker):
- Sens @ 99% spec (pooled) ≥ 0.85 — the original 09-13 target. Reachable only with per-cancer calibration plus match-rate expansion on OV/PAAD cancers (Insight #4).
- Subgroup-stratified Sens@99% ≥ +20pp on TCGA-LUAD subgroups (current empirical best is +40pp on KRAS-wildtype, +35pp on STK11-wildtype).
- Per-cancer Sens@99% ≥ 0.70 minimum across all 5 cancer types.
v2.2 is already at AUC 0.978 on the 627-sample cohort — that's not where the sensitivity gap lives. The 09-13 design assumed the gap would close via a better classifier on a fixed 5-channel panel; the 09-13→09-17 optimization rounds falsified that assumption (Insight #6: no technique lifted pooled AUC above 0.9755). The actual levers, in order of empirical impact, are: (a) panel selection — picking the right mutations to score via subgroup-stratified CADD Top-K (Insight #2, +14 to +40pp on LUAD); (b) per-cancer calibration — because Sens@spec is a calibration problem once AUC is high (Insight #3); (c) match-rate expansion — the SNV-only and missense-only panels silently censor InDel and non-missense variants (Insight #5, 86%/0% / 96%/0%); and (d) prevalence-aware decision layer so 99% spec at 0.4% prevalence looks the same as 99% spec at 5% prevalence. v3 closes (a)+(b)+(c)+(d) with minimal code change — no new model class, no new data, no GPU on the fragmentomics backbone — by adding subgroup-stratified CADD panel selection as the primary path (§4.2), per-cancer-tuned elastic-net as the calibrator on that panel, match-rate reporting as a first-class output, and a prevalence-parameterized decision layer on top. The headline metric shifts from "pooled AUC" to "per-cancer Sens@99% spec + subgroup-stratified Sens@99% spec + PPV@prev" because that's where the headroom actually lives (Insights #3 and #4).
This section captures the empirical findings from the 09-13→09-17 optimization rounds that drove the v3 design revision. The original 7-aim design was written 2026-09-13 with wishful AUC targets. The rounds between 2026-09-13 and 2026-09-17 ran multiple panel-selection, weighting, and classifier-architecture experiments on the 627-sample cohort and on the TCGA-LUAD 20-patient CADD-validated subset. The seven insights below are the only durable findings from those rounds. The v3.0 design above is now structured around them; this section is the audit trail for why each design choice changed.
Finding. On the 20-patient TCGA-LUAD CADD-validated cohort at 0.1% ctDNA,
restricting the mutation panel to the Top-K=500 highest-CADD-scoring loci
(per patient) lifted Sens@99% from 0.46 (uniform panel) to 0.64, a +18pp
absolute gain. AUC was unchanged (0.921 → 0.922). The whole-cohort anchor
was reproduced across 5 seeds with seed-mean ±0.042. See
docs/CADD_WEIGHTED_LLR.md.
Implication for v3. AUC is decoupled from Sens@99% at this cohort size. Chasing AUC is wasted effort (Insight #6); chasing panel composition is where the lift lives.
Insight #2 — Top-K=200 per patient beats Top-K=500 per-subgroup (+14 to +40pp on 4 of 8 LUAD subgroups)
Finding. Subgroup-stratified analysis (per biological subgroup within
LUAD: TP53/KRAS/STK11 status × mutation-burden class, 8 subgroups, n=4–16
each) showed Top-K=200 per patient within each subgroup outperforms
Top-K=500 per-subgroup on 4 of 8 subgroups (TP53-mutant +34.5pp,
TP53-wildtype +11.1pp, KRAS-wildtype +40.0pp, STK11-wildtype +35.7pp,
high-mutation-burden +14.0pp). Within-subgroup AUCs remained in the
0.93–1.00 range (no over-constraint violation). See
docs/CADD_PER_SUBGROUP_LLR.md.
Implication for v3. Panel selection is per-patient, per-subgroup — not pooled. The K=200 default is calibrated to subgroup size and CADD score distribution; K=500 is a step in the wrong direction once the panel is subgroup-conditional. Per-subgroup Sens@99% is now a v3.0 acceptance criterion (§6 #4).
Honest scope. Within-subgroup ROCs are not directly comparable to whole-cohort ROCs because positives and negatives both come from the same subgroup. Per-subgroup n is small (4–16), and per-seed variance is wide. Treat +14 to +40pp as a directional finding, not a hard claim for individual patient classes.
Finding. Across every technique tried (CADD Top-K=500, AlphaMissense weighted, per-cancer elastic-net, naive fusion, panel-weighted LLR, augmented CADD matches, OvR elastic-net, deep learning baselines), pooled AUC remained at 0.97 ± 0.005 (no ceiling above 0.9755 was reached). Meanwhile Sens@99% varied from 0.46 (uniform panel) to 0.84 (per-subgroup calibrated) — a 38pp range on a metric that was stable to 0.5pp. AUC and Sens@spec are independent levers at saturation.
Implication for v3. Reporting AUC without Sens@spec (or vice versa) is misleading. v3 §4 reporting rules now require both, side-by-side. The v3.0 acceptance gate is on Sens@spec (per-cancer and subgroup), not on AUC. AUC is a no-regression guardrail only (Insight #6).
Finding. Per-cancer Sens@99% on the 627-sample cohort is not uniform. Aggregate Sens@99% sits around 0.55–0.65 once averaged, but per-cancer decomposition shows two cancer types carry the headroom:
- OV (ovarian): Sens@99% 0.25 — worst performing cancer type.
- PAAD (pancreatic): Sens@99% 0.45 — second worst.
- BRCA / CRC / HCC / LUAD cluster at 0.60–0.80.
The aggregate number hides the OV/PAAD failure mode. Per-cancer reporting is the only way to see where the next unit of gain lives.
Implication for v3. v3.0 acceptance gate now includes per-cancer Sens@99% ≥ 0.50 minimum (§6 #3). Aggregate Sens@99% ≥ 0.80 is necessary but not sufficient. OV/PAAD data is not in the current cohort; per-cancer validation on those cancers is flagged as a v3.1 dependency, not a v3.0 blocker.
Insight #5 — Match rate is the hidden bottleneck
Finding. Mutation-panel match rate is variant-type-dependent and silently censors signal:
- CADD panel: 86% SNV match rate, 0% InDel match rate
(
results/cadd_indel_matches.json). - AlphaMissense panel: 96% missense-SNV match rate, 0% other
variant type match rate
(
results/alphamissense_matchrate_unbuilt.json).
Cancers whose mutational burden is dominated by InDels (e.g. MSI-high CRC) or non-missense variants are not measurable by either panel — not because the classifier fails, but because the panel never scores those mutations.
Implication for v3. Match rate per cancer type per panel is now a first-class output (§4.2 match-rate budget). v3.0 acceptance flags cancers with match rate < 80% as "panel-coverage-limited" in the results table (§6 #11). Sens@99% on those cancers is a panel-coverage bound, not a classifier limit, and must be reported as such.
Finding. Across the 09-13→09-17 rounds, every technique tried left pooled AUC within 0.9750–0.9755 on the 627-sample cohort (the fragmentomics channels are saturated). Techniques tested: CADD Top-K=500, CADD Top-K=200 per-subgroup, AlphaMissense weighted, augmented CADD matches, naive-mean fusion, learned-mixing weight, OvR elastic-net, deeper elastic-net (higher C sweep), per-study z-score harmonization on/off, hierarchical study dummies with shrinkage. None moved AUC.
Implication for v3. AUC is no longer a target; it is a no-regression guardrail. v3.0 acceptance does not require AUC improvement (§2, §6). Engineering effort that would have gone to "raise AUC by 0.005" should instead go to per-cancer calibration and panel match-rate expansion.
Finding. Deep-learning baselines (small MLP, 1D-CNN over channel vectors, SGD/RF/SVM all benchmarked against LR) lost to plain LR on this cohort. With 627 patients and ~63K feature dimensions after extraction, deep models overfit; the gap was consistent across 5 seeds. This is the empirical basis for the 09-13 design's CPU-only, sklearn-bound constraint — and the 09-13 design was right on this point even where it was wrong on panel selection.
Implication for v3. The GPU/MPS compute target (§1) is retained as optional, off the critical path. If a deep model class is ever added (v3.1+), it must serve as a learned cross-channel feature extractor that produces lower-dimensional embeddings for an LR-style calibrator — not as an end-to-end classifier. At n=627, capacity hurts. Calibration, panel selection, and match-rate expansion are the levers that help.
The 09-13 v3 design was written with AUC as the headline metric and Sens@99% = 0.85 as the target. The 09-13→09-17 rounds falsified that framing: AUC is saturated, the 0.85 target is above the empirical ceiling, and the only lever that moved Sens@99% by double digits was subgroup-stratified CADD panel selection (+14 to +40pp within biological subgroups, Insight #2). v3 is now redesigned around what actually works: per-cancer and per-subgroup Sens@99% reporting, CADD Top-K=200 per-patient panel selection as the primary path, OvR elastic-net as the secondary calibrator, match-rate reporting as a first-class output, and a v3.0 acceptance gate at Sens@99% = 0.80 (empirical ceiling) with per-cancer ≥ 0.50 floor — not at 0.85 (above ceiling) with aggregate-only reporting. The 0.85 number is preserved as the v3.1 stretch bar. The design now aligns with what moved the metric, not with what the original proposal hoped would move it.
End of design proposal. Awaiting approval before any code is written.