#147 range caret: NaN poisons a process-lifetime static -- and the caret's input was never logged

Oracle: "no range finder on this drop" + a screenshot -- tick marks present,
moving caret absent, one drop, only tester affected.

Not the host, not the chassis, not his destroyed HUD.  From the four field
logs: he WAS hosting (`[lobby] host:` appears only in his log) but range
computed fine on his node (1806 nonzero samples) and the reticle built on all
6 drops; a second tester flew a Thor the same night without hosting and saw
nothing, and the ladder is shared HudSimulation/BTReticleRenderable, not
per-chassis content; his HUD was destroyed twice but for 11s and 26s only, and
a destroyed HUD costs the LOCK (_DAT_004b7ec4 = 0.75), not the caret.

THE DEFECT.  sShownRange -- what the caret binds to -- is a function-level
static in mech4's targeting step: one cell for the whole process, shared by
every mech, carried across drops, never re-seeded.  NaN is ABSORBING in

    step = trueRange - sShownRange;
    if (step >  maxStep) step =  maxStep;      // false for NaN
    if (step < -maxStep) step = -maxStep;      // false for NaN
    sShownRange += step;

so one poisoned frame makes it NaN for the life of the process.  The consumer
repeats the mistake -- BTReticleRenderable::Draw clamps with the same two
comparisons -- so NaN reaches AddPoint/ConcatMatrix and the caret + its bar
become degenerate geometry that STOPS RENDERING, while every static reticle
element including the tick marks still draws.  That is the reported symptom
exactly, and it is sticky until relaunch.

WHY NO LOG COULD SETTLE IT.  The caret's actual input had NO diagnostic
anywhere: BT_RANGE_LOG instruments the PICK (#4), and [target]'s `range=` is a
SEPARATE locally-recomputed Sqrt in the weapon-range check -- neither is
sShownRange or gBTHudRangeStorage.  Grepping the field logs for NaN returns
nothing because the poisoned variable was never printed.  Absence of the
signal was not evidence of absence.

FIX (4 parts):
  1. re-seed sShownRange when the viewpoint mech CHANGES, so a new drop starts
     at the binary's 1200 default.  Deliberately NOT on respawn -- that reuses
     the entity, and the binary does not reset the readout on respawn either.
  2. producer NaN trap -> re-seed to 1200 instead of propagating.
  3. NaN-safe consumer clamp (test x == x first) -> fall back to the authentic
     no-target peg rather than rendering nothing.
  4. BT_RANGE_LOG now prints the caret's real input at 1 Hz plus a
     "[range] NaN TRAPPED" receipt, so the next field log CAN settle it.

STATUS [T3 on the field link].  The defect and the symptom match exactly and
the fix stands on its own merits -- a process-lifetime static feeding unguarded
float geometry is a bug regardless.  But the causal link to Oracle's report is
INFERENCE: the NaN source is unidentified and this has not been reproduced.
Field-verify with BT_RANGE_LOG=1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018SgmXGNMXavXiafKXf9MDC
This commit is contained in:
Joe DiPrima
2026-08-08 07:16:32 -05:00
co-authored by Claude Opus 5
parent 832bec0966
commit 1ae57398f1
4 changed files with 162 additions and 1 deletions
+15
View File
@@ -568,6 +568,21 @@ and every instrument is now live [T2]:**
threshold for HUD page visibility" was neither heat nor page visibility) — corrected in place,
with the live lock/slide implementation staying in mech4.cpp's targeting step, which had both
thresholds right all along.
**THE CARET CAN DIE FOR THE WHOLE SESSION — #147, fixed 2026-08-08 [T2 code, T3 field link]:**
`sShownRange` (mech4's targeting step) is a **function-level static** — one cell per process,
shared by every mech, carried across drops. **NaN is absorbing** in `step = trueRange
sShownRange; sShownRange += step`, and neither the producer's clamps nor
`BTReticleRenderable::Draw`'s (`range < minRange` / `range > maxRange`) catch it — both
comparisons are **false for NaN**. A poisoned value therefore reaches `AddPoint`/`ConcatMatrix`,
and the caret + its bar become **degenerate geometry that stops rendering while the static tick
marks keep drawing** — the exact reported symptom ("no range finder on this drop", ticks there,
caret gone), sticky until relaunch. Fixed four ways: re-seed on mech change (not on respawn —
that reuses the entity, and the binary doesn't reset the readout either), a producer NaN trap, a
NaN-safe consumer clamp falling back to the 1200 peg, and — the reason no log could settle it —
**`BT_RANGE_LOG` now prints the caret's actual input** (`[range] caret input shown=`). It never
did before: `BT_RANGE_LOG` instrumented the PICK (#4) and `[target]`'s `range=` is a *separate*
locally-recomputed Sqrt in the weapon-range check, so grepping field logs for NaN found nothing
because the poisoned variable was never printed. Causal link to the field report is INFERENCE.
**GAP — the 100 m RANGE BIAS is NOT reconstructed [T1 read, unimplemented]:** HudSimulation
subtracts `_DAT_004b7ecc` (100.0f) from `RangeToTarget@0x1EC` **every frame while the timed
flag @0x22C is set**, accumulating @0x21C by `time_slice` until it reaches @0x1D8, then clearing