- G1 is spelled "added == removed == resized == renamed == 0, .text delta 0", but
gate_failures() took only added and removed and fired on text_delta > N: a pull build
whose .text SHRANK, or whose functions were resized with zero net delta - the exact
shape a null-guard or ternary rewrite produces - walked through
`--threshold 0 --fail-on-symbol-set-change --fail-on-added-bytes 0` green
- --fail-on-symbol-set-change now covers the four buckets the report prints, and
--fail-on-added-bytes 0 means byte-identical rather than "did not grow" (a positive
budget keeps the one-sided meaning). --fail-on-text-delta is the explicit spelling for
a run that wants no byte budget at all
- the gates read the threshold-0 buckets whatever --threshold says: --threshold is a
report control, and a gate that read the thresholded resize list would have quietly
weakened itself the day someone raised it. The run says so in its output
- the self-test drives the three shapes that used to pass (a shrunk .text at budget 0, a
resize and a rename at zero net delta) from the same canned transcripts
- --fail-on-added-bytes was a reserved no-op that printed "this run stays
informational"; it now exits non-zero when .text grew past the budget, and
--fail-on-symbol-set-change joins it for the added/removed buckets. Together they
are the spelling of P1's G1 ("the pull build is byte-identical"): --threshold 0
--fail-on-symbol-set-change --fail-on-added-bytes 0.
- Default behaviour is unchanged: with no gate flag the tool prints its report and
exits 0, which is what every existing caller and the informational
monolith-symbol-report job expect.
- A gate fires AFTER the Markdown and JSON are written, never before: the report is
the diagnosis, and a CI job that failed before uploading its artifact is one
nobody can act on.
- The decision lives in a pure gate_failures(), so --self-test drives it from the
same two canned transcripts as the buckets: each flag fires on the canned
add/remove/+100 delta, each stays quiet when it was not asked for, and a
tolerated budget is tolerated. A gate whose only test is a real build is a gate
nobody re-tests.
- P0.5's acceptance gate requires every `nm --defined-only -S` delta to be explainable
per symbol, and nothing in the tree reads nm or size today.
- The problem the tool exists to solve: de-nesting a type renames every mangled name
that mentions it, including inside template arguments, so a raw nm diff of a pure move
looks catastrophic. --strip-scope 'A::B::C::' rewrites 'A::B::C::X' to 'A::B::X' on the
demangled name before comparing, which folds those into a renamed-only bucket - same
normalised name, byte-identical size - and leaves the real churn visible.
- Buckets sorted by |delta|: removed, added, resized, renamed-only, unchanged, plus
.text/.data/.bss/Total from `size --format=sysv`; --only-names narrows the listing,
--markdown/--json write the report the integrator pastes into the merge commit.
- Always exits 0 (this is informational, ARCHITECTURE.md:507); --fail-on-added-bytes is
accepted and documented as reserved for the day it becomes a hard gate.
- The docstring carries the guard rails a reader would otherwise supply by assumption:
same CMAKE_BUILD_TYPE (the visibility presets differ per configuration), LTO off on
both sides, same compiler - and every report prints both paths and their byte sizes.
- --self-test runs two canned nm/size transcripts through the same parser and bucketer
and pins all five buckets, including that a de-nested member folds to renamed rather
than to an added+removed pair.