First reproduction of #137 in a bench. The reset path is exonerated and the real defect is located, though not yet explained. DECOMP RE-READ (what the reset actually does): Mech::Reset @0049fb74 walks the roster from index 2 calling vtable +0x28 (slot 10 = ResetToInitialState), then @0049f788. Myomers::RTIS @004b8aa4 -> PoweredSubsystem::RTIS @004b0e6c -> ALWAYS HeatSink::RTIS @004ad760, whose first act is param_1[0x45] = param_1[0x4f] // currentTemperature = startingTemperature (byte 0x114 = 0x13C). Our port matches AND additionally resets heatEnergy, which it must, since HeatSinkSimulation derives currentTemperature = heatEnergy / thermalMass. The freeze itself is the derating curve @004b8ac0: temp >= degradation(@0x118) -> falls off temp >= FAILURE(@0x11C) -> 0.0 -> chain MAX 0 -> speedDemand *= 0. Nothing in the reset chain touches Myomers::speedEffect (@0x31C). MEASURED (scratchpad/night14/myofreeze.sh -- hot mech, repeated self-kill): at-reset Myomers T=77 deg=1000 fail=2000 speedEffect=0 <- stale post-reset Myomers T=77.14 speedEffect=1 <- 1 frame post-reset Myomers T=9297.8 fail=2000 speedEffect=0 post-reset Myomers T=11620.4 fail=2000 speedEffect=0 So, in order: * THE RESET WORKS. T is exactly startingTemperature at the reset. My original bench was right about that much; it just stopped looking there. * THE STALE speedEffect IS REAL BUT HARMLESS -- it survives the reset (no RTIS writes it) yet self-heals on the very next tick. Not the bug. * THE MYOMERS THEN RUN AWAY: 77 -> ~11,600 against a FAILURE point of 2,000, nearly 6x over, in seconds. That is not heat earned by running; that is a runaway, and it is what pins speedDemand at 0 until it cools -- exactly Oracle's "maxed heat bar ... unable to move until it cools off". * Rate: 7 of 105 census samples over the failure temp (~6.7%), against the field's 5-of-61 respawns (~8%). Same order, so the bench is reproducing the field condition and not a bench artifact. * The myomers link to Condenser5 (mass=250000 k=190000) -- literally Oracle's "loop 5 and generator D heating up as all the excess heat goes into the loop". NOT YET ESTABLISHED, and the reason this is a checkpoint and not a fix: whether the runaway is CAUSED by the respawn or is a heat-model calibration problem that merely CORRELATES with it (you die when you overheat, so respawns cluster around hot periods). The bench drives at 95% throttle with continuous missile autofire, which is abusive, and the [heat-t] census has no pre-first-reset samples to compare against. Next step is to instrument the heat INPUT and diff a respawn-adjacent window against a steady-running window. Probe added: [myofreeze] prints T / degradation / FAILURE / speedEffect per myomers plus the chain MAX the mover multiplies by, AT the reset and for ~4 s after (armed by Mech::Reset, sampled where the multiplier is formed). The post-reset window is what nothing was watching -- sampling only at the reset is what made this look innocent and got the ticket wrongly closed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018SgmXGNMXavXiafKXf9MDC
89 lines
4.4 KiB
Bash
89 lines
4.4 KiB
Bash
#!/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.95 BT_SELF_DAMAGE=7
|
|
export BT_AUTOFIRE=1 BT_AF_MISSILE=1 BT_AF_PERIOD=4
|
|
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 540
|
|
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
|