@@ -92,12 +92,16 @@ facts computed from the two closure graphs and the merge-base tree, not by an en
9292 (renames canonicalized), so nothing was dropped or swapped.
93932 . ** Added-module novelty:** every module the head closure evaluates that the base closure did
9494 not is absent from the merge-base tree at its rename-canonicalized path AND carries no code
95- from a source deleted since the merge-base. A module the tree already had, sitting outside
96- the closure, becoming eager — under its own name, a renamed one, or a rewritten move that
97- evades git's ` -M ` similarity pairing — is a new edge, not re-homed code; and without this
98- fact, weight deleted elsewhere in the closure would fund it. Rename canonicalization
99- preserves modules ALREADY IN the base closure; it is not a pass for an external module
100- moved in, which fails the check at the base path the merge-base necessarily still has.
95+ from a source deleted since the merge-base — matched when the added module shares ** two or
96+ more identical normalized top-level statements** with one deleted source; a single shared
97+ statement is treated as coincidence, because short constants (a schema version, a default
98+ timeout) repeat across the repo and branding every reuse a move would poison new files).
99+ A module the tree already had, sitting outside the closure, becoming eager — under its own
100+ name, a renamed one, or a rewritten move that evades git's ` -M ` similarity pairing — is a
101+ new edge, not re-homed code; and without this fact, weight deleted elsewhere in the closure
102+ would fund it. Rename canonicalization preserves modules ALREADY IN the base closure; it is
103+ not a pass for an external module moved in, which fails the check at the base path the
104+ merge-base necessarily still has.
1011053 . ** Flat weight:** the closure's total top-level statement count — wiring lines excluded, so
102106 re-homing is invisible to it — does not exceed the merge-base closure's.
103107
@@ -109,14 +113,19 @@ unrelated shrinkage paying for a smuggled edge — and the three together admit
109113while refusing any growth in the closure's module-scope statement weight. Statements were chosen
110114over bytes because formatting and comment churn move bytes while statements track the thing being
111115preserved: module-scope declarations and calls. The guarantee is exactly ** newly evaluated
112- modules are new code — new paths carrying no deleted source's statements — and total statement
113- weight is flat over a preserved closure** , not equality of eager behavior: a split that REPLACES
114- an existing statement with a more expensive top-level call inside an existing file moves no
115- count and no weight, so it passes — nothing count-based, in modules or statements, can see a
116- like-for-like replacement. That residue is bounded by what the tolerance cannot hide: every
117- module the head newly evaluates is a path the merge-base tree did not have AND carries none of
118- the statements of any source deleted since the merge-base (so it cannot smuggle pre-existing
119- implementation into eager scope, rewritten or not), the hard ADR-0019 checks (façade exactness,
116+ modules are new code — new paths the gate's deleted-source probe cannot match to any deleted
117+ source's statement text — and total statement weight is flat over a preserved closure** , not
118+ equality of eager behavior: a split that REPLACES an existing statement with a more expensive
119+ top-level call inside an existing file moves no count and no weight, so it passes — nothing
120+ count-based, in modules or statements, can see a like-for-like replacement. That residue is
121+ bounded by what the tolerance cannot hide: every module the head newly evaluates is a path the
122+ merge-base tree did not have AND fails the gate's move probe — its top-level statements do not
123+ share two or more identical normalized statements with any single source deleted since the
124+ merge-base (` movedFromDeletedSource ` ). This probe is deliberately conservative and textual:
125+ code REWRITTEN or reformulated during a move does not match it and would pass as novel, so what
126+ is verified is "no verbatim transplant of a deleted source," not "no repurposed logic" (the
127+ weight fact is what still refuses a transplant made heavier). What keeps the residue small is
128+ everything around the probe: the hard ADR-0019 checks (façade exactness,
120129no platform implementation before binding) stay count- and pattern-based under it, and the
121130added-module listing every failure prints is what a reviewer reads. The planted tests in
122131` scripts/__tests__/closure-growth-rule.test.ts ` pin every failing direction.
0 commit comments