# ADR-19: Multi-source unit vocabulary and derived-combo decision **Status**: accepted (2026-06-13) **Blocks**: heterogeneous multi-source repetitions roadmap, phases D1–D8. Land BEFORE any D1–D8 schema change; gates the deferred public prediction-level decision. ## Context `docs/HETEROGENEOUS_MULTISOURCE_REPETITIONS_ROADMAP.md` extends the coordinator to pipelines where one physical sample carries several observations per source with asymmetric cardinalities across sources (e.g. `A=2/B=3/C=2`). Before any schema work (D1–D8), the unit vocabulary and the migration posture must be frozen so downstream phases share one set of terms and none of them silently promotes a breaking public prediction level. The core stays a coordinator: no feature-buffer materialization, no fitted-model internals; identities, relations, reducers, OOF/fold safety, selection, lineage and replay only. ## Decision ### Frozen vocabulary These names are canonical for D1–D8 and follow-up work. Renames require an ADR-superseder. | Term | Meaning | |---|---| | `physical_sample` | Logical sample requested by the user; the leakage, target and default output unit. Owns all its source observations and repetitions. Aligns with `SampleId`. | | `source_sample` | Per-source view of one physical sample (its observations within a single source). Intermediate domain for source-aware models/late fusion; not a public prediction level in the mainline. | | `observation` | One physical row/acquisition in one source. Aligns with `ObservationId`; many observations may share one `physical_sample`. | | `combo` | Derived observation/unit built by combining one observation (or aligned tuple) per source for the same physical sample. Stored relation-backed with `component_observation_ids` and `origin_sample_id`. | | `EntityUnitLevel` | Internal enum naming the unit domain of a relation row, port, reducer or view: `physical_sample`, `source_sample`, `observation`, `combo`. Additive Rust type and schema vocabulary; distinct from the public `PredictionLevel`. | | `PredictionUnitId` | Stable identity of a non-sample prediction row (already used for target/group caches). Carries derived/relation unit ids for combos without adding a public combo prediction level. | | `ReductionPlan` | Contract for moving values across unit levels/axes (`role`, `axis`, `input_unit_level`, `output_unit_level`, `method`, `weight_source`). A `combo`/`observation -> physical_sample` reduction is mandatory for any row-multiplying representation to yield sample output. | | `RepresentationPlan` | Contract for a host-built feature representation (aggregate, cartesian, Monte-Carlo cartesian, stack fixed, stack padded/masked) plus the relation delta, replay manifest, cardinality contract and missing-source policy it emits. Core plans contracts and host handles only. | | `FitInfluencePolicy` | Contract for how per-sample influence is enforced at fit time (`auto`, `uniform_rows`, `equal_sample_influence`, `resample_equalized`, `backend_loss_weight`, `scorer_only`, `strict_weight_support`). Distinct from `AggregationWeights`: influence is a fit contract, not an aggregation weight. | ### Mainline: combo is a derived observation, not a public prediction level In the mainline implementation (D1–D8) a `combo` is a relation-backed derived observation/unit. The public prediction-cache levels stay `observation`, `sample`, `target`, `group`; `policy::PredictionLevel` gains no `combo` or `source_sample` variant. Cartesian/Monte-Carlo training, combo-to-sample reducers, replay and audit operate through the relation table (`component_observation_ids`, `origin_sample_id`) and the existing sample-level OOF/aggregation paths. Final public output remains sample-level. ### Deferred gate: first-class public combo / source_sample levels Promoting `combo` and/or `source_sample` to first-class public `PredictionLevel` / `PredictionUnitId` values is deferred until a separate ADR explicitly approves the public-contract change. It is needed only when a downstream public prediction cache, selector or meta-model consumes combo/source rows directly. Approval must carry: cache/schema migration notes; an ABI/binding impact review across prediction-cache metadata schemas, `PredictionBlock`, JSON fixtures, CLI/Wasm validation, Python bindings and the C ABI snapshot; and compliance with ADR-02 and ADR-14 (versioning, dual-read or migration edge, CHANGELOG, negative fixtures). Until then the relation-backed `meta_row_domain=combo` path covers combo meta-models without a public combo cache. ### ADR-02 migration checklist (this feature) Every D1–D7 phase that changes a public type applies ADR-02 in that same phase (D8 audits coherence; it is not a catch-up phase): 1. **Optional fields first** — new relation/port/edge/reducer/selection/cache fields land as `Option` with a documented default; v1 readers ignore unknown fields per `additionalProperties`. 2. **Defaults / dual-read** — defaults reproduce current sample-level behavior; relation-aware validation is stricter only when relation metadata is present; old fixtures still validate or are dual-read migrated. 3. **Fixture update** — the schema JSON, local fixture and conformance pack move with the Rust type; cross-repo parity is re-checked with `DAG_ML_DATA_REPO` when shared contracts change. 4. **CHANGELOG entry** — every wire-shape change is recorded under ADR-02 (and ADR-14 for any deprecation). 5. **C ABI decision** — each phase explicitly records "ABI touched (bump `abi_snapshot.v1.json` + `dag_ml.h` + tests)" or "ABI not touched"; relation/prediction-unit tables cross the ABI only if the deferred first-class-combo gate is later approved. ## Consequences - Downstream phases use derived-observation combos by default; the first-class public combo level is isolated as one deferred public-contract decision. - The vocabulary is surfaced in `docs/COORDINATOR_SPEC.md`, `docs/ARCHITECTURE.md` and `docs/contracts/README.md` so schema and Rust work share one glossary. - The roadmap's public-contract matrix plus this per-phase ADR-02 checklist are the audit basis for D8. ## Risk - A premature first-class `combo` level would force a breaking prediction-cache/ABI migration. Mitigation: the relation-backed mainline plus the explicit deferred gate keep that change opt-in and reviewed.