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

5.3 KiB
Raw Blame History

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, ADR-0007 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.