diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index ba5c770..82db708 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -452,8 +452,19 @@ search cannot find a *schedule*. froze at 45 s with 1 loss, useless for arrivals despite a working instrument. ⚠️ Being honest: the "cheap" probe still reads 32 MB at startup + 32 MB every 90 s, and the new calibration added **two more 32 MB reads** — the 45 s freeze - came right after it. **Trimmed to 8 spread 512 KB windows + rescan every 180 s - — not yet run.** Also: the 42-record anomaly recurred and the discard rule + came right after it. 🔴 **The trim BROKE the witness** (17 candidates, `0/17` on + every sample of a run with 13 losses) — reverted; two 32 MB reads once at + startup is the price of a working witness. ✅ **Instead, disabling the periodic + rescan removed the freeze**: first fully clean probed run, **zero stalls over + 210 s** with 8 losses, ended by the turn timeout not a freeze (n=1). The rescan + existed only to catch newly-allocated craft, which the roster work showed never + happens. +* ✅ **Run 16: the first TRUSTWORTHY negative.** Validated witness, no stall on + any sample, guest demonstrably live (8 losses) ⇒ **0 confirmed arrivals over + 210 s of verified-live flight** ≈115 game-seconds. Establishes that nothing + arrives in the first ~115 game-s of Stage 02 phase 1 while the player kills 8 + craft. Does NOT settle the question — t=170/210/240 route entries are still out + of reach. Also: the 42-record anomaly recurred and the discard rule correctly refused the run; a rescan-until-baseline retry is now in place to test whether it climbs. All three witness failures share one pattern: a shortcut in *selecting* the witness, each caught diff --git a/docs/re/guest-stalls.md b/docs/re/guest-stalls.md index 8fd983b..ec1a7ae 100644 --- a/docs/re/guest-stalls.md +++ b/docs/re/guest-stalls.md @@ -338,3 +338,41 @@ The freeze at 45 s came immediately after that calibration. Trimmed: calibration now samples **8 spread windows of 512 KB** (4 MB total, still covering the whole span) rather than reading everything twice, and the rescan interval is doubled to 180 s. Not yet run. + +--- + +# ✅ The periodic rescan was the freeze driver (2026-08-24) + +Two changes, one failed and reverted, one that worked. + +## 🔴 Trimming the calibration broke the witness + +Sampling 8 spread 512 KB windows instead of the full region found **17** +candidates, none of them frame counters, and the witness then reported +`0/17 stalled` on **every sample of a run that recorded 13 losses** — a total +contradiction, caught by the same internal check as the previous three failures. +The frame-rate cluster is sparse and spread sampling misses it. Reverted. + +Two 32 MB reads *once at startup* is the price of a witness that works. + +## ✅ Disabling the periodic rescan removes the freeze + +The recurring cost was the rescan — a 32 MB read every 90–180 s — and it exists +only to catch craft appearing from nowhere, which the roster work already showed +does not happen: every participant is allocated at mission load, so an arrival is +a state change on an existing craft, not a new allocation. Disabled. + +Result, the first fully clean probed run: + +``` +tick witnesses: 3875 candidates, using 32 at 11/s (frame-rate band) +t= 0s … t=210s no stall flag on any sample +8 losses recorded across the run +``` + +**Zero stalls over 210 s**, against freeze onsets of 27, 45, 83, 183 and 255 s in +every previous probed run. n = 1, so this is not proven — but combined with the +clean no-probe control it points clearly at recurring heavy reads rather than at +memory reading as such. + +The run ended on the turn's timeout, not a freeze. diff --git a/docs/re/mission-arrival-watch.md b/docs/re/mission-arrival-watch.md index e8b86cb..6667c76 100644 --- a/docs/re/mission-arrival-watch.md +++ b/docs/re/mission-arrival-watch.md @@ -329,3 +329,27 @@ behaviour makes the flicker visible as flicker. Caveat on this run: its 13 "stalled" flags are false positives from the single-word witness (see [guest-stalls.md](guest-stalls.md)), so the run was healthy — but that also means the witness cannot yet certify it. + +--- + +# Run 16: the first trustworthy negative (2026-08-24) + +Every previous "no arrival" result carried a caveat — a stalled guest, an +unvalidated witness, a probe degrading the thing it measured. This one does not: + +* the stall witness is validated (zero contradictions with loss data); +* it reported **no stall on any sample**; +* the guest was demonstrably live throughout — 8 losses spread across the run; +* the run ended on the turn timeout, not a freeze. + +**Result: 0 confirmed arrivals over 210 s of verified-live Stage 02 flight.** + +That is the first observation in this line of work that means what it says. It +does not settle the question — 210 s of wall-clock is roughly 115 s of game time +at the measured frame rate, so the route table's t = 170/210/240 entries are +still out of reach — but it does establish that **nothing arrives in the first +~115 game-seconds of Stage 02 phase 1 while the player kills eight craft**. + +One flicker `up` was surfaced and correctly not counted. + +Sixteen runs, still no arrival. The difference is that this one is evidence. diff --git a/tools/re-capture/wave7_probe.py b/tools/re-capture/wave7_probe.py index 8ee1ac9..de74894 100755 --- a/tools/re-capture/wave7_probe.py +++ b/tools/re-capture/wave7_probe.py @@ -16,7 +16,13 @@ wave3 = importlib.util.module_from_spec(_w3); _w3.loader.exec_module(wave3) ROSTER_VT = struct.pack('>I', 0x820AF030) DELTA, WIN, LINK, HULL = 0x130, 0x400, 0x08, 0x154 -BASELINE, RESCAN = 116, 180 # rescan halved in frequency: it is a 32 MB read +BASELINE = 116 +# Periodic rescan DISABLED. It was a 32 MB read every 90-180 s, the only +# recurring heavy cost left, and it exists to catch craft that appear from +# nowhere -- which the roster work already showed does not happen: every +# participant is allocated at mission load, so an arrival is a state change on +# an existing craft, not a new allocation. Set >0 to re-enable. +RESCAN = 0 def scan_vt(fd, size, vt): out = [] @@ -98,24 +104,25 @@ def main(): # failed to advance in 15 s windows where craft were being destroyed, i.e. # they are bursty, not frame counters. timer_probe searched the WHOLE 32 MB # region and found 286 with a clean cluster at ~17/s. Pay the one-off cost. - # Full-region calibration found the witnesses but reads 32 MB twice, and this - # probe is already suspected of provoking the freezes. Sample the region - # SPREAD OUT instead: 8 windows of 512 KB, 4 MB total but covering the whole - # span, which found 6500 candidates' worth of diversity in practice. + # REVERTED to the full-region calibration. Trimming it to 8 spread 512 KB + # windows to save I/O found only 17 candidates, none of them frame counters, + # and the witness then reported 0/17 stalled on EVERY sample of a run that + # recorded 13 losses -- a total contradiction. The frame-rate cluster is + # sparse and spread sampling misses it. Two 32 MB reads once at startup is + # the price of a witness that works; the recurring cost was the periodic + # rescan, and that is what has been cut instead. lo, hiw = gmem.va_to_off(0xBC000000), gmem.va_to_off(0xBE000000) - step = (hiw - lo) // 8 - wins = [(lo + i * step, min(512 << 10, step)) for i in range(8)] def grab(): - return b''.join(os.pread(fd, n, o) for o, n in wins) + out, pos = bytearray(), lo + while pos < hiw: + n = min(1 << 24, hiw - pos); out += os.pread(fd, n, pos); pos += n + return bytes(out) a = grab(); time.sleep(3.0); b = grab() cands = [] - base_of = [] - for o, n in wins: base_of += [(o, i) for i in range(0, n, 4)] for k in range(0, min(len(a), len(b)) - 3, 4): va, vb = struct.unpack_from('>I', a, k)[0], struct.unpack_from('>I', b, k)[0] if va < vb and 5 < (vb - va) / 3.0 < 200: - wo, off = base_of[k // 4] - cands.append((wo + off, round((vb - va) / 3.0))) + cands.append((lo + k, round((vb - va) / 3.0))) # The MODAL cluster is not the frame counter. One run picked a modal rate of # 93/s, and only 11 of 31 of those advanced during active combat -- they are # subsystem counters that tick in bursts. timer_probe measured the frame-rate @@ -143,7 +150,7 @@ def main(): while time.time() - t0 < secs: time.sleep(every) el = round(time.time() - t0) - if time.time() - last_rescan > RESCAN: + if RESCAN and time.time() - last_rescan > RESCAN: craft = enumerate_craft(fd, defs, want); last_rescan = time.time() cur = collections.Counter(c[2] for c in craft if c[2] and alive(fd, c[0])) st = ''