clay-borg/workplans/CB-WP-0048-a-configuration-not-a-variant.md
tegwick 98f600b1d3
Some checks failed
ci / check (push) Failing after 3s
Workplan status is active, not in_progress
The spec is explicit: a WORKPLAN is active|done|paused; in_progress is a
TASK status. I used the task vocabulary on the workplan frontmatter, and
the hub rejected both with a 422 on every sync.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 23:48:27 +02:00

183 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: CB-WP-0048
kind: product
title: "A configuration, not a variant"
status: active
state_hub_workstream_id: "44ceb8d8-e6e2-46d6-a694-fd275804b771"
---
# 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
state_hub_task_id: "fe1a39f4-d123-4c83-be64-35901603edcd"
```
**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: done
priority: high
state_hub_task_id: "4b8bfc25-0e4d-4fdc-9dad-5cdb66798207"
```
Per ADR-0022 D1. Identity as data, behaviour exhaustive, `resolve()`
between them.
**Done 2026-08-08.** `catalog.rs` reads schema 2 (aspects, modules,
profiles) and **refuses a schema it does not understand by name** — a
reader that silently accepted schema 1 would answer questions about
aspects over a file that has none. `config.rs` holds `Configuration`
(identity), `Rules` (behaviour) and `resolve()`.
Nothing about the aspect list, the defaults, or which modules are
proposed is written in our source; all of it is read. The tests locate
the proposed module **by searching the catalog for `status: proposed`**,
so if ground-game implements it upstream the test looks elsewhere rather
than going stale.
| mutation | what went red |
|---|---|
| the two errors become one sentence | *"a module the catalog HAS was reported as unknown"* |
| one-per-aspect check removed | *"two modules on one aspect must be refused"* |
| identity refuses what it cannot run | *"selecting a catalog module must NAME a configuration"* |
| legacy lookup stops reading the catalog | *"h2 and profile h2 are not the same point in aspect space"* |
`scoped_plus_attack_soothe` now resolves — `problem_stress.scoped` ×
`attack_relief.self_soothe_ge4`, the combination the three-armed enum
could not express.
**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
state_hub_task_id: "3232a451-b60a-42f6-8200-b8115d4b35b9"
```
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
state_hub_task_id: "5be13b74-bcc9-4697-96bb-91f1966c760b"
```
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
state_hub_task_id: "ff135f86-2265-4db5-b412-23fcb68df489"
```
**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.