re: a title negative that survives its own cross-check

Three earlier "the title never appears" claims came from instruments
later found broken -- a stale pixel oracle, a 41 s sampling interval, a
freezing stream. This one carries its own evidence.

title_probe_xchecked.py restarts its capture stream every 30 s AND prints
its reading beside an independent `import` grab every 60 s:

  1851 frames in 560.2 s = 3.30 fps
  cross-checks 9, disagreements 1
  max glyph 0

  t= 62s stream   6.05 | import   0.07  disagree (a fade, logos mid-transition)
  t=123s stream   7.40 | import   7.49  agree
  t=183s stream   8.18 | import   8.29  agree
  t=243s stream   0.23 | import   0.10  agree
  t=311s stream  89.68 | import  89.51  agree
  t=371s stream  80.97 | import  81.58  agree
  t=426s stream 117.43 | import 117.72  agree
  t=487s stream  77.71 | import  76.25  agree
  t=546s stream  70.43 | import  70.55  agree

Eight of nine agree within 2%, fps held at 3.30 with no collapse to 1.60,
and the surface moved through dark and bright phases. So the frames were
live: over 560 continuous seconds from launch, sampled 3.3 times a
second, the interactive title's green (A) plate never appears while the
game renders throughout. The final frame correlates 0.0145 / -0.0047 /
0.0102 with our title / main menu / EXTRAS renders -- attract-movie
content, not a UI screen.

Why remains unknown. live-title-press-a.png with its 753 glyph pixels
proves the title was reachable from this container on 2026-08-28, and
clearing the shader cache fixed the black surface but not this.

The two emulator-side questions (gamma control, 8AX vs ptbase) are
therefore blocked on a characterised failure rather than a suspicion.
Neither blocks the five menu screens, so I am returning to static work;
the probe is committed for whoever picks it up.

METHOD: a probe that cross-checks itself turns "no result" into a result.
This commit is contained in:
Sylpheed RE agent
2026-08-29 02:14:21 +00:00
parent 801a9116c4
commit 9c16fa7860
4 changed files with 136 additions and 0 deletions

View File

@@ -77,6 +77,29 @@ alongside it.
| **Q10** | **What are a music bank's sub-waves?** `BGM_001.slb` is three sub-waves — 10 KB, 4.47 MB, 4.67 MB — and we currently **concatenate them blindly** into one 347 s track. Two near-equal halves could be intro + loop, or two variations, or two halves of one piece. A menu that loops its music needs to know which | The role of each sub-wave, established for at least the menu BGM. "Concatenate" is a decision, not a default — right now it is a default nobody chose |
| **S1** | ~~**Ready Room probe.**~~ **DONE 2026-08-28 — [no-go](../re/ready-room-probe.md).** It is 2D and enumerates fine, but the pak is briefing/tactical-map content, not the Ready Room menu | ✅ go/no-go written |
## 🔴 Emulator-side questions are blocked — the title is not reachable here
**Status 2026-08-29, instrument-verified.** Two open items need a running menu:
the gamma control behind [tone curve](../re/structures/ui-render-tone-curve.md),
and separating `8AX` from `ptbase` in
[8AX](../re/structures/ui-8ax-fullres-background.md). Both are blocked.
**The negative is now solid.** A probe that restarts its capture stream every
30 s and cross-checks itself against an independent grabber every 60 s
(9 checks, 8 agreeing to within 2 %) ran **560 continuous seconds from launch at
3.30 fps**: the interactive title's green Ⓐ plate never appeared, while the game
rendered throughout. The final frame correlates 0.0145 / 0.0047 / 0.0102 with
our title / main-menu / `EXTRAS` renders — attract-movie content.
**Why is unknown.** `live-title-press-a.png` (753 glyph pixels) proves it was
reachable from this container on 2026-08-28. Clearing the shader cache fixed a
*black surface* but not this.
⚠️ Neither blocked item blocks the five menu screens. See
[capture-harness-status](../re/capture-harness-status.md) for the full trail,
including three earlier "the title never appears" claims that were withdrawn
because the instrument was broken each time.
## 🟡 Needs one more run — a Japanese-locale capture
Recorded rather than worked around, per "do not improvise around a blocker".

View File

@@ -544,3 +544,10 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the
"2 391 frames, zero hits" reads as overwhelming and collapses to nothing once
the frames may all be the same frame. Sample count is not evidence of coverage
unless the samples are known to be independent.
* **A probe that cross-checks itself turns "no result" into a result.** Three
successive "the title never appears" claims were withdrawn because the
instrument was broken each time. The version that finally held prints its own
reading beside an independent grabber's every 60 s — 9 checks, 8 agreeing to
within 2 % — so the negative arrives with its own evidence that the frames were
live. Building the cross-check into the tool costs a few lines and is what
separates a measurement from a silence.

View File

