CB-WP-0006 T05: K9's assertion, K11's format, and the AM-11 suites

K11 is implemented: crates/cb-events/src/store.rs, magic + version header,
4-byte little-endian length prefix, append-only. Reimplemented not
assimilated per ADR-0005 §2 — no new dependency, and AM-4a/AM-4b are
unchanged at 246,250 / 317,021 because nothing entered the graph.

The operative clause is "detected", so corruption is tested rather than
assumed: a tail short by one byte, a half-written length prefix, a length
prefix corrupted to claim more than the file holds, foreign magic, and a
future format version are each rejected with a distinct error. A reader
that accepts a truncated tail is worse than no format, because it silently
returns a short history that looks complete.

AM-11 is earned. LogStore has two impls — MemLogStore and FileLogStore —
driven through ONE conformance(). The trait carries raw/set_raw precisely
so the corruption controls live in the shared suite: a format contract
that only one impl enforces is not a contract. The same shape is
retro-fitted to KernelRng, which is what AM-11 actually names: ChaChaRng
and NullRng now pass one suite asserting bounds, draw(1) == 0, determinism
across fresh instances, and shuffle preserving the multiset. They were
previously exercised by two separate tests, which is why "met, narrow" was
never earned and ADR-0005 §4 downgraded it.

K9 gets the assertion it did not have: snapshot at seq N + events N+1..M
must equal the from-genesis fold, hash-compared, on GroundState,
single-seed on purpose — AM-7's probe folds a multi-seed log, which is not
a replay of anything, and that defect is not repeated. Two positive
controls: the log must exceed 50 events, and the mid-log snapshot must
differ from the end state or "apply the remainder" is vacuous.

Proof it works: the exact mutation that SURVIVED in CB-WP-0005 — making
Snapshot::take discard its EventSeq — now fails on the K9 assertion.

AM-11's mutation breaks NullRng::draw to return its bound and the shared
suite fails. That is what M-D4-SWAP claims — either impl substitutable —
and exactly what two separate per-impl tests could never demonstrate.

M-D1-MUT: 7 -> 8 of 14. CB-EV-0001's scoreboard is refreshed: AM-2, AM-5
and AM-9 added, AM-6 moved to enforced, and the headline total corrected
from 4 to 8 — it had gone stale inside the same workplan that produced it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-01 10:50:52 +02:00
parent 5f7d9015d9
commit 98c6cd24c3
12 changed files with 638 additions and 19 deletions

View file

@ -30,7 +30,7 @@ the property is false? A row can be *measured* and still enforce nothing.
| AM-1b link, kernel | 18 K-rules named in source | 15/18 | **unmet** | reported until 2026-08-31 |
| AM-4a dep weight, shipped runtime | ≤250,000 third-party lines | 246,250 (23 crates) | **met** | **yes** | <!-- fact:am4a_loc -->
| AM-4b dep weight, dev toolchain | ≤350,000 third-party lines | 317,021 (29 crates) | **met** | **yes** | <!-- fact:am4b_loc -->
| AM-6 throughput | ≥100,000 events/s | 1,651,400 events/s | **met, 16.5×** | **no** — nothing compares any number to 100,000 |
| AM-6 throughput | ≥100,000 events/s | 2,017,009 events/s (`make am6`) | **met, 20.2×** | **yes** — CB-WP-0006 T01 |
| AM-7 scaling | ≥0.9× at 20× workload | 1.08× | **met** | **no** — no code computes the ratio |
| AM-7 replay, timing | 100k events ≤5s | 2.18 ms (CI 2.142.23) | **met, 2,290×** | **yes** |
| AM-7 replay, hash-identical | bit-identical fold | — | **WITHDRAWN** | **no** — mutation-proven inert |
@ -38,13 +38,24 @@ the property is false? A row can be *measured* and still enforce nothing.
| AM-8 lint | fmt + clippy clean | clean, `-D warnings` | **met** | **yes** |
| AM-10 foreign types | 0 in `cb-*-api` signatures | — | **WITHDRAWN** | **no** — no such crate; population empty |
| AM-10 determinism lint (K6) | zero `HashMap`/`HashSet` in game state | 0 | **met** | **yes** |
| AM-11 impl pairs | ≥2 impls under **one conformance suite** | pair yes, suite **none** | **UNMET** | **no** — the suite does not exist |
| AM-11 impl pairs | ≥2 impls under **one conformance suite** | 2 ports, 2 impls each, one shared suite per port | **met 2026-08-01** | **yes** — break one impl and the shared suite fails |
| AM-2 LOC per rule | ≤40 | 27.2 (1,575 impl lines / 58 rules) | **met** | **yes** — CB-WP-0006 T02 |
| AM-3 synthetic workload LOC | ≤50 | — | **blocked** — the artifact has never been built | **no** |
| AM-5 clean release build | ≤60 s on bnt-lap001 | 37.3 s dev / 41.2 s shipped, best of 3, quiet | **met, 1.6×** | **no** — spec declares it ungated |
| AM-9 peak RSS | ≤64 MB | 13.4 MB | **met, 4.8×** | **yes** — CB-WP-0006 T03 |
| AM-12 cost | per-task USD | **$93.15** pinned, per task via `make cost` | **met** | **yes** | <!-- fact:pinned_total -->
AM-2, AM-3, AM-5 and AM-9 are not reported: see §6. All four are
`unmutatable` — no instrument exists to invert.
**M-D1-MUT over the whole acceptance table: 8 of 14 rows enforced**
(`make mutation-check`), up from 4 when the instrument was first run.
AM-4c is retained in that denominator after its withdrawal, deliberately:
a score improved by deleting the question is not an improvement.
**M-D1-MUT over the whole acceptance table: 4 of 14 rows enforced.**
Of the six not enforced: **AM-3** is blocked on an artifact that was never
built; **AM-4c** and **AM-5** cannot fail because the spec declares them
untargeted/ungated; **AM-10** was withdrawn; **AM-7** and **AM-8** are
partial — some clauses live, some inert. §6's note that AM-2/AM-5/AM-9 are
"not reported" was true until CB-WP-0006 and is superseded by the rows
above.
### 1a. What was corrected, and why
@ -286,7 +297,12 @@ That is what CB-WP-0002 is for. The AM-12 row above should be read as
exercised. It is the only port with a pair so far, so the metric is
met narrowly and will mean more once storage has one.~~
**Corrected 2026-07-31:** the pair exists; the **conformance suite does
not**. M-D4-SWAP is a bool over impls "passing the same conformance
not**. **Resolved 2026-08-01 (CB-WP-0006 T05):** `cb_kernel::rng::conformance`
drives `ChaChaRng` and `NullRng`; `cb_events::store::conformance` drives
`MemLogStore` and `FileLogStore`. One suite per port, both impls through
it. AM-11 is **met** and mutation-verified — breaking `NullRng::draw` to
return its bound fails the shared suite. The original text follows.
<!-- historical --> M-D4-SWAP is a bool over impls "passing the same conformance
suite", and the two impls are exercised by two separate, non-shared
tests — there is no `fn conformance<R: KernelRng>(…)` that both are
driven through. `grep -rn conformance` over every `.rs` returns a single