From 536206e89c615c381aed82fbb1e6696c9455dcdf Mon Sep 17 00:00:00 2001 From: sylph-decoder Date: Wed, 2 Sep 2026 21:06:55 +0000 Subject: [PATCH] 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 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 Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t --- docs/re/f5-verified-with-full-quad-reader.md | 63 ++++++++++++++++++ tools/re-capture/read_draws.py | 67 ++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 docs/re/f5-verified-with-full-quad-reader.md create mode 100755 tools/re-capture/read_draws.py diff --git a/docs/re/f5-verified-with-full-quad-reader.md b/docs/re/f5-verified-with-full-quad-reader.md new file mode 100644 index 00000000..aa7d50a1 --- /dev/null +++ b/docs/re/f5-verified-with-full-quad-reader.md @@ -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 f436–f438 **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. diff --git a/tools/re-capture/read_draws.py b/tools/re-capture/read_draws.py new file mode 100755 index 00000000..71c5c9bb --- /dev/null +++ b/tools/re-capture/read_draws.py @@ -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}")