diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 520034f..5a52dc4 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -454,9 +454,12 @@ search cannot find a *schedule*. 90 s, and the new calibration added **two more 32 MB reads** — the 45 s freeze 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 + startup is the price of a working witness. ✅🟡 **Instead, disabling the periodic + rescan LARGELY removes the freeze** — corrected from "removes" after more runs: + clean at 210/240/300 s but **frozen at 60 s** on a fourth. Tally — heavy probe + froze at 27/45/83/183/255 s; cheap+rescan at 183 s; cheap, no rescan: 3 of 4 + clean past 200 s. A large probability improvement, **not a fix**. Practical + rule: run, check the witness, discard frozen runs (~3 in 4 usable). 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 diff --git a/docs/re/guest-stalls.md b/docs/re/guest-stalls.md index ec1a7ae..294c68c 100644 --- a/docs/re/guest-stalls.md +++ b/docs/re/guest-stalls.md @@ -376,3 +376,40 @@ 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. + +--- + +# Freezes are stochastic, not eliminated (2026-08-24) + +The previous entry claimed disabling the periodic rescan "removes the freeze" on +n = 1. With more runs that is too strong. The tally: + +| configuration | freeze onset | +|---|---| +| heavy probe | 27, 45, 83, 183, 255 s | +| cheap probe + 90 s rescan | 183 s | +| **cheap probe, no rescan** | clean 210 s, clean 240 s, clean 300 s, **frozen at 60 s** | + +Three of four runs now survive past 200 s where essentially none did before, so +removing the rescan is a **real and large improvement** — but it is a change in +probability, not a fix, and the wording is corrected accordingly. + +The witness handled the frozen run correctly: losses stop at t = 45 s, stalls are +flagged from t = 60 s onward, no contradiction. + +## ✅ Calibration is no longer the bottleneck + +The candidate search was the last Python loop over 8 million words. Replaced with +two numpy vector operations (`AGENT.md` notes numpy is installed, and its absence +used to look like a logic bug). 7250 candidates found, and the startup cost is +now small enough that **boot dominates**: ~190 s of title movie plus ~35 s to +flight, against a 595 s turn cap, leaves roughly 350 s of observation. + +Boot is now the binding constraint on how much game time one turn can see, and +it is the only remaining lever worth pulling if longer windows are needed. + +## Practical consequence + +Do not treat a single frozen run as evidence of anything. Run, check the witness, +discard the frozen ones, and keep the clean runs — about three in four are usable +now, which is workable where it previously was not. diff --git a/tools/re-capture/wave7_probe.py b/tools/re-capture/wave7_probe.py index c79179b..2504903 100755 --- a/tools/re-capture/wave7_probe.py +++ b/tools/re-capture/wave7_probe.py @@ -126,11 +126,18 @@ def main(): 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 = [] - 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: - cands.append((lo + k, round((vb - va) / 3.0))) + # The candidate search was the last Python loop over 8 million words and cost + # most of the remaining startup time. numpy does the same comparison as two + # vector ops. (AGENT.md: numpy is installed; its absence used to look like a + # logic bug.) + import numpy as np + n4 = min(len(a), len(b)) // 4 * 4 + A = np.frombuffer(a[:n4], dtype='>u4').astype(np.int64) + B = np.frombuffer(b[:n4], dtype='>u4').astype(np.int64) + d = B - A + rate = d / 3.0 + idx = np.nonzero((d > 0) & (rate > 5) & (rate < 200))[0] + cands = [(lo + int(i) * 4, int(round(float(rate[i])))) for i in idx] # 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