clay-borg/decisions/ADR-0011-vendor-the-edition.md
tegwick 2da19a49b7 CB-WP-0021 T01/T02/T05: the engine plays its own data — AM-7 blocks
ADR-0011 decided it: vendor the CSV with a checked digest, read it with
a ~50-line reader, and let the hashes move.

The declaration's constraint was measured against the WRONG BUDGET. It
said a CSV crate costs 21,613 against AM-4a's 3,798 of headroom, '5.7x
over, settled by measurement'. But setup and problem_priorities are
cfg(scenarios) and are not in the shipped runtime at all, so AM-4a never
sees them. Against AM-4b, csv costs 17,651 against 19,742 -- it FITS,
with 2,091 to spare. It is refused anyway, on proportion: 89% of the
budget's remaining capacity to read 20 rows. The revisit condition is
stated (nested quoting, embedded newlines, multiple dialects).

GR-S01 now deals Surface + hidden 1..=k as ruled, with edition values and
suits. Measured: 6/9/12 available against thresholds 5/7/9 -- the game is
winnable at every seat count, which is what the maintainer could not do.
gd0001 is INVERTED, not deleted, and now also asserts the 6/9/12 so a
deal that is reachable for the wrong reason still fails.

Blast radius was scenario expectations, exactly as the ADR predicted: no
scenario pinned a hash and no bundle is committed. Six scenarios and two
unit tests updated, each with a note. gr-e01-threshold-unreachable-2p is
RENAMED to -reachable- and rewritten as the non-provisional import check
ground-game asked for by name. gr-e03's setup was restructured, not just
renumbered: with values 2,2,2 its personal-edge test would have tied
three ways and asserted nothing.

BLOCKING: AM-7 fails at median 0.845 against its 0.9 floor. Isolated
across three runs -- 3 problems + stand-in 0.97, 3 problems + edition
0.909, 4 problems + edition 0.845. State is BOUNDED (proven: identical
after 5k and 100k events), so this is not the unbounded-growth defect
AM-7 exists to catch; it is a bigger working set streaming a long log.
Whether AM-7's floor is still right for a larger aggregate is a spec
question and lowering it requires an ADR, so it is not being tuned here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 00:47:56 +02:00

118 lines
5.3 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.

# ADR-0011: vendor the edition, read it by hand, and let the hashes move
status: accepted
date: 2026-08-04
decided by: agent, under the standing loop authorization
tier: M (structural M — adds or refuses an external dependency and changes
how a game is set up; chaos d8=7 → no override). Tier M merges survey and
decision into one document, which this is.
references: [CB-WP-0021](../workplans/CB-WP-0021-import-the-edition.md),
[ADR-0007](ADR-0007-render-html-not-a-port.md) D3 (the acquisition rule),
GROUND-WP-0002 T01 and GROUND-WP-0004 (ground-game's rulings)
## Context
`editions/ground-darvo-r0/Problems.csv` is authoritative (ground-game,
2026-08-03) and the engine has been inventing Problem values and suits.
GR-S01's deal was then ruled (2026-08-04): **Surface always hidden
`1..k`**, k = 2/3/4, giving available points **6 / 9 / 12** — which holds
*only* with the real values.
## Correction: the constraint was measured against the wrong budget
**CB-WP-0021's declaration said a CSV crate costs 21,613 lines against
AM-4a's 3,798 of headroom — "5.7× over, settled by measurement rather
than preference."**
`setup` and `problem_priorities` are `#[cfg(feature = "scenarios")]`.
**They are not in the shipped runtime at all**, so AM-4a never sees them
and never would have. The budget that applies is AM-4b, and measured
against *its* graph:
| | lines |
|---|---:|
| AM-4b headroom (745,000 725,258) | **19,742** |
| `csv` marginal cost (`csv` + `csv-core`; `ryu`, `itoa`, `memchr` already present via `serde_json`) | **17,651** |
**It fits, with 2,091 lines to spare.** The declaration's confident
"settled by measurement" was measurement of the wrong thing — the third
premise this pass has had to correct, and the second where a real number
was computed against a mis-chosen denominator.
## Decision 1 — refuse `csv`, on proportion rather than impossibility
It fits and it is still refused, and the distinction matters because the
argument has to survive someone re-running the numbers.
**17,651 lines is 89% of everything AM-4b has left, to read 20 rows.** The
next dependency after it would have 2,091 lines to live in. A hand-rolled
reader for this grammar is ~50 lines we own.
INTENT's rule is *"assimilate the implementation"* — but that is about
**mature optimized libraries** for hard problems. Splitting quoted CSV
fields is not one, and `Problems.csv` is 20 rows read once at setup.
Taking a general parser here would spend the budget's remaining capacity
on the easiest problem we have.
**If the data grows into something a hand reader should not own — nested
quoting, embedded newlines, multiple files with differing dialects — this
decision is wrong and `csv` is the answer.** That is the condition to
revisit under, stated now rather than left to taste.
## Decision 2 — vendor the file, with a checked provenance
`ground-game` is a separate repository. Two options:
| | cost |
|---|---|
| **sibling checkout** | the build depends on a path that may not exist. CI runs `rust:1.97` with this repo only, so `make all` would fail or silently skip — and a silent skip is the class this project has found seven times |
| **vendor a copy** | clay-borg carries content it does not own, and a stale copy is worse than no copy |
**Vendored**, at `editions/ground-darvo-r0/Problems.csv`, with a
`PROVENANCE` note naming the upstream repo, path and revision.
The staleness answer is a **committed digest of the upstream file**. A
check compares it when `../ground-game` is present, and reports
**`upstream not checked out`** as a distinct outcome when it is not — never
a pass. That is the shape ADR-0009 used for `node`: an absent thing is
reported absent, not treated as satisfied.
**Acquisition rule (ADR-0007 D3):** this is content the build causes to be
present, and it is ours now. It is not third-party *code* and does not
enter AM-4, but the provenance note is what stops it becoming
unattributed.
## Decision 3 — the hashes move, and nothing pins them
Problem values and suits enter `GroundState`, which is hashed (K7). The
declaration called this *"the reason the ADR exists."* Measured:
| | |
|---|---|
| scenario files pinning a state hash | **0** |
| replay bundles committed | **0** |
| scenarios asserting on `problems.N.*` | **10 of 25** |
**So the feared blast radius is not there.** K8's double-run compares two
runs of the same build; AM-7's probe compares segments within one run;
`replay-test` generates its bundle at run time. All are self-consistent
and survive a content change by construction.
What breaks is **scenario expectations** — which is exactly what *should*
break when the content changes, and is why they are written as `expect`
blocks rather than hashes.
**No hash is grandfathered and none is recorded as "was".** A recorded
hash that outlives the content it describes is a lie with a timestamp.
## Consequences
- The deal fix and the import land **together**. The ruled 6/9/12 holds
only with real values; the same deal with the stand-in gives 6/10/15, a
game nobody ruled on.
- `gd0001` is **inverted, not deleted** — it is the record of why the game
became winnable.
- If ground-game revises `r0` in place, the digest check fails loudly.
Per GROUND-WP-0002 T01's proposed contract, `point_value` and
`required_solution` may not change within a revision; this is the
mechanism that notices if they do.