From a743ea64a37b16ab1cf39791507630f3eb87d443 Mon Sep 17 00:00:00 2001 From: BlackMark Date: Thu, 30 Jul 2026 16:32:35 +0200 Subject: [PATCH] tool: a size collision across build trees is an error, not a coin toss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- tools/sizes.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/sizes.py b/tools/sizes.py index 184e460..520b136 100755 --- a/tools/sizes.py +++ b/tools/sizes.py @@ -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])