T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
# CB-EV-0001 — GROUND game kernel: acceptance evidence
|
|
|
|
|
|
|
AM-4: gate scenario YAML, retarget on audited source, re-measure
Adopts both remediations from CB-EV-0001 §4 (maintainer decision).
Option A — serde_yaml is now optional behind cb-game-runtime's
`scenarios` feature. The scenario module, the ScenarioGame impl and the
string parsers behind it are cfg-gated; cb-sim opts in explicitly. Both
configurations compile and lint clean under -D warnings.
A trap worth recording: `default-features = false` on a *member*
dependency is silently ignored when the workspace dependency does not
specify it. The first attempt gated nothing while looking correct — the
build succeeded and cargo tree still showed all six YAML crates. Fixed
by setting it on the workspace dependency. This is the positive-control
failure mode in miniature: success was not evidence the change applied.
Retarget — AM-4 now measures third-party source under audit, split by
build configuration, replacing a crate count that was unreachable
without undoing K5/K7 and that does not compare across ecosystems.
Re-measured via the new `make dep-weight`, whose own positive control
refuses to report when any crate's source cannot be located:
shipped runtime 23 crates 246,250 lines target <=250,000 met
dev toolchain 29 crates 317,021 lines target <=350,000 met
own source 3,408 lines
Scenario tooling costs 70,771 lines a shipped game never compiles —
the split the single number was hiding.
Targets are set at current measurement plus headroom, so they bind on
future growth rather than retroactively passing what had failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:35:41 +02:00
|
|
|
|
Status: **T08 complete. AM-4 remediated and re-measured 2026-07-31.**
|
Amend CB-EV-0001; add CB-WP-0002 for cost accounting
AM-4: measured what each remediation option actually buys, rather than
leaving one recommendation unquantified. serde_yaml optional removes 6
crates, not 5 — ryu belongs to that group, since serde_json now uses
zmij for floats. Full ladder: -6 to 27, serde_json -4 more to 23,
inlining SHA-256 -8 to 19, inlining ChaCha12 -4 to 15. Only
reimplementing a primitive gets under 20, so the target is unreachable
without undoing K5/K7.
Also records that crate count compares badly across ecosystems, and
offers the alternative the count is a proxy for: 307,317 lines of
third-party source under audit against 3,398 of our own.
AM-12: corrected from "uncomputable" to measured. The refusal to
estimate was right; the claim that no instrument existed was wrong.
Session transcripts carry exact per-message usage including the cache
breakdown. This session cost $248.46 at Fable 5 rates, of which 53% is
cache reads — cost is driven by context size times turn count, not by
output volume. What is still missing is per-task attribution, since
nothing marks task boundaries in a transcript.
CB-WP-0002 makes cost measurable and attributable: survey the
instruments, decide the attribution model by ADR, spec metrics that
include cost composition rather than a bare total, build a collector
whose positive control refuses to emit unreconciled numbers, and prove
it by answering a question that could not be answered before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:26:03 +02:00
|
|
|
|
Recorded: 2026-07-31. Amended 2026-07-31 — §4 gains measured savings per
|
|
|
|
|
|
remediation option, and §5 corrects AM-12 from "uncomputable" to
|
|
|
|
|
|
measured-at-session-level; see CB-WP-0002.
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
Workplan: CB-WP-0001, task T08
|
|
|
|
|
|
Spec: `specs/GameKernel.md` §4 (AM-1..AM-12)
|
|
|
|
|
|
Baseline: `research/CB-RES-0001-game-kernel.md`, measurements in
|
|
|
|
|
|
`research/CB-RES-0001-harness/boardgame-io/results-260731.json`
|
|
|
|
|
|
|
|
|
|
|
|
Machine: WSL2, Linux 6.18.33.2-microsoft-standard-WSL2, rustc 1.97.1,
|
|
|
|
|
|
`--release`, Criterion 1s warm-up / 3s measurement.
|
|
|
|
|
|
|
|
|
|
|
|
## 1. Scoreboard
|
|
|
|
|
|
|
|
|
|
|
|
| Metric | Target | Measured | Verdict |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| AM-1 rule coverage | 100% of GR-rules | 58/58 (100%) | **met** |
|
2026-07-31 10:24:39 +02:00
|
|
|
|
| AM-4a dep weight, shipped runtime | ≤250,000 third-party lines | 246,250 (23 crates) | **met** | <!-- fact:am4a_loc -->
|
|
|
|
|
|
| AM-4b dep weight, dev toolchain | ≤350,000 third-party lines | 317,021 (29 crates) | **met** | <!-- fact:am4b_loc -->
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
| AM-6 throughput | ≥100,000 events/s | 1,651,400 events/s | **met, 16.5×** |
|
|
|
|
|
|
| AM-7 scaling | ≥0.9× at 20× workload | 1.08× | **met** |
|
CI: enforce every gate; close the silent-skip holes
The gates existed; CI ran half of them and tolerated the failure case.
- cb-sim no longer has a "tolerable" non-zero exit. An unregistered game
prefix is a failure, and a run in which nothing executed is a failure.
Previously CI carried `|| test $? -eq 2`, so renaming a scenario prefix
would have skipped every scenario while the pipeline stayed green.
Verified with a negative control.
- CI now runs make coverage (AM-1) and make dep-weight (AM-4), both
added after CI was written and neither enforced until now.
- dep-weight enforces its targets instead of only reporting them.
- CI lints the shipped-runtime configuration separately, so the feature
split cannot rot unnoticed.
- Dropped the stale `make deps` target, which still measured the retired
crate-count metric.
The positive-control rule is now executable: CI runs
`cargo bench -- --test`, which executes every benchmark once, so a
workload that stalls fails the build.
That step immediately found a fourth instance of the error class it was
written for. The committed replay benchmark was the broken version — an
earlier patch never applied, leaving a command sequence that omits
Resolve, so every round produced nothing and the log-building loop spun
forever. It had never run to completion; the reported AM-7 replay
numbers came from a probe test instead. Fixed, given the same positive
control as the round loop, and re-measured from the benchmark: 100k
events fold in 2.18ms (95% CI 2.14-2.23), against a 5s budget.
Evidence now reports confidence intervals rather than point estimates,
so the 3% regression rule in MetricsAndScenarios is enforceable.
The finding worth carrying: writing the positive-control rule into
InnerLoop v1.0 did not prevent the next instance. Making it a CI step
did.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 04:02:33 +02:00
|
|
|
|
| AM-7 replay | 100k events ≤5s | 2.18 ms (CI 2.14–2.23) | **met, 2,290×** |
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
| AM-8 determinism | zero divergence, 10 replays | 1 distinct hash / 10 runs | **met** |
|
|
|
|
|
|
| AM-8 lint | fmt + clippy clean | clean, `-D warnings` | **met** |
|
|
|
|
|
|
| AM-10 foreign types | zero `HashMap`/`HashSet` | 0 | **met** |
|
|
|
|
|
|
| AM-11 impl pairs | null + reference per port | 1 of 1 (`KernelRng`) | **met, narrow** |
|
Amend CB-EV-0001; add CB-WP-0002 for cost accounting
AM-4: measured what each remediation option actually buys, rather than
leaving one recommendation unquantified. serde_yaml optional removes 6
crates, not 5 — ryu belongs to that group, since serde_json now uses
zmij for floats. Full ladder: -6 to 27, serde_json -4 more to 23,
inlining SHA-256 -8 to 19, inlining ChaCha12 -4 to 15. Only
reimplementing a primitive gets under 20, so the target is unreachable
without undoing K5/K7.
Also records that crate count compares badly across ecosystems, and
offers the alternative the count is a proxy for: 307,317 lines of
third-party source under audit against 3,398 of our own.
AM-12: corrected from "uncomputable" to measured. The refusal to
estimate was right; the claim that no instrument existed was wrong.
Session transcripts carry exact per-message usage including the cache
breakdown. This session cost $248.46 at Fable 5 rates, of which 53% is
cache reads — cost is driven by context size times turn count, not by
output volume. What is still missing is per-task attribution, since
nothing marks task boundaries in a transcript.
CB-WP-0002 makes cost measurable and attributable: survey the
instruments, decide the attribution model by ADR, spec metrics that
include cost composition rather than a bare total, build a collector
whose positive control refuses to emit unreconciled numbers, and prove
it by answering a question that could not be answered before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:26:03 +02:00
|
|
|
|
| AM-12 cost | per-task USD | $248.46 session; per-task pending | **partial** |
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
|
|
|
|
|
|
AM-2, AM-3, AM-5 and AM-9 are not reported: see §6.
|
|
|
|
|
|
|
|
|
|
|
|
## 2. Throughput and scaling (AM-6, AM-7)
|
|
|
|
|
|
|
|
|
|
|
|
Workload: 3-player GROUND rounds, 7 commands and 13 events per round
|
|
|
|
|
|
(12 on a game's fifth round, where GR-R09 ends the game). Pinned by
|
|
|
|
|
|
`bench_shape` in `games/ground/src/lib.rs`, so a change to the workload
|
|
|
|
|
|
breaks the test rather than silently rescaling the metric.
|
|
|
|
|
|
|
|
|
|
|
|
| Rounds | Throughput (events/s) | vs 5k |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| 5,000 | 1,524,200 | 1.00× |
|
|
|
|
|
|
| 10,000 | 1,638,200 | 1.07× |
|
|
|
|
|
|
| 20,000 | 1,577,000 | 1.03× |
|
|
|
|
|
|
| 40,000 | 1,626,000 | 1.07× |
|
|
|
|
|
|
| 100,000 | 1,651,400 | 1.08× |
|
|
|
|
|
|
|
CI: enforce every gate; close the silent-skip holes
The gates existed; CI ran half of them and tolerated the failure case.
- cb-sim no longer has a "tolerable" non-zero exit. An unregistered game
prefix is a failure, and a run in which nothing executed is a failure.
Previously CI carried `|| test $? -eq 2`, so renaming a scenario prefix
would have skipped every scenario while the pipeline stayed green.
Verified with a negative control.
- CI now runs make coverage (AM-1) and make dep-weight (AM-4), both
added after CI was written and neither enforced until now.
- dep-weight enforces its targets instead of only reporting them.
- CI lints the shipped-runtime configuration separately, so the feature
split cannot rot unnoticed.
- Dropped the stale `make deps` target, which still measured the retired
crate-count metric.
The positive-control rule is now executable: CI runs
`cargo bench -- --test`, which executes every benchmark once, so a
workload that stalls fails the build.
That step immediately found a fourth instance of the error class it was
written for. The committed replay benchmark was the broken version — an
earlier patch never applied, leaving a command sequence that omits
Resolve, so every round produced nothing and the log-building loop spun
forever. It had never run to completion; the reported AM-7 replay
numbers came from a probe test instead. Fixed, given the same positive
control as the round loop, and re-measured from the benchmark: 100k
events fold in 2.18ms (95% CI 2.14-2.23), against a 5s budget.
Evidence now reports confidence intervals rather than point estimates,
so the 3% regression rule in MetricsAndScenarios is enforceable.
The finding worth carrying: writing the positive-control rule into
InnerLoop v1.0 did not prevent the next instance. Making it a CI step
did.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 04:02:33 +02:00
|
|
|
|
Replay — folding one growing event log back into state (Criterion
|
|
|
|
|
|
95% CI, low/median/high):
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
|
CI: enforce every gate; close the silent-skip holes
The gates existed; CI ran half of them and tolerated the failure case.
- cb-sim no longer has a "tolerable" non-zero exit. An unregistered game
prefix is a failure, and a run in which nothing executed is a failure.
Previously CI carried `|| test $? -eq 2`, so renaming a scenario prefix
would have skipped every scenario while the pipeline stayed green.
Verified with a negative control.
- CI now runs make coverage (AM-1) and make dep-weight (AM-4), both
added after CI was written and neither enforced until now.
- dep-weight enforces its targets instead of only reporting them.
- CI lints the shipped-runtime configuration separately, so the feature
split cannot rot unnoticed.
- Dropped the stale `make deps` target, which still measured the retired
crate-count metric.
The positive-control rule is now executable: CI runs
`cargo bench -- --test`, which executes every benchmark once, so a
workload that stalls fails the build.
That step immediately found a fourth instance of the error class it was
written for. The committed replay benchmark was the broken version — an
earlier patch never applied, leaving a command sequence that omits
Resolve, so every round produced nothing and the log-building loop spun
forever. It had never run to completion; the reported AM-7 replay
numbers came from a probe test instead. Fixed, given the same positive
control as the round loop, and re-measured from the benchmark: 100k
events fold in 2.18ms (95% CI 2.14-2.23), against a 5s budget.
Evidence now reports confidence intervals rather than point estimates,
so the 3% regression rule in MetricsAndScenarios is enforceable.
The finding worth carrying: writing the positive-control rule into
InnerLoop v1.0 did not prevent the next instance. Making it a CI step
did.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 04:02:33 +02:00
|
|
|
|
| Events | Time (median) | 95% CI | Rate |
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
| 10,010 | 184.8 µs | 180.9 – 189.3 µs | 54.2M events/s |
|
|
|
|
|
|
| 100,072 | 2.183 ms | 2.142 – 2.226 ms | 45.8M events/s |
|
|
|
|
|
|
|
|
|
|
|
|
**Correction (2026-07-31).** These originally read 465 µs / 4.13 ms and
|
|
|
|
|
|
were taken from the `replay_probe` **test**, not from the benchmark —
|
|
|
|
|
|
because the benchmark did not work. See §2a.
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
|
|
|
|
|
|
### The comparison against boardgame.io, stated carefully
|
|
|
|
|
|
|
|
|
|
|
|
boardgame.io measured **1,930 moves/s at 5,000 moves**, falling to
|
|
|
|
|
|
**870 moves/s at 20,000**, and **did not finish 100,000 moves in 300 s**.
|
|
|
|
|
|
Our figure in the same unit is ~129,000 rounds/s × 7 = **~903,000
|
|
|
|
|
|
commands/s**, and 100,000 rounds complete in 776 ms.
|
|
|
|
|
|
|
|
|
|
|
|
That is roughly a 400–500× ratio, and it is **not a like-for-like
|
|
|
|
|
|
measurement**. Four differences matter, all favouring us:
|
|
|
|
|
|
|
|
|
|
|
|
1. **Different language and process model.** Rust in-process against
|
|
|
|
|
|
Node.js with immutable state, patch generation and undo history.
|
|
|
|
|
|
2. **Different feature set.** boardgame.io's per-move cost includes
|
|
|
|
|
|
producing client patches and maintaining undo state; the run with
|
|
|
|
|
|
`--disable-undo` still degraded (0.66× at 40k). We do neither.
|
|
|
|
|
|
3. **Different workload shape.** Our 5-round games (GR-R09) bound state
|
|
|
|
|
|
size by construction. The boardgame.io harness ran one match with
|
|
|
|
|
|
unbounded history, which is exactly the axis it degraded on.
|
|
|
|
|
|
4. **No network or storage layer** on our side.
|
|
|
|
|
|
|
|
|
|
|
|
Point 3 is the important one and it is why the flat curve in the table
|
|
|
|
|
|
above is *weak evidence on its own* — a game that resets every five
|
|
|
|
|
|
rounds cannot exhibit history-growth degradation. The replay benchmark
|
|
|
|
|
|
is the honest test of that axis, because there the log grows without
|
|
|
|
|
|
bound, and it stays linear (21.5M → 24.2M events/s from 10k to 100k).
|
|
|
|
|
|
|
|
|
|
|
|
**Claim we are willing to defend:** the kernel meets AM-6 and AM-7 with
|
|
|
|
|
|
large margin, and does not degrade as event-log length grows.
|
|
|
|
|
|
**Claim we are not making:** that Clay-Borg is ~450× "faster than
|
|
|
|
|
|
boardgame.io" as a like-for-like engine comparison. Per the
|
|
|
|
|
|
InnerLoop parity-cap rule, a cross-runtime ratio this coarse is not a
|
|
|
|
|
|
verdict, it is a direction.
|
|
|
|
|
|
|
|
|
|
|
|
### A measurement error found and corrected
|
|
|
|
|
|
|
|
|
|
|
|
The first run of this benchmark reported **9.3M events/s with a
|
|
|
|
|
|
perfectly flat curve** — a number that would have been reported as a
|
|
|
|
|
|
93× beat of AM-6. It was wrong. The workload had P2 selecting SUPPORT
|
|
|
|
|
|
every round while being attacked; after three rounds P2 sat at Stress 4,
|
|
|
|
|
|
GR-R03 rejected the SUPPORT, the round never completed, and the loop
|
|
|
|
|
|
spun on rejected commands. Throughput was computed as
|
|
|
|
|
|
`rounds × 13 events` while most rounds produced 2.
|
|
|
|
|
|
|
|
|
|
|
|
Found by a probe test asserting that a round produces events at all.
|
|
|
|
|
|
The benchmark now asserts the per-round event count on every round and
|
|
|
|
|
|
panics rather than measuring a stalled loop. The corrected figure is
|
|
|
|
|
|
**5.6× lower** than the bogus one.
|
|
|
|
|
|
|
CI: enforce every gate; close the silent-skip holes
The gates existed; CI ran half of them and tolerated the failure case.
- cb-sim no longer has a "tolerable" non-zero exit. An unregistered game
prefix is a failure, and a run in which nothing executed is a failure.
Previously CI carried `|| test $? -eq 2`, so renaming a scenario prefix
would have skipped every scenario while the pipeline stayed green.
Verified with a negative control.
- CI now runs make coverage (AM-1) and make dep-weight (AM-4), both
added after CI was written and neither enforced until now.
- dep-weight enforces its targets instead of only reporting them.
- CI lints the shipped-runtime configuration separately, so the feature
split cannot rot unnoticed.
- Dropped the stale `make deps` target, which still measured the retired
crate-count metric.
The positive-control rule is now executable: CI runs
`cargo bench -- --test`, which executes every benchmark once, so a
workload that stalls fails the build.
That step immediately found a fourth instance of the error class it was
written for. The committed replay benchmark was the broken version — an
earlier patch never applied, leaving a command sequence that omits
Resolve, so every round produced nothing and the log-building loop spun
forever. It had never run to completion; the reported AM-7 replay
numbers came from a probe test instead. Fixed, given the same positive
control as the round loop, and re-measured from the benchmark: 100k
events fold in 2.18ms (95% CI 2.14-2.23), against a 5s budget.
Evidence now reports confidence intervals rather than point estimates,
so the 3% regression rule in MetricsAndScenarios is enforceable.
The finding worth carrying: writing the positive-control rule into
InnerLoop v1.0 did not prevent the next instance. Making it a CI step
did.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 04:02:33 +02:00
|
|
|
|
### 2a. A fourth measurement error, found by enforcing the rule
|
|
|
|
|
|
|
|
|
|
|
|
The replay benchmark committed alongside this evidence was **the broken
|
|
|
|
|
|
version**. A `python3` patch that was supposed to replace its
|
|
|
|
|
|
log-building loop never applied, leaving a sequence that omits `Resolve`
|
|
|
|
|
|
— so `EndRound` was rejected, every round produced no events, and the
|
|
|
|
|
|
`while log.len() < target` loop spun forever. It was never run to
|
|
|
|
|
|
completion; the AM-7 replay numbers were taken from a separate probe
|
|
|
|
|
|
test instead, and the dead benchmark was committed and left hanging.
|
|
|
|
|
|
|
|
|
|
|
|
Found by adding `cargo bench -- --test` to CI, which runs every
|
|
|
|
|
|
benchmark once. That is the fourth instance of one error class in this
|
|
|
|
|
|
project — a harness that appears to work while doing no work — and the
|
|
|
|
|
|
**first one caught by a gate rather than by noticing**.
|
|
|
|
|
|
|
|
|
|
|
|
The replay loop now carries the positive control the round loop already
|
|
|
|
|
|
had: it asserts each round appended events and fails rather than
|
|
|
|
|
|
spinning. Corrected figures are in the table above; both configurations
|
|
|
|
|
|
still clear the AM-7 budget by three orders of magnitude.
|
|
|
|
|
|
|
|
|
|
|
|
The lesson recorded for the loop: writing the positive-control rule into
|
|
|
|
|
|
`specs/InnerLoop.md` did **not** prevent the next instance. Making it a
|
|
|
|
|
|
CI step did. Prose rules do not enforce themselves.
|
|
|
|
|
|
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
## 3. Determinism (AM-8)
|
|
|
|
|
|
|
|
|
|
|
|
- Every scenario runs twice per invocation with the same seed and fails
|
|
|
|
|
|
on state-hash divergence (K8). 21/21 pass.
|
|
|
|
|
|
- Ten consecutive full runs of all 21 scenarios produced **one distinct
|
|
|
|
|
|
output hash**, i.e. zero divergence.
|
|
|
|
|
|
- `cargo fmt --check` and `cargo clippy --workspace --all-targets
|
|
|
|
|
|
-D warnings` are clean.
|
|
|
|
|
|
- `clippy.toml` denies `HashMap`/`HashSet` workspace-wide (K6); the
|
|
|
|
|
|
aggregate holds only ordered collections, so iteration order cannot
|
|
|
|
|
|
vary between runs.
|
|
|
|
|
|
|
AM-4: gate scenario YAML, retarget on audited source, re-measure
Adopts both remediations from CB-EV-0001 §4 (maintainer decision).
Option A — serde_yaml is now optional behind cb-game-runtime's
`scenarios` feature. The scenario module, the ScenarioGame impl and the
string parsers behind it are cfg-gated; cb-sim opts in explicitly. Both
configurations compile and lint clean under -D warnings.
A trap worth recording: `default-features = false` on a *member*
dependency is silently ignored when the workspace dependency does not
specify it. The first attempt gated nothing while looking correct — the
build succeeded and cargo tree still showed all six YAML crates. Fixed
by setting it on the workspace dependency. This is the positive-control
failure mode in miniature: success was not evidence the change applied.
Retarget — AM-4 now measures third-party source under audit, split by
build configuration, replacing a crate count that was unreachable
without undoing K5/K7 and that does not compare across ecosystems.
Re-measured via the new `make dep-weight`, whose own positive control
refuses to report when any crate's source cannot be located:
shipped runtime 23 crates 246,250 lines target <=250,000 met
dev toolchain 29 crates 317,021 lines target <=350,000 met
own source 3,408 lines
Scenario tooling costs 70,771 lines a shipped game never compiles —
the split the single number was hiding.
Targets are set at current measurement plus headroom, so they bind on
future growth rather than retroactively passing what had failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:35:41 +02:00
|
|
|
|
## 4. AM-4 — remediated and re-measured
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
|
AM-4: gate scenario YAML, retarget on audited source, re-measure
Adopts both remediations from CB-EV-0001 §4 (maintainer decision).
Option A — serde_yaml is now optional behind cb-game-runtime's
`scenarios` feature. The scenario module, the ScenarioGame impl and the
string parsers behind it are cfg-gated; cb-sim opts in explicitly. Both
configurations compile and lint clean under -D warnings.
A trap worth recording: `default-features = false` on a *member*
dependency is silently ignored when the workspace dependency does not
specify it. The first attempt gated nothing while looking correct — the
build succeeded and cargo tree still showed all six YAML crates. Fixed
by setting it on the workspace dependency. This is the positive-control
failure mode in miniature: success was not evidence the change applied.
Retarget — AM-4 now measures third-party source under audit, split by
build configuration, replacing a crate count that was unreachable
without undoing K5/K7 and that does not compare across ecosystems.
Re-measured via the new `make dep-weight`, whose own positive control
refuses to report when any crate's source cannot be located:
shipped runtime 23 crates 246,250 lines target <=250,000 met
dev toolchain 29 crates 317,021 lines target <=350,000 met
own source 3,408 lines
Scenario tooling costs 70,771 lines a shipped game never compiles —
the split the single number was hiding.
Targets are set at current measurement plus headroom, so they bind on
future growth rather than retroactively passing what had failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:35:41 +02:00
|
|
|
|
**Original result: NOT MET, 33 transitive crates against a ≤20 target.**
|
|
|
|
|
|
Resolved by adopting both remediations (maintainer decision,
|
|
|
|
|
|
2026-07-31): `serde_yaml` was made optional, and the metric was
|
|
|
|
|
|
retargeted onto third-party source under audit.
|
|
|
|
|
|
|
|
|
|
|
|
### Re-measurement (`make dep-weight`)
|
|
|
|
|
|
|
|
|
|
|
|
| Configuration | Crates | Third-party LOC | Target | Verdict |
|
|
|
|
|
|
|---|---|---|---|---|
|
2026-07-31 10:24:39 +02:00
|
|
|
|
| Shipped runtime (`--no-default-features`) | 23 | 246,250 | ≤250,000 | **met** | <!-- fact:am4a_loc --><!-- fact:am4a_target -->
|
|
|
|
|
|
| Dev toolchain (default features) | 29 | 317,021 | ≤350,000 | **met** | <!-- fact:am4b_loc --><!-- fact:am4b_target -->
|
AM-4: gate scenario YAML, retarget on audited source, re-measure
Adopts both remediations from CB-EV-0001 §4 (maintainer decision).
Option A — serde_yaml is now optional behind cb-game-runtime's
`scenarios` feature. The scenario module, the ScenarioGame impl and the
string parsers behind it are cfg-gated; cb-sim opts in explicitly. Both
configurations compile and lint clean under -D warnings.
A trap worth recording: `default-features = false` on a *member*
dependency is silently ignored when the workspace dependency does not
specify it. The first attempt gated nothing while looking correct — the
build succeeded and cargo tree still showed all six YAML crates. Fixed
by setting it on the workspace dependency. This is the positive-control
failure mode in miniature: success was not evidence the change applied.
Retarget — AM-4 now measures third-party source under audit, split by
build configuration, replacing a crate count that was unreachable
without undoing K5/K7 and that does not compare across ecosystems.
Re-measured via the new `make dep-weight`, whose own positive control
refuses to report when any crate's source cannot be located:
shipped runtime 23 crates 246,250 lines target <=250,000 met
dev toolchain 29 crates 317,021 lines target <=350,000 met
own source 3,408 lines
Scenario tooling costs 70,771 lines a shipped game never compiles —
the split the single number was hiding.
Targets are set at current measurement plus headroom, so they bind on
future growth rather than retroactively passing what had failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:35:41 +02:00
|
|
|
|
| Our own source | — | 3,408 | — | — |
|
|
|
|
|
|
|
|
|
|
|
|
Scenario tooling costs **70,771 lines that a shipped game never
|
|
|
|
|
|
compiles**. That split is the substantive result: the single number
|
|
|
|
|
|
previously reported conflated a runtime concern with a test concern.
|
|
|
|
|
|
|
|
|
|
|
|
**What actually changed in the build.** `cb-game-runtime` gained a
|
|
|
|
|
|
`scenarios` feature carrying `serde_yaml`; the scenario module, the
|
|
|
|
|
|
`ScenarioGame` impl and the string parsers behind it are `#[cfg]`-gated.
|
|
|
|
|
|
Both configurations compile and lint clean under `-D warnings`.
|
|
|
|
|
|
|
|
|
|
|
|
One trap worth recording: setting `default-features = false` on a
|
|
|
|
|
|
*member* dependency is silently ignored when the workspace dependency
|
|
|
|
|
|
does not specify it, so the first attempt gated nothing while appearing
|
|
|
|
|
|
to work — `cargo tree` still showed all six YAML crates. The fix was
|
|
|
|
|
|
setting `default-features = false` on the workspace dependency itself,
|
|
|
|
|
|
with `cb-sim` opting into `scenarios` explicitly. This is exactly the
|
|
|
|
|
|
class of error InnerLoop v1.0's positive-control rule targets: the build
|
|
|
|
|
|
succeeded and the feature flag looked applied. It was caught by checking
|
|
|
|
|
|
the dependency graph rather than trusting that the edit had worked.
|
|
|
|
|
|
|
|
|
|
|
|
### Why the target moved, and why that is not moving the goalposts
|
|
|
|
|
|
|
|
|
|
|
|
The ≤20 crate target was retired for two measured reasons, both
|
|
|
|
|
|
recorded before the decision was taken:
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
|
|
|
|
|
|
Attribution:
|
|
|
|
|
|
|
|
|
|
|
|
| Group | Crates | Count |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `sha2` (K7 state hashing) | sha2, digest, block-buffer, crypto-common, generic-array, typenum, cpufeatures, cfg-if | 8 |
|
|
|
|
|
|
| serde derive chain | serde_derive, proc-macro2, quote, syn, unicode-ident | 5 |
|
Amend CB-EV-0001; add CB-WP-0002 for cost accounting
AM-4: measured what each remediation option actually buys, rather than
leaving one recommendation unquantified. serde_yaml optional removes 6
crates, not 5 — ryu belongs to that group, since serde_json now uses
zmij for floats. Full ladder: -6 to 27, serde_json -4 more to 23,
inlining SHA-256 -8 to 19, inlining ChaCha12 -4 to 15. Only
reimplementing a primitive gets under 20, so the target is unreachable
without undoing K5/K7.
Also records that crate count compares badly across ecosystems, and
offers the alternative the count is a proxy for: 307,317 lines of
third-party source under audit against 3,398 of our own.
AM-12: corrected from "uncomputable" to measured. The refusal to
estimate was right; the claim that no instrument existed was wrong.
Session transcripts carry exact per-message usage including the cache
breakdown. This session cost $248.46 at Fable 5 rates, of which 53% is
cache reads — cost is driven by context size times turn count, not by
output volume. What is still missing is per-task attribution, since
nothing marks task boundaries in a transcript.
CB-WP-0002 makes cost measurable and attributable: survey the
instruments, decide the attribution model by ADR, spec metrics that
include cost composition rather than a bare total, build a collector
whose positive control refuses to emit unreconciled numbers, and prove
it by answering a question that could not be answered before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:26:03 +02:00
|
|
|
|
| serde runtime + json | serde, serde_core, serde_json, itoa, memchr, zmij | 6 |
|
|
|
|
|
|
| `serde_yaml` (scenario files only) | serde_yaml, unsafe-libyaml, indexmap, hashbrown, equivalent, ryu | 6 |
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
| `rand_chacha` (K5 seeded RNG) | rand_chacha, rand_core, ppv-lite86, zerocopy | 4 |
|
|
|
|
|
|
| Clay-Borg crates | cb-kernel, cb-events, cb-game-runtime, games-ground | 4 |
|
|
|
|
|
|
|
AM-4: gate scenario YAML, retarget on audited source, re-measure
Adopts both remediations from CB-EV-0001 §4 (maintainer decision).
Option A — serde_yaml is now optional behind cb-game-runtime's
`scenarios` feature. The scenario module, the ScenarioGame impl and the
string parsers behind it are cfg-gated; cb-sim opts in explicitly. Both
configurations compile and lint clean under -D warnings.
A trap worth recording: `default-features = false` on a *member*
dependency is silently ignored when the workspace dependency does not
specify it. The first attempt gated nothing while looking correct — the
build succeeded and cargo tree still showed all six YAML crates. Fixed
by setting it on the workspace dependency. This is the positive-control
failure mode in miniature: success was not evidence the change applied.
Retarget — AM-4 now measures third-party source under audit, split by
build configuration, replacing a crate count that was unreachable
without undoing K5/K7 and that does not compare across ecosystems.
Re-measured via the new `make dep-weight`, whose own positive control
refuses to report when any crate's source cannot be located:
shipped runtime 23 crates 246,250 lines target <=250,000 met
dev toolchain 29 crates 317,021 lines target <=350,000 met
own source 3,408 lines
Scenario tooling costs 70,771 lines a shipped game never compiles —
the split the single number was hiding.
Targets are set at current measurement plus headroom, so they bind on
future growth rather than retroactively passing what had failed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:35:41 +02:00
|
|
|
|
1. **It was unreachable without undoing the spec's own contracts.**
|
|
|
|
|
|
Measured ladder: `serde_yaml` optional −6 (→27), dropping
|
|
|
|
|
|
`serde_json` −4 (→23), inlining SHA-256 −8 (→19), inlining ChaCha12
|
|
|
|
|
|
−4 (→15). Nothing reaches 20 except reimplementing a primitive that
|
|
|
|
|
|
K5 or K7 requires — trading an audited implementation for a
|
|
|
|
|
|
scoreboard number.
|
|
|
|
|
|
2. **Crate count does not compare across ecosystems.** Rust splits
|
|
|
|
|
|
crates far more finely than npm. The same granularity difference made
|
|
|
|
|
|
"33 vs 120 npm packages" flatter us *and* made ≤20 punish us.
|
|
|
|
|
|
|
|
|
|
|
|
Third-party source under audit is what the count was proxying for, is
|
|
|
|
|
|
comparable across ecosystems, and cannot be gamed by granularity. The
|
|
|
|
|
|
new targets are set at roughly the current measurement plus headroom,
|
|
|
|
|
|
so they bind on future growth rather than retroactively passing
|
|
|
|
|
|
something that failed: adding another `serde_yaml`-sized dependency to
|
|
|
|
|
|
the shipped runtime would breach AM-4a.
|
|
|
|
|
|
|
|
|
|
|
|
What we did **not** do: hand-roll SHA-256 or ChaCha to win a count.
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
|
|
|
|
|
|
## 5. Cost log (AM-12)
|
|
|
|
|
|
|
|
|
|
|
|
Per `specs/MetricsAndScenarios.md` §1a. Model: Claude Fable 5, at
|
|
|
|
|
|
`benchmarks/baselines/model-prices.toml` rates ($10/$50 per MTok).
|
|
|
|
|
|
|
|
|
|
|
|
| Task | Model | Iterations | Notes |
|
|
|
|
|
|
|---|---|---|---|
|
Amend CB-EV-0001; add CB-WP-0002 for cost accounting
AM-4: measured what each remediation option actually buys, rather than
leaving one recommendation unquantified. serde_yaml optional removes 6
crates, not 5 — ryu belongs to that group, since serde_json now uses
zmij for floats. Full ladder: -6 to 27, serde_json -4 more to 23,
inlining SHA-256 -8 to 19, inlining ChaCha12 -4 to 15. Only
reimplementing a primitive gets under 20, so the target is unreachable
without undoing K5/K7.
Also records that crate count compares badly across ecosystems, and
offers the alternative the count is a proxy for: 307,317 lines of
third-party source under audit against 3,398 of our own.
AM-12: corrected from "uncomputable" to measured. The refusal to
estimate was right; the claim that no instrument existed was wrong.
Session transcripts carry exact per-message usage including the cache
breakdown. This session cost $248.46 at Fable 5 rates, of which 53% is
cache reads — cost is driven by context size times turn count, not by
output volume. What is still missing is per-task attribution, since
nothing marks task boundaries in a transcript.
CB-WP-0002 makes cost measurable and attributable: survey the
instruments, decide the attribution model by ADR, spec metrics that
include cost composition rather than a bare total, build a collector
whose positive control refuses to emit unreconciled numbers, and prove
it by answering a question that could not be answered before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:26:03 +02:00
|
|
|
|
| CB-WP-0001 (whole session, T01–T09) | claude-fable-5 | 6 T08 code iterations + benchmarks | $248.46 measured; per-task split pending CB-WP-0002 |
|
|
|
|
|
|
|
|
|
|
|
|
**Correction (2026-07-31).** This section originally recorded AM-12 as
|
|
|
|
|
|
*uncomputable*. That was wrong. The declining to estimate was right; the
|
|
|
|
|
|
conclusion that no instrument existed was not. Every session transcript
|
|
|
|
|
|
(`~/.claude/projects/<slug>/<session>.jsonl`) carries exact per-message
|
|
|
|
|
|
`usage` including the cache breakdown. Read for this session:
|
|
|
|
|
|
|
|
|
|
|
|
| Component | Tokens | Cost (Fable 5) |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| Output | 585,528 | $29.28 |
|
|
|
|
|
|
| Cache read | 131,863,164 | $131.86 |
|
|
|
|
|
|
| Cache write (1h) | 4,365,668 | $87.31 |
|
|
|
|
|
|
| Input | 1,090 | $0.01 |
|
|
|
|
|
|
| **Session total** | | **$248.46** (~$124 on Opus 5) |
|
|
|
|
|
|
|
|
|
|
|
|
**53% of the cost is cache reads**, not output. Cost in an agentic loop
|
|
|
|
|
|
is driven by context size × turn count, which no "tokens per task"
|
|
|
|
|
|
metric would have surfaced.
|
|
|
|
|
|
|
|
|
|
|
|
Still missing is *attribution*: this is a whole-session figure, not a
|
|
|
|
|
|
per-task one, because nothing marks task boundaries in the transcript.
|
|
|
|
|
|
That is what CB-WP-0002 is for. The AM-12 row above should be read as
|
|
|
|
|
|
"session-level cost measured; per-task attribution pending CB-WP-0002".
|
T08 complete: benchmarks, determinism evidence, and one missed metric
evidence/CB-EV-0001-game-kernel.md records the acceptance run against
the CB-RES-0001 baseline.
Met: AM-1 rule coverage 58/58; AM-6 throughput 1.65M events/s against a
100k target; AM-7 scaling 1.08x at 20x workload and a 100k-event replay
in 4.13ms against a 5s budget; AM-8 zero divergence over 10 full runs
with fmt and clippy clean; AM-10 zero foreign collection types.
Not met and reported as such: AM-4 at 33 transitive crates against a
<=20 target. Attribution is in the evidence file. The recommended fix
is making serde_yaml optional (-5, a test-only concern), after which
the remainder is sha2 and rand_chacha, which K5 and K7 require. We are
not hand-rolling crypto primitives to win a dependency count.
AM-12 is recorded as uncomputable: per-task token counts were never
instrumented, and inventing a USD figure would defeat the metric.
A measurement error was found and corrected before publication. The
first benchmark reported 9.3M events/s on a flat curve. The workload
had a player selecting SUPPORT while parked at Stress 4, so GR-R03
rejected it, rounds never completed, and throughput was computed for
rounds that never happened. The bench now asserts the per-round event
count and panics rather than measuring a stalled loop. The corrected
figure is 5.6x lower.
The evidence file states plainly what the boardgame.io comparison does
and does not support: the ~450x command-rate ratio is cross-runtime and
cross-feature-set, so it is a direction, not a verdict, per the
InnerLoop parity-cap rule.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 03:09:14 +02:00
|
|
|
|
|
|
|
|
|
|
## 6. Metrics not reported
|
|
|
|
|
|
|
|
|
|
|
|
- **AM-2, AM-3, AM-5** — specification-quality metrics that need a
|
|
|
|
|
|
second capability to compare against; a single data point is not a
|
|
|
|
|
|
measurement.
|
|
|
|
|
|
- **AM-9 (≤64MB)** — not instrumented. The aggregate is a few KB and
|
|
|
|
|
|
the largest log measured here is 100k events, so the budget is very
|
|
|
|
|
|
unlikely to bind, but "unlikely" is not "measured" and it is left
|
|
|
|
|
|
unclaimed.
|
|
|
|
|
|
- **AM-11** — the `KernelRng` null/reference pair exists and is
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
## 7. Rules implemented under a provisional default
|
|
|
|
|
|
|
|
|
|
|
|
Ten U-items in `specs/GroundRules.md` carry PROVISIONAL defaults. Those
|
|
|
|
|
|
realized here are U2 (clamp on every application), U3 (DENY with no
|
|
|
|
|
|
legal target is a no-op that still advances), U4 (deck reshuffle), U5
|
|
|
|
|
|
(REVERSE owner relief applies whether or not the Reverse was rejected)
|
|
|
|
|
|
and U8 (GROUND—OU cancellation precedes Protection).
|
|
|
|
|
|
|
|
|
|
|
|
One further ambiguity was found during T08 and is **not** in the U-list:
|
|
|
|
|
|
**GR-E02's "successes"** is undefined in dataset 0.1. It is implemented
|
|
|
|
|
|
as the count of claimed Problems. Both scoring scenarios are marked
|
|
|
|
|
|
`provisional: true`.
|
|
|
|
|
|
|
|
|
|
|
|
All provisional behaviour lives behind named functions and is covered by
|
|
|
|
|
|
scenarios tagged `provisional: true`, so a ground-game ruling flips a
|
|
|
|
|
|
scenario rather than the kernel (K16). **Action for ground-game:** rule
|
|
|
|
|
|
on the ten U-items and on GR-E02's "successes".
|