diff --git a/workplans/CB-WP-0011-inspectable-table.md b/workplans/CB-WP-0011-inspectable-table.md new file mode 100644 index 0000000..9e57a3d --- /dev/null +++ b/workplans/CB-WP-0011-inspectable-table.md @@ -0,0 +1,166 @@ +--- +id: CB-WP-0011 +kind: product +title: "Stage 1, first slice: an inspector that shows everything" +status: todo +--- + +# Purpose + +Tier **S** — and this is the **first time the chaos roll has changed +anything** in six declarations. + +``` +structural tier L (INTENT stage 1 creates a new capability port) +chaos d4 = 4 → override +override roll shuf -e S M L → S +declared tier S (structural L, chaos 4 → S) +``` + +Declaration 6 of 12 in the calibration window. The `CHAOS` gate in +`gates.toml` has `caught = []` and a `review_by` of 2026-09-30; this pass +is the first entry it can earn, whichever way the outcome falls. + +## What tier S is allowed to mean here + +A rolled-down tier relaxes *process weight*. It does not relax the hard +gate — **no implementation code for a capability exists before its ADR is +committed** — because that gate is stated unconditionally and the tier +table governs only "weight of steps 1–3", not whether step 3 happens. + +The two rules meet head-on: stage 1's headline deliverable is a rendering +port (`cb-render-api` / `-null` / `-wgpu`, ArchitectureBlueprint §Port +pattern), a port needs an ADR, and tier S has no ADR. + +**Resolution: split the declaration rather than lawyer either rule.** +This packet is the part of stage 1 that creates no port, adds no +dependency, and therefore trips no ADR gate. The GUI half gets its own +tier declaration and its own chaos roll later, informed by what this pass +learns. Two facts make that ordering the right one anyway: + +- **AM-4a has 3,750 lines of headroom** (246,250 of 250,000 measured by + `make dep-weight`). Any 2D toolkit is a five- to six-figure line count. + The dependency decision needs a survey, and the roll just deleted the + survey — so the honest move is to not make the decision this pass. +- The second-use rule cuts the same way. A renderer that has never had to + show the whole of `GroundView` does not yet know what a view needs. + +## The finding this packet acts on + +`tools/cb-play/src/table.rs:113` already has +`render(view: &GroundView) -> String`. It is stage 1's renderer, written +in stage 0 to prompt a human, and **it drops roughly a third of what the +projection carries**: + +| in `GroundView` | in `render` | +|---|---| +| `mode`, `focus`, `solution_discard` | absent | +| `ground_modes`, `ground_choices` | absent | +| `support_responses`, `darvo_targets` | absent | +| `protection`, `freedom_gate_lifted` | absent | +| `protected_this_round` on a Problem | absent | +| `personal`, `coalitions`, `mastery` on the outcome | absent | + +Nothing is wrong: it was written to show a human their legal moves, and +it does. But "inspectable" is the word INTENT uses for stage 1, and an +inspector that silently omits the DARVO state machine and the GROUND +practice — the two mechanics this game exists to model — is not one. + +The interesting part is that **no test could have caught this**, because +every assertion a renderer test naturally makes is satisfied by any +render. That is the harness-does-nothing class again, in its presentation +form. + +## Task: the inspector shows everything, and a gate says so + +```task +id: CB-WP-0011-T01 +status: todo +priority: high +``` + +Extend `render` to cover every field of `GroundView`, and — the load- +bearing half — add the check that keeps it covering them. + +**The check.** Serialize the view to `serde_json::Value`, walk it for +every object key reachable in a populated state, and require each key to +be classified in exactly one of two lists in the test: + +- `RENDERED` — the inspector shows it, asserted against a state where the + field holds a distinctive value that appears in the output; +- `OMITTED` — with a stated reason per entry (e.g. `hand_size`, which is + redundant when `hand` is shown to its owner). + +A new field on `GroundView` that is in neither list **fails the test**. +That is the same shape as `not_control_gates` in `gates.toml`, for the +same reason: silence must cost something. + +**Vacuity controls (M-D1-MUT).** The test must go red for a stated reason +when (a) a field is deleted from the render, (b) a field is moved from +`RENDERED` to `OMITTED` while still being rendered, and (c) the walk is +made to return an empty key set — the EXPECT-VACUOUS control, since a +coverage test over zero keys passes trivially and that is precisely how +this check would rot. + +Build the fixture from a **mid-DARVO, mid-GROUND** state, not a fresh +deal: most of the missing fields are empty at deal time, and a coverage +test run against a state where the fields are absent is the same lie in a +different costume. + +## Task: `cb-play --inspect` walks a recorded game + +```task +id: CB-WP-0011-T02 +status: todo +priority: high +``` + +Stage 1 asks for a *debug inspector*. The inspector above renders one +state; this makes it walk a game. + +Deliver `cb-play --inspect [--as SEAT|spectator]`: +replay the recorded commands and print the full table after each step, +from the chosen viewpoint. A `.cbreplay` bundle and a scenario YAML are +both accepted — both already reconstruct a command sequence. + +This is the first thing in the project that answers *"what did the table +look like when it went wrong?"* without adding a `dbg!` and re-running. +It costs no dependency: replay, projection and render all exist. + +**Acceptance.** Inspecting a recorded game must reproduce the end-state +hash the bundle records — an inspector that shows a state the replay did +not reach is worse than none. Assert the hash, and mutate the replay to +prove the assertion fires. + +**Projection is not optional here.** `--as 2` must show P3's hand and +must not show anyone else's. The stage-0 test that checked this was +vacuous twice before it held (CB-EV-0007); seat the assertion where the +hidden thing is actually hidden. + +## Task: evidence, and what the chaos roll cost + +```task +id: CB-WP-0011-T03 +status: todo +priority: medium +``` + +Write `evidence/CB-EV-0009-inspectable-table.md` covering: + +1. **Did tier S produce a worse outcome than tier L would have?** The + only honest way to answer is to name what the deleted survey would + have decided and check whether this pass needed it. State the answer + even if it is "no, and that is one data point against the tier table", + because that is the finding the calibration window is for. +2. The field-coverage gap as measured before and after T01. +3. Cost and session shape for the pass, per `make status`. The previous + pass opened at a **HARD** session-shape breach (338,852 mean context + against a 300,000 ceiling) and a **45%** trailing-3 meta share against + a soft 25%. This pass is `product`; report where both land. +4. Update `gates.toml`: `CHAOS` gets its first `caught` entry — the roll + fired, and what it changed. + +**Do not update the ADR or INTENT.** Stage 1 is not shipped when this +lands; the port half is untouched and INTENT's stage-1 line stays open. +Marking a stage complete because part of it works is the failure mode +stage 0 avoided by leaving the CLI player open for three passes.