--- id: CB-WP-0048 kind: product title: "A configuration, not a variant" status: in_progress --- # Purpose ``` structural tier M (changes the state and therefore the recording, and replaces the selector every consumer names) declared tier M ``` Implements [ADR-0022](../decisions/ADR-0022-a-configuration-is-a-point-in-aspect-space.md), adopting ground-game catalog **schema 2** per [CB-RES-0010](../research/CB-RES-0010-game-aspects-and-design-space.md). ## The gap, stated exactly `scoped_plus_attack_soothe` is a profile in the vendored catalog **today**. clay-borg cannot name it — not "has not implemented", *cannot express*. `Variant` has three arms; H1 bundles flat pressure with self-soothe and cannot be taken apart, H2 gives scoping alone, and their product has no arm. Every future combination has the same problem. The kernel is not the obstacle: **four sites branch on the variant and each belongs to exactly one aspect** (ADR-0022 Context). The selector is the blob. ## Task: the mirror is held by the gate ```task id: CB-WP-0048-T00 status: done priority: high ``` **Done 2026-08-08.** The schema-2 mirror (`catalog.yaml`, `ASPECTS.md`, `CATALOG.md`, `modules/`) arrived vendored with **no recorded digests** — 19 files — and `make edition-check` was red on the working tree. - Digests are now **generated by walking `editions/`**, not typed. Two reviews (CB-REV-0002 #8, CB-REV-0003 #8) already found hand-written lists that made their own controls vacuous; a mirror that grows a *directory* is precisely the case a maintained list loses. - `editions/PROVENANCE-catalog.md` was a file **inside the mirrored tree that upstream does not have**, which the gate correctly refused as unexplained. Folded into `editions/ground-darvo-r0/PROVENANCE.md` — provenance *about* the mirror is ours, so it does not live inside the thing it describes. **Noted, not fixed:** `vendored_files()` matches only `.csv` inside the edition directory, so a non-CSV added *there* is invisible to both the digest and the freshness check. The sibling walk has no such hole. The exposure is one file today (`PROVENANCE.md`, ours) — but this is F26's shape again and should be closed on its own terms. ## Task: `Configuration` and `Rules` ```task id: CB-WP-0048-T01 status: todo priority: high ``` Per ADR-0022 D1. Identity as data, behaviour exhaustive, `resolve()` between them. **Controls:** - **a proposed module is refused BY NAME**, distinguishably from a typo — the two-error requirement is the reason this shape was chosen, so it is the first test; - **two modules on one aspect are refused** (GAME↔MODEL validation); - **an aspect the catalog has and the kernel does not know still parses**, because a configuration must be nameable before it is runnable; - **`Rules` has no catch-all arm**, so a new module cannot fall through silently (CB-WP-0034's exhaustiveness, which caught two commands before any test ran); - mutation-proven, each control against its own defect. ## Task: legacy ids alias forever ```task id: CB-WP-0048-T02 status: todo priority: high ``` Per ADR-0022 D2, through the catalog's own `legacy_experiment_id`. **Controls:** - **all 26 recordings replay byte-identically**, which is the whole constraint — `make sim` is the authority; - **`--variant h2` and `--profile h2` produce the same resolved configuration**, asserted on the resolved object rather than on behaviour, so the equivalence is exact and not a coincidence of outcomes; - **the expansion comes from the catalog**, not a table in our source: a second copy of ground-game's mapping is F25's shape. ## Task: the resolved configuration is recorded ```task id: CB-WP-0048-T03 status: todo priority: high ``` Per ADR-0022 D3 and CB-RES-0010 §5.3: `cb-play` sessions, replays, trial logs and panel cells carry the **resolved** module list. **Controls:** - **the recorded configuration is the applied one**, proven the way CB-WP-0046 proved the trial-log stamp — from the state, not the flag; - **the trial-log marker carries the configuration**, closing the gap CB-WP-0047 left open (variant stamped, scenario and mode not); - **a bare field write cannot leave a module inert** — the H2 defect had three call sites and this is the generalisation of it. ## Task: the page and the panels speak aspects ```task id: CB-WP-0048-T04 status: todo priority: normal ``` **Controls:** - **the table names every live module**, not a legacy id — the CB-WP-0044 family: a rules change the page cannot name is reported as "no changes"; - **panels facet by aspect** (CB-RES-0010 §5.4), so "hold all aspects fixed, vary one" is what the harness does rather than what a reader reconstructs; - **a module nothing measured is reported unmeasured**, not absent. ## Not done here - **No policy reads the configuration.** F27 already records that the three scoring modes produce identical play because `GreedyPolicy` never consults `state.mode`; the same will be true of every aspect. Selection without aspect-aware policies gives configurations we can run and cannot evaluate — CB-RES-0010 §5.4's last bullet is F27 restated. **This is the next pass, and it is a prerequisite for measuring any new module**, not a nicety. - **`end_condition` and `problem_deal` get no kernel path here.** Both their non-default modules are `status: proposed`; T01 makes them nameable and refusable, which is what ADR-0022 asks for and all it asks for. - **`specs/Taxonomy.md` does not yet distinguish aspect from stratum.** ADR-0022 D0 states the distinction; the taxonomy is where it belongs, and without it the two vocabularies will be crossed in exactly the way the taxonomy exists to prevent.