# Prespecified analysis protocol — ST000284 / AN000452
Frozen before any predictive model results on 2026-09-29.
## Scope and execution
Use native Notebook and the existing enabled local Python environment only. No installations, application changes, remote compute, or parallel scientific jobs. Stop additional resampling by 02:17 UTC and finish verification and delivery by 02:32 UTC.
## Comparison
Primary: CRC (64) versus Polyp (76), cancer positive. Polyps provide a clinically relevant non-cancer disease comparator; this is not a prospective screening cohort and does not answer detection of all advanced neoplasia.
Prespecified exploratory comparisons: CRC versus Healthy (84); CRC versus pooled Polyp/Healthy. Do not select the primary comparison using results. Preserve control subtype reporting.
All 224 deposited samples are eligible unless unresolvable labels, conflicting exact duplicate profiles or technical invalidity prevent analysis. Retain statistical outliers. Record exclusions. No arbitrary sample missingness exclusion; show sensitivity if extremes appear.
The deposit has 113 measured metabolites; source study describes 234 samples and 158 monitored metabolites. Do not impute missing people or unreported analytes. Document upstream feature inclusion and preprocessing as incompletely verified.
## Independence
All Subject ID fields are '-'. No participant, batch, site or collection-date identifiers are supplied. Sample-ID disjointness cannot establish participant independence. Known exact duplicate profiles, if present, are kept together in splits or conflicting labels are treated as a blocker. Otherwise use sample-level splits under an explicit unverified independence assumption. Batch/site holdout and participant-cluster bootstrap are unavailable without identifiers.
## Predictive workflow
Cancer positive; fixed classification threshold 0.5. Primary model: L2 logistic regression, liblinear solver, no class weighting, maximum 2000 iterations, tolerance 1e-6. Tune C in [0.01, 0.1, 1, 10] by inner ROC AUC; ties follow ascending grid order.
Pipeline: numeric conversion, nonfinite or nonpositive intensities marked missing; drop features with >20% training missingness, median-impute using training data, log10 transform, remove training-constant features, standardize with training means/SDs. No supervised metabolite selection and no whole-cohort fitted normalization. All fitted operations are inside every inner/outer training fold. A dummy model predicts the outer-training class fraction.
Three outer repeats, each 5 stratified folds, seeds 17, 29, 43. Inner 3-fold stratified CV, seed 1000 + 10*repeat_index + outer_fold_index. Use these same rules across prespecified comparisons. All observations receive exactly one out-of-fold prediction per repeat. Save membership, labels, predictions, chosen C, feature counts and convergence warnings. Fit one model tuned on the full primary cohort solely to illustrate apparent training optimism; do not use it to estimate held-out performance.
## Metrics and uncertainty
Primary metric: pooled out-of-fold ROC AUC per repeat. Secondary: average precision with prevalence reference, balanced accuracy, sensitivity, specificity, Brier score, and confusion counts at threshold 0.5. Report calibration descriptively, not as validated population risk.
Use repeat 1 (seed 17), fixed in advance, for the principal point estimate and confidence intervals. Compute 1000 stratified sample bootstrap resamples of its saved label/prediction pairs with seed 20260929; percentile 95% intervals. These are approximate, conditional on the fitted cross-validation procedure and assume independent samples; they do not include full training-pipeline uncertainty. Do not bootstrap folds, treat repeats as independent, or pool repeat predictions as new people.
Permutation test: 99 permutations, seed 20260930, of primary-cohort labels. For each rerun the complete first-repeat nested CV procedure including training-only preprocessing and tuning; regenerate stratified folds under permuted labels with the same seeds. Compare ROC AUC using (1 + number(null_auc >= observed_auc))/(1 + completed_permutations). Label validity conditional on sample exchangeability; this is not a confounder-adjusted causal test. If the 02:17 UTC cutoff interrupts permutations, report incomplete counts and do not present a completed confirmatory permutation test.
## Alternative explanations
On primary-cohort outer splits, evaluate age/sex/BMI-only logistic regression with training imputation/scaling and the same C grid. Exclude diagnosis, stage, group, IDs and any outcome-derived fields from predictors.
Evaluate a missingness-only logistic regression baseline if missingness indicators vary; otherwise report it uninformative. Show clinical distributions, stage composition and missingness by group, and exact sample/feature duplicate counts. No batch correction without batch labels.
Healthy-only and pooled non-cancer comparisons use the same model and 3x5 nested protocol, with no additional bootstrap/permutation testing. Explain that these are overlapping within-study comparisons, not external replications.
## Verification and reporting
Check exact metadata/matrix alignment, unique sample IDs/features, split disjointness, complete out-of-fold coverage, finite predictions, training-only preprocessing, and convergence. Independently rerun the finalized primary workflow in a clean execution namespace from saved inputs and compare splits and metrics at numerical tolerance 1e-10.
Publish analysis.py, requirements.txt recording existing versions, analysis_report.md, this protocol, and analysis_evidence.zip with provenance/checksums, licensed input data, tables, predictions, splits, settings, figures and run instructions. Preserve all planned findings including weak or null results; explain external cohort and prospective validation requirements.

## Quality-audit clarification (before model fitting)
The matrix contains 69 entries equal to exactly 1 across 7 metabolites; whether these encode censoring or upstream imputation is undocumented. Retain them as positive values in the frozen primary pipeline. Add explicitly exploratory robustness checks using the same primary-cohort folds: treat exact 1 as missing before training-only filtering/imputation, and fit an exact-1-indicator-only baseline. These checks do not change the primary estimate or test. The ordinary missingness-only baseline is uninformative because no explicit missing/nonpositive entries exist.