@@ -223,3 +223,59 @@ single A/B does not exclude run-to-run variation. The old caches are kept in
**Still no title.** Even rendering, `max glyph 0` — but that number now comes
from a stalling probe, so it establishes nothing either way.
---
## ✅ A negative I can finally stand behind — measured with a self-validating probe
**2026-08-29.** Three earlier "the title never appears" claims came from
instruments that were later found broken (a stale pixel oracle, a 41 s sampling
interval, a freezing stream). This run used
[`tools/re-capture/title_probe_xchecked.py`](../../tools/re-capture/title_probe_xchecked.py),
which restarts its stream every 30 s **and cross-checks itself against an
independent `import` grab every 60 s**, printing both numbers.
```
1851 frames in 560.2 s = 3.30 fps
cross-checks 9, disagreements 1
max glyph 0
```
| t | stream | import | |
|---|---|---|---|
| 62 s | 6.05 | 0.07 | disagree — a fade, the boot logos are mid-transition |
| 123 s | 7.40 | 7.49 | agree |
| 183 s | 8.18 | 8.29 | agree |
| 243 s | 0.23 | 0.10 | agree |
| 311 s | 89.68 | 89.51 | agree |
| 371 s | 80.97 | 81.58 | agree |
| 426 s | 117.43 | 117.72 | agree |
| 487 s | 77.71 | 76.25 | agree |
| 546 s | 70.43 | 70.55 | agree |
Eight of nine agree to within 2 %, the fps held at 3.30 (no collapse to 1.60),
and the surface plainly moved through dark and bright phases. **The frames were
live and the negative is real:**
> ✅ **Measured — over 560 continuous seconds from launch, sampled 3.3 times a
> second by a cross-validated probe, the interactive title's green Ⓐ plate never
> appears, while the game renders throughout.**
The final frame correlates **0.0145** with our title render, **0.0047** with the
main menu and **0.0102** with `EXTRAS` — it is attract-movie content, not a UI
screen at all.
## ❔ Why, still unknown
`live-title-press-a.png` is committed and carries 753 glyph pixels, so the title
*was* reachable from this container on 2026-08-28. Nothing found so far explains
the change. Clearing the shader cache fixed the *black surface* but not this.
## Where this leaves the emulator-side questions
The two open items that need a running menu — the gamma control
([tone curve](structures/ui-render-tone-curve.md)) and separating `8AX` from
`ptbase` ([8AX](structures/ui-8ax-fullres-background.md)) — remain blocked, now
on a well-characterised and instrument-verified failure rather than on a
suspicion. Neither blocks the five menu screens. **Returning to static work**;
the probe is committed for whoever picks this up.

View File

@@ -0,0 +1,50 @@
"""Fixed probe + a live cross-check against an independent grabber.
Stream is restarted every 30 s (a single long-lived one freezes). Every 60 s an
`import` grab of the same display is taken and compared: if the two disagree by
more than a few percent, the stream is lying and the run's numbers are void.
"""
import subprocess, sys, time, os
import numpy as np
from PIL import Image
W,H,SURF = 1280,720,45
LIMIT = float(sys.argv[1]) if len(sys.argv)>1 else 540
RESTART = 30
def _open():
return subprocess.Popen(["ffmpeg","-loglevel","error","-f","x11grab","-draw_mouse","0",
"-video_size",f"{W}x{H}","-i",":98","-r","4","-f","rawvideo","-pix_fmt","rgb24","-"],
stdout=subprocess.PIPE, bufsize=W*H*3*2)
def independent():
subprocess.run(["/usr/local/bin/screenshot","/tmp/xcheck.png"],capture_output=True)
try: return float(np.asarray(Image.open("/tmp/xcheck.png").convert("RGB")).astype(int)[SURF:].mean())
except Exception: return -1.0
p=_open(); n=W*H*3
t0=time.time(); seg=t0; lastx=t0; lastr=-1
frames=0; best=0; disagree=0; checks=0
while time.time()-t0 < LIMIT:
if time.time()-seg > RESTART:
p.kill(); p=_open(); seg=time.time()
buf=p.stdout.read(n)
if len(buf)<n:
p.kill(); p=_open(); seg=time.time(); continue
a=np.frombuffer(buf,np.uint8).reshape(H,W,3).astype(int)
frames+=1
sm=float(a[SURF:].mean())
r,g,b=a[:,:,0],a[:,:,1],a[:,:,2]
c=int(((g>130)&(g-r>45)&(g-b>45)).sum())
if c>best:
best=c
if c>=400:
Image.fromarray(a[SURF:].astype(np.uint8)).save("/sylph-home/re/shots/probe3-title.png")
print(f"*** TITLE t={time.time()-t0:.1f}s glyph={c}",flush=True)
if time.time()-lastx > 60:
lastx=time.time(); checks+=1
iv=independent()
bad = iv>=0 and abs(iv-sm) > max(3.0, 0.15*max(iv,sm))
if bad: disagree+=1
print(f" t={int(time.time()-t0):4d}s stream {sm:7.2f} | import {iv:7.2f} "
f"{'<-- DISAGREE' if bad else 'agree'} glyph {c}",flush=True)
p.kill(); dt=time.time()-t0
print(f"{frames} frames in {dt:.1f}s = {frames/dt:.2f} fps")
print(f"cross-checks {checks}, disagreements {disagree}")
print(f"max glyph {best}")