#120: arm the propagation tripwire for self-attribution + two convergence regression pins

Investigation: retail's growth propagation RECURSES natively too
(AddViewToPortals -> FixCellList -> AdjustCellView -> AddViewToPortals,
Ghidra 0x005a52d0/0x005a5250/0x005a5770, no depth guard) - the in-place
recursion shape is faithful; retail's safety is fast convergence. Our
depth-128 firing means slow/non-saturating growth (each lap of a portal
cycle nests one recursion level), not necessarily a true infinite loop.

Two dat-backed sweeps over the corner-building cell set could NOT
reproduce the T5 firing:
- PortalPlaneCrossings_InPlacePropagationConverges: +/-6cm eye sweep
  across every portal plane, seeded from both sides.
- InCellDirectionSweep_InPlacePropagationConverges: 3024 builds, in-cell
  eye grid x 8 yaw x 3 pitch (the walking-and-turning regime).
Both pass with 0 firings -> production-only ingredients suspected (full
lookup graph - one T5 firing was 0x0162, another building - and/or the
real camera path).

Armed: PortalVisibilityBuilder.ConvergenceTripwireCount (test
observable, both Build + look-in sites) + DumpPropagationChain - on the
next firing the log carries root cell, eye, per-cell frequency summary,
and the 24-entry chain tail, so the cycle's structure (A<->B ping-pong
vs 3-cycle laps) reads directly off the output. Both sweeps stay as
regression pins.

App tests: 227 green (was 225; +2 pins).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-11 15:57:25 +02:00
parent af5d424df0
commit 2d15084243
3 changed files with 206 additions and 3 deletions

View file

@ -3992,9 +3992,26 @@ cells the user was walking). T2's in-place growth (which replaced the
`MaxReprocessPerCell=16` cap) re-propagated one cell's view 128 times
within a single build — a re-emission cycle the dedup misses, or growth
ping-ponging through a reciprocal portal pair. May be load-bearing for
#117/#118 (runaway view growth → wrong clip/punch volumes). Investigate
FIRST among the post-T5 set: loudest signal, cheapest repro (the tripwire
self-reports), and a correctness invariant of the new code.
#117/#118 (runaway view growth → wrong clip/punch volumes).
**Investigation (2026-06-11, post-T5):** retail RECURSES natively too
(`AddViewToPortals → FixCellList → AdjustCellView → AddViewToPortals`,
Ghidra 0x005a52d0/0x005a5250/0x005a5770 — no depth guard), so the
recursion shape is faithful and retail's safety is FAST CONVERGENCE; our
depth-128 means slow/non-saturation our dedup admits (each lap of a
portal cycle nests one level deeper). Two dat-backed harness sweeps over
the full corner-building cell set could NOT reproduce
(`CornerFloodReplayTests.PortalPlaneCrossings_InPlacePropagationConverges`
— ±6 cm across every portal plane, both seed sides — and
`InCellDirectionSweep_InPlacePropagationConverges` — 3024 builds, in-cell
eye grid × 8 yaw × 3 pitch): firings = 0. Production-only ingredients
suspected: the full lookup graph (production reaches far more cells; one
T5 firing was 0x0162, a different building) and/or the real camera path.
**Tripwire armed for self-attribution** (`DumpPropagationChain`): the next
firing logs the root cell, eye, per-cell frequency, and the chain tail —
the cycle's structure reads directly off the log. Both sweeps stay as
regression pins (`PortalVisibilityBuilder.ConvergenceTripwireCount`).
Revisit on the next firing (the #117/#118 re-gate launch will carry it).
---