re: F5 survives the full-quad reader, measured rather than asserted

Last iteration I asserted F5 was unaffected by the truncating-reader bug
because it compared like with like. Asserting that is the move that produced
the bug, so this measures it. The new reader sees 9.7 quads/frame vs ~7.5.

Scalar that needs no element identification: quads mid-ramp (0<a<250) per
frame goes 6,4,4,4,2,1,4,3 -> 0 at f436, while the control never reaches 0
anywhere in 48 frames of build-in. One frame with nothing part-way through a
ramp is the cut.

Bonus the old reader could not show: both sweeps enter at f436-438 at their
declared opening alphas -- pteff03 at 255, pteff03a at 1,2,3,4,6,11,17 from
its declared 0.

Refutation attempt on the port's "clock jumps to 236.0": tried and failed.
My bound is [100,238), which contains 236 -- consistent, not independent
confirmation.

Adds tools/re-capture/read_draws.py so the truncating regex is not re-rolled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
This commit is contained in:
sylph-decoder
2026-09-02 21:06:55 +00:00
parent f50de2c821
commit 536206e89c
2 changed files with 130 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
# F5 re-verified with a reader that sees every quad — the snap holds
**Question:** does F5's "Ⓐ snaps the whole title" survive a reader that reads
every quad, not the first vertex of each draw?
**What the human looks at:** nothing new — this re-checks an answer already
given. Pass = the snap conclusion is unchanged.
**What this does NOT cover:** the snap target's exact instant, F6.
**Instrument:** ⟨capture⟩ — same three logs, re-read by
[`tools/re-capture/read_draws.py`](../../tools/re-capture/read_draws.py).
## Why re-check
[`f6-unit11`](f6-unit11-pteff03a-IS-drawn.md) found my reader took the first
`v:` per draw line and dropped the rest, which killed two findings. I asserted
F5 was unaffected because it compared like with like. **Asserting that is the
same move that produced the bug**, so it is measured here instead. The new
reader finds **9.7 quads/frame against ~7.5**.
## ✅ Measured — it holds, and it is sharper than before
Count of quads **mid-ramp** (`0 < α < 250`) per frame:
| | mid-ramp quads |
|---|---|
| press run, f428…f435 | 6 · 4 · 4 · 4 · 2 · 1 · 4 · 3 |
| **press run, f436** | **0** |
| control `f6b`, 48 frames of build-in | **never 0** (min 2) |
One frame in which **nothing on screen is part-way through a ramp** — every
element at a settled alpha at once. The control never does this anywhere in its
build-in. That is the cut, on a scalar that needs no element identification.
The count rises again after (1 · 2 · 2 · 2 · 3) because the snap **restarts the
leaves**: both sweeps then run their own ramps from their own `t=0`.
## ✅ A bonus the old reader could not show
At f436f438 **both** sweeps enter, each at its declared opening alpha:
* `pteff03` at x = 1.69, **α255** — its leaf declares α255 at `t=0`;
* `pteff03a` at x = +1.99, **α1, 2, 3, 4, 6, 11, 17** — its leaf declares **α0**
at `t=0` ramping to 128, and x = 1721 is the right-hand start.
Both leaves' declared openings, observed directly. This is the same page's
`indices=8` batching that unit 11 uncovered.
## Refutation attempt — the port's "clock jumps 109.4 → 236.0"
The port reports a filmed boot showing the snap landing on **236.0**. I tried to
contradict it and **could not**: at f436 the sweeps sit at α255, so the parent's
declared ramp puts the title clock in `[100, 238)` — past `t=100` and before the
`238…250` exit. 236.0 sits inside that. **Their figure survives**, but my data
cannot separate 236 from 200; it is consistent, not independently confirmed.
## Not settled
* The snap target, still `[100,238)` from my side.
* Whether any *other* finding of mine rests on the truncating reader. Unit 10's
decomposition and the pulse ratio are the two candidates and are **not**
re-checked here.

67
tools/re-capture/read_draws.py Executable file
View File

@@ -0,0 +1,67 @@
#!/usr/bin/env python3
"""Read a Xenia UI draw log -- EVERY quad, not the first vertex of each draw.
⚠️ THIS EXISTS BECAUSE THE OBVIOUS READER IS WRONG. A draw line carries
`indices=N` vertices on ONE `v:` line, and N is routinely 8 -- two quads batched
into a single draw. A reader that takes the first `v: [...]` match per line sees
one of them and silently drops the rest.
That produced a clean, complete-looking negative twice in this corpus
(`f6-unit5-pteff03a-never-drawn.md`, `f6-unit6-...`), both refuted by
`f6-unit11-pteff03a-IS-drawn.md`. `REFUTED.md` L170 had already recorded a draw
carrying two rotated parallelograms. Use this reader; do not re-roll the regex.
from read_draws import read
frames = read(path) # {frame: [Quad, ...]} Quad = (page, verts, alpha, blend, cx)
"""
import re, collections
_F = re.compile(r'^--- frame (\d+) ')
_TEX = re.compile(r'tex\[base=(0x[0-9A-F]+) (\d+)x(\d+) fmt=(\d+)(?: h=([0-9A-F]+))?\]')
_IDX = re.compile(r'indices=(\d+)')
_V = re.compile(r'\[(-?\d+\.\d+),(-?\d+\.\d+),z=[-\d.]+,col=([0-9A-F]{8})\]')
class Quad(tuple):
__slots__ = ()
def __new__(cls, page, verts, alpha, blend, cx):
return tuple.__new__(cls, (page, verts, alpha, blend, cx))
page = property(lambda s: s[0])
verts = property(lambda s: s[1])
alpha = property(lambda s: s[2])
blend = property(lambda s: s[3])
cx = property(lambda s: s[4])
def read(path):
frames = collections.defaultdict(list)
frame = page = blend = None
idx = 0
for line in open(path, errors='replace'):
m = _F.match(line)
if m:
frame = int(m.group(1)); continue
if frame is None:
continue
mt = _TEX.search(line)
if mt:
page = mt.group(5) or mt.group(1)
mi = _IDX.search(line); idx = int(mi.group(1)) if mi else 0
mb = re.search(r'blend=(0x[0-9A-F]+)', line); blend = mb.group(1) if mb else None
continue
if page is not None and ' v: ' in line:
vs = _V.findall(line)
# every group of 4 vertices is one quad; a partial tail is dropped
for q in range(len(vs) // 4):
quad = vs[q*4:(q+1)*4]
cx = sum(float(v[0]) for v in quad) / 4
frames[frame].append(Quad(page, [(float(a), float(b)) for a, b, _ in quad],
int(quad[0][2][:2], 16), blend, round(cx, 3)))
page = None
return dict(frames)
if __name__ == '__main__':
import sys
fr = read(sys.argv[1])
ks = sorted(fr)
tot = sum(len(v) for v in fr.values())
print(f"{len(ks)} frames, {tot} quads, frames {ks[0]}..{ks[-1]}")
print(f"mean quads/frame {tot/len(ks):.2f}")