diff --git a/docs/re/script-runtime-probe.md b/docs/re/script-runtime-probe.md index 4ada3a3f..dd4a36fd 100644 --- a/docs/re/script-runtime-probe.md +++ b/docs/re/script-runtime-probe.md @@ -447,7 +447,32 @@ is not — in a mission that is demonstrably flying. That contradicts four earli runs where the header was found within seconds of flight. **I do not have an explanation**, and I am not going to invent one. Candidates -worth separating next time: the script is loaded later than I assumed and the -earlier runs sampled later; the probe raced a load; or this run entered flight -by a different path. The cheap discriminator is to poll for the header from the -moment flight starts and record *when* it appears, rather than sampling once. +worth separating: the script is loaded later than I assumed; the probe raced a +load; or this run entered flight by a different path. + +### ✅ Two of the three candidates are refuted — the script is resident IMMEDIATELY + +`tools/re-capture/ssb_watch.py` polls all three markers from the moment flight is +detected. On a normal run: + +``` +[ 0.0s] header FIRST SEEN (1) +[ 0.0s] ADN110 FIRST SEEN (4) +[ 0.0s] mission FIRST SEEN (1) +[ 0.0s] header=1 ADN110=4 mission=0xBC79C960 +``` + +**All three are resident at the very first sample**, so "the script loads later +than I assumed" and "the probe raced a load" are both **out**. There is no window +in which a healthy mission is flying without its script in memory. + +🔑 And a detail that matters more than it looks: at that moment `screen_id` +reported **`other`, not `flight`** — the script is fully loaded and the +`ScriptMission` locatable *before* the flight HUD appears. So residency is not +gated on the HUD, and a run showing the HUD without the script is in a state a +healthy run never passes through. + +That leaves the third candidate: **the anomalous run's mission never loaded**, +and its `IN FLIGHT` was a misdetection. Consistent with what became of it — it +was **frozen on a black screen** when checked afterwards. Not proven, but it is +now the only surviving explanation rather than one of three. diff --git a/tools/re-capture/ssb_watch.py b/tools/re-capture/ssb_watch.py new file mode 100644 index 00000000..77146f91 --- /dev/null +++ b/tools/re-capture/ssb_watch.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""When does the mission script become resident in guest memory? + +One run reached `IN FLIGHT` with the `.ssb` header ABSENT from guest memory and +`find_mission` returning NOTFOUND, while the manifest string `Stage02.ssb` was +present (script-runtime-probe.md). Three explanations were possible: the script +loads later than assumed, the probe raced a load, or that run never really +entered the mission. + +Sampling once cannot separate them. This polls from the moment flight is +detected and records WHEN each marker appears: + + header the .ssb's 20-byte header -> the script image itself + ADN110 a symbol-table string -> its symbol table + mission find_mission() succeeding -> the live ScriptMission + +Usage: ssb_watch.py [secs] [every_s] +""" +import os +import sys +import time + +sys.path.insert(0, __file__.rsplit('/', 1)[0]) +import gmem +import isl +import squadron_state as S + +def main(): + ssb = isl.load('/tmp/Stage02.ssb') + hdr = ssb[:20] + secs = float(sys.argv[1]) if len(sys.argv) > 1 else 300 + every = float(sys.argv[2]) if len(sys.argv) > 2 else 10 + path = gmem.mem_path() + size = os.path.getsize(path) + t0 = time.time() + seen = {} + with open(path, 'rb', buffering=0) as f: + while time.time() - t0 < secs: + t = time.time() - t0 + marks = {} + marks['header'] = len(S._find(f, size, hdr)) + marks['ADN110'] = len(S._find(f, size, b'ADN110')) + m, _fb = (None, None) + if marks['header']: + m, _fb = S.find_mission(f, size, ssb) + marks['mission'] = 1 if m else 0 + for k, v in marks.items(): + if v and k not in seen: + seen[k] = t + print(' [%6.1fs] %-8s FIRST SEEN (%s)' % (t, k, v), flush=True) + print(' [%6.1fs] header=%d ADN110=%d mission=%s' % ( + t, marks['header'], marks['ADN110'], + ('0x%08X' % m) if m else 'no'), flush=True) + if len(seen) == 3: + print('all three resident at %.1fs' % t, flush=True) + return 0 + time.sleep(every) + print('timed out; seen=%s' % seen, flush=True) + return 1 + + +if __name__ == '__main__': + sys.exit(main())