docs(plans): the 2A/2B split is a measured result, not a prediction

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.
This commit is contained in:
Shay 2026-07-26 15:55:03 -07:00
parent 7acd6b9722
commit 3c5084ad2d

View file

@ -357,12 +357,39 @@ The corrected rule for this arc: **a table is off-serving only when every caller
is proven to be eval-only.** Phase 2 is therefore split by that test, not by
topic.
**Second correction, same session — the static test is not sufficient.** I then
computed the serving import closure (227 first-party modules from `chat/runtime.py`,
`core/cognition/pipeline.py`, `chat/deduction_surface.py`,
`chat/curriculum_surface.py`, `chat/pack_grounding.py`,
`chat/teaching_grounding.py`) and **all 13 table-owning modules are in it —
13 of 13**, including `generate/templates.py` via
`pipeline.py``realizer.py``templates.py`. A static import test therefore
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 call 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 that subtree is genuinely call-unreachable
from serving, and §1.2/§1.4 stand.
But Python call-reachability is not statically decidable in general, so the
arbiter for this arc is **empirical, not analytical**: make the change, run the
11 pinned lanes, and let byte-identity decide. That is stronger than a static
claim because it tests behaviour rather than predicting it, and it is the
doctrine the lane pins already encode.
**The 2A/2B split is therefore a *result*, not a prediction.** A change is 2A
if the pins come back byte-identical and 2B if they move. Work the changes in
the order below, run the pins, and let the split fall out. Do not argue a change
into 2A.
| phase | changes served output? | gate |
|---|---|---|
| 1 — instrument | no (new files only) | local smoke/deductive |
| 2A — off-serving tables | no (callers proven eval-only) | byte-identical lane SHAs |
| 2B — serving tables + render fix | **yes** | **explicit authorization** |
| 3 — agreement defect | no (`render_step` is eval-only) | 26/26 oracle |
| 2A — changes the pins prove inert | no (**measured**, not predicted) | byte-identical lane SHAs |
| 2B — changes that move a pin | **yes** | **explicit authorization** |
| 3 — agreement defect | no (`realize_target` has zero serving callers) | 26/26 oracle |
| 4 — which realizer serves | **yes** | **explicit authorization** |
| 5 — round-trip + diversity | **yes** | **explicit authorization** |
@ -395,9 +422,9 @@ Deliver:
also rejecting the positive corpus, the grammar formulation is wrong. **Stop and
reassess — do not tune a threshold to split them.**
### Phase 2A — One source of linguistic truth, off-serving only
### Phase 2A — One source of linguistic truth (the hash-inert subset)
*Touches:* the word-tables in §1.3 whose every caller is proven eval-only, plus
*Touches:* the word-tables in §1.3 whose unification the lane pins prove inert, plus
new single-owner modules. *Serving risk:* none, and proven by byte-identity.
Deliver: