re: clean run reproduces; startup was eating a third of the window

Second consecutive run with no stall flag on any sample, which confirms that
disabling the periodic rescan is what fixed the freezes. Nine losses, four
flicker increases all correctly rejected, and zero confirmed arrivals. The
trustworthy negative now extends to 240 s of verified-live flight, roughly 132
game-seconds, so nothing arrives past the route table's t=90 or t=120 entries
either.

The flicker rate is worth noting: about one spurious increase per minute. That
is the rate at which the old rule would have been manufacturing arrivals.

The arithmetic of the run exposed a waste. Boot finished at 249 s, the probe ran
240 s, and the turn's 595 s cap fired, leaving about 100 s unaccounted for
between them -- the witness calibration and the initial craft enumeration.

enumerate_craft was iterating every 4-byte word of 32 MB in Python, eight
million steps, to find fourteen fixed needles. Replaced with bytes.find() per
definition VA, which is the same search at C speed and is what the vtable scan
already did. Not yet run.

If that recovers most of the hundred seconds the observation window grows from
about 240 s to about 340 s, roughly 187 game-seconds, which would finally reach
the t=170 route entry -- the first of the schedule's later arrivals that no run
has yet been able to observe.
This commit is contained in:
Sylpheed RE agent
2026-08-24 18:31:51 +00:00
parent d674fee457
commit 14385ae170
3 changed files with 66 additions and 13 deletions

View File

@@ -464,7 +464,15 @@ search cannot find a *schedule*.
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
of reach.
***Run 17 reproduces it (n=2)**: no stalls through **t=240 s** (~132 game-s),
9 losses, 0 confirmed arrivals — so nothing arrives past the t=90 and t=120
route entries either. 4 flicker `up`s correctly rejected (~1/min, which is why
the persistence rule matters). 🔴 **Startup eats ~100 s of a ~350 s budget**:
`enumerate_craft` iterated every 4-byte word of 32 MB in Python (8M steps) to
find 14 fixed needles. **Replaced with `bytes.find()` per definition VA — not
yet run**; if it recovers that time the window reaches ~340 s ≈ 187 game-s,
finally covering the **t=170** entry. 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

View File

@@ -353,3 +353,40 @@ still out of reach — but it does establish that **nothing arrives in the first
One flicker `up` was surfaced and correctly not counted.
Sixteen runs, still no arrival. The difference is that this one is evidence.
---
# Run 17: the clean result reproduces, and the window is being eaten by startup
## ✅ Reproduced (n = 2)
```
tick witnesses: 4584 candidates, using 32 at 11/s
t= 0s … t=240s no stall flag on any sample
9 losses, 4 flicker `up` events (none from zero), 0 confirmed arrivals
```
Second consecutive run with no stalls, confirming that disabling the periodic
rescan is what fixed the freezes. The trustworthy negative now extends to
**240 s of verified-live flight ≈ 132 game-seconds**, past the route table's
t = 90 and t = 120 entries with nothing arriving.
Four increases were surfaced and all correctly rejected — the flicker rate is
substantial, roughly one per minute, which is exactly why the persistence rule
matters.
## 🔴 Startup costs ~100 s of a ~350 s budget
The arithmetic does not add up unless something slow sits between them: boot
finished at 249 s, the probe ran 240 s, and the turn's 595 s cap fired. That
leaves about **100 s unaccounted for** — the witness calibration plus the initial
craft enumeration.
`enumerate_craft` was iterating **every 4-byte word of 32 MB in Python**, 8
million steps, to find 14 fixed needles. Replaced with `bytes.find()` per
definition VA, which is the same search at C speed and is what the vtable scan
already did.
If that recovers most of the 100 s, the observation window grows from ~240 s to
~340 s — about **187 game-seconds**, which would finally reach the **t = 170**
route entry. Not yet run.

View File

@@ -39,24 +39,32 @@ def scan_vt(fd, size, vt):
return sorted(out)
def enumerate_craft(fd, defs, want):
"""Heavy: full heap scan. Returns [(base, unit, roster_off)]."""
"""Full heap scan. Returns [(base, unit, roster_off)].
Was iterating every 4-byte word of 32 MB in Python -- 8 million steps -- and
the startup cost worked out at roughly 100 s of wall-clock, which is a third
of the observation window a turn can afford. Each definition VA is a fixed
4-byte needle, so bytes.find() does the same search at C speed.
"""
lo, hi = gmem.va_to_off(entities2.ENT_VA_LO), gmem.va_to_off(entities2.ENT_VA_HI)
out, pos = [], lo
while pos < hi:
m = min(1 << 24, hi - pos)
blob = os.pread(fd, m, pos)
for k in range(0, len(blob) - 3, 4):
nm = defs.get(blob[k:k+4])
if not nm: continue
base = pos + k - DELTA
head = os.pread(fd, WIN, base)
own = None
for j in range(0, len(head) - 3, 4):
(p,) = struct.unpack_from('>I', head, j)
if p in want: own = want[p]; break
out.append((base, nm, own))
for needle, nm in defs.items():
i = blob.find(needle)
while i != -1:
if (pos + i) % 4 == 0:
base = pos + i - DELTA
head = os.pread(fd, WIN, base)
own = None
for j in range(0, len(head) - 3, 4):
(p,) = struct.unpack_from('>I', head, j)
if p in want: own = want[p]; break
out.append((base, nm, own))
i = blob.find(needle, i + 1)
pos += m
return out
return sorted(out)
def alive(fd, base):
try: return struct.unpack('>f', os.pread(fd, 4, base + HULL))[0] > 0