re: the positive control ran, and is still inconclusive

poke_control.sh (self-retrying, succeeded on attempt 1 with no freeze) set the
player's hull at pos+0x154 to 1. Twelve seconds later the game had put
0x447A0000 back.

That establishes an asymmetry worth having: the hull word is continuously
rewritten by the game, while the unit-record fields held our value untouched for
60s. It separates fields the game maintains from fields nobody writes.

But it is not yet a control. The after-frame shows a red WARNING banner -- and
the before-frame already shows MISSILE ALERT, so the ship was under attack in
both and the banner is not attributable to the poke. A value being overwritten
proves the game writes that address, not that it read ours.

Correction: hull at pos+0x154 is 0x447A0000, a FLOAT 1000.0, not the 1500 the
pilot logs -- those are different fields or scales and should not be conflated.

The settling refinement: poke in a tight loop for several seconds so the value is
low whenever the game samples it. If hull is authoritative the ship dies and the
screen goes to GAME OVER; if it survives, the field is a readout.

The reusable part is the harness: boot -> verify animating -> locate -> act, with
a freeze at any step costing one retry rather than the iteration.
This commit is contained in:
Sylpheed RE agent
2026-08-25 17:36:00 +00:00
parent cee5cffebf
commit 77bc300692
2 changed files with 100 additions and 0 deletions

View File

@@ -322,3 +322,44 @@ rather than informative, and the two null results above must be read as
**What still stands** from these runs is only what was *observed*, not poked:
the arrival transitions, the destruction of ADN111, and the mission-over branch.
## 🟡 The positive control ran — and is still inconclusive
`tools/re-capture/poke_control.sh` (self-retrying; **succeeded on attempt 1**, no
freeze) poked the player's hull at `position + 0x154` to `1`, on the theory that
the game visibly reacts to a dead player.
```
hull before: 0x447A0000 (= 1000.0f)
poke 0x447A0000 -> 0x00000001 OK
hull after : 0x447A0000 (12 s later -- the game put it back)
screen before: flight screen after: flight
```
**What it establishes:** the hull word is **continuously rewritten by the game**,
so a poke there cannot persist — unlike the unit-record fields, which held our
value for 60 s untouched. That asymmetry is itself informative: it separates
fields the game actively maintains from fields nobody is writing.
**Why it is still not a control.** I looked for a visible reaction and found a
red `WARNING` banner in the after-frame — but the before-frame already shows
`MISSILE ALERT`, i.e. the ship was under attack in both. **The banner is not
attributable to the poke**, and I am not going to count it. A value being
overwritten proves the game writes that address; it does not prove the game
*read* ours.
**New fact, and a correction:** hull at `pos + 0x154` reads `0x447A0000` — a
**float, 1000.0** — not the `1500` the pilot logs. The pilot's `hull=` is a
different field or scale, and the two should not be conflated.
### The refinement that would settle it
Poke in a **tight loop** for several seconds so the value is low whenever the
game samples it, rather than once between two of its own writes. If hull is
authoritative, the ship dies and the screen goes to `GAME OVER` — unambiguous. If
it survives a sustained low hull, the field is a readout and the authoritative
copy is elsewhere. Either answer is worth having.
**The self-retrying harness works** and is the reusable part of this
iteration: boot → verify animating → locate → act, with a freeze at any step
costing one retry instead of a whole iteration.

View File

@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# POSITIVE CONTROL for the poke method: does a write to /dev/shm reach the guest?
#
# Two poke experiments produced no observable effect, and without this control
# "the game ignored it" cannot be told from "the write never arrived"
# (script-runtime-probe.md). The stick test only proves the value persisted in
# the shared-memory FILE.
#
# The control has to be something the GAME visibly reacts to. Hull is ideal:
# it lives at `player position + 0x154`, and dropping it to 1 should destroy the
# player -- an unambiguous screen change, no OCR needed.
#
# Self-retrying, because roughly two runs in three freeze and a freeze was
# costing a whole iteration. Each attempt: boot, check the guest is animating,
# locate the player, poke, look. A freeze at any step costs a retry.
set -u
export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98
export PYTHONPATH=/sylph-home/.local/lib/python3.12/site-packages
SD="$(cd "$(dirname "$0")" && pwd)"
ATTEMPTS="${1:-3}"
alive_and_moving(){ python3 -c "
import sys; sys.path.insert(0,'$SD')
import frozen; d,_=frozen.frozen(5.0); sys.exit(1 if d else 0)"; }
for a in $(seq 1 "$ATTEMPTS"); do
echo "=== attempt $a/$ATTEMPTS ($(date +%T))"
pkill -9 -x xenia_canary 2>/dev/null; pkill -9 -f '[p]ilot.py' 2>/dev/null; sleep 2
rm -f /tmp/xenia-canary.lock
"$SD/launch_mission.sh" fly >/tmp/pc-boot.log 2>&1 || { echo " boot failed"; continue; }
if ! alive_and_moving; then echo " guest already frozen after boot"; continue; fi
pos=""
for t in 1 2 3; do
python3 "$SD/pad.py" set "rt=1" >/dev/null 2>&1; sleep 4
python3 "$SD/pad.py" clear >/dev/null 2>&1
pos=$(timeout 120 python3 "$SD/entities2.py" self 0x130 2>/dev/null \
| sed -n 's/.*pos va \(0x[0-9a-fA-F]*\).*/\1/p' | head -1)
[ -n "$pos" ] && break
echo " locate attempt $t: no player"
done
[ -n "$pos" ] || { echo " could not locate the player"; continue; }
if ! alive_and_moving; then echo " froze during locate"; continue; fi
hull=$((pos + 0x154))
echo " player pos $pos, hull $(printf '0x%X' $hull)"
before=$(python3 "$SD/gpoke.py" r32 "$hull" 1 2>/dev/null | tail -1)
echo " hull before: $before"
screenshot /tmp/pc-before.png >/dev/null 2>&1
python3 "$SD/gpoke.py" w32 "$hull" 1 2>&1 | tail -2
sleep 12
screenshot /tmp/pc-after.png >/dev/null 2>&1
echo " hull after : $(python3 "$SD/gpoke.py" r32 "$hull" 1 2>/dev/null | tail -1)"
echo " screen before: $(python3 "$SD/screen_id.py" /tmp/pc-before.png | head -1)"
echo " screen after : $(python3 "$SD/screen_id.py" /tmp/pc-after.png | head -1)"
python3 "$SD/frozen.py" --pair /tmp/pc-before.png /tmp/pc-after.png 2>&1 | tail -1
echo "CONTROL RAN"; exit 0
done
echo "CONTROL DID NOT RUN in $ATTEMPTS attempts"; exit 1