CB-WP-0019 T01/T02: AM-4b asks what a contributor acquires
Some checks failed
ci / check (push) Failing after 4s

The two AM-4 budgets had the SAME scope -- one package, no dev edges --
while claiming to bound different things. AM-4b now measures the
workspace with dev edges: 57 crates / 725,258 lines where it read 29 /
317,021, having been blind to 28 crates and 408,237 lines, more source
than its own target.

Target 745,000, ~2.7% of room -- the same margin ADR-0008 D3 gave AM-4a,
applied to a number that grew because the instrument was repaired, not
because anything was added. The target moved to fit the measurement.

T02: proc-macros are COUNTED here and excluded from AM-4a, on purpose.
AM-4a asks what ships and a proc-macro never ships. AM-4b asks what is
acquired, and ADR-0007 D3's acquisition rule counts what the build
fetches -- 'it does not ship' is no answer to 'we downloaded it'. When
the rules disagree, the question each budget asks decides. Measured
share 109,585 lines / 15.1% against AM-4a's 36.2%, so ADR-0008 D2's
refusal to borrow the ratio was right by more than a factor of two.

Caught by this project's own earlier work twice: the mutation
find-string went stale and --self-test reported it BUILD-FREE (the check
CB-WP-0015 added after AM-4a's rotted for two passes), then the DFD gate
caught facts.toml carrying the old numbers.

CB-EV-0001 and ADR-0004 carried live fact: tags on historical readings.
A dated record asserting a CURRENT value is a category error, so those
occurrences are marked as-measured instead of retro-edited, and ADR-0004
gains a supersession note.

make all exits 0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-08-03 19:04:54 +02:00
parent 8793749654
commit 0b6f7c5bc8
7 changed files with 171 additions and 20 deletions

View file

@ -27,19 +27,39 @@ import sys
from repo import cargo_bin, enter_root
# AM-4a asks what a GAME SHIPS, so it measures one package's non-dev
# graph. AM-4b asks what a CONTRIBUTOR ACQUIRES, so it measures the whole
# workspace including dev edges (CB-WP-0019 T01). They had the same scope
# until 2026-08-03, which left AM-4b blind to 28 crates and 408,237 lines
# — more source than its own target.
PACKAGE = "games-ground"
WORKSPACE = "--workspace"
# ADR-0008 D2. `--edges normal` includes proc-macro crates, which run in
# the compiler and never reach a shipped binary — 89,048 lines, 36.2% of
# what this tool used to call "what a game ships", `syn` alone 66,916. The
# shipped-runtime configuration now excludes them.
#
# AM-4b is deliberately NOT corrected here: its proc-macro share has not
# been measured, and correcting a second instrument on the strength of the
# first one's ratio is the error this change exists to fix.
# AM-4b's proc-macro share is now MEASURED: 109,585 lines, 15.1% of its
# real graph (CB-WP-0019 T02). It is deliberately **not** excluded, and
# that is the opposite of AM-4a's treatment for a stated reason:
#
# AM-4a excludes proc-macros because they run in the compiler and never
# reach a shipped binary — counting them in "what a game ships" was
# simply false.
#
# AM-4b counts them, because ADR-0007 D3's acquisition rule counts what
# the build causes to be FETCHED, and a proc-macro is fetched, compiled
# and unaudited on a contributor's machine exactly like any other
# dependency. "It does not ship" is no answer to "we downloaded it".
#
# When the two rules disagree, the question each budget asks decides:
# AM-4a asks what ships, AM-4b asks what is acquired.
#
# ADR-0008 D2 said this share was unmeasured. That is no longer true.
PROC_MACRO_EXCLUDED = ["--edges", "normal,no-proc-macro"]
CONFIGS = {
"shipped-runtime": ["--no-default-features"] + PROC_MACRO_EXCLUDED,
"dev-toolchain": [],
"shipped-runtime": ["-p", PACKAGE, "--no-default-features"] + PROC_MACRO_EXCLUDED,
"dev-toolchain": [WORKSPACE, "--edges", "normal,dev"],
}
# AM-4a / AM-4b targets from specs/GameKernel.md §4. Breaching one fails
@ -50,9 +70,19 @@ CONFIGS = {
# 161,000 keeps ~2.4% of room where 250,000 kept ~1.5% — the small
# rounding up is the only thing this decision gives back, because a target
# with 1.5% of room fails on a dependency's patch release.
# CB-WP-0019 T01: the target moves to fit the measurement, never the
# reverse. AM-4b now measures 725,258 where it used to read 317,021 — not
# because anything was added, but because it started looking at what it
# always claimed to bound. A 350,000 target against a 725,258 reading
# would be a budget that is simply breached, which teaches nothing.
#
# 745,000 keeps ~2.7% of room, on ADR-0008 D3's reasoning that a target
# with ~1.5% fails on a dependency's patch release. It is NOT generosity:
# it is the same margin AM-4a got, applied to a number that grew because
# the instrument was fixed.
TARGETS = {
"shipped-runtime": 161_000,
"dev-toolchain": 350_000,
"dev-toolchain": 745_000,
}
@ -70,7 +100,10 @@ def crates(extra_args):
)
sys.exit(1)
out = subprocess.run(
[cargo, "tree", "-p", PACKAGE, "--prefix", "none"]
# The package/workspace selector now comes from the config, so the
# two budgets can ask different questions. Before CB-WP-0019 both
# were pinned to one package, which is what made AM-4b blind.
[cargo, "tree", "--prefix", "none"]
+ (extra_args if "--edges" in extra_args else ["--edges", "normal"] + extra_args),
capture_output=True,
text=True,
@ -109,6 +142,19 @@ def source_lines(name, version):
return 0
def _dev_only_dependency_is_counted():
"""AM-4b must actually see dev edges — the defect it was blind to.
`quick-js` is a dev-dependency of `cb-render-html` and is the crate
that exposed the scope defect: it landed in CB-WP-0014, AM-4b did not
move, and the ADR that added it withdrew its own cost argument as a
result. If this budget stops seeing it, the blindness is back.
"""
dev = crates(CONFIGS["dev-toolchain"])
shipped = crates(CONFIGS["shipped-runtime"])
return "quick-js" in dev, "quick-js" in shipped
def self_test():
"""Each assertion pins a failure this tool must detect.
@ -119,6 +165,21 @@ def self_test():
"""
results = []
def _check(name, ok, detail=""):
results.append((name, ok, detail))
# CB-WP-0019 T01: the two budgets must ask DIFFERENT questions, and
# `quick-js` is the case that proves it. It is a dev-dependency of
# cb-render-html; it landed in CB-WP-0014, AM-4b did not move, and
# ADR-0009 withdrew its own cost argument as a result. If AM-4b stops
# seeing it the blindness is back; if AM-4a starts seeing it, the
# shipped budget has been widened by accident.
in_dev, in_shipped = _dev_only_dependency_is_counted()
_check("AM-4b sees a dev-only dependency", in_dev,
"quick-js is dev-only and is what exposed the scope defect")
_check("AM-4a does NOT see a dev-only dependency", not in_shipped,
"the shipped budget must stay about what ships")
def check(name, ok, detail=""):
results.append((name, ok, detail))

View file

@ -135,10 +135,15 @@ def rows():
mutate=("tools/dep-weight.py",
'"shipped-runtime": 161_000,', '"shipped-runtime": 1_000,')),
Row("AM-4b", "third-party LOC, dev toolchain <= 350,000",
# CB-WP-0019 T01 widened this to the whole workspace with dev
# edges, and the literal moved with it. The stale find-string was
# caught build-free by `--self-test`, which is the check
# CB-WP-0015 added after AM-4a's mutation rotted unnoticed for two
# passes. Second catch, first one that cost nothing.
Row("AM-4b", "third-party LOC, what a contributor acquires <= 745,000",
verify=py + ["tools/dep-weight.py"],
mutate=("tools/dep-weight.py",
'"dev-toolchain": 350_000,', '"dev-toolchain": 1_000,')),
'"dev-toolchain": 745_000,', '"dev-toolchain": 1_000,')),
# Deliberately RETAINED in this denominator after its withdrawal
# from the acceptance table. Dropping it would move M-D1-MUT from