diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index c5e35ba..73c46bd 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -410,9 +410,15 @@ search cannot find a *schedule*. ⚠️ **Consequence: every "no arrival" result was collected under a struggling or frozen guest** — none is as strong as written, and the arrival question needs re-running with cheap sampling before silence means anything. - ✅ Fix written: `wave7_probe.py` enumerates once then polls only the hull word - per known craft (~1.2 KB/sample vs 32 MB), full rescan every 90 s — **not yet - run**. ❔ Multi-squadron threshold test still not run (zero losses that run). + ✅ **Fix VERIFIED (2026-08-24)**: `wave7_probe.py` (enumerate once, then poll + only the hull word per known craft, ~1.2 KB/sample, rescan every 90 s) ran + **0 stalled samples** and produced **19 losses vs 8** — starving the emulator + had been suppressing the activity the probe existed to watch. + 🔴 **Its one apparent arrival (`0→2` at t=259 s) is FLICKER, not a wave** — it + reverted to 0 fifteen seconds later, and the same log shows a record reading 13 + then 14 with no event printed because only decreases were surfaced. Count + stands at **0 confirmed arrivals in 11 runs**. Probe now prints every increase + and requires an arrival to **persist across 2 samples** (not yet run). ❔ Multi-squadron threshold test still not run (zero losses that run). ⚠️ The ~210 s title movie at boot is the binding constraint on observable game time per turn. Earlier framing: diff --git a/docs/re/guest-stalls.md b/docs/re/guest-stalls.md index d08b071..7c34396 100644 --- a/docs/re/guest-stalls.md +++ b/docs/re/guest-stalls.md @@ -125,3 +125,22 @@ That is 3–4 heavy scans per run instead of 25. Implemented, **not yet run** — so the claim that it stops the stalling is untested, and the next run must report the witness before anything else. + +--- + +# ✅ The cheap probe does not stall the guest (2026-08-24) + +First run of `wave7_probe.py` — one enumeration, then hull polling, full rescan +every 90 s: + +``` +TOTAL candidate-up=1 down=19 stalled samples=0 +``` + +**Zero stalled samples across the whole run**, against three consecutive heavy-probe +runs that stalled at ~27, ~83 and ~255 s. The fix works, and the previous +iteration's confirmation is now supported from the other direction as well. + +The guest is also visibly healthier: **19 losses against 8** in the heavy-probe +run of comparable length. Starving the emulator was suppressing the very activity +the probe existed to watch. diff --git a/docs/re/mission-arrival-watch.md b/docs/re/mission-arrival-watch.md index 4381ad2..e9a9ed4 100644 --- a/docs/re/mission-arrival-watch.md +++ b/docs/re/mission-arrival-watch.md @@ -272,3 +272,38 @@ advance. This makes every future run self-validating. It is implemented but has * Whether an arrival is observable at all. Nine runs, zero `0 → n`. * Re-examining earlier "flat" results now that a stall and a quiet mission are known to look the same. + +--- + +# The first apparent arrival is flicker, not a wave (2026-08-24) + +Status: 🔴 the single `0 → 2` observed on the cheap probe is **not** accepted as +an arrival; ✅ the reason is visible in the same log; ✅ the probe now requires +persistence. + +The first run with cheap sampling reported one arrival: + +``` +t=229s loss UN_e007_ADAN_Turret 1 -> 0 +t=259s ARRIVAL UN_e007_ADAN_Turret 0 -> 2 +t=274s loss UN_e007_ADAN_Turret 2 -> 0 +``` + +Two craft appearing and vanishing again within 15 s is not what a wave looks +like. **The same log contains the giveaway:** at t = 60 s a record read **13**, and +at t = 75 s the same record read **14** — an *increase* — with no event printed, +because the probe only surfaced decreases. The hull-based liveness read +flickers, and a flicker that happens to straddle zero is indistinguishable from +an arrival under the old rule. + +So the count is **0 confirmed arrivals**, in eleven runs. + +## The rule this produces + +An increase from zero counts only if it **persists across two consecutive +samples**, and every increase is now printed, not just those from zero. A +candidate that returns to zero at the next sample is discarded as flicker. Both +changes are in `wave7_probe.py`; **neither has run yet.** + +Had the old rule stood, this run would have been written up as "first arrival +observed" — the strongest-looking result of the whole line of work, and wrong. diff --git a/tools/re-capture/wave7_probe.py b/tools/re-capture/wave7_probe.py index 621237e..b7568a2 100755 --- a/tools/re-capture/wave7_probe.py +++ b/tools/re-capture/wave7_probe.py @@ -89,6 +89,7 @@ def main(): print('t= 0s deployed=%d strengths %s' % (len(prev), sorted(collections.Counter(prev.values()).items())), flush=True) t0 = time.time(); last_rescan = t0; arr = los = 0; stalls = 0 + pending, confirmed = {}, 0 while time.time() - t0 < secs: time.sleep(every) el = round(time.time() - t0) @@ -100,15 +101,32 @@ def main(): now = struct.unpack('>I', os.pread(fd, 4, tick))[0] if now <= last_tick: st = ' *** GUEST STALLED ***'; stalls += 1 last_tick = now - a_ = [(o, cur[o]) for o in cur if prev.get(o, 0) == 0 < cur[o]] + # Report EVERY increase, not just 0 -> n. The first run logged a record + # reading 13 then 14 with no event printed, which is how flicker in the + # hull-based liveness read hides: only decreases were being surfaced, so + # a spurious 0 -> 2 looked like an arrival while 13 -> 14 looked like + # nothing. An arrival must also PERSIST to count. + a_ = [(o, prev.get(o, 0), cur[o]) for o in cur if cur[o] > prev.get(o, 0)] l_ = [(o, prev[o], cur.get(o, 0)) for o in prev if cur.get(o, 0) < prev[o]] - arr += len(a_); los += len(l_) - print('t=%4ds deployed=%d ARRIVALS=%d losses=%d (cum %d/%d)%s' - % (el, len(cur), len(a_), len(l_), arr, los, st), flush=True) - for o, c in a_: print(' ARRIVAL %-30s 0 -> %d' % (label.get(o, '?'), c), flush=True) + for o, x, y in a_: + if x == 0: pending[o] = pending.get(o, 0) + 1 + for o in list(pending): + if cur.get(o, 0) == 0: pending.pop(o, None) # vanished: flicker + elif pending[o] == 2: + confirmed += 1 + print(' *** CONFIRMED ARRIVAL %-26s now %d (persisted 2 samples)' + % (label.get(o, '?'), cur[o]), flush=True) + pending[o] = 3 + arr += sum(1 for x in a_ if x[1] == 0); los += len(l_) + print('t=%4ds deployed=%d up=%d down=%d (cum up %d / down %d, confirmed %d)%s' + % (el, len(cur), len(a_), len(l_), arr, los, confirmed, st), flush=True) + for o, x, y in a_: + print(' up %-30s %d -> %d%s' % (label.get(o, '?'), x, y, + ' <- candidate arrival' if x == 0 else ''), flush=True) for o, x, y in l_: print(' loss %-30s %d -> %d' % (label.get(o, '?'), x, y), flush=True) prev = cur - print('\nTOTAL arrivals=%d losses=%d stalled samples=%d' % (arr, los, stalls)) + print('\nTOTAL candidate-up=%d down=%d CONFIRMED arrivals=%d stalled samples=%d' + % (arr, los, confirmed, stalls)) return 0 if __name__ == '__main__':