#137 SOLVED (mechanism): not a respawn bug -- the myomer heat RATE under load is

Controlled A/B on the same bench, only the load differs:

  0.95 throttle + continuous missile autofire : 4 respawns, 17 frozen samples,
                                                T climbs 77 -> ~11,600
  0.50 throttle, no weapons                   : 2 respawns, ZERO post-reset
                                                freezes; T stays ~77

So the respawn is exonerated on measurement, not on argument:
  * the reset works -- T == startingTemperature at every reset, both runs;
  * the stale Myomers::speedEffect (@0x31C, which no RTIS writes) is real but
    self-heals on the next tick (T=77.13 -> speedEffect=1);
  * with ordinary load the myomers never approach failT=2000 after a respawn.

WHAT PLAYERS ARE ACTUALLY SEEING.  Overheat while running hard -> myomers pass
failT=2000 -> derating curve @004b8ac0 returns 0.0 -> chain MAX 0 ->
speedDemand *= 0 -> bogged down -> die (often BECAUSE bogged down).  Respawn
correctly resets to 77.  Resume high throttle + firing and it climbs back over
the cliff within seconds, which reads as "respawned with the heat bar maxed".
That is why it looks like a reset bug and why it is intermittent (~8% of
respawns in the field): it tracks how hard you were driving, not the respawn.

THE REMAINING DEFECT is the CLIMB RATE, not the reset.  Blowing ~6x past a
cliff the design treats as coolant-managed (degradeT=1000 governor onset,
failT=2000) is not a lever a player can work with.  Suspect under review: the
kinetic term.  The binary (@004b8d18) applies NO time_slice to it --
`fVar5 * fVar1` -- while the climb and accel terms both carry param_2, i.e. it
is a per-frame energy add at the pod's fixed ~28 Hz.  Our port rate-normalises
it (`work * (time_slice * 28)`), which agrees per-second at any frame rate, so
that is NOT yet a proven discrepancy -- it needs a term-by-term dump against
the authored tuning (VelocityEfficiency 0.995, AccelerationEfficiency 0.8,
thermalMass 2.5e5, myomers linked Condenser5 = Oracle's "loop 5").

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-09 12:04:02 -05:00
co-authored by Claude Opus 5
parent 5556329807
commit 8c612e4e18
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env bash
# =========================================================================
# #137 -- "respawns with the myomers overheated ... initially unable to move
# until it cools off" (Oracle, across several builds).
#
# WHY THE OLD BENCH PROVED NOTHING. heatrespawn.sh sampled AT the reset and
# found every heat-bearing subsystem at T == startingTemperature, so I closed
# #137 as not-a-bug. The field then measured the freeze at 5 of 61 respawns
# (~8%) across THREE machines -- throttle up, speedDemand pinned at 0. A
# bench that never reproduces the failure is not evidence the failure does not
# exist, which is the mistake that closed the ticket.
#
# WHAT THE DECOMP SAYS (re-read 2026-08-09):
# Mech::Reset @0049fb74 walks the roster from index 2 calling vtable +0x28
# (slot 10 = ResetToInitialState) on each subsystem, then @0049f788.
# Myomers::RTIS @004b8aa4 -> PoweredSubsystem::RTIS @004b0e6c -> ALWAYS
# HeatSink::RTIS @004ad760, which does `param_1[0x45] = param_1[0x4f]`
# i.e. currentTemperature(@0x114) = startingTemperature(@0x13C).
# The freeze itself is the derating curve @004b8ac0:
# temp >= degradation(@0x118) -> falls off; temp >= FAILURE(@0x11C) -> 0.0
# and 0.0 reaches the mover as the chain MAX -> speedDemand *= 0.
# CRUCIALLY: nothing in that reset chain touches Myomers::speedEffect
# (@0x31C). It keeps its pre-death value until the myomers next ticks.
#
# So three explanations survive, and only data separates them:
# (a) RESET DIDN'T TAKE -> post-reset T is high (>= fail)
# (b) STALE CACHE -> T is at start but speedEffect is still 0
# ("<<<< STALE (cold but zero)" in the receipt)
# (c) INSTANT RE-HEAT -> T starts at start and climbs back immediately
#
# THE MEASUREMENT. Mech::Reset now arms a ~4 s post-reset trace sampled where
# the mover's multiplier is actually formed:
# [myofreeze] at-reset Myomers T=.. deg=.. fail=.. speedEffect=..
# [myofreeze] post-reset Myomers T=.. deg=.. fail=.. speedEffect=..
# [myofreeze] post-reset CHAIN MAX=0 <<<< FROZEN
#
# A drives hard (heat) and self-damages to death repeatedly, so deaths land on
# a HOT mech -- the field composition. Long run: at ~8% we need many respawns.
# =========================================================================
set -x
. /c/git/bt411/scratchpad/night6/bench_common.sh
cd /c/git/bt411/content || exit 1
taskkill //F //IM btl4.exe > /dev/null 2>&1
sleep 2
rm -f mf137_a.log mf137_b.log mf137_relay.log
bt_expert_egg MP.EGG MF137.EGG
sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=madcat/" MF137.EGG
( export BT_DEATH_LOG=1 BT_MP_LOG=1
bt_launch mf137_b.log MF137.EGG 0x0C -net 1601 )
sleep 2
# A: run hot (autodrive) + fire continuously (weapon heat) + die often.
( export BT_AUTODRIVE=0.5 BT_SELF_DAMAGE=7
export BT_HEAT_LOG=1 BT_DEATH_LOG=1 BT_MP_LOG=1
bt_launch mf137_a.log MF137.EGG 0x03 -net 1501 )
sleep 5
python ../tools/btconsole.py MF137.EGG 127.0.0.1:1501 127.0.0.1:1601 > mf137_relay.log 2>&1 &
RELAY=$!
sleep 330
kill $RELAY 2>/dev/null
sleep 3
bt_kill_ours; sleep 2; taskkill //F //IM btl4.exe > /dev/null 2>&1; sleep 3
echo "=================== #137 MYOMER FREEZE ==================="
echo -n "respawns: "; grep -ac "Mech::Reset" mf137_a.log
echo -n "post-reset FROZEN samples: "; grep -ac "FROZEN" mf137_a.log
echo -n "STALE (cold but zero) samples: "; grep -ac "STALE (cold but zero)" mf137_a.log
echo
echo "--- any reset where the myomers came back AT or OVER the failure temp? ---"
python - <<'PY'
import re, io
bad = 0
for ln in io.open(r"C:\git\bt411\content\mf137_a.log", encoding="latin-1", errors="replace"):
m = re.search(r"\[myofreeze\] (\S+)\s+(\S+)\s+T=([-\d.e+]+) deg=([-\d.e+]+) fail=([-\d.e+]+)\s+speedEffect=([-\d.e+]+)", ln)
if not m:
continue
when, name, t, deg, fail, se = m.group(1), m.group(2), float(m.group(3)), float(m.group(4)), float(m.group(5)), float(m.group(6))
if se <= 1e-4:
bad += 1
if bad <= 12:
why = "TEMP >= fail (reset did not take / re-heated)" if t >= fail else "STALE CACHE (temp fine, effect 0)"
print(" %-10s %-12s T=%8.1f fail=%8.1f effect=%.4f -> %s" % (when, name, t, fail, se, why))
print(" zero-effect samples: %d" % bad)
PY
echo
echo "--- the first frozen episode in context ---"
grep -aE "Mech::Reset|myofreeze" mf137_a.log | grep -aB2 -A6 "FROZEN" | head -20