Skip to content

M5 WP5.2: preregister the v2 nested protocol, candidate list and numerical gates - #30

Merged
utkuvibing merged 3 commits into
mainfrom
codex/m5-2-v2-protocol-preregistration
Aug 25, 2026
Merged

M5 WP5.2: preregister the v2 nested protocol, candidate list and numerical gates#30
utkuvibing merged 3 commits into
mainfrom
codex/m5-2-v2-protocol-preregistration

Conversation

@utkuvibing

Copy link
Copy Markdown
Owner

Summary

Commits the complete v2 experimental protocol before any v2 experiment runs — M5 work item 6, unblocked by WP5.1's sealing:

  • Protocol contract (\docs/modeling/wp5_2-v2-nested-protocol-contract.md): outer leave-one-tournament-out over WC2018/Euro2020/WC2022/Euro2024 in fixed order, inner match-grouped CV semantics, fold-local preprocessing obligations, exactly four candidate families with frozen search spaces, fixed metrics + uncertainty mechanics, calibration policy without isotonic, internal replacement rule, one-time sealed AFCON 2023 + Copa América 2024 qualification, stop conditions, and the binding evidence hierarchy (outer-LOTO = internal selection evidence only).
  • Machine-readable gates (\data/model/v2_protocol.json): every threshold verbatim from the post-M4 roadmap; mirrors \�2_evaluation_registry.json.
  • Shared fold primitive (\ ouchline.modeling.v2_folds): config-driven, no caller override of k (read from the frozen config, pinned to 5), scope-closed inner construction rejecting foreign/unsealed scopes and other folds' held-out tournaments. Only materialized manifest generation remains deferred to M7.
  • Calibration fitting semantics preregistered: per outer fold, intercept-only/Platt fitted only from inner-CV OOF raw predictions inside the training partition and applied to that fold's untouched holdout; final refit sequence fixed step-by-step; external constant_prevalence = full-development prevalence frozen before sealed-set opening.
  • ADR 0016 records the methodology change with rejected alternatives.
  • 47 unit contracts: exact allowed-key schema, threshold literals pinned three ways (config ↔ prose ↔ tests), registry mirror checks, primitive behavior + loud-failure paths.

Boundaries

No v2 experiment executed anywhere in this diff; one production module added that no serving/training path imports yet; no changes to model code, migrations, frontend, v1 artifacts, or the deployed release.

Validation

Local: 47/47 tests green, mypy strict clean across 130 source files, ruff format+lint clean, docs link checker OK. CI is the broader acceptance gate for the full suite.

…rical gates

Commit the complete v2 experimental protocol before any v2 experiment runs: outer leave-one-tournament-out scopes over WC2018/Euro2020/WC2022/Euro2024 in fixed order, inner match-grouped CV (match_id, k=5, sorted by match_date then match_id, index % 5, shuffle=false, no seed), mechanical simplicity-first tie behavior, fold-local preprocessing obligations, exactly four frozen candidate families with frozen hyperparameter search spaces, WP2.7 bootstrap mechanics extended with tournament-stratified pooled resampling (2000 replicates, seed 0, paired differences, 95% percentile intervals), calibration policy without isotonic, internal replacement rule, one-time sealed AFCON 2023 + Copa America 2024 qualification gate, stop conditions, and the binding evidence hierarchy that labels outer-LOTO results as internal selection evidence only.

Artifacts: docs/modeling/wp5_2-v2-nested-protocol-contract.md, data/model/v2_protocol.json (machine-readable gates mirroring v2_evaluation_registry.json), backend/tests/test_wp5_2_protocol.py (allowed-key schema, threshold literals, registry consistency, executable target-free fold semantics on synthetic fixtures), ADR 0016. No model code changes; zero experiments executed under the protocol.
…ation fitting semantics

Move the frozen target-free fold semantics into one production module, touchline.modeling.v2_folds (outer LOTO specs validated against the config's development pool with sealed-scope rejection, inner match-grouped assignment sorted by match_date then match_id with index % 5, disjoint non-degenerate partitions, loud failure on duplicate ids, missing dates, foreign or sealed scopes), and have the unit contract import and exercise that primitive directly; only materialized fold-manifest generation remains deferred to M7. Add real contract-prose consistency checks that pin the normative fragments of wp5_2-v2-nested-protocol-contract.md (scopes, inner rule, tie behavior, bundle evaluator, replacement, calibration, external gate, bootstrap, bins, slices, search spaces, evidence hierarchy) against the whitespace-normalized Markdown, so config, prose and tests cannot drift apart. Preregister the exact leakage-free calibration fitting procedure: per outer fold, intercept-only/Platt candidates are fitted only from inner-CV OOF raw predictions inside the outer training partition and applied to that fold's untouched outer-holdout predictions; the final refit sequence generates development OOF raw predictions, fits the selected calibrator there, refits the base model on all development rows and freezes the model-calibrator pair before sealed-set opening; define the external constant_prevalence reference as the full-development prevalence frozen before any sealed set is opened.

No v2 experiment runs under any of this: the diff adds one production module imported by no serving or training path yet, config gates, prose and tests. Validation: 42 unit contracts green, mypy strict clean across 130 source files, ruff format and lint clean, docs link checker OK.
… frozen config

Remove the caller-controlled split_count parameters from assign_inner_folds and inner_partition: k is now read from data/model/v2_protocol.json through inner_split_count, which rejects missing, non-integer or below-minimum values and fails unless the configured value equals the preregistered five, so caller code can never make k silently differ from the preregistered protocol. Inner-fold construction now rejects any match scope outside the v2 development pool - not only sealed sets - and accepts a keyword-only training_scopes narrowing that pins matches to one outer fold's training partition while itself refusing to widen beyond the frozen pool. Regression tests prove the override is gone from both signatures, that a tampered config k fails loudly with an ADR-required message, that a foreign unsealed scope (99, 9) is rejected on the inner path, and that another outer fold's held-out tournament cannot enter inner CV when the partition is declared.

Semantics unchanged: (match_date, match_id) sort, index % 5, shuffle=false, no seed. No v2 experiment runs. Validation: 47 unit contracts green, mypy strict clean across 130 source files, ruff format and lint clean, docs link checker OK.
@vercel

vercel Bot commented Aug 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
touchline-intelligence Ready Ready Preview Aug 25, 2026 4:14pm

@utkuvibing
utkuvibing merged commit 2eb0817 into main Aug 25, 2026
5 checks passed
@utkuvibing
utkuvibing deleted the codex/m5-2-v2-protocol-preregistration branch August 25, 2026 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant