# OOF Campaign Fixtures This document defines the first reproducible OOF campaign fixtures owned by `dag-ml`. The fixtures are intentionally tiny and identity-heavy: they prove fold and prediction semantics before any real model execution exists. ## Fixture Set Directory: `examples/fixtures/oof_campaign/` | Fixture | Purpose | |---|---| | `uc6_oof_success_predictions.json` | UC6-shaped positive stacking case | | `uc11_train_prediction_refusal.json` | UC11-shaped leakage refusal case | ## Shared Sample Universe Use six sample ids and three folds: | Fold | Validation samples | Train samples | |---|---|---| | `fold0` | `S001`, `S004` | `S002`, `S003`, `S005`, `S006` | | `fold1` | `S002`, `S005` | `S001`, `S003`, `S004`, `S006` | | `fold2` | `S003`, `S006` | `S001`, `S002`, `S004`, `S005` | Rows inside prediction blocks should be deliberately shuffled to force joins by `sample_id`, never by input row position. ## UC6 Success Required top-level fields: | Field | Shape | |---|---| | `fold_set` | fold assignments by absolute sample ids | | `join_policy` | `allow_train_predictions_as_features=false`, `join_on="sample_id"` | | `requested_sample_order` | `["S001", "S002", "S003", "S004", "S005", "S006"]` | | `prediction_blocks` | three validation-only producers | Prediction block fields: | Field | Requirement | |---|---| | `prediction_id` | stable fixture id | | `producer_node` | e.g. `branch:b0.model:pls` | | `partition` | exactly `validation` | | `fold_id` | `fold0`, `fold1`, or `fold2` | | `sample_ids` | validation samples for that fold, possibly shuffled | | `values` | one regression column per sample | | `target_names` | `["y"]` | Assertions: - join succeeds with default safe policy; - output sample order equals `requested_sample_order`; - output columns are producer namespaced; - each sample has exactly one validation prediction per producer; - no train/leakage flags are present. ## UC11 Refusal Required difference from UC6: - at least one prediction block has `partition="train"`; - `join_policy.allow_train_predictions_as_features=false`; - `join_policy.include_partitions=["train", "validation"]`. Assertions: - execution refuses before building meta features; - error kind is structured OOF leakage; - payload includes `node_id="merge:pred"`; - payload lists every train violator with producer, partition and fold id; - remediation text tells the user to use validation-only OOF predictions or to explicitly opt into the unsafe policy. ## Boundary `dag-ml` owns folds, prediction blocks, OOF joins, leakage refusal and campaign fingerprints. `dag-ml-data` owns schema/model-input/adapter-plan fixtures only. ## Stacking REFIT Coverage Contract The runtime distinguishes three stacking REFIT outcomes: | Case | Required policy | Outcome | |---|---|---| | full validation-OOF coverage for the refit sample universe | default `require_full_coverage` | meta-model REFIT may consume OOF | | incomplete but otherwise well-formed validation OOF | `cv_only` or `skip_refit_on_incomplete_oof` on `metadata.stacking_oof_refit_contract.policy` | stacking REFIT is skipped | | incomplete OOF without explicit policy, train/final/test prediction input, missing fold ids, unknown folds, fold/sample mismatch or duplicate validation rows | none | rejected with a stable cause such as `partial_oof_without_policy` | ## D8 Conformance Pack `docs/contracts/conformance_pack.v1.json` pins canonical digests for `uc6_oof_success_predictions.json` and `uc11_train_prediction_refusal.json`. D8 uses them for the `stacking_oof_contract.v1` and `row_vs_sample_selection_mismatch.v1` scenarios, so changes to these fixtures must update the pack and rerun `python3 scripts/validate_contracts.py`.