tool: a size collision across build trees is an error, not a coin toss
sizes.py merged every owned tree's rows and let the last one win, so a stale reflect tree — last built before the window constants moved — reported the atmega8's old stock size over the fresh build and failed the README check with yesterday's number. Generated and reflect must answer with the same bytes (the identity invariant), so the same target measuring two sizes is a stale tree or an identity breach; collect() refuses now, naming both trees. The stale reflect trees are removed — the reflect sweep rebuilds them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,6 +84,20 @@ def collect() -> dict[str, list[tuple[str, int, int]]]:
|
||||
for match in SIZE_TEST.finditer((tree / "CTestTestfile.cmake").read_text()):
|
||||
found.setdefault(chip, []).append((match["name"], match["elf"], int(match["limit"])))
|
||||
sizes = measure([elf for rows in found.values() for _, elf, _ in rows], tool)
|
||||
# A chip's generated and reflect trees must answer with the same bytes
|
||||
# (the identity invariant), so the same target measuring two sizes means
|
||||
# a stale tree — or an identity breach. Either is a finding; picking one
|
||||
# silently is how a gate reports another build's numbers as today's.
|
||||
for chip, rows in found.items():
|
||||
seen: dict[str, tuple[int, str]] = {}
|
||||
for name, elf, _ in rows:
|
||||
if elf not in sizes:
|
||||
continue
|
||||
if name in seen and seen[name][0] != sizes[elf]:
|
||||
sys.exit(f"{chip} {name}: {seen[name][0]} B in {seen[name][1]} but "
|
||||
f"{sizes[elf]} B in {elf} — a stale tree (rebuild or remove it) "
|
||||
f"or a cross-mode identity breach")
|
||||
seen.setdefault(name, (sizes[elf], elf))
|
||||
measured = {
|
||||
chip: sorted(((name, sizes[elf], limit) for name, elf, limit in rows if elf in sizes),
|
||||
key=lambda row: -row[1])
|
||||
|
||||
Reference in New Issue
Block a user