docs+tools: a mission freeze that made an experiment lie, and the counter is not a class head-count

Three things from one Stage 02 run.

The address recurs a third time: HUD=4 RAM=4 at 0xbdb59668, so 3 of the 5 runs
measured put the counter exactly there.

The counter is NOT a live class head-count. With the counter at 4 the typed
entity list was 8 attackers, 7 friendly Delta Sabers, 7 turrets and the player -
no class has 4 members and no pair of them sums to 4. That sharpens the corpus's
existing "012 against 118 live ADAN" note from "not the hostile count" to "not
the count of any class this enumeration can see".

The flag experiment itself proves nothing, and why is the useful part. It found
20 offsets where exactly 4 of 23 entities agree, then reported "the counter never
moved" for 600 s. The guest had stopped advancing ten seconds into flight:
pilot.py logged 724 s of identical speed/yaw/pitch, and two screenshots six
seconds apart were byte-identical, max delta 0 over 863325 pixels - while
screen_id said "flight", the emulator burned 212% CPU and every liveness check
passed. So that was a fact about a dead world. Withdrawn along with it: the claim
in ob_session.sh that the counter climbs on its own in the first minutes, which
one advancing run supports and this one cannot.

frozen.py makes it a single call, checked in both directions (0 on the frozen
pair, 254 on two frames of a live run), and ob_hunt/ob_flag now say GUEST FROZEN
rather than waiting out their timeouts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
This commit is contained in:
Sylpheed RE agent
2026-08-23 19:10:41 +00:00
parent 2a844e6da9
commit fcd9fcecf8
5 changed files with 308 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 KiB

View File

@@ -0,0 +1,83 @@
# An in-mission freeze, and the first cut at what `REMAINING OB` counts
**2026-08-23.** One Stage 02 run, aimed at the ❔ that
[`structures/mission-objective-counter.md`](structures/mission-objective-counter.md)
leaves open — *what* the counter counts. It produced one clean refutation, one
new confirmation, and one experiment that proves nothing, for a reason worth
having a tool for.
## ✅ The address recurs a third time
`HUD=4 RAM=4` at `0xbdb59668`, read before anything else was attempted. That is
the **third** run putting the counter at that exact address (runs 1, 4, 5 of the
five measured), against runs 2 and 3 where it read a hard 0. The rule in the
structure file — *try it, check it against the HUD, re-scan when it reads 0*
survives another test.
## 🔴 The counter is not a live class head-count
With the counter at **4**, the typed live entity list was:
| n | class |
|---|---|
| 8 | `UN_e010_ADAN_Attacker_S` |
| 7 | `UN_f001_TCAF_DeltaSaber_T` |
| 7 | `UN_e007_ADAN_Turret` |
| 1 | `UN_f001_TCAF_DeltaSaber_T_Player` |
**No class has 4 members**, and no sum of two of them does either. This is a
sharper version of the corpus's existing note (`012` on the HUD against 118 live
ADAN): it is not merely "not the hostile count", it is not the count of *any*
class the enumeration can see. So the counter is either a subset marked some
other way — the `OB` badge — or a number the mission script keeps of its own.
## 🔴 The flag experiment proves nothing — the guest had frozen
The plan was the same shape that settled the address: find every 4-byte offset
around an entity where exactly *N* entities agree, then require the survivors to
agree exactly *N* times after the counter moves. The first half ran —
**20 candidate offsets** where exactly 4 of the 23 entities share a value — and
then the counter sat at 4 for the whole 600 s window and the run reported
"counter never moved".
**That reading was wrong, and the correction is the useful part.** The guest had
stopped advancing about **ten seconds into flight**:
* `pilot.py` logged 5 910 samples over 734 s and every one after t=10.6 s carried
the identical speed, yaw and pitch — **724 s of frozen state**.
* Two screenshots six seconds apart were **byte-identical**: max difference 0
over 863 325 pixels ([`captures/stage02-inmission-freeze.png`](captures/stage02-inmission-freeze.png)).
* Everything that is supposed to notice, did not: `screen_id.py` still said
`flight`, the emulator was alive at **212 % CPU**, and the process liveness
check every script uses was satisfied.
So "the counter never moved" was a fact about a dead world, not about the game.
🔴 **Withdrawn with it:** the claim written into `ob_session.sh` that the counter
"climbs on its own as waves spawn in the first minutes" — in a run that *is*
advancing it did (004 → 008 → 012 twice), but this run cannot support the general
statement and the two facts had been about to be conflated.
**The freeze is intermittent, not universal:** the run an hour earlier stepped
the counter twice and had `pilot.py` in `DEFEND` at t=629 s with changing state.
### [`frozen.py`](../../tools/re-capture/frozen.py)
Two screenshots, a gap, exact comparison — no tolerance, because a stopped
simulation is *identical*, not merely similar. Checked in both directions: 0 on
the frozen pair, `max_pixel_delta=254` on two frames of a live one. `ob_hunt.py`
and `ob_flag.py` now consult it after a minute of no movement and say
`GUEST FROZEN` instead of quietly waiting out their timeout.
## What is still open
***The flag itself.** 20 candidate offsets exist but have never been put to
the second test. Repeat `ob_flag.py` on a run that is actually advancing; it
now aborts early instead of wasting the window if it is not.
***Whether the enumeration can even see the objectives.** `entities2.typed`
finds entities by their position *changing*, so a stationary objective is
invisible to it. Stage 02's objective is "shoot down all invading enemy
fighters", which move — but a null result would not rule out a flag on objects
this method never enumerates, and that limit is now written into the tool.
***What causes the freeze.** Not investigated here at all; this run only
establishes that it happens, that it can happen within seconds of the HUD
appearing, and how to detect it in one call.