Third correction, same session, again from measurement rather than
argument.
I specified the Phase 2A/2B split as "a table is off-serving only when
every caller is proven eval-only," then computed the actual serving import
closure: 227 first-party modules from the six real serving entries, and
ALL 13 table-owning modules are in it — 13 of 13, including
generate/templates.py via pipeline.py -> realizer.py -> templates.py. So
the static test I had just written returns "everything is serving" and
discriminates nothing.
Import-reachable is not call-reachable. Importing a module executes its
table literals (pure data) but not its functions. The chain
realize_target -> render_step -> _inflect_predicate -> base_form is closed
and small enough to verify exhaustively, and realize_target has zero
non-eval non-test callers, so §1.2 and §1.4 stand as written.
But Python call-reachability is not statically decidable in general.
So the arbiter for this arc is EMPIRICAL: make the change, run the 11
pinned lanes, let byte-identity decide. That is stronger than a static
claim because it tests behaviour instead of predicting it, and it is what
the lane pins already encode as doctrine.
Consequence: 2A and 2B are now defined as a RESULT. A change is 2A if the
pins come back byte-identical and 2B if they move. Work the changes in
order, run the pins, let the split fall out — do not argue a change into
2A.