#!/usr/bin/env bash # #154: OBSERVE (not infer) which generator stateAlarm edge starts the warning # sequence. BT_GEN_HEAT drives the viewpoint mech's Generator A over # FailureTemperature (trip), then the injection window closes and it cools # through DegradationTemperature (recovery). Receipts: # [gen] GeneratorA TRIPPED/RESTARTING (BT_HEAT_LOG, powersub.cpp) # [audioedge] old= new= trig= inv= ... FIRED/no-fire (BT_AUDIO_EDGE, AUDWTHR.cpp) # [statecfg] authored trigger table + the #154 fixup line (BT_ATTRBIND_LOG) # SHIPPED content: trip (2->4) silent, recovery (4->0) fires (captured 2026-08-11). # WITH the #154 deviation (default): trip FIRES the enter-4 Start, recovery and # the respawn 4->0 edge are silent. PASS = trip-edge fires>=1, old=4 fires==0. # (BT_GEN_WARN_SHIPPED=1 restores shipped behavior; bench asserts the deviation.) 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 3 rm -f genedge.log bt_expert_egg MP.EGG GEN.EGG sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=madcat/" GEN.EGG ( export BT_GEN_HEAT=150000000 BT_GEN_HEAT_DELAY=15 BT_GEN_HEAT_SECS=18 export BT_HEAT_LOG=1 BT_AUDIO_EDGE=1 BT_ATTRBIND_LOG=1 bt_launch genedge.log GEN.EGG 0x03 -egg GEN.EGG ) sleep 300 bt_kill_ours; sleep 2; taskkill //F //IM btl4.exe >/dev/null 2>&1 python - <<'PY' import io, re txt = io.open("genedge.log", encoding="latin-1", errors="replace").read() lines = txt.splitlines() trip = [i for i,l in enumerate(lines) if "TRIPPED" in l and "GeneratorA" in l] rest = [i for i,l in enumerate(lines) if "RESTARTING" in l and "GeneratorA" in l] print("=== #154 LIVE EDGE OBSERVATION ===") print("GeneratorA TRIPPED lines :", len(trip)) print("GeneratorA RESTARTING lines:", len(rest)) def edges_near(idx, span=30): lo, hi = max(0, idx-span), min(len(lines), idx+span) return [l for l in lines[lo:hi] if "[audioedge]" in l] if trip: print("--- [audioedge] around FIRST TRIP (line %d) ---" % trip[0]) for l in edges_near(trip[0]): print(" ", l.strip()[:120]) if rest: print("--- [audioedge] around FIRST RESTART (line %d) ---" % rest[0]) for l in edges_near(rest[0]): print(" ", l.strip()[:120]) # the authored generator trigger table cfg = [l for l in lines if "statecfg" in l and ("GeneratorState" in l or "Generator" in l)] print("--- [statecfg] generator rows (%d) ---" % len(cfg)) for l in cfg[:10]: print(" ", l.strip()[:140]) # verdict material: any FIRED edge with new=4 (trip-edge start)? fired = [l for l in lines if "[audioedge]" in l and "FIRED" in l] trip_fired = [l for l in fired if re.search(r"old=\d+ new=4 ", l)] rec_fired = [l for l in fired if re.search(r"old=4 new=\d+ ", l)] print("FIRED with new=4 (trip) :", len(trip_fired)) print("FIRED with old=4 (recovery):", len(rec_fired)) fixup = sum(1 for l in lines if "#154 fixup" in l) print("fixup receipts :", fixup) if not trip: print("VERDICT: INCONCLUSIVE -- no trip occurred (raise BT_GEN_HEAT)") elif not rest: print("VERDICT: PARTIAL -- tripped but no recovery in window (lengthen run / check cooling)") elif fixup >= 1 and len(trip_fired) >= 1 and len(rec_fired) == 0: print("VERDICT: PASS -- #154 deviation live: warning at TRIP, silent at recovery") else: print("VERDICT: FAIL -- trip-edge fires=%d (want >=1), recovery-edge fires=%d (want 0), fixups=%d" % (len(trip_fired), len(rec_fired), fixup)) PY