One filter to select (004 -> 008, 35897 -> 7), a second on a transition it was NOT selected by (008 -> 012, 7 -> 1), leaving exactly one address; then three live paired readings, RAM/screenshot/RAM, all agreeing with the HUD. The address is the same one run 1 reported. That does NOT reverse yesterday's refutation and the entry says so explicitly: runs 2 and 3 read a hard 0 there on an allocated page while the HUD counted, so "it is there in every run" stays refuted. What is withdrawn is the stronger claim that the number was meaningless - it recurs exactly, in 2 of the 4 runs measured, and run 3's amber candidate 0xbdb49668 sits one 64 KB page below it at the identical page offset 0x9668. The practical rule is therefore: try 0xbdb59668, check it against the HUD, re-scan when it reads 0. Why run 3 failed and run 4 did not is also recorded, because it is a method lesson rather than luck: the evidence was always in the first four minutes of the stage, and the earlier runs simply could not look often enough - every HUD reading cost a human round trip, so the 008 step went by between two of them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
201 lines
10 KiB
Markdown
201 lines
10 KiB
Markdown
# `REMAINING OB` — the mission's own objective counter, in RAM
|
||
|
||
**Status:** ✅ `CONFIRMED` for one Stage 02 run: a **big-endian u32** whose value
|
||
is exactly the HUD's `REMAINING OB`, verified across two transitions it was not
|
||
selected by. 🟡 the address itself is from one run — **cross-run stability is
|
||
untested**. ❔ what it counts, and whether objective-marked entities carry a flag.
|
||
|
||
## Why it matters
|
||
|
||
[`autopilot-memory-driven.md`](../autopilot-memory-driven.md) ranks this its
|
||
problem #2: a pilot that flies well but ignores the objective cannot finish a
|
||
mission. Its 300 s run took no damage, killed one fighter, and watched
|
||
`REMAINING OB` **rise** from 004 to 011 as waves spawned. Reading the counter is
|
||
what turns "shoot whatever is nearest" into "shoot what closes the mission".
|
||
|
||
## The find
|
||
|
||
Guest VA **`0xbdb59668`**, big-endian u32, in a Stage 02 run on the
|
||
[upstream baseline](../upstream-baseline.md).
|
||
|
||
| time | RAM `0xbdb59668` | HUD |
|
||
|---|---|---|
|
||
| selected on | 18 → 24 | 018 → 024 |
|
||
| t+40 s | 24 | 024 |
|
||
| t+75 s | **23** | **023** |
|
||
| t+110 s | **22** | **022** |
|
||
| t+145 s | 22 | 022 |
|
||
| t+180 s | 22 | 022 |
|
||
|
||
The two middle rows are the ones that matter: the candidate was filtered on the
|
||
18→24 transition, and it then tracked 24→23→22 on its own.
|
||
|
||
## Method, and the two traps in it
|
||
|
||
`tools/re-capture/ob_scan.py`. Scan one snapshot for the current value, then
|
||
filter that candidate set against **live** `/dev/shm/xenia_memory_*` at the next
|
||
distinct value. Only the first pass needs the 4.8 GB copy.
|
||
|
||
**🔴 Filter on a change, not a repeat.** The first attempt scanned at 19, filtered
|
||
at 19 again, then at 18 — and left **zero** survivors. The value moves between
|
||
the memory copy and the screenshot that reads it, so "still 19" is not reliable.
|
||
Scanning 18 and filtering on 24 gave exactly one candidate on the first try.
|
||
|
||
**🔴 Verify across a transition you did not select on.** An earlier differential
|
||
over 19→18 also gave exactly one candidate, `0xbc22e83c` — and it was **wrong**:
|
||
read live it held 26 while the HUD showed 017. It was an unrelated counter that
|
||
happened to step 19→18 in the same window. One matching transition is not
|
||
evidence; the same offset tracking a *later*, unselected change is.
|
||
|
||
## What is not settled
|
||
|
||
* 🟡 **Cross-run stability.** `0xbdb59668` is from a single run. The corpus's
|
||
other runtime finds are re-scanned per run, so the durable result here is the
|
||
**method**, not the number. Testing whether the address repeats costs one boot.
|
||
* ❔ **What it counts.** It rose 18 → 24 while waves spawned and fell as things
|
||
died, so it is objective targets remaining, not kills. Whether every `OB`-badged
|
||
entity is one of them, and whether that badge is a flag in the entity object, is
|
||
the natural follow-on — and it is what the pilot actually needs to *choose*
|
||
targets rather than merely know how many are left.
|
||
* ❔ Whether the same address holds for other stages.
|
||
|
||
## 🔴 2026-08-23 — cross-run stability: **REFUTED**
|
||
|
||
`0xbdb59668` does **not** repeat. A second Stage 02 run on the same baseline
|
||
(rebuilt per [`../dynamic-re-state-restore.md`](../dynamic-re-state-restore.md))
|
||
reads **0** at that address for the whole mission while the HUD counts up:
|
||
|
||
| HUD | RAM `0xbdb59668` |
|
||
|---|---|
|
||
| `004` (t+18 s, flight entry) | 0 |
|
||
| `008` | 0 |
|
||
| `012` (held for ~5 min) | 0 |
|
||
|
||
A **second, independent** run of the same mission repeats it: at `TIME 01:08.96`
|
||
with the HUD on `REMAINING OB 004`, the word is again **0**
|
||
([`../captures/stage02-inflight-run2-ob-address-zero.png`](../captures/stage02-inflight-run2-ob-address-zero.png)).
|
||
|
||
Read three times back-to-back at `004` — all zero — and `±64 bytes` around it are
|
||
zero too. The page is **not** a sparse hole (`SEEK_DATA` at the offset returns the
|
||
offset itself; the next hole is 5 MB later), so this is an allocated,
|
||
zero-filled word, not an unmapped read. The number was a **per-run artefact**, as
|
||
this file already suspected; the method is the durable part, and every future
|
||
session must re-scan.
|
||
|
||
**And re-finding it in the new run failed too — recorded because both failures
|
||
are instructive.** Two candidates were produced and both were killed by the
|
||
"verify across a transition you did not select on" rule:
|
||
|
||
* `0xbc2377dc` — held 12 at selection, then went 12 → 18 while the HUD stayed
|
||
`012`, and was reading **3** two minutes later at HUD `012`.
|
||
* `0xbd295b04` — read 12 once, then 0, 21, 54, 46, 4 in the next 50 s. Noise.
|
||
|
||
🔴 **A correction to this file's own method note.** The trap is *not* that the
|
||
scan is slow: `ob_scan.py scan` over the live `/dev/shm` image takes **0.9 s**
|
||
(9 555 hits at value 12; the SEEK_DATA extent walk skips the sparse 4.6 GB). The
|
||
real trap is that **`REMAINING OB` climbs fast early in Stage 02** — `004` → `008`
|
||
→ `012` inside about four minutes as waves spawn — so a scan is only valid if the
|
||
HUD is confirmed to hold the same value **immediately before and after it**. The
|
||
earlier `4 ∩ 8` intersection here came back with no survivor for exactly that
|
||
reason: the value-8 scan was actually taken at 12.
|
||
|
||
**What blocked finishing it:** with the pilot in `RETIRE` (hull 340/1500) nothing
|
||
was killing objectives, and the counter sat at `012` for five minutes — no
|
||
transition to filter on. A run that intends to find this address needs a pilot
|
||
that is still *engaging*, which is the same constraint
|
||
[`../autopilot-memory-driven.md`](../autopilot-memory-driven.md) records.
|
||
|
||
**Unchanged and still open:** what the counter counts, and whether an `OB`-badged
|
||
entity carries a flag in its entity object. Nothing in this pass touched that.
|
||
|
||
## 🟡 2026-08-23 (later) — a third run, a HUD-clean scan, and one candidate that is very hard to explain away
|
||
|
||
With the launcher repaired the scan could finally be run the way the method
|
||
asks. **HUD confirmed `004` immediately before *and* after the scan** (0.9 s), so
|
||
the 39 596-entry candidate set is valid at that value; then filtered when the
|
||
HUD was confirmed at `012`.
|
||
|
||
| step | HUD | candidates |
|
||
|---|---|---|
|
||
| scan | `004` (confirmed both sides) | 39 596 |
|
||
| filter | `012` (confirmed) | **17** |
|
||
| later sample | — (see caveat) | 7 still 12 |
|
||
|
||
**The survivor of interest is `0xbdb49668`** — the run-1 address `0xbdb59668`
|
||
**minus exactly `0x10000`**, at the identical page offset `0x9668`.
|
||
|
||
**That is not a cheap coincidence, and it was checked rather than admired.** Of
|
||
all 39 596 words holding `4` at scan time, exactly **2** sit at page offset
|
||
`0x9668`; one of those two is in the 17, and it is this one. A survivor landing
|
||
there by chance is a ~5 × 10⁻⁵ event. The reading it suggests — the counter lives
|
||
at a fixed offset inside an allocation whose **base moves by whole 64 KB pages**
|
||
between runs — is a testable claim, not a story: a third scan should again land
|
||
on `…9668`.
|
||
|
||
**Why this is 🟡 and not ✅, stated plainly.** The corpus's own rule is that a
|
||
candidate is believable once it tracks a transition it was *not* selected on,
|
||
against the HUD. This one has not done that yet:
|
||
|
||
* The "later sample" that killed 10 of the 17 was taken **during the GAME OVER
|
||
flash** — the escorted ACROPOLIS was lost at ~13 min — so the HUD counter was
|
||
not on screen. The ten that *moved* are genuinely refuted; the seven that did
|
||
not move are merely **unrefuted**, which is a much weaker thing, and the
|
||
counter may simply be frozen once the mission ends.
|
||
* Between the filter and the mission end, `REMAINING OB` sat at `012` for about
|
||
ten minutes: `pilot.py` spent the window in EVADE/RETIRE at hull 822/1500 and
|
||
killed no objectives, so there was no transition to use.
|
||
|
||
**The one experiment that settles it:** a run where the pilot is still engaging,
|
||
scanning and filtering as above, then watching `0xbdb4·9668`-equivalent across a
|
||
HUD-verified change. The blocker is the pilot's survival, not the method — and
|
||
the same run would answer whether the page offset repeats, which is the stronger
|
||
result of the two.
|
||
|
||
## ✅ 2026-08-23 (third pass) — the counter is at `0xbdb59668` again, and the refutation above is *refined*, not reversed
|
||
|
||
Run 4, with the hunt automated end to end
|
||
([`ob_hunt.py`](../../tools/re-capture/ob_hunt.py) + the HUD reader), produced
|
||
**exactly one** surviving address:
|
||
|
||
```
|
||
scanned at 004: 35897 candidates (HUD confirmed 004 on BOTH sides)
|
||
004 -> 008 [SELECTED ON] 7 survive
|
||
008 -> 012 [verification (unselected)] 1 survive
|
||
va 0xbdb59668
|
||
```
|
||
([`captures/ob-hunt-stage02-run4.log`](../captures/ob-hunt-stage02-run4.log),
|
||
[`captures/ob-hunt-stage02-run4.json`](../captures/ob-hunt-stage02-run4.json))
|
||
|
||
That is the corpus's own standard met: one filter to select, a **second on a
|
||
transition it was not selected by** to verify. Then three live paired readings —
|
||
RAM read, screenshot, RAM read again — all `ram 12 -> 12 / hud 012`.
|
||
|
||
And it is **the same number as run 1**.
|
||
|
||
### What this does and does not overturn
|
||
|
||
| claim | status |
|
||
|---|---|
|
||
| the counter sits at `0xbdb59668` in *every* run | 🔴 **still refuted.** Runs 2 and 3 read a hard 0 there, on an allocated page, while the HUD counted 004 → 012. Nothing about run 4 makes those readings wrong. |
|
||
| the run-1 number was a meaningless artefact | 🔴 **withdrawn.** It recurs *exactly*: 2 of the 4 runs measured put the counter at `0xbdb59668`. |
|
||
| the address moves by whole 64 KB pages | 🟡 **consistent, twice.** Run 3's best candidate was `0xbdb49668` — one 64 KB page below — and both known-good addresses share the page offset **`0x9668`**. Run 3's candidate was only ever amber, so this rests on one confirmed address plus one plausible one. |
|
||
|
||
**So the practical rule is: try `0xbdb59668` first and *check it against the
|
||
HUD*, and re-scan when it reads 0.** A scan costs about five minutes now
|
||
(`ob_session.sh`), most of that being the boot.
|
||
|
||
### Why run 3 failed and run 4 did not
|
||
|
||
Nothing about the game changed; the *procedure* did. Run 3 filtered once, on
|
||
`004 → 012`, because the intermediate `008` step went by unnoticed between two
|
||
hand readings — and its 17 survivors then had no second transition to be tested
|
||
against, since `REMAINING OB` held at `012` for ten minutes while the pilot sat
|
||
in EVADE. Run 4 caught **both** early steps, five seconds apart from polling,
|
||
because reading the HUD stopped costing a human round trip. The evidence needed
|
||
was always in the first four minutes of the stage; the earlier runs were simply
|
||
not able to look often enough.
|
||
|
||
**Still open, and untouched:** ❔ what the counter counts, and whether an
|
||
`OB`-badged entity carries a flag in its entity object. That is the part the
|
||
autopilot actually needs, and knowing the address is only its precondition.
|