From the Decoder's catch at shutdown, not my own. They found their Canary logger change -- the texture content hash -- living UNCOMMITTED in /canary, a checkout pushed nowhere. Two committed findings could not be reproduced without it: it is the field separating "the buffer rotated" from "a frame was decoded", and its absence is what cost two withdrawn positions on units/second in one day. They committed it and exported it into the repo as a patch. Their generalisation applies here too, so I audited this side at the same moment. One real instance: DECISIONS.md cited `/reborn/docs/re/captures/main-menu-oracle.png` as the evidence for a decision. The capture IS in the repository, at docs/re/captures/main-menu-oracle.png -- only the path was stale, left by the monorepo move. Fixed. The other /reborn mentions are deliberate: one warns the mount is empty, one is a table row listing the pattern as an example of what not to do. ⚠️ tools/port/check-citations does NOT catch this class, and the gap is named rather than quietly left: it scans for REPO paths that fail to resolve, and an absolute path is not a repo path, so it slips through. Extending it to flag off-repo absolutes -- especially ones whose basename does resolve in-repo, the recoverable case -- is the obvious next increment and is deliberately NOT done here. It was found minutes before a shutdown, and a hasty checker is worse than a named gap. Index regenerated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX
16591 lines
900 KiB
Markdown
16591 lines
900 KiB
Markdown
# Decisions
|
||
|
||
One entry per decision that outlives the container it was made in. Newest last.
|
||
A decision that lives only in an agent's context is lost when that container
|
||
dies, which is what this file is for.
|
||
|
||
---
|
||
|
||
|
||
<!-- INDEX: generated by tools/port/index-decisions -- do not hand-edit -->
|
||
|
||
339 sections. Search this before re-deriving anything.
|
||
|
||
* [P0 — the exporter, 2026-08-28](#p0--the-exporter-2026-08-28)
|
||
* [P1 — Godot draws the screen, 2026-08-28](#p1--godot-draws-the-screen-2026-08-28)
|
||
* [P1 gate — the diff, and what it found](#p1-gate--the-diff-and-what-it-found)
|
||
* [P2 — keyframe animation, 2026-08-28](#p2--keyframe-animation-2026-08-28)
|
||
* [`rest` misidentifies six elements, and the running game says so](#rest-misidentifies-six-elements-and-the-running-game-says-so)
|
||
* [The title is not settled, and P2 does not claim it](#the-title-is-not-settled-and-p2-does-not-claim-it)
|
||
* [P2, corrected — the pin moved, and the settle rule was wrong, 2026-08-28](#p2-corrected--the-pin-moved-and-the-settle-rule-was-wrong-2026-08-28)
|
||
* [The reference renderer was stale for three diff runs](#the-reference-renderer-was-stale-for-three-diff-runs)
|
||
* [The menu had no background, and P1 called that correct, 2026-08-29](#the-menu-had-no-background-and-p1-called-that-correct-2026-08-29)
|
||
* [P3 — splash → title, unattended, 2026-08-29](#p3--splash--title-unattended-2026-08-29)
|
||
* [P3 gate](#p3-gate)
|
||
* [Answers taken from the RE agent without re-deriving them](#answers-taken-from-the-re-agent-without-re-deriving-them)
|
||
* [P4 — the intro video, 2026-08-29](#p4--the-intro-video-2026-08-29)
|
||
* [P4 gate](#p4-gate)
|
||
* [RETRACTION — `sylpheed-cli` is not the oracle, 2026-08-29](#retraction--sylpheed-cli-is-not-the-oracle-2026-08-29)
|
||
* [P5 groundwork — the focus record, checked against a capture, 2026-08-29](#p5-groundwork--the-focus-record-checked-against-a-capture-2026-08-29)
|
||
* [P5 — navigation, 2026-08-29](#p5--navigation-2026-08-29)
|
||
* [`--headless` cannot draw, and the port hung instead of saying so, 2026-08-29](#--headless-cannot-draw-and-the-port-hung-instead-of-saying-so-2026-08-29)
|
||
* [Refutation — the focus ring IS drawn rotated, and it is not at 0° in either capture](#refutation--the-focus-ring-is-drawn-rotated-and-it-is-not-at-0-in-either-capture)
|
||
* [P5 end to end — and the title does not say `PRESS Ⓐ`, 2026-08-29](#p5-end-to-end--and-the-title-does-not-say-press--2026-08-29)
|
||
* [P6 — menu audio, 2026-08-29](#p6--menu-audio-2026-08-29)
|
||
* [P6 gate — the audio is in the mix, and a null control says which part](#p6-gate--the-audio-is-in-the-mix-and-a-null-control-says-which-part)
|
||
* [P3, reopened — the boot title was missing the `PRESS Ⓐ` plate, 2026-08-29](#p3-reopened--the-boot-title-was-missing-the-press--plate-2026-08-29)
|
||
* [P5 — the focus ring spins, 2026-08-29](#p5--the-focus-ring-spins-2026-08-29)
|
||
* [P3, corrected — the plate needs no authored delay at all, 2026-08-29](#p3-corrected--the-plate-needs-no-authored-delay-at-all-2026-08-29)
|
||
* [P7 — the new-game intro, 2026-08-29](#p7--the-new-game-intro-2026-08-29)
|
||
* [P7 gate](#p7-gate)
|
||
* [Modding — rule 4 was never implemented, 2026-08-29](#modding--rule-4-was-never-implemented-2026-08-29)
|
||
* [Refutation — the paint-order key, and the reach of its tie-break](#refutation--the-paint-order-key-and-the-reach-of-its-tie-break)
|
||
* [Correction — the runtime "clipping" I flagged 🔴 twice was overstated](#correction--the-runtime-clipping-i-flagged--twice-was-overstated)
|
||
* [The P1 regression harness had been broken since the monorepo merge, 2026-08-29](#the-p1-regression-harness-had-been-broken-since-the-monorepo-merge-2026-08-29)
|
||
* [Refutation — "builds 0/1 and 10/11 are the loading screen" is false in the index space this export uses](#refutation--builds-01-and-1011-are-the-loading-screen-is-false-in-the-index-space-this-export-uses)
|
||
* [The intro's missing dialogue was an export gap, not a transcode bug, 2026-08-29](#the-intros-missing-dialogue-was-an-export-gap-not-a-transcode-bug-2026-08-29)
|
||
* [Refutation, of my own exporter — MISSION §6 pins a downmix matrix, and the exporter ships a different one](#refutation-of-my-own-exporter--mission-6-pins-a-downmix-matrix-and-the-exporter-ships-a-different-one)
|
||
* [Refutation of my own two-stem reading — and it had already been adopted elsewhere](#refutation-of-my-own-two-stem-reading--and-it-had-already-been-adopted-elsewhere)
|
||
* [The mono fold I warned about, in the comment directly above the code that did it](#the-mono-fold-i-warned-about-in-the-comment-directly-above-the-code-that-did-it)
|
||
* [The leading chunk is the TAIL of the full one — measured, and it is why the region over-covers](#the-leading-chunk-is-the-tail-of-the-full-one--measured-and-it-is-why-the-region-over-covers)
|
||
* [Third reading of a voice region, and this one is decoded: three presentations of one take](#third-reading-of-a-voice-region-and-this-one-is-decoded-three-presentations-of-one-take)
|
||
* [The transcode cache had never hit, because the wipe ran first](#the-transcode-cache-had-never-hit-because-the-wipe-ran-first)
|
||
* [`settle_time()` — the answer arrived, and it refutes my own 🔴 more than it confirms it](#settle_time--the-answer-arrived-and-it-refutes-my-own--more-than-it-confirms-it)
|
||
* [The voice presentation is now unambiguously the port's choice, and the recommendation behind it was withdrawn](#the-voice-presentation-is-now-unambiguously-the-ports-choice-and-the-recommendation-behind-it-was-withdrawn)
|
||
* [Refutation of my dual-mono inference — the measurement stands, the generalisation does not](#refutation-of-my-dual-mono-inference--the-measurement-stands-the-generalisation-does-not)
|
||
* [Two rows of the P1 baseline were comparing blank frames and reporting OK](#two-rows-of-the-p1-baseline-were-comparing-blank-frames-and-reporting-ok)
|
||
* [Refutation attempt — the loading-screen variants, and it survived](#refutation-attempt--the-loading-screen-variants-and-it-survived)
|
||
* [🔴 The voice export is known incomplete — the game decodes all three streams at once](#the-voice-export-is-known-incomplete--the-game-decodes-all-three-streams-at-once)
|
||
* [🔴 The oracle capture does not contain the intro — a controlled negative](#the-oracle-capture-does-not-contain-the-intro--a-controlled-negative)
|
||
* [🔴 Take 2 is clean, my instrument was not, and the negative had to be re-earned](#take-2-is-clean-my-instrument-was-not-and-the-negative-had-to-be-re-earned)
|
||
* [Every music bank was summed at 1/3 when only two sub-waves are music — 3.52 dB, since P6](#every-music-bank-was-summed-at-13-when-only-two-sub-waves-are-music--352-db-since-p6)
|
||
* [Take 2 was starved, my correlator was fine, and `check-capture` was incomplete](#take-2-was-starved-my-correlator-was-fine-and-check-capture-was-incomplete)
|
||
* [The settle run carries an unmeasured real-time factor — and the numbers it touches were already unauthored](#the-settle-run-carries-an-unmeasured-real-time-factor--and-the-numbers-it-touches-were-already-unauthored)
|
||
* [`verify-dwell` — the comparison that refuted my own 🔴, made repeatable](#verify-dwell--the-comparison-that-refuted-my-own--made-repeatable)
|
||
* [The `PRESS Ⓐ` plate pulses — authored per element, because the census forbids a rule](#the-press--plate-pulses--authored-per-element-because-the-census-forbids-a-rule)
|
||
* [✅ The oracle finally speaks: the exported voice IS the game's centre channel](#the-oracle-finally-speaks-the-exported-voice-is-the-games-centre-channel)
|
||
* [The stripping control passes — `S00A` is obtainable, and the gate is cleared](#the-stripping-control-passes--s00a-is-obtainable-and-the-gate-is-cleared)
|
||
* [The correctness harness the docs promised for eight milestones did not exist](#the-correctness-harness-the-docs-promised-for-eight-milestones-did-not-exist)
|
||
* [Refutation attempt — the tone curve survives in its stated reach and not past it](#refutation-attempt--the-tone-curve-survives-in-its-stated-reach-and-not-past-it)
|
||
* [Identifying the capture's focused button — and my harness was posing the port wrong](#identifying-the-captures-focused-button--and-my-harness-was-posing-the-port-wrong)
|
||
* [`tools/port/which-focus` — the Decoder asked for a detector, and it carries its own control](#toolsportwhich-focus--the-decoder-asked-for-a-detector-and-it-carries-its-own-control)
|
||
* [The title's 1.82 % — three of my own explanations refuted, and the format has no blend mode](#the-titles-182---three-of-my-own-explanations-refuted-and-the-format-has-no-blend-mode)
|
||
* [🔴 The exporter dropped nested `.rat` leaf geometry on 45 elements — and it is the title's 1.82 %](#the-exporter-dropped-nested-rat-leaf-geometry-on-45-elements--and-it-is-the-titles-182)
|
||
* [The leaf composition is decoded and implemented — and it does **not** close the 1.82 %](#the-leaf-composition-is-decoded-and-implemented--and-it-does-not-close-the-182)
|
||
* [The −324 was the old keyframe association, and the corrected one is available **today**](#the-324-was-the-old-keyframe-association-and-the-corrected-one-is-available-today)
|
||
* [Re-running the P5/P6 gate after eight iterations of changes](#re-running-the-p5p6-gate-after-eight-iterations-of-changes)
|
||
* [Pinned `formats-pin-2026-08-29c` — and the knob I tested last iteration was retired](#pinned-formats-pin-2026-08-29c--and-the-knob-i-tested-last-iteration-was-retired)
|
||
* [Refuted — my own "the single non-whole-multiple scale in the export"](#refuted--my-own-the-single-non-whole-multiple-scale-in-the-export)
|
||
* [The 11.5 px was the fit's resolution, and the lesson inverts](#the-115-px-was-the-fits-resolution-and-the-lesson-inverts)
|
||
* [🔴 The focus ring had silently stopped, and BLOCKED had listed it](#the-focus-ring-had-silently-stopped-and-blocked-had-listed-it)
|
||
* [The plate's period is now the disc's 105, and it disagrees with the measurement](#the-plates-period-is-now-the-discs-105-and-it-disagrees-with-the-measurement)
|
||
* [The plate's period is 120, decoded — and it was falsified with my own ring number](#the-plates-period-is-120-decoded--and-it-was-falsified-with-my-own-ring-number)
|
||
* [✅ A settled screen is ONE instant, and it collapsed three residuals at once](#a-settled-screen-is-one-instant-and-it-collapsed-three-residuals-at-once)
|
||
* [Their census, and a framing of mine they sharpened](#their-census-and-a-framing-of-mine-they-sharpened)
|
||
* [Their "do not draw all five flashes" flag — checked, and it does not apply here](#their-do-not-draw-all-five-flashes-flag--checked-and-it-does-not-apply-here)
|
||
* [✅ The `publisher_logo` residual was a missing black hold, and we had both dismissed it](#the-publisher_logo-residual-was-a-missing-black-hold-and-we-had-both-dismissed-it)
|
||
* [`ptlogo_back2eff3` — recorded, deliberately not acted on](#ptlogo_back2eff3--recorded-deliberately-not-acted-on)
|
||
* [The narrow settle windows are harmless, and I can now say why](#the-narrow-settle-windows-are-harmless-and-i-can-now-say-why)
|
||
* [Refuted, mine — "the menu residual is localised on the `ptloop` sweeps"](#refuted-mine--the-menu-residual-is-localised-on-the-ptloop-sweeps)
|
||
* [Refuted — "the developer splash is one composited quad, the bounding box of the three logos"](#refuted--the-developer-splash-is-one-composited-quad-the-bounding-box-of-the-three-logos)
|
||
* [The black hold is 9 units, not 12 — measured in draws rather than luminance](#the-black-hold-is-9-units-not-12--measured-in-draws-rather-than-luminance)
|
||
* [The title's sweeps loop — measured, and the field could not have told us](#the-titles-sweeps-loop--measured-and-the-field-could-not-have-told-us)
|
||
* [The menus' residual is the tone floor, not structure — and `extras` is not really 3× worse](#the-menus-residual-is-the-tone-floor-not-structure--and-extras-is-not-really-3-worse)
|
||
* [Refutation attempt — their 239.8-unit figure, checked from my export](#refutation-attempt--their-2398-unit-figure-checked-from-my-export)
|
||
* [🔴 The loading screens are black at *every* instant — which proves the layer rule wrong for a layerless element](#the-loading-screens-are-black-at-every-instant--which-proves-the-layer-rule-wrong-for-a-layerless-element)
|
||
* [Their `eff3` retraction — my refusal was right, and my refutation found the same bug](#their-eff3-retraction--my-refusal-was-right-and-my-refutation-found-the-same-bug)
|
||
* [The forced backdrop: two of sixteen screens were black for their whole life](#the-forced-backdrop-two-of-sixteen-screens-were-black-for-their-whole-life)
|
||
* [Refutation attempt — the forced-backdrop rule's quantifier, and whether it misses a case](#refutation-attempt--the-forced-backdrop-rules-quantifier-and-whether-it-misses-a-case)
|
||
* [The 256/211 was never a disagreement — and my own census had already said so](#the-256211-was-never-a-disagreement--and-my-own-census-had-already-said-so)
|
||
* [The clock freezes at settle — the port's settle window, seen from the other side](#the-clock-freezes-at-settle--the-ports-settle-window-seen-from-the-other-side)
|
||
* [🔴 Withdrawn — "the boot is known too fast [refuted]". The splash dwells are declared, and the port was already playing them](#withdrawn--the-boot-is-known-too-fast-refuted-the-splash-dwells-are-declared-and-the-port-was-already-playing-them)
|
||
* [Refutation attempt — their two splash boundaries are not anchored the same way](#refutation-attempt--their-two-splash-boundaries-are-not-anchored-the-same-way)
|
||
* [Their corrected boundaries check out against the file — all six, exactly](#their-corrected-boundaries-check-out-against-the-file--all-six-exactly)
|
||
* [The n=1 disclosure, and the one port constant that rests on a single run](#the-n1-disclosure-and-the-one-port-constant-that-rests-on-a-single-run)
|
||
* [P6 gate — sound on the P5 walk, verified, and the tool I nearly shipped instead](#p6-gate--sound-on-the-p5-walk-verified-and-the-tool-i-nearly-shipped-instead)
|
||
* [Their `.tbm` self-refutation does not reach this archive — and it fixes my guard anyway](#their-tbm-self-refutation-does-not-reach-this-archive--and-it-fixes-my-guard-anyway)
|
||
* [Coverage is now tested per instant, because scale animates](#coverage-is-now-tested-per-instant-because-scale-animates)
|
||
* [P7 gate — the new-game intro plays and returns, and a defect I nearly invented](#p7-gate--the-new-game-intro-plays-and-returns-and-a-defect-i-nearly-invented)
|
||
* [`ScreenView.skipped` was correct and unread since P1 — now it says so itself](#screenviewskipped-was-correct-and-unread-since-p1--now-it-says-so-itself)
|
||
* [Refutation attempt — "the element declared first paints first"](#refutation-attempt--the-element-declared-first-paints-first)
|
||
* [The menu bed plays under the cutscene, nobody decided that, and it stays](#the-menu-bed-plays-under-the-cutscene-nobody-decided-that-and-it-stays)
|
||
* [`wait:<seconds>`, and the bed's loop seam is 3.4 seconds of silence](#waitseconds-and-the-beds-loop-seam-is-34-seconds-of-silence)
|
||
* [Two harness bugs, and the defect the second one was hiding](#two-harness-bugs-and-the-defect-the-second-one-was-hiding)
|
||
* [The `PRESS Ⓐ` plate: four bugs in a row, and a number I have been misquoting](#the-press--plate-four-bugs-in-a-row-and-a-number-i-have-been-misquoting)
|
||
* [The title's residual is the sweep phase, and the sweeps fit at ~400 units, not 357.7](#the-titles-residual-is-the-sweep-phase-and-the-sweeps-fit-at-400-units-not-3577)
|
||
* [A second capture closes the sweep-geometry question, and the plate matches at 0.00093 %](#a-second-capture-closes-the-sweep-geometry-question-and-the-plate-matches-at-000093)
|
||
* [`--focus=` did nothing on the menu path, and the corpus had an untested focus capture](#--focus-did-nothing-on-the-menu-path-and-the-corpus-had-an-untested-focus-capture)
|
||
* [The last unused capture, placed — and its residual is the oracle's, not the port's](#the-last-unused-capture-placed--and-its-residual-is-the-oracles-not-the-ports)
|
||
* [`MODDING.md` had five rules and no check. Now it has one, and all five pass](#moddingmd-had-five-rules-and-no-check-now-it-has-one-and-all-five-pass)
|
||
* [Five authored values had no reader — including the one I asked for measurements into](#five-authored-values-had-no-reader--including-the-one-i-asked-for-measurements-into)
|
||
* [`FORMAT.md` declared the port's own export invalid, and a failed export is not atomic](#formatmd-declared-the-ports-own-export-invalid-and-a-failed-export-is-not-atomic)
|
||
* [`check-all`, a verdict that ignored its own statistic, and a claim of mine that was wrong](#check-all-a-verdict-that-ignored-its-own-statistic-and-a-claim-of-mine-that-was-wrong)
|
||
* [The `title` disagreement, localised — and the question I filed for it was the wrong one](#the-title-disagreement-localised--and-the-question-i-filed-for-it-was-the-wrong-one)
|
||
* [Auditing `BLOCKED.md` found three stale rows, and the undated ones were all three](#auditing-blockedmd-found-three-stale-rows-and-the-undated-ones-were-all-three)
|
||
* [The record already answered last iteration's question, under headings that name it](#the-record-already-answered-last-iterations-question-under-headings-that-name-it)
|
||
* [🔴 Twenty-one messages to a dead address, each one warning me it was dead](#twenty-one-messages-to-a-dead-address-each-one-warning-me-it-was-dead)
|
||
* [The forced-backdrop pass is load-bearing on two screens, not six](#the-forced-backdrop-pass-is-load-bearing-on-two-screens-not-six)
|
||
* [Re-running the Decoder's necessity census: every figure reproduces, and what that is worth](#re-running-the-decoders-necessity-census-every-figure-reproduces-and-what-that-is-worth)
|
||
* [A second witness for the pixel-cost claim, from a different renderer](#a-second-witness-for-the-pixel-cost-claim-from-a-different-renderer)
|
||
* [Reconciling the two ink figures, and what "has its own key" is resting on](#reconciling-the-two-ink-figures-and-what-has-its-own-key-is-resting-on)
|
||
* [Not one of the 80 has a decoded key — and the port's four are the rule's oracle check](#not-one-of-the-80-has-a-decoded-key--and-the-ports-four-are-the-rules-oracle-check)
|
||
* [A withholding reason that was false, and the measurement beside it that was not](#a-withholding-reason-that-was-false-and-the-measurement-beside-it-that-was-not)
|
||
* [The sweep discriminator resolves: different frames, and a sweep position cannot date one](#the-sweep-discriminator-resolves-different-frames-and-a-sweep-position-cannot-date-one)
|
||
* [Their trap, run against my tree — and I found its mirror instead](#their-trap-run-against-my-tree--and-i-found-its-mirror-instead)
|
||
* [The plate pulses — measured, and the port was wrong on the boot's end state](#the-plate-pulses--measured-and-the-port-was-wrong-on-the-boots-end-state)
|
||
* [A static overlay now advances, and a refutation attempt on the pulse floor](#a-static-overlay-now-advances-and-a-refutation-attempt-on-the-pulse-floor)
|
||
* [Their pulse floor reproduces exactly once the predicate is named — 159, to the pixel](#their-pulse-floor-reproduces-exactly-once-the-predicate-is-named--159-to-the-pixel)
|
||
* [My rendered pulse, counted in their units — and #4 refutes the voice value without fixing it](#my-rendered-pulse-counted-in-their-units--and-4-refutes-the-voice-value-without-fixing-it)
|
||
* [The voice export now carries every qualifying stream — and a unity sum was refused by our own check](#the-voice-export-now-carries-every-qualifying-stream--and-a-unity-sum-was-refused-by-our-own-check)
|
||
* [Their stream assignment does not fit my region — weights NOT applied](#their-stream-assignment-does-not-fit-my-region--weights-not-applied)
|
||
* [The resolver starts late, and my "duplicate tail" was a real stream all along](#the-resolver-starts-late-and-my-duplicate-tail-was-a-real-stream-all-along)
|
||
* [The export knew the voice was incomplete; the runtime did not say so](#the-export-knew-the-voice-was-incomplete-the-runtime-did-not-say-so)
|
||
* [The voice export is complete — new pin, and the cause was a "within one bank" cap](#the-voice-export-is-complete--new-pin-and-the-cause-was-a-within-one-bank-cap)
|
||
* [The positional weights are applied — keyed by byte size, so the key is a check](#the-positional-weights-are-applied--keyed-by-byte-size-so-the-key-is-a-check)
|
||
* [🔴 Unexplained: `verify-menu-audio`'s dead-press check has started failing](#unexplained-verify-menu-audios-dead-press-check-has-started-failing)
|
||
* [External ground truth for every three-chunk region — the movies' own durations](#external-ground-truth-for-every-three-chunk-region--the-movies-own-durations)
|
||
* [The menu bed loops at 61.93 s — and my 3.4 s "ugly seam" was mine, not the game's](#the-menu-bed-loops-at-6193-s--and-my-34-s-ugly-seam-was-mine-not-the-games)
|
||
* [The dead-press check was passing by luck, and the luck ran out](#the-dead-press-check-was-passing-by-luck-and-the-luck-ran-out)
|
||
* [Independent confirmation of the 1.5 MB cap — the mechanism, not just the conclusion](#independent-confirmation-of-the-15-mb-cap--the-mechanism-not-just-the-conclusion)
|
||
* [The loop is a runtime field, the two readings conflict, and the port keeps what it shipped](#the-loop-is-a-runtime-field-the-two-readings-conflict-and-the-port-keeps-what-it-shipped)
|
||
* [The duration is confirmed and the window is wrong — and the start is now a visible field](#the-duration-is-confirmed-and-the-window-is-wrong--and-the-start-is-now-a-visible-field)
|
||
* [The loop window is measured — `-ss 9.44 -t 61.87` — and the near-silence count tracked the error](#the-loop-window-is-measured---ss-944--t-6187--and-the-near-silence-count-tracked-the-error)
|
||
* [Applying "grep the corpus for the claim" to my own corpus](#applying-grep-the-corpus-for-the-claim-to-my-own-corpus)
|
||
* [A refuted-claim register, because the audit found what the audit found](#a-refuted-claim-register-because-the-audit-found-what-the-audit-found)
|
||
* [State of the port, and a claim I built on for a week without checking](#state-of-the-port-and-a-claim-i-built-on-for-a-week-without-checking)
|
||
* [Identifying their submenu capture: edges where intensity could not](#identifying-their-submenu-capture-edges-where-intensity-could-not)
|
||
* [`on_cancel`: one half measured, and a MEASURED stamp removed from the other](#on_cancel-one-half-measured-and-a-measured-stamp-removed-from-the-other)
|
||
* [BLOCKED.md's five "blocking" rows were all answered, some days ago](#blockedmds-five-blocking-rows-were-all-answered-some-days-ago)
|
||
* [The plate came back in the game and not in the port](#the-plate-came-back-in-the-game-and-not-in-the-port)
|
||
* [🔴 `verify-screen` was nondeterministic, and it looked fine most of the time](#verify-screen-was-nondeterministic-and-it-looked-fine-most-of-the-time)
|
||
* [🔴 WITHDRAWN — the JP capture does NOT go against the port; I scored the wrong frame](#withdrawn--the-jp-capture-does-not-go-against-the-port-i-scored-the-wrong-frame)
|
||
* [*(This heading read: "The JP title capture adjudicates `title_jp` — and it goes](#this-heading-read-the-jp-title-capture-adjudicates-title_jp--and-it-goes)
|
||
* [against the port." Withdrawn in full below. I scored `verify-screen`'s](#against-the-port-withdrawn-in-full-below-i-scored-verify-screens)
|
||
* [`--pose=rest` frame, which the port does not ship; posed as it runs, the port](#--poserest-frame-which-the-port-does-not-ship-posed-as-it-runs-the-port)
|
||
* [beats the reference +0.9994 to +0.8727. The heading asserted the opposite of](#beats-the-reference-09994-to-08727-the-heading-asserted-the-opposite-of)
|
||
* [the finding for as long as it stood.)*](#the-finding-for-as-long-as-it-stood)
|
||
* [🔴 CORRECTION: the port did not move away from the game — I scored the wrong frame](#correction-the-port-did-not-move-away-from-the-game--i-scored-the-wrong-frame)
|
||
* [The `rest()` flash defect reaches four screens I ship — and the port already survives it](#the-rest-flash-defect-reaches-four-screens-i-ship--and-the-port-already-survives-it)
|
||
* [Correction: those two are the *sound* path, which makes the rule stronger](#correction-those-two-are-the-sound-path-which-makes-the-rule-stronger)
|
||
* [The two loading screens are no longer black, and it was the paint order](#the-two-loading-screens-are-no-longer-black-and-it-was-the-paint-order)
|
||
* [Adjudicating the Decoder's `rest()` replacement against the game](#adjudicating-the-decoders-rest-replacement-against-the-game)
|
||
* [The boot's own end frame, scored against the game for the first time](#the-boots-own-end-frame-scored-against-the-game-for-the-first-time)
|
||
* [Refutation attempt: the settle-instant candidate is **not** uniformly better](#refutation-attempt-the-settle-instant-candidate-is-not-uniformly-better)
|
||
* [My own predictor holds — and the evidence that made it *better than width* does not](#my-own-predictor-holds--and-the-evidence-that-made-it-better-than-width-does-not)
|
||
* [Checking my own tree for the ordinal foot-gun that just voided three of theirs](#checking-my-own-tree-for-the-ordinal-foot-gun-that-just-voided-three-of-theirs)
|
||
* [Looking for a case that separates width from mid-ramp — there is none, and I nearly invented one](#looking-for-a-case-that-separates-width-from-mid-ramp--there-is-none-and-i-nearly-invented-one)
|
||
* [Auditing my tree for the disc-wide ordinal foot-gun](#auditing-my-tree-for-the-disc-wide-ordinal-foot-gun)
|
||
* [Their withdrawn "~14 units of black hold" — my authored 9 survives it](#their-withdrawn-14-units-of-black-hold--my-authored-9-survives-it)
|
||
* [🔴 CORRECTION: my 18-vs-19 "agreement" compared two different intervals](#correction-my-18-vs-19-agreement-compared-two-different-intervals)
|
||
* [`check-all` passes — after an hour-long hang that was the suite's own fault](#check-all-passes--after-an-hour-long-hang-that-was-the-suites-own-fault)
|
||
* [Ⓐ and Ⓑ are not the same shape, and my `black_hold` treats them as if they were](#and--are-not-the-same-shape-and-my-black_hold-treats-them-as-if-they-were)
|
||
* [🔴 `check-all` excused two failing rows with a reason that is measurably false](#check-all-excused-two-failing-rows-with-a-reason-that-is-measurably-false)
|
||
* [`black_hold_units` 9 → 0, and why not the value that fits best](#black_hold_units-9--0-and-why-not-the-value-that-fits-best)
|
||
* ["Already up to date" is not evidence that I am current](#already-up-to-date-is-not-evidence-that-i-am-current)
|
||
* [Re-deriving `black_hold_units` against four measurements, not three](#re-deriving-black_hold_units-against-four-measurements-not-three)
|
||
* [🔴 CORRECTION: my "the eras render identically" measurement was void](#correction-my-the-eras-render-identically-measurement-was-void)
|
||
* [🔴 CORRECTION: my branch *is* the stale era, and the reference binary was never the workspace build](#correction-my-branch-is-the-stale-era-and-the-reference-binary-was-never-the-workspace-build)
|
||
* [`exit_ramp_units`: the refuted constant was living in a default](#exit_ramp_units-the-refuted-constant-was-living-in-a-default)
|
||
* [Auditing the whole tree for "a deleted value that something still supplies"](#auditing-the-whole-tree-for-a-deleted-value-that-something-still-supplies)
|
||
* [Counting the fallbacks instead of inspecting them — and one I had misjudged](#counting-the-fallbacks-instead-of-inspecting-them--and-one-i-had-misjudged)
|
||
* [The oracle harness was nondeterministic, and I quoted its numbers for a dozen iterations](#the-oracle-harness-was-nondeterministic-and-i-quoted-its-numbers-for-a-dozen-iterations)
|
||
* [Answering "an unenumerated set" — don't enumerate, test](#answering-an-unenumerated-set--dont-enumerate-test)
|
||
* [🔴 The third clock was in my own list, and I did not wire it](#the-third-clock-was-in-my-own-list-and-i-did-not-wire-it)
|
||
* [🔴 WITHDRAWN — the leaf-phase minimum measures the capture, not the game](#withdrawn--the-leaf-phase-minimum-measures-the-capture-not-the-game)
|
||
* [*(This heading read: "The leaf phase was an arbitrary choice; the capture turns](#this-heading-read-the-leaf-phase-was-an-arbitrary-choice-the-capture-turns)
|
||
* [out to determine it." [refuted] Refuted 97 lines below by the replication on `title`,](#out-to-determine-it-refuted-refuted-97-lines-below-by-the-replication-on-title)
|
||
* [which minimises at a different phase for the same object. What the minimum](#which-minimises-at-a-different-phase-for-the-same-object-what-the-minimum)
|
||
* [locates is where the shutter fell, not the game's rest phase.)*](#locates-is-where-the-shutter-fell-not-the-games-rest-phase)
|
||
* [Cross-checking their leaf reading against my export — it reconciles](#cross-checking-their-leaf-reading-against-my-export--it-reconciles)
|
||
* [Replicating the phase result on the title — it fails, and the failure is the finding](#replicating-the-phase-result-on-the-title--it-fails-and-the-failure-is-the-finding)
|
||
* [Their masking rule, implemented — and it does not transfer to my screens](#their-masking-rule-implemented--and-it-does-not-transfer-to-my-screens)
|
||
* [Their "the game may not draw these leaves" hypothesis — my curves say *sometimes*](#their-the-game-may-not-draw-these-leaves-hypothesis--my-curves-say-sometimes)
|
||
* [Using the clean splash rows to measure the tone curve — and repeating a documented mistake](#using-the-clean-splash-rows-to-measure-the-tone-curve--and-repeating-a-documented-mistake)
|
||
* [Localising the 1.92 splash floor: it is glyph edges, and off them the port is ~1 RMSE from the game](#localising-the-192-splash-floor-it-is-glyph-edges-and-off-them-the-port-is-1-rmse-from-the-game)
|
||
* [Their draw-stream result checked against my export — three confirmations and one correction](#their-draw-stream-result-checked-against-my-export--three-confirmations-and-one-correction)
|
||
* [Nested leaves may advance at half rate — a CONDITIONAL exposure, not a defect](#nested-leaves-may-advance-at-half-rate--a-conditional-exposure-not-a-defect)
|
||
* [*(This heading read "a quantified defect in shipped output". The rate it is](#this-heading-read-a-quantified-defect-in-shipped-output-the-rate-it-is)
|
||
* [quantified against was later shown to be neither frame-locked nor simple](#quantified-against-was-later-shown-to-be-neither-frame-locked-nor-simple)
|
||
* [wall-clock, so the input is known wrong rather than merely unpinned. Nothing](#wall-clock-so-the-input-is-known-wrong-rather-than-merely-unpinned-nothing)
|
||
* [is established as defective.)*](#is-established-as-defective)
|
||
* [Their Route 1 is closed for the whole archive, not just the title](#their-route-1-is-closed-for-the-whole-archive-not-just-the-title)
|
||
* [The off-edge splash residual is **not** tonal — and I was comparing it to the wrong floor](#the-off-edge-splash-residual-is-not-tonal--and-i-was-comparing-it-to-the-wrong-floor)
|
||
* [Their linearity gate, applied to my side of the ratio — and an inversion](#their-linearity-gate-applied-to-my-side-of-the-ratio--and-an-inversion)
|
||
* [The leaf thread, closed — one export value verified against the game, one self-check abandoned](#the-leaf-thread-closed--one-export-value-verified-against-the-game-one-self-check-abandoned)
|
||
* [Delivering the phase term where the numbers are, not where I found them](#delivering-the-phase-term-where-the-numbers-are-not-where-i-found-them)
|
||
* [The boot verified as a *sequence*, not just at its endpoint](#the-boot-verified-as-a-sequence-not-just-at-its-endpoint)
|
||
* [Refuting the "8.5 % systematic" in the splash dwells — it is the span, not the clock](#refuting-the-85--systematic-in-the-splash-dwells--it-is-the-span-not-the-clock)
|
||
* [The fifth member of the family is mine: "drawn" is not "visible"](#the-fifth-member-of-the-family-is-mine-drawn-is-not-visible)
|
||
* [Auditing `--black`, and a rule that falls out of it](#auditing---black-and-a-rule-that-falls-out-of-it)
|
||
* [🔴 CORRECTION: my backdrop predicate is exact in `GP_TITLE` and its reading was wrong](#correction-my-backdrop-predicate-is-exact-in-gp_title-and-its-reading-was-wrong)
|
||
* [Sweeping my own `--help` and headers, after theirs](#sweeping-my-own---help-and-headers-after-theirs)
|
||
* [`black_hold_units`: my own tripwire has tripped, and I am not resolving it alone](#black_hold_units-my-own-tripwire-has-tripped-and-i-am-not-resolving-it-alone)
|
||
* [Their sharpened tell, applied to my tree: two descriptions the code below had already refuted](#their-sharpened-tell-applied-to-my-tree-two-descriptions-the-code-below-had-already-refuted)
|
||
* [The grep found two more — and the reason is my correction *habit*, not my attention](#the-grep-found-two-more--and-the-reason-is-my-correction-habit-not-my-attention)
|
||
* [Auditing headings — and my own index was amplifying the withdrawn ones](#auditing-headings--and-my-own-index-was-amplifying-the-withdrawn-ones)
|
||
* [Ranking instructions above descriptions — swept, and the worst class is clean](#ranking-instructions-above-descriptions--swept-and-the-worst-class-is-clean)
|
||
* [Live-but-undocumented flags — and I wrote a dead instruction while fixing dead instructions](#live-but-undocumented-flags--and-i-wrote-a-dead-instruction-while-fixing-dead-instructions)
|
||
* [Their `XPR_*` lead traced and closed — and their class found in my own lane](#their-xpr_-lead-traced-and-closed--and-their-class-found-in-my-own-lane)
|
||
* [Branches that announce themselves — their lesson, applied where it already bit me](#branches-that-announce-themselves--their-lesson-applied-where-it-already-bit-me)
|
||
* [Every documented invocation verified — and one runs forever without saying so](#every-documented-invocation-verified--and-one-runs-forever-without-saying-so)
|
||
* [🔴 I promoted an unverified claim of theirs to a fact, against data I had authored](#i-promoted-an-unverified-claim-of-theirs-to-a-fact-against-data-i-had-authored)
|
||
* [The half-guard they named, tested — and it found a real gap on first use](#the-half-guard-they-named-tested--and-it-found-a-real-gap-on-first-use)
|
||
* [The ordered pair determines the gap — and nothing declared predicts it](#the-ordered-pair-determines-the-gap--and-nothing-declared-predicts-it)
|
||
* [The overlay leaf-pin fix, verified live with a negative control](#the-overlay-leaf-pin-fix-verified-live-with-a-negative-control)
|
||
* [Their incoming-primitive observation, checked — and a sharpening they can use](#their-incoming-primitive-observation-checked--and-a-sharpening-they-can-use)
|
||
* [`PORT-MISSION.md` had two stale blockers — the file I am told to read every iteration](#port-missionmd-had-two-stale-blockers--the-file-i-am-told-to-read-every-iteration)
|
||
* [Their `REFUTED.md` gap, in my tree — where I already had the mechanism and fed it nothing](#their-refutedmd-gap-in-my-tree--where-i-already-had-the-mechanism-and-fed-it-nothing)
|
||
* [Building the withdrawal-time hook — the thing we agreed neither of us was about to close](#building-the-withdrawal-time-hook--the-thing-we-agreed-neither-of-us-was-about-to-close)
|
||
* [Applying "a correction is a new claim" to my own most recent correction](#applying-a-correction-is-a-new-claim-to-my-own-most-recent-correction)
|
||
* [P0 gate — recorded at last, and the gap it belongs to](#p0-gate--recorded-at-last-and-the-gap-it-belongs-to)
|
||
* [Their sufficiency gap, run on `authored/` — clean, after I nearly reported 35 false positives](#their-sufficiency-gap-run-on-authored--clean-after-i-nearly-reported-35-false-positives)
|
||
* [Their absence shape on my own citations — and the wording gap in my P0 closure](#their-absence-shape-on-my-own-citations--and-the-wording-gap-in-my-p0-closure)
|
||
* [The off-edge splash residual, localised — three mechanisms ruled out, one honest description](#the-off-edge-splash-residual-localised--three-mechanisms-ruled-out-one-honest-description)
|
||
* [Full regression after a session of edits — and the phase term moving two published rows](#full-regression-after-a-session-of-edits--and-the-phase-term-moving-two-published-rows)
|
||
* [Narrowing my own hook — 33 was a measurement of the regex](#narrowing-my-own-hook--33-was-a-measurement-of-the-regex)
|
||
* [Their Q10 correction checked, and the register's cost is per-*mention*, not per-correction](#their-q10-correction-checked-and-the-registers-cost-is-per-mention-not-per-correction)
|
||
* [The contract I read every iteration is 3 185 lines shorter than the contract](#the-contract-i-read-every-iteration-is-3-185-lines-shorter-than-the-contract)
|
||
* [A refutation attempt on `+0x08 is the loop length` — it survives, and the port adopts it](#a-refutation-attempt-on-0x08-is-the-loop-length--it-survives-and-the-port-adopts-it)
|
||
* [The contract is checked now, not read — `tools/port/contract-check`](#the-contract-is-checked-now-not-read--toolsportcontract-check)
|
||
* [A refutation attempt on the fade numbers — it survives, from a third reader](#a-refutation-attempt-on-the-fade-numbers--it-survives-from-a-third-reader)
|
||
* [The walk is checked too, and "only the ring moves" tested against my own renderer](#the-walk-is-checked-too-and-only-the-ring-moves-tested-against-my-own-renderer)
|
||
* [The `+0x08` ask came back answered — and is not consumable yet](#the-0x08-ask-came-back-answered--and-is-not-consumable-yet)
|
||
* [The pin moves to `formats-pin-2026-08-30b`, and the port stops owning `+0x08`](#the-pin-moves-to-formats-pin-2026-08-30b-and-the-port-stops-owning-0x08)
|
||
* [The menu remembers its cursor — a measured P5 defect, fixed and scoped](#the-menu-remembers-its-cursor--a-measured-p5-defect-fixed-and-scoped)
|
||
* [🔴 Correction, same day: I encoded an absence of measurement as a finding](#correction-same-day-i-encoded-an-absence-of-measurement-as-a-finding)
|
||
* [The `kind` sweep I said I owed: 15 labels, and 7 rested on a neighbour's argument](#the-kind-sweep-i-said-i-owed-15-labels-and-7-rested-on-a-neighbours-argument)
|
||
* [A refutation attempt on Q2's map of `GP_TITLE` — the count is right, the list is short](#a-refutation-attempt-on-q2s-map-of-gp_title--the-count-is-right-the-list-is-short)
|
||
* [An authored value became a measured one, and a difference-only check got an origin](#an-authored-value-became-a-measured-one-and-a-difference-only-check-got-an-origin)
|
||
* [EXTRAS resets — measured. The assertion was right and that does not make it evidence.](#extras-resets--measured-the-assertion-was-right-and-that-does-not-make-it-evidence)
|
||
* [Running the port as a player finds two things reading it did not](#running-the-port-as-a-player-finds-two-things-reading-it-did-not)
|
||
* [The boot's wall-clock seconds are a property of this container, not of the port](#the-boots-wall-clock-seconds-are-a-property-of-this-container-not-of-the-port)
|
||
* [Their negative result, and the trap in choosing the more general instrument](#their-negative-result-and-the-trap-in-choosing-the-more-general-instrument)
|
||
* [🔴 Correction: my media-versus-wall-clock method cannot audit container pacing](#correction-my-media-versus-wall-clock-method-cannot-audit-container-pacing)
|
||
* [The leak was not mine — a negative result, and the "fix" is reverted](#the-leak-was-not-mine--a-negative-result-and-the-fix-is-reverted)
|
||
* [A second narrow anchor, where I had already found the weakness and not acted](#a-second-narrow-anchor-where-i-had-already-found-the-weakness-and-not-acted)
|
||
* [Reported: a live-reading HANDOFF section that two later ones have overtaken](#reported-a-live-reading-handoff-section-that-two-later-ones-have-overtaken)
|
||
* [Their rule applied backwards: my video result is stronger than my withdrawal said](#their-rule-applied-backwards-my-video-result-is-stronger-than-my-withdrawal-said)
|
||
* [🔴 I measured my own claim and it is wrong: the player skips, heavily](#i-measured-my-own-claim-and-it-is-wrong-the-player-skips-heavily)
|
||
* [🔴 Correcting the correction: the frame probe is an UPPER BOUND, and my contrast was contention](#correcting-the-correction-the-frame-probe-is-an-upper-bound-and-my-contrast-was-contention)
|
||
* [The P4 fidelity question, attempted: four traps reproduced, no verdict yet](#the-p4-fidelity-question-attempted-four-traps-reproduced-no-verdict-yet)
|
||
* [Changing the KIND of quantity answered it on the first attempt](#changing-the-kind-of-quantity-answered-it-on-the-first-attempt)
|
||
* [🔴 My seek trap was over-general — the Decoder narrowed it](#my-seek-trap-was-over-general--the-decoder-narrowed-it)
|
||
* [A capital letter hid a refuted claim in the file whose job is to say what is open](#a-capital-letter-hid-a-refuted-claim-in-the-file-whose-job-is-to-say-what-is-open)
|
||
* [The difference path cannot verify a lossless encode — so nothing it says counts](#the-difference-path-cannot-verify-a-lossless-encode--so-nothing-it-says-counts)
|
||
* [The identity rule, turned back on my own newest tool — and it was biased](#the-identity-rule-turned-back-on-my-own-newest-tool--and-it-was-biased)
|
||
* [Their refutation attempt on my band check found a coverage hole and two defects](#their-refutation-attempt-on-my-band-check-found-a-coverage-hole-and-two-defects)
|
||
* [🔴 RETRACTED: the `S00A` coverage hole was my control's filter, not the check](#retracted-the-s00a-coverage-hole-was-my-controls-filter-not-the-check)
|
||
* [Their two tools had the shape I shipped, and the general form is sharper now](#their-two-tools-had-the-shape-i-shipped-and-the-general-form-is-sharper-now)
|
||
* [Closing the two-directional gap: the control harness now asserts itself](#closing-the-two-directional-gap-the-control-harness-now-asserts-itself)
|
||
* [The register check had no executable control, and an empty register passed forever](#the-register-check-had-no-executable-control-and-an-empty-register-passed-forever)
|
||
* [Two harness gaps closed, and one of them was mine done by hand](#two-harness-gaps-closed-and-one-of-them-was-mine-done-by-hand)
|
||
* [All four submenus reset, and I am not promoting it to a rule](#all-four-submenus-reset-and-i-am-not-promoting-it-to-a-rule)
|
||
* [🔴 The counter-example I kept asking for was in a file I wrote](#the-counter-example-i-kept-asking-for-was-in-a-file-i-wrote)
|
||
* [The last control harness, and a clean sweep for the top-item assumption](#the-last-control-harness-and-a-clean-sweep-for-the-top-item-assumption)
|
||
* [Settled: a submenu resets to its OWN OPENING ITEM, not to its top item](#settled-a-submenu-resets-to-its-own-opening-item-not-to-its-top-item)
|
||
* [Their refutation attempt on `extras/initial_focus` — checked against the bytes, twice](#their-refutation-attempt-on-extrasinitial_focus--checked-against-the-bytes-twice)
|
||
* [Menu focus does not survive a reboot — and the reach matters more than the result](#menu-focus-does-not-survive-a-reboot--and-the-reach-matters-more-than-the-result)
|
||
* [Liveness: every one of my tools passed on an empty input](#liveness-every-one-of-my-tools-passed-on-an-empty-input)
|
||
* [Their `ring_row.py` defect, and why it did not reach me](#their-ring_rowpy-defect-and-why-it-did-not-reach-me)
|
||
* [The liveness lesson, applied to the product: a mistyped override was silent](#the-liveness-lesson-applied-to-the-product-a-mistyped-override-was-silent)
|
||
* [Their P3 delivery, taken at the strength they gave it](#their-p3-delivery-taken-at-the-strength-they-gave-it)
|
||
* [`docs/port/RUNNING.md` — the P5 gate needed a human and had no runbook](#docsportrunningmd--the-p5-gate-needed-a-human-and-had-no-runbook)
|
||
* [Their `BGM_103` report: the row was already corrected, and it carries their diagnosis](#their-bgm_103-report-the-row-was-already-corrected-and-it-carries-their-diagnosis)
|
||
* [The shared-state problem is two gaps, and only one of them needs a human](#the-shared-state-problem-is-two-gaps-and-only-one-of-them-needs-a-human)
|
||
* [The mirror of `peer-head`: my register was judging their files from my stale tree](#the-mirror-of-peer-head-my-register-was-judging-their-files-from-my-stale-tree)
|
||
* [Their zero held, mine was six, and the difference is structural rather than hygiene](#their-zero-held-mine-was-six-and-the-difference-is-structural-rather-than-hygiene)
|
||
* [A peer hit cannot be adjudicated from the phrase alone — demonstrated, not argued](#a-peer-hit-cannot-be-adjudicated-from-the-phrase-alone--demonstrated-not-argued)
|
||
* [The register now records what each dead claim ASSERTED, not just how it was worded](#the-register-now-records-what-each-dead-claim-asserted-not-just-how-it-was-worded)
|
||
* [DIFFICULTY is a dialog, and the count-match it weakens was one I had recorded](#difficulty-is-a-dialog-and-the-count-match-it-weakens-was-one-i-had-recorded)
|
||
* [Their note about instruments applies to me more than to them](#their-note-about-instruments-applies-to-me-more-than-to-them)
|
||
* [The reach I recorded as theirs closed, and re-running it with a broader filter held](#the-reach-i-recorded-as-theirs-closed-and-re-running-it-with-a-broader-filter-held)
|
||
* [Refuted: their language-sprite reading of the `GP_DIALOG` residual](#refuted-their-language-sprite-reading-of-the-gp_dialog-residual)
|
||
* [🔴 I relayed a claim I had not checked, inside the sentence where I said I had](#i-relayed-a-claim-i-had-not-checked-inside-the-sentence-where-i-said-i-had)
|
||
* [Their `.prm` correction, checked against my renderer — and their technique, run here](#their-prm-correction-checked-against-my-renderer--and-their-technique-run-here)
|
||
* [The incentive they named, stated plainly](#the-incentive-they-named-stated-plainly)
|
||
* [They closed the 37 — conclusion confirmed, one supporting leg does not reproduce](#they-closed-the-37--conclusion-confirmed-one-supporting-leg-does-not-reproduce)
|
||
* [Naming an untested bound is what got it tested](#naming-an-untested-bound-is-what-got-it-tested)
|
||
* [Auditing my own multi-leg claims: the one that mattered holds, and now says why](#auditing-my-own-multi-leg-claims-the-one-that-mattered-holds-and-now-says-why)
|
||
* [Closing one of my own, and a second relayed count from the same delivery](#closing-one-of-my-own-and-a-second-relayed-count-from-the-same-delivery)
|
||
* [The oracle capture's own focus state was never established — now it is, by exclusion](#the-oracle-captures-own-focus-state-was-never-established--now-it-is-by-exclusion)
|
||
* ["Independently" dies on a fact, and I decline to re-add the pairing they restored](#independently-dies-on-a-fact-and-i-decline-to-re-add-the-pairing-they-restored)
|
||
* [Their docstring point found three stale claims in my code](#their-docstring-point-found-three-stale-claims-in-my-code)
|
||
* [Their variant found a fourth in my tree: a stale JUSTIFICATION, not a stale number](#their-variant-found-a-fourth-in-my-tree-a-stale-justification-not-a-stale-number)
|
||
* [`audit-kinds` was auditing 16 of 71 authored justifications, and never said so](#audit-kinds-was-auditing-16-of-71-authored-justifications-and-never-said-so)
|
||
* [Their failed detector, recorded so I do not rebuild it](#their-failed-detector-recorded-so-i-do-not-rebuild-it)
|
||
* [Triaging the 52: thirteen were provenance claims, and two failed on sight](#triaging-the-52-thirteen-were-provenance-claims-and-two-failed-on-sight)
|
||
* [My own triage under-counted, and three uncited measurements surfaced behind it](#my-own-triage-under-counted-and-three-uncited-measurements-surfaced-behind-it)
|
||
* [🔴 My mechanism does not reproduce in my own corpus — measured, and it is refuted](#my-mechanism-does-not-reproduce-in-my-own-corpus--measured-and-it-is-refuted)
|
||
* [The backfill: 17 was 12, and 12 is now 0](#the-backfill-17-was-12-and-12-is-now-0)
|
||
* [🔴 Their record layout was wrong and I had copied it — fourth relayed aside](#their-record-layout-was-wrong-and-i-had-copied-it--fourth-relayed-aside)
|
||
* [🔴 My falsifier never identified the offset — the half I called a formality did](#my-falsifier-never-identified-the-offset--the-half-i-called-a-formality-did)
|
||
* [The 92.3 %-versus-49.6 % gap: same numerator, and their filter is not applied](#the-923--versus-496--gap-same-numerator-and-their-filter-is-not-applied)
|
||
* [🔴 Correcting my own correction: none of the 1 530 is a question without content](#correcting-my-own-correction-none-of-the-1-530-is-a-question-without-content)
|
||
* [The one load-bearing thing in the denominator thread, checked against the port](#the-one-load-bearing-thing-in-the-denominator-thread-checked-against-the-port)
|
||
* [Quantifying the one thing neither agent can move](#quantifying-the-one-thing-neither-agent-can-move)
|
||
* [Verified their merge-state claim rather than relaying it — and it improves the ask](#verified-their-merge-state-claim-rather-than-relaying-it--and-it-improves-the-ask)
|
||
* [The number in my decision document was stale the moment I committed it](#the-number-in-my-decision-document-was-stale-the-moment-i-committed-it)
|
||
* [A command without a pass condition is half a check](#a-command-without-a-pass-condition-is-half-a-check)
|
||
* [What every failure this week actually was](#what-every-failure-this-week-actually-was)
|
||
* [The remaining multi-leg claims audited — and the pattern I predicted is not there](#the-remaining-multi-leg-claims-audited--and-the-pattern-i-predicted-is-not-there)
|
||
* [Their JP menu capture, corroborated from the disc — and the legs are genuinely different](#their-jp-menu-capture-corroborated-from-the-disc--and-the-legs-are-genuinely-different)
|
||
* [They have taken the relay finding, and it now has a direction](#they-have-taken-the-relay-finding-and-it-now-has-a-direction)
|
||
* [The independent pair was an accident — the rule that would make it deliberate](#the-independent-pair-was-an-accident--the-rule-that-would-make-it-deliberate)
|
||
* [A workflow defect of mine, on its fourth occurrence](#a-workflow-defect-of-mine-on-its-fourth-occurrence)
|
||
* [The menu residual, decomposed — and half of 13.06 is tone](#the-menu-residual-decomposed--and-half-of-1306-is-tone)
|
||
* [Refutation: the peer's tone/geometry positive control rests on a number of mine that cannot carry it](#refutation-the-peers-tonegeometry-positive-control-rests-on-a-number-of-mine-that-cannot-carry-it)
|
||
* [The menu's edge residual is **not** a misregistration — the Decoder's discriminator, run](#the-menus-edge-residual-is-not-a-misregistration--the-decoders-discriminator-run)
|
||
* [`GP_DIALOG` 2/3 restored to `authored/flow.json` — on a measurement this time](#gp_dialog-23-restored-to-authoredflowjson--on-a-measurement-this-time)
|
||
* [The residual map: no local displacement either, and the split I expected is not there](#the-residual-map-no-local-displacement-either-and-the-split-i-expected-is-not-there)
|
||
* [Suppression beats coordinates: the menu residual is two frame elements, drawn too dark](#suppression-beats-coordinates-the-menu-residual-is-two-frame-elements-drawn-too-dark)
|
||
* [The frames generalise, premultiplied alpha is refuted, and the shortfall tracks the background](#the-frames-generalise-premultiplied-alpha-is-refuted-and-the-shortfall-tracks-the-background)
|
||
* [Which blend? Additive halves the error, on both frames — proposed, not adopted](#which-blend-additive-halves-the-error-on-both-frames--proposed-not-adopted)
|
||
* [Refutation attempt: the Decoder's kind-0 claim survives, checked from my own data](#refutation-attempt-the-decoders-kind-0-claim-survives-checked-from-my-own-data)
|
||
* [The blend is measured, so the port draws it — main_menu 13.21 → 10.67](#the-blend-is-measured-so-the-port-draws-it--main_menu-1321--1067)
|
||
* [🔴 Refuted: my "no fully-opaque pixel" sharpener](#refuted-my-no-fully-opaque-pixel-sharpener)
|
||
* [The sweeps: a measured blend, a corroborated identification, and a confound in my own evidence](#the-sweeps-a-measured-blend-a-corroborated-identification-and-a-confound-in-my-own-evidence)
|
||
* [A leak I introduced, and a reach sentence that understates its own gap by four elements](#a-leak-i-introduced-and-a-reach-sentence-that-understates-its-own-gap-by-four-elements)
|
||
* [EXTRAS is complete: 1.97 → 0.63, and the two metrics disagree about it](#extras-is-complete-197--063-and-the-two-metrics-disagree-about-it)
|
||
* [🔴 Refuted: my kind census was a two-screen generalisation, one message after I criticised theirs](#refuted-my-kind-census-was-a-two-screen-generalisation-one-message-after-i-criticised-theirs)
|
||
* [🔴 Refuted: the sweeps DO run on the menu, and my instrument was measuring my own renderer](#refuted-the-sweeps-do-run-on-the-menu-and-my-instrument-was-measuring-my-own-renderer)
|
||
* [The plate's highlight is additive — and my harness poses it at the one phase where it is invisible](#the-plates-highlight-is-additive--and-my-harness-poses-it-at-the-one-phase-where-it-is-invisible)
|
||
* [🔴 A reproduce recipe that names a path off this repo is not a recipe](#a-reproduce-recipe-that-names-a-path-off-this-repo-is-not-a-recipe)
|
||
|
||
<!-- /INDEX -->
|
||
## P0 — the exporter, 2026-08-28
|
||
|
||
### The exporter reads one authored file, and stamps its provenance into the output
|
||
|
||
`export/` is derived and `authored/` is hand-written, and the natural reading of
|
||
that is that the exporter never touches `authored/`. But a screen has to be
|
||
*called* something, and the disc does not name its builds — the identification of
|
||
build 5 as the main menu is HANDOFF Q2, **measured against a live capture**, not
|
||
a field.
|
||
|
||
Two ways to handle that:
|
||
|
||
1. the exporter emits `build_05.json` and the runtime renames it from
|
||
`authored/screen_names.json`;
|
||
2. the exporter reads that map and writes `main_menu.json` directly.
|
||
|
||
Chose **2**, with a condition: every name it applies carries `name_source:
|
||
"authored"` and a `name_why` quoting the evidence, and `check` **rejects** an
|
||
authored name with no `why`. The file that lands in `export/` is therefore still
|
||
honest about which of its fields is a measurement — which is the property the
|
||
derived/authored split exists to protect — while a human opening the tree sees
|
||
`main_menu.json` rather than having to resolve a rename in their head. A build
|
||
nobody has identified exports as `build_NN` with `name_source: "index"`, which is
|
||
a locator and not a claim.
|
||
|
||
This is the **only** authored input the exporter takes. Everything else in
|
||
`authored/` is applied by the runtime over `export/`.
|
||
|
||
### Sprites are per screen, not a flat pool
|
||
|
||
`main_menu` and `extras` both ship a `ptbase.t32` and they are different
|
||
pictures. A flat `sprites/` directory would have silently collided; whichever
|
||
screen exported second would have won, and the loser would have drawn the wrong
|
||
background with no error anywhere. `sprites/<subdir>/<screen>/<name>.png`.
|
||
|
||
### The format is executable
|
||
|
||
`sylpheed-export check --out export` validates a tree against `docs/port/FORMAT.md`
|
||
with no disc in hand. It exists because "the export is correct" is otherwise an
|
||
assertion, and because the P0 gate is *"validates against FORMAT.md"* — which is
|
||
not a thing anyone can confirm by reading.
|
||
|
||
It reads the tree the way Godot will: as a stranger, with no access to the disc,
|
||
the decoders, or the exporter's internals. It deliberately does **not** check the
|
||
export against the disc — that is what `sylpheed-cli screen render` is for, at P1.
|
||
|
||
Checked that it bites, rather than assuming: five mutations of a valid
|
||
`main_menu.json` — a broken `paint_order` permutation, a dangling
|
||
`focus_sprite`, a reversed `buttons` list, a `#rrggbbaa` colour, an invented
|
||
`name_source` — are each caught with a specific message.
|
||
|
||
### The highlight sprite pairs by name; `opt ` is exported but not believed
|
||
|
||
FORMAT v1 said `focus_sprite` came from the element's `opt ` link. That reading
|
||
was **measured and refuted** by the RE agent, and this export shows why plainly:
|
||
on the main menu, `opt ` chains `ptloop01 → ptloop02 → ptbtn01` — two decorations
|
||
and then a button. It is a linked list of something, and it is not focus.
|
||
|
||
The highlight is paired by **sprite name** instead (`ptbtn01.t32` ↔
|
||
`ptbtn01f.t32`), which is HANDOFF's convention and holds for all 54 real pairs on
|
||
the disc. It resolves all five main-menu buttons. The raw link is still exported
|
||
as `opt_link`, renamed so that nothing downstream mistakes it for navigation, and
|
||
so that whoever eventually decodes it has the data.
|
||
|
||
Note this is 🟡 a naming convention, not a decoded field. It is authored in
|
||
effect, and lives in the exporter only because it is a rule over disc data rather
|
||
than a value we chose.
|
||
|
||
### The paint order is exported, not authored
|
||
|
||
Q3 decoded it — a `u16` layer key at `+0x0A` of each `T8aD` sprite header,
|
||
stable-sorted with declaration index. So it is read in the exporter, per the
|
||
contract's own rule for a decoded answer, and `paint_order` in `export/` is a
|
||
derived field. `"paint_order"` is gone from `unresolved`; **`paint_order_ties`
|
||
replaces it**, because the tie-break is still unknown and costs one element's
|
||
blend on one screen.
|
||
|
||
Where an element has no `T8aD` header the key comes from the decoders' table of
|
||
keys **measured off the running game**. That is a different kind of fact, so it
|
||
is labelled: `layer_source` is `"sprite"`, `"implied"` or `"none"`, and a
|
||
consumer that needs to know whether a layer is read or measured can tell.
|
||
|
||
### Colours are exported as two fields with the byte order in the name
|
||
|
||
There are two modulate colours and they multiply: `tint` is RGBA, `fade` is
|
||
**ARGB** and its high byte is the alpha that ramps. v1's single `"#ffffffff"`
|
||
could not carry both and silently discarded the ramping alpha. They are exported
|
||
as `tint_rgba` and `fade_argb`, raw hex, byte order in the key — because getting
|
||
it backwards is silent and looks like an art bug rather than a parse bug.
|
||
|
||
### `t` stays raw
|
||
|
||
HANDOFF Q1 is answered — linear ramp, 2 units per rendered frame, working
|
||
conversion 1 unit = 1/60 s — but that conversion is **measured off the running
|
||
game, not read from the file**, and the finding itself flags the 27.6 present-
|
||
frames/second measurement as the part worth re-testing. If the game turns out to
|
||
present at 60 Hz, every duration halves.
|
||
|
||
So `t` is exported exactly as the disc spells it, `keyframe_time_unit` stays in
|
||
`unresolved`, and the conversion will live in one authored place at P2. One
|
||
constant to change, in a file that says it is a decision.
|
||
|
||
### The final keyframe has no `t`, and `check` enforces that
|
||
|
||
The disc has no time slot on the last keyframe of a group. A file that carries
|
||
one there has invented it. `check` rejects it — this is the one place where the
|
||
temptation to emit a plausible number is strongest and the resulting error is
|
||
completely invisible.
|
||
|
||
---
|
||
|
||
## P1 — Godot draws the screen, 2026-08-28
|
||
|
||
### The Godot side reads the manifest, not a path
|
||
|
||
`ExportTree` is the only class that knows where `export/` is: `SYLPHEED_EXPORT`
|
||
if set, otherwise `<project>/../export`. Screens are addressed by their manifest
|
||
**name** (`main_menu`), never by a file path, so the runtime never encodes the
|
||
archive's subdirectory and a re-export that moves a file does not break it. It
|
||
also checks `format` on both the manifest and each screen, and refuses a tree it
|
||
was not built to read rather than half-drawing one.
|
||
|
||
Textures are read as bytes and decoded with `load_png_from_buffer` at runtime.
|
||
They are deliberately **not** Godot-imported resources: `export/` is gitignored
|
||
and regenerated wholesale, and a `.import` sidecar per sprite would be derived
|
||
state living next to derived state, invalidated on every re-export.
|
||
|
||
### One CanvasItem draws the whole screen
|
||
|
||
`ScreenView._draw` walks `paint_order` and draws each element itself, rather
|
||
than making a node per element and leaning on `z_index`. The export's
|
||
`paint_order` is already back-to-front, so honouring it is a loop; expressing
|
||
the same order through sixteen nodes' z-indices would hide the one thing that is
|
||
still unresolved about it — the **ties** — behind Godot's own sibling rules,
|
||
where a change in the export would silently become a change in Godot's tree
|
||
order instead of a visible change in the draw sequence.
|
||
|
||
### P1 draws `rest` and nothing else
|
||
|
||
Every element is drawn at its resting pose. No keyframe interpolation: that is
|
||
P2, and it depends on the keyframe time unit, which is **measured** rather than
|
||
decoded. A milestone whose gate is a pixel diff must not have a measured
|
||
constant inside it, or the diff stops being evidence about the port.
|
||
|
||
For the same reason `focused_id` is empty at P1. Initial focus was measured as
|
||
unstable boot to boot (HANDOFF Q5), so choosing one is an authored decision and
|
||
it belongs to P5, where a human is pressing keys.
|
||
|
||
### Nearest-neighbour, and why that is not a preference
|
||
|
||
`TEXTURE_FILTER_NEAREST`. The export is a 1:1 copy of the disc's own texels and
|
||
elements draw at up to 500 %; a bilinear filter invents detail the disc does not
|
||
have. It is also what the reference renderer does — `ui_layout::blit` maps
|
||
destination to source by integer division — so a filter difference cannot
|
||
masquerade as a placement difference in the diff.
|
||
|
||
### The capture is the SubViewport, not the window
|
||
|
||
The screen is drawn into a `SubViewport` sized to the export's own `design`
|
||
rectangle and shown through a container that scales it to the window. The first
|
||
attempt captured `get_viewport()` and got **1235×695**: there is a window manager
|
||
on the Xvfb display and its title bar had eaten 45×25 px of a screen the export
|
||
declares as 1280×720. A gate that compares a rescaled 1235×695 capture against a
|
||
1280×720 composite measures the compositor.
|
||
|
||
So `--capture` grabs the SubViewport texture: exactly the design rectangle,
|
||
independent of the window, directly comparable with `screen render` with no crop
|
||
and no resample. The windowed run is still worth doing — it is what proves a
|
||
human sees the screen — but it is not what the numbers come from.
|
||
|
||
## P1 gate — the diff, and what it found
|
||
|
||
`tools/port/verify-screen` renders every screen in the manifest both ways and reports
|
||
the largest per-channel difference anywhere in the frame. Both renderers are held
|
||
to the same inputs: the reference CLI built by `build-reference-cli` from the
|
||
revision the exporter is **pinned** to (not `/reborn/target/`, which is a live
|
||
mount that moves mid-iteration), `--black` because the screen carries its own
|
||
background, and `--primitives --animated` because those are what make the CLI
|
||
draw the same element set the port draws at rest.
|
||
|
||
| screen | build | max per-channel Δ | |
|
||
|---|---|---|---|
|
||
| `main_menu` | 5 | **3** | the P0/P1 gate screen |
|
||
| `main_menu_jp` | 8 | 3 | |
|
||
| `extras` / `extras_jp` | 6 / 9 | 4 / 3 | |
|
||
| `press_start` / `press_start_jp` | 2 / 3 | 1 | |
|
||
| `build_00` / `build_01` | 0 / 1 | 3 | |
|
||
| `build_10` / `build_11` | 10 / 11 | **0** | byte-identical |
|
||
| `title` | 4 | 6 | paint-order tie, below |
|
||
| `title_jp` | 7 | 154 | sampling phase, below |
|
||
|
||
`main_menu` — the milestone's own gate — agrees to **≤3/255 on every channel of
|
||
every pixel**, RMSE 0.38 %, with **no** pixel differing by more than 4 %. 3/255
|
||
is what integer-truncating compositing in the CLI and float rounding on a GPU
|
||
differ by; there is no structural disagreement anywhere in the frame.
|
||
|
||
Three screens exceed that, and each has a named cause rather than a threshold.
|
||
|
||
### `title`: a tie in the paint order — neither renderer is wrong
|
||
|
||
Build 4 is the one screen where the CLI uses a paint order **measured off the
|
||
running game** instead of deriving it. Compared against the order this port
|
||
exports, every single disagreement is **inside a tie** — the two orders differ
|
||
only among elements carrying *identical* layer keys (`0x8083`, the `back2` glow
|
||
group, and `0x80a0`):
|
||
|
||
```
|
||
derived : … 15, 16, 17, 18, 0, 1, 2, 3, 4, 5, 7, …
|
||
measured: … 15, 18, 16, 17, 0, 2, 4, 7, 1, 3, 5, …
|
||
```
|
||
|
||
That is exactly the residual HANDOFF Q3 documents and this export already
|
||
declares in `unresolved: ["paint_order_ties"]`. It is worth stating what it
|
||
costs: **904 px** in the glow band at (445,117)–(1195,313), all of them 4–6/255.
|
||
The port keeps the stable sort, per HANDOFF's own recommendation. Nothing to fix,
|
||
and nothing to tune — a "fix" here would be fitting the port to one screen's
|
||
capture.
|
||
|
||
Two of the reordered indices (`0x80a0`) are `kind & 0x4` template instances that
|
||
both renderers skip, so the only real reorder outside the glow group is
|
||
`ptlogo2` against `ptlogo_tm`, which do not overlap.
|
||
|
||
### `title_jp`: nearest-neighbour sampling phase — the CLI is the one I would call wrong
|
||
|
||
`title_jp` is the **only** screen in the export with a drawn element at a scale
|
||
that is not a whole multiple of 100 %: `ptlogo_eff2` at 125 %. It is also the
|
||
only screen with a difference above 6/255. The two facts are the same fact.
|
||
|
||
At a non-integer ratio the two renderers pick different source texels:
|
||
|
||
* `ui_layout::blit` samples the source at the destination pixel's **top-left
|
||
corner** — `sxi = col * sw / dw`.
|
||
* A GPU samples at the destination pixel's **centre** — `floor((col+0.5)·sw/dw)`.
|
||
|
||
At 125 % those disagree on one column in five, which is why the differing pixels
|
||
are ~30 above 100/255 strung along thin diagonal edges rather than a shifted
|
||
region. At every whole multiple of 100 % they agree exactly, which is why the
|
||
other eleven screens are clean.
|
||
|
||
**Which is wrong:** the CLI, I think. Corner-sampled nearest is a half-
|
||
destination-pixel bias toward the top-left that no rasteriser produces, and the
|
||
Xenon GPU that drew this screen sampled at pixel centres. But I have no
|
||
framebuffer capture of `title_jp` and the disagreement is sub-pixel on one glow,
|
||
so this is a reading, not a measurement — recorded in `docs/port/BLOCKED.md` rather
|
||
than acted on. **The port is not changing to match**, because matching the CLI
|
||
here would mean deliberately reproducing a half-pixel offset in order to make a
|
||
number smaller.
|
||
|
||
### `extras`: two pixels
|
||
|
||
Two pixels at 4/255. Rounding.
|
||
|
||
### What the diff cannot tell us
|
||
|
||
The pivot question in `docs/port/BLOCKED.md` predicted that a P1 diff could not
|
||
distinguish "anchor scale to the declared pivot" from "anchor to half the
|
||
texture", because both renderers use the declared pivot. That prediction held:
|
||
the port and the CLI agree on every scaled element, and that agreement is **not
|
||
evidence** about which anchor the game uses. It stays open.
|
||
|
||
### ~~`pteff05.t32` and `pteff04.t32` have no sprite, and that is correct~~
|
||
|
||
**RETRACTED 2026-08-29. This was wrong, and it was the most consequential thing
|
||
on this page.** See "The menu had no background" below.
|
||
|
||
---
|
||
|
||
## P2 — keyframe animation, 2026-08-28
|
||
|
||
### The time unit is authored, in one file, and says loudly that it is not on the disc
|
||
|
||
`authored/timing.json`. HANDOFF Q1 is answered — linear ramp, 2 units per
|
||
rendered frame, 1 unit = 1/60 s — but that conversion is **measured off the
|
||
running game**, not read from a file, which is exactly the case the
|
||
derived/authored split exists for. It is expressed as
|
||
`keyframe_units_per_second: 60` rather than seconds-per-unit so the value is
|
||
exact instead of a repeating decimal, and it carries the two independent lines
|
||
that support it. `t` stays raw everywhere in `export/`; seconds appear only
|
||
where this file is applied, which is one line of `boot.gd`.
|
||
|
||
`exit_ramp_seconds` is deliberately **null**. See below.
|
||
|
||
### The timeline stops at the last *timed* keyframe, and never plays the exit
|
||
|
||
The last keyframe of every group carries **no `t`** — the disc has no time slot
|
||
there. Across this export that final frame is an *exit* pose: for 116 of 134
|
||
elements it differs from the last timed keyframe **in alpha only** (a fade-out),
|
||
for 12 it is the loading splash's scale-and-slide exit, and for 6 it is
|
||
identical (no exit animation at all).
|
||
|
||
So the group is `pre-roll → ramp in → hold → [exit]`, and the port plays it up to
|
||
the hold and stops. Playing into the exit would mean **inventing how long the
|
||
ramp takes**, because the disc does not say. That duration is the screen
|
||
transition — HANDOFF Q7 measured it at ~0.4 s — and it belongs to P3, with its
|
||
own evidence. This is why `exit_ramp_seconds` is null rather than 0.4: P2 has no
|
||
business holding it.
|
||
|
||
### The interpolation is checked by where it lands, not by inspection
|
||
|
||
For **8 of the 12** screens the settled timeline is **byte-identical** to the
|
||
`--pose=rest` render. That is the useful assertion: the port walks the keyframes
|
||
with an authored time unit and arrives, to the pixel, at the pose the pinned
|
||
decoders independently identify as the resting one. `tools/screen-strip` reports
|
||
this per screen, so a change to the interpolation that drifts by one unit shows
|
||
up as a diff rather than as nothing.
|
||
|
||
The four that differ do so for two distinct reasons, below.
|
||
|
||
## `rest` misidentifies six elements, and the running game says so
|
||
|
||
On `main_menu`, the settled timeline and `rest` differ in exactly one region:
|
||
**400×470 at (440,108)** — the bounding box of `ptframe1` and `ptframe2`, and
|
||
nothing else on the screen.
|
||
|
||
`rest` puts both at their **first** keyframe: off-position and fully
|
||
transparent. The keyframes say they slide (620,108)→(440,108) and (403,267)→
|
||
(583,267) while fading 0x00→0xff, and then hold that pose for their last three
|
||
keyframes including the untimed one.
|
||
|
||
`docs/re/captures/main-menu-oracle.png`, a capture of the running game,
|
||
**shows them**: the bright circuit-frame bracket around the menu, with a ring at
|
||
the bottom right. Cropping the same 250×180 region from the capture and from
|
||
both renders puts the ring and its elbow trace in the port's timeline render
|
||
**pixel-aligned with the game's**, and absent from the `rest` render. That is
|
||
geometry, not luminance, so it does not depend on the capture's gamma or on the
|
||
fact that it was taken with `NEW GAME` focused.
|
||
|
||
### Why the decoders get it wrong, precisely
|
||
|
||
`ui_layout::rest_plateau` excludes a run of identical keyframes that **ends the
|
||
group**, because that run is normally the exit — the comment cites the pause
|
||
menu, where taking the trailing run erased the word PAUSE. That exclusion is
|
||
right in general and wrong for an element with **no exit animation**, where the
|
||
trailing run *is* the hold. The rule then falls back to an earlier run, which
|
||
for a slide-in is the invisible pre-roll.
|
||
|
||
The condition that identifies the affected elements exactly, with no false
|
||
positives in this export, is:
|
||
|
||
> the final untimed keyframe has the **same pose** as the last timed keyframe
|
||
|
||
Six elements match it and `rest` misses all six: `ptframe1`/`ptframe2` on
|
||
`main_menu` and `main_menu_jp`, and `pteff02` on `title` and `title_jp`. This is
|
||
a **finding for the RE agent** about `sylpheed-formats`, not something this port
|
||
fixes: the decoders are pinned and must not be reimplemented here. The port
|
||
simply does not use `rest` — it derives the arrived pose from the keyframes,
|
||
which needs no heuristic — and `verify-screen` still asks for `--pose=rest` so
|
||
that renderer-vs-renderer diffing compares like with like.
|
||
|
||
Note what this says about P1: the port and the reference renderer **agreed** on
|
||
`main_menu` to 3/255, and both were missing two elements the game draws. Two
|
||
renderers reading the same field through the same decoder agreeing is not
|
||
evidence that the field is right. `docs/port/BLOCKED.md` had already said that about
|
||
the pivot; here it bit for real.
|
||
|
||
## The title is not settled, and P2 does not claim it
|
||
|
||
`title` and `title_jp` differ between the two modes by much more (max 142 and
|
||
247), and there the disagreement is **not** the six-element bug alone. `rest`
|
||
picks a mid-timeline hold for several glows (`pteff01`, `ptlogoall_eff`,
|
||
`ptlogoall_eff2`, `ptlogo_back2eff5`) where the timeline runs on to a much
|
||
brighter pose.
|
||
|
||
I could not settle which is right, and did not try to make the numbers agree:
|
||
|
||
* No element's alpha ever reverses direction anywhere in this export, so the
|
||
title's 4.48 s timeline is a slow one-way ramp, not a pulse — which removes the
|
||
obvious reason to expect a loop, but does not prove there is none.
|
||
* The only live title capture composites the **`PRESS Ⓐ` plate (build 2) over
|
||
the title (build 4)**, so it cannot be diffed against build 4 alone. Mean
|
||
luminance is oracle 64.1, `rest` 62.8, timeline 80.0 — which looks like it
|
||
favours `rest`, except that the plate *adds* brightness and `rest` is carrying
|
||
a 25 % black dim quad (`pteff02`) that is itself one of the six misidentified
|
||
elements. The comparison is confounded in both directions and settles nothing.
|
||
* **Both modes are visibly wrong anyway.** Side by side with the capture, the
|
||
port draws a washed-out cyan glow slab across the logo that the running game
|
||
does not have — in `rest` mode too. That is a third problem, independent of
|
||
this one, and it is P3's.
|
||
|
||
So: the timeline is the default because it is derived from the disc's own
|
||
keyframes with one measured constant and no heuristic, and because it is proven
|
||
right on the screen this milestone gates. On the title it is **unverified**, and
|
||
P3 should not assume P2 settled it.
|
||
|
||
---
|
||
|
||
## P2, corrected — the pin moved, and the settle rule was wrong, 2026-08-28
|
||
|
||
### Answering the RE agent's question: which six, and on what screens
|
||
|
||
They asked, having found only two elements on the English main menu satisfying
|
||
the condition this port proposed. The six span the whole 12-screen export:
|
||
|
||
| element | screens | trailing run |
|
||
|---|---|---|
|
||
| `ptframe1`, `ptframe2` | `main_menu`, `main_menu_jp` | alpha `0xff` — **visible** |
|
||
| `pteff02` | `title`, `title_jp` | alpha `0x00` — **transparent** |
|
||
|
||
So four of the six are the pair they already found, once per language build, and
|
||
their alpha rule accepts exactly those. The other two are `pteff02`, whose
|
||
trailing run is transparent, so their rule **excludes** it and leaves `rest` at
|
||
`0x40`.
|
||
|
||
**That exclusion is right, and their own measurement proves it.** `pteff02` is
|
||
the 25 % dim quad; they measured the title render going from **+13.14 to +0.55**
|
||
against the plate-free capture once the dim is drawn. `rest` must therefore stay
|
||
at `0x40` and must *not* move to the transparent trailing run — which is what
|
||
their rule does. Two investigations converging from opposite directions.
|
||
|
||
The condition this port proposed was **too loose**; the alpha discriminator is
|
||
the correct rule and the port has no amendment to offer.
|
||
|
||
### The pin moved 8b6dbcf → 5414db3
|
||
|
||
Its own commit, and what I wanted from it is the fixed `ui_layout::rest()`.
|
||
Pinned at `5414db3` rather than `4bc9706` where the fix was written, because
|
||
`5414db3` is where it carries its disc-wide check — 30 of 13 991 elements move,
|
||
4 become visible, **0 become invisible**.
|
||
|
||
The re-export is the evidence the change was contained: **two files changed, and
|
||
within them exactly four `rest` blocks** — `ptframe1`/`ptframe2` on both main
|
||
menus moving from `(620,108)/(403,267)` at `t=16` and alpha `0x00` to
|
||
`(440,108)/(583,267)` at `t=62` and alpha `0xff`. Every diff line pairs; the
|
||
other ten screens are byte-identical, `pteff02` did not move, and no sprite
|
||
changed.
|
||
|
||
### The settle rule was wrong, and their title finding is what showed it
|
||
|
||
P2 shipped "hold the last **timed** keyframe", on the reasoning that the exit is
|
||
the final untimed frame. **That is wrong**, and the title is the counter-example:
|
||
`pteff02` holds at `t=46` with the dim at alpha `0x40` and then ramps to `0x00`
|
||
by `t=236`. The exit is not only the untimed frame — it can be a long run of
|
||
timed ones. Running to the end drops the dim and makes the whole screen ~13/255
|
||
too bright, which is exactly the luminance excess P2 recorded (oracle 64.1,
|
||
`rest` 62.8, timeline 80.0) and could not explain.
|
||
|
||
A group is `pre-roll → ramp in → hold → ramp out → post-roll`, and a screen that
|
||
has arrived sits on **the hold**. So the timeline now plays in and stops at
|
||
`rest`, which is the decoders' identification of that hold and carries its own
|
||
`t`. `settle_units()` is `rest.t`.
|
||
|
||
The check is that the disagreement vanishes: on **all twelve** screens the
|
||
settled timeline is now byte-identical to the `--pose=rest` render, where before
|
||
this change four of them differed by up to 247/255. The timeline's endpoint
|
||
*should* be `rest` — the animation is what the timeline adds, not a different
|
||
destination — so this is the property to want, and it now holds without a
|
||
special case.
|
||
|
||
That also retires P2's open question about looping, from the other side: the RE
|
||
agent measured that groups hold rather than loop (`ptloop01`/`ptloop02` park
|
||
off-screen at x=1521 and x=−839; 18 s of settled title sits at sd ≤ 0.01).
|
||
|
||
## The reference renderer was stale for three diff runs
|
||
|
||
Worth recording as a process failure, because it defeated the project's whole
|
||
verification method for a while and it failed *silently*.
|
||
|
||
After bumping the pin I rebuilt the reference CLI, and `build-reference-cli`
|
||
reported success at rev `5414db3`. `verify-screen` then showed `main_menu`
|
||
jumping from 3/255 to **72/255**. The natural reading — the port had regressed —
|
||
was wrong. The port was right and **the reference was a revision behind**: the
|
||
shared `CARGO_TARGET_DIR` still held a `sylpheed-cli` built from `8b6dbcf`, and
|
||
cargo reported `Finished in 0.13s` and left it in place. Building into a clean
|
||
target directory produced a binary that resolves `ptframe1` to `(440,108) t=62`;
|
||
the shared one still said `(620,108) t=16`.
|
||
|
||
The old check — "does `screen list` run?" — cannot catch this, because a stale
|
||
binary runs perfectly.
|
||
|
||
Two changes:
|
||
|
||
* `build-reference-cli` builds into `$CARGO_TARGET_DIR/reference-cli/$rev`, a
|
||
tree **keyed by the pinned revision**, so a new pin has no artifacts to reuse.
|
||
A stable copy is placed alongside for consumers.
|
||
* It then checks the binary **against `export/`**: both come from the same pin,
|
||
so if the CLI resolves `ptframe1`'s rest differently from what the exporter
|
||
wrote, the two halves of the verification are not the same revision and it
|
||
fails loudly. It compares the two rather than asserting a literal, so it stays
|
||
true when the pin moves again.
|
||
|
||
`docker/bin/` is baked into the image, so this takes effect on the next image
|
||
build; until then the repo copy has to be invoked by path. The RE agent hit the
|
||
same class of trap this session from the other side (`./target/debug` stale
|
||
against a redirected `CARGO_TARGET_DIR`). It is worth naming the general shape:
|
||
**a build system reporting success is not evidence that the artifact you are
|
||
about to trust is the code you pinned.**
|
||
|
||
### What this did not change
|
||
|
||
`title` (6/255), `extras` (4/255) and `title_jp` (154/255) are unchanged, and
|
||
their diagnoses stand — a paint-order tie, two pixels, and nearest-neighbour
|
||
sampling phase at 125 % scale. The title's swoosh defect the RE agent localised
|
||
(drawn thick and white where the game draws it thin and pink) is untouched by
|
||
any of this and remains P3's.
|
||
|
||
---
|
||
|
||
## The menu had no background, and P1 called that correct, 2026-08-29
|
||
|
||
The pin moved `5414db3 → f817dd5` for `56cc7ac`, "a RATC child's name is stated,
|
||
not inferred". `ratc::parse` had named each child by scanning backwards for the
|
||
last printable run of bytes before its magic. For `pteff05.t32` the three
|
||
trailing payload bytes are `38 41 58` — `8AX` — which beat the real name, so the
|
||
child registered under a name no element declares and resolved to no sprite.
|
||
|
||
`pteff05.t32` is the **full-resolution background of all five menu screens**.
|
||
|
||
So every render this port has produced of a menu screen has been missing its
|
||
background, and P1 wrote that up as a property of the disc: *"the bundle declares
|
||
them and carries zero RATC children for either, so there is no texture on the
|
||
disc to export."* That sentence was false. The bundle carries the child; the
|
||
decoder was handing back the wrong name for it. Retracted above rather than
|
||
edited away.
|
||
|
||
### What the re-export shows
|
||
|
||
Six new sprites and nothing else: `pteff05.png` on `main_menu`, `extras` and
|
||
their Japanese twins, `pteff04.png` on both titles. Per screen the JSON gains a
|
||
`sprite` line and `layer_source` moves `"implied" → "sprite"` — the layer key is
|
||
now **read from the file** instead of taken from the decoders' table of keys
|
||
measured off the running game. That is the derived/authored ratchet turning the
|
||
right way, in the exporter rather than in `authored/`.
|
||
|
||
`pteff05.png` is **1280×720**; `ptbase.png`, which had been carrying the
|
||
background alone, is 640×360 drawn at 200 %. The screen was being shown its own
|
||
art at half resolution.
|
||
|
||
### Measured against the live capture, not against the other renderer
|
||
|
||
Whole-frame RMSE of the settled `main_menu` against
|
||
`captures/main-menu-oracle.png`:
|
||
|
||
| | RMSE |
|
||
|---|---|
|
||
| before this pin | 8.05 % |
|
||
| with the real background | **5.92 %** |
|
||
|
||
A 26 % reduction, and it is the right kind of evidence: the reference renderer
|
||
was missing the same element for the same reason, so a renderer-vs-renderer diff
|
||
could not have found this. It is the third time on this project that the
|
||
capture caught something both renderers agreed on — the bracket, the title dim
|
||
quad, and now the background.
|
||
|
||
`verify-screen` after the bump is unchanged in character: everything at 3–4/255
|
||
except `title` (6, the paint-order tie) and `title_jp` (155, the sampling phase).
|
||
Both renderers gained the background together.
|
||
|
||
### One thing the comparison says that I did not expect
|
||
|
||
Rendering with `--focus=ptbtn01`, which is how the capture was taken, makes the
|
||
RMSE **worse** — 5.92 % → 7.00 %. The port *replaces* an element's sprite with
|
||
its `*f` twin; `sylpheed-cli`'s own `--focus` is documented as drawing the
|
||
focused record **over** the base element. Those are different operations, and
|
||
the capture shows a ring marker beside `NEW GAME` that the port does not draw.
|
||
|
||
This is P5's, not P2's, and it is not being guessed at here. Raised in
|
||
`docs/port/BLOCKED.md`.
|
||
|
||
---
|
||
|
||
## P3 — splash → title, unattended, 2026-08-29
|
||
|
||
### The splash is located by entry index, because no rule can find it
|
||
|
||
The RE agent looked for a content predicate and there is none: design size fails
|
||
(every extra composable bundle sampled is 1280×720, the same as every screen) and
|
||
element count fails (fragments run 2…15 elements in `GP_OPTIONS`/`GP_SAVE_LOAD`
|
||
while the splash halves are 3 and 7 — the ranges overlap).
|
||
|
||
So `screen_builds` is now `is_build` **plus an authored allow-list of entry
|
||
indices**, in `authored/screen_names.json` under `also_export`, each with a `why`
|
||
that says it is a locator and not a claim. This is safe in `GP_TITLE` and would
|
||
not be in general: there, widening adds exactly four bundles and all four are
|
||
real screens with zero fragments. That is why it is an allow-list rather than a
|
||
loosened predicate.
|
||
|
||
**There were two splash screens and the port had neither.** Entries 11/14 are the
|
||
developer logos (GAME ARTS / SETA / studio anima); entries **10/13 are the SQUARE
|
||
ENIX publisher wordmark, the first thing the boot shows**, and nothing in this
|
||
project had noticed them. Both pairs are region twins — ™ on 10, ® on 13 — and
|
||
the port shows one of each, not both.
|
||
|
||
### `authored/screen_names.json` is now keyed by pak entry, not by ordinal
|
||
|
||
Widening the enumeration renumbers the ordinals, and a name that moves when the
|
||
enumeration rule changes is not a name. The file had always called the entry
|
||
"the stronger locator"; it is now the only stable one. In `GP_TITLE` the two
|
||
coincide across all 16 entries, which is also the numbering `sylpheed-cli screen
|
||
--build N --all` takes — so `verify-screen` now passes `--all`, and without it
|
||
`--build 10` would have landed on entry 12.
|
||
|
||
The two previously-unnamed plates therefore renamed `build_10`/`build_11` →
|
||
`build_12`/`build_15`. Their names were always locators; now they locate the
|
||
right thing.
|
||
|
||
### The exit is the group playing itself out, not a black rect over a freeze
|
||
|
||
HANDOFF's answer to ask 2 was (a), and it came with a test that discriminates
|
||
rather than a plausibility argument. Under "a black quad over a frozen screen"
|
||
every region is scaled by the same 1−α, so the button-region / background-region
|
||
brightness **ratio** stays constant through the fade. Measured, it falls
|
||
6.495 → 5.574 → 3.105 → 2.125 → 1.935 — a 3.4× monotonic drop. The screen plays
|
||
out: `pteff00.prm` ramps to opaque black while the labels, `ptmsg`, `pteff10`
|
||
and `pteff12` ramp to transparent, and `ptframe1`/`ptframe2` hold.
|
||
|
||
Implemented by giving the final untimed keyframe a **synthetic time**,
|
||
`exit_ramp_units` after the last timed one, and then interpolating it like any
|
||
other. One code path: the difference between arriving and leaving is only how far
|
||
`t` is allowed to run, not a second kind of animation.
|
||
|
||
`exit_ramp_units = 24` (~0.4 s) is authored, and `authored/timing.json` carries
|
||
the RE agent's own reach caveat rather than smoothing it: the filmstrip is
|
||
downsampled and the button region contains some background, so this pins the
|
||
**direction**, not 0.4 s to ±0.05 s, and it is one transition pair.
|
||
|
||
### Nothing waits on a timer the disc does not carry
|
||
|
||
`dwell` in `authored/flow.json` is deliberately empty. Each screen's dwell is its
|
||
own keyframe group — the publisher wordmark reaches its hold at t=235 (3.92 s),
|
||
the developer logos at t=190 (3.17 s), both read from the disc. Adding a hold on
|
||
top would be inventing a number nobody measured. The pacing you see is the
|
||
disc's own, and the file says where a measured number would go.
|
||
|
||
### The last screen holds
|
||
|
||
A screen plays itself out because something is taking its place. Nothing takes
|
||
the title's place yet, so the sequencer holds there. A boot that ends by fading
|
||
to black is a boot that looks like it crashed. P4 puts the intro video in front
|
||
of the title and P5 gives the title somewhere to go.
|
||
|
||
### `flow.json` reproduces an observation and says so
|
||
|
||
Q6 closed with a negative: the order is in none of the four places it could have
|
||
been, and a transition is a call with a name argument chosen by code. So this
|
||
file is authored and its header says plainly that it reproduces what was watched,
|
||
not what any file states. The intro video's place in the real boot is **named as
|
||
a gap** rather than the order being quietly rewritten to hide it.
|
||
|
||
## P3 gate
|
||
|
||
`godot --path port -- --boot --film=/tmp/boot` runs unattended:
|
||
|
||
```
|
||
publisher_logo → developer_logos at 4.65 s → title at 8.57 s
|
||
boot sequence complete after 13.05 s, holding on title
|
||
```
|
||
|
||
The filmstrip shows each screen fading in, holding, and fading through black into
|
||
the next, and the title staying up. `verify-screen` covers all **16** screens
|
||
now; the four new splash bundles come in at max 1–2/255 against the reference
|
||
renderer. The three known differences are unchanged: `title` 6 (paint-order tie),
|
||
`main_menu` 4, `title_jp` 155 (sampling phase at 125 % scale).
|
||
|
||
## Answers taken from the RE agent without re-deriving them
|
||
|
||
* **Focus stays "replace".** Over-vs-instead is unobservable: the focused sprite
|
||
covers the base at 100 % of base-visible pixels, and the two compositions
|
||
differ by RMSE 1.1 inside the button rect — under the gamma floor. The port's
|
||
guess was right for the wrong reason, and the actual gap is that
|
||
`ptbtn0Nf.rat` declares **two** sprites — `ptbtneff01.t32`, a glowing ring, and
|
||
then the bright label — where `ptbtn0N.rat` declares one. The ring is P5's, and
|
||
its placement inside the record is **not decoded**, so it will be authored from
|
||
the capture and marked as such.
|
||
* **RMSE against captures has a floor, so stop chasing it.** The capture is
|
||
`≈ 255·(render/255)^γ` with γ ≈ 1.49 on the menu and `EXTRAS`, 1.34 on the
|
||
title, and it is a ramp *the game installed* (`VdGetCurrentDisplayGamma` at
|
||
video init), not a capture-path artefact to subtract. Its reach is narrow —
|
||
the flat patches it was fitted on are almost all dark — so the port will not
|
||
extrapolate it across the range, and will not apply it to rendered output on
|
||
this evidence. It is a comparison constant, not a rendering one.
|
||
* **Rotation is escalated to a human and the port has not acted.** The RE half is
|
||
answered — rotate about the **declared pivot**, measured against the GPU
|
||
capture — and it has zero effect on the five screens at rest. The port will
|
||
carry `rotation_deg` in a future FORMAT v3 because carrying a decoded field the
|
||
renderer ignores beats dropping it, but it will not draw it until the
|
||
divergence question is settled.
|
||
|
||
---
|
||
|
||
## P4 — the intro video, 2026-08-29
|
||
|
||
### Theora at 720p is fine here, and no runtime dependency is requested
|
||
|
||
MISSION §6 anticipated that Theora might be too poor at 720p and permitted the
|
||
FFmpeg-GDExtension fallback to be **proposed**. It is not needed, and this was
|
||
measured rather than judged by eye alone. SSIM against the decoded source over a
|
||
10 s sample: **0.9863 at `-q:v 6`, 0.9896 at 8, 0.9924 at 10**. At 200 % zoom on
|
||
the reel's hardest case — fine serif text and soft gradients over near-black,
|
||
where Theora breaks first — q8 is indistinguishable from the source.
|
||
|
||
`-q:v 8`, and **no GDExtension is being proposed or adopted**.
|
||
|
||
`-ac 2` because the source is **6-channel** WMA Pro and Godot's Theora path is
|
||
not a surround one. That downmix is a decision, so it lives in the recorded
|
||
command where a modder can see and change it rather than in prose.
|
||
|
||
### The exact command is in the manifest, per MISSION §6
|
||
|
||
`export/manifest.json` gains a `videos` array, each entry carrying the verbatim
|
||
`ffmpeg` line that produced it. A modder who dislikes the quality re-runs one
|
||
line instead of reverse-engineering what was done to their video — which is the
|
||
whole reason this project converts the disc rather than reading it at runtime.
|
||
|
||
### A cache, and why that is not a hand-edit
|
||
|
||
`export/` is regenerated wholesale, but re-encoding 232 s of video on every run
|
||
costs ~4 minutes to produce a byte-identical file, and an exporter nobody re-runs
|
||
is worse than a cache. So each movie gets a `.cmd` sidecar recording the command
|
||
and the source size, and the encode is skipped only when both match exactly. Any
|
||
change to either re-encodes. This is derived state validating derived state, not
|
||
a hand-edit.
|
||
|
||
### The player renders into the design viewport, not beside it
|
||
|
||
First attempt parented the `VideoStreamPlayer` to the Boot node. It played, and
|
||
every captured frame was **black**: the capture reads the SubViewport, and the
|
||
player was rendering to the window. Worth stating as more than a capture bug —
|
||
everything this port draws composes in the export's own 1280×720 design space,
|
||
and a movie outside that space is outside the coordinate system every screen is
|
||
expressed in.
|
||
|
||
### Ⓐ skips, because Q9 measured it
|
||
|
||
The only input the port handles so far. HANDOFF Q9: one Ⓐ press skips a movie,
|
||
measured — the title was reached at 57 s against a 193 s baseline. Menu
|
||
navigation is still P5.
|
||
|
||
## P4 gate
|
||
|
||
`godot --path port -- --boot --film=…` runs
|
||
`publisher_logo → developer_logos → ADV.ogv → title`, unattended. The filmstrip
|
||
shows the SQUARE ENIX ident, then the reel's live-action-styled CG, then the
|
||
title. The movie's place in the boot is **measured, not decoded** — Q9 decodes
|
||
`ADVERTISE_MOVIE → ADV.wmv` from the movie manifest, but *where it sits in the
|
||
boot order* is what the RE agent watched, and `authored/flow.json` says so.
|
||
|
||
### What I cannot verify from here
|
||
|
||
**Audible playback.** This container has no audio device — Godot falls back to
|
||
the dummy driver. What is verified is that the Vorbis stream exists in the
|
||
transcode, is 2-channel, and decodes. Whether Godot emits it audibly is
|
||
unconfirmed and is stated as unconfirmed rather than assumed from the stream's
|
||
presence. It is a cheap check for anyone with a sound device and an impossible
|
||
one here.
|
||
|
||
---
|
||
|
||
## RETRACTION — `sylpheed-cli` is not the oracle, 2026-08-29
|
||
|
||
**This corrects a framing that runs through everything above, so it is a
|
||
retraction rather than an edit.** Every place this file called
|
||
`sylpheed-cli screen render` *"the reference renderer"* — and it does so
|
||
repeatedly, starting at P1 — overstated what it is.
|
||
|
||
The correction comes from the human, via the RE agent, in their words: Reborn
|
||
"was/is just a GUI explorer and extraction CLI for verifying the decoding of the
|
||
various files. It may very well be wrong." **The oracle is the Xenia Canary
|
||
capture and the game.**
|
||
|
||
So `tools/port/verify-screen` is a **consistency check between two decoders that
|
||
share their assumptions**, and a regression detector. It is not a correctness
|
||
check, and agreement in it is not evidence of correctness.
|
||
|
||
### The embarrassing part is that this file already knew
|
||
|
||
After the `ptframe1` case, P2's write-up says: *"Two renderers reading one field
|
||
through one decoder agreeing is not evidence that the field is right."* Then P1's
|
||
numbers kept being quoted as though 3/255 against `sylpheed-cli` meant the port
|
||
was right. Having the principle written down did not stop me leaning on the
|
||
agreement — which is worth recording, because that is the failure mode, not
|
||
ignorance of the principle.
|
||
|
||
**Three times** both renderers agreed and both were wrong, all three caught by a
|
||
capture and catchable by nothing else:
|
||
|
||
| | what both got wrong | how it surfaced |
|
||
|---|---|---|
|
||
| `pteff05` | the menu screens had **no background** | the RE agent decoded the RATC child name |
|
||
| scale 0 | drawn at full size instead of collapsed | RE agent's control run |
|
||
| `rest()` | `ptframe1`/`ptframe2` invisible; the menu bracket missing | `main-menu-oracle.png` |
|
||
|
||
### What changes
|
||
|
||
* `tools/port/verify-screen` says all of this in its own header, calls the CLI the
|
||
**comparison** renderer, and a `DIFFERS` row now means "we moved apart, find
|
||
out which of us moved" rather than "the port is wrong".
|
||
* The correctness question moves to the captures. The RE agent has committed
|
||
nine of them with an index at `docs/re/captures/ORACLE-CAPTURES.md`, covering
|
||
all five screens in scope — including a **main menu with `OPTIONS` focused**,
|
||
whose difference from the unfocused menu isolates exactly what focus changes.
|
||
* Three cautions travel with any capture comparison, and they are the RE agent's:
|
||
the captures are **not gamma-neutral** (γ ≈ 1.49 menu, 1.34 title — there is a
|
||
floor, do not chase it); **geometry is sound** (best alignment 0,0 at corr
|
||
0.9466, so a positional disagreement is real); and each is **one moment of a
|
||
still-animating screen**, so compare settled poses or regions known to be at
|
||
rest.
|
||
|
||
### What does not change
|
||
|
||
The port keeps running `verify-screen` over all 16 screens every iteration. A
|
||
consistency check is still worth having — it is total, it is cheap, and it is
|
||
what catches a divergence the RE agent introduces on their side. It is simply
|
||
not a grade, and this file will stop quoting it as one.
|
||
|
||
## P5 groundwork — the focus record, checked against a capture, 2026-08-29
|
||
|
||
P5 is the lowest unfinished milestone (P0–P4 are gated above). This iteration did
|
||
not implement navigation; it did the measurement P5 needs first, because the one
|
||
thing P5 is built on — how a focused button is drawn — had three claims attached
|
||
to it and none of them had been checked from this side.
|
||
|
||
### The exporter already emits the focus record's second element
|
||
|
||
HANDOFF ask 3 answers with a 🔴: *"what you are actually missing is the focus
|
||
record's SECOND element"* — `ptbtneff01.t32`, a 42×46 glowing ring, declared
|
||
before the bright label in `ptbtn0Nf.rat`.
|
||
|
||
**That gap is in the renderer, not the exporter.** `export/screens/title/main_menu.json`
|
||
already carries both, in declaration order, under `focus.elements`, for all five
|
||
buttons — `ptbtneff01` then `ptbtn0Nf`, each with its own pivot, rest pose and
|
||
keyframes. Nothing needs to change in `crates/sylpheed-export` for the ring. What
|
||
is missing is that `screen_view.gd` draws only one sprite per focused button.
|
||
Recording this so P5 does not re-open the exporter looking for it.
|
||
|
||
The ring's size checks out exactly: `ptbtneff01.png` is **42×46**, as stated.
|
||
|
||
### The `(7,7)` focus offset survives a refutation attempt, uniquely
|
||
|
||
Ask 3 states the focused sprite covers the base at 100.0 % of base-visible pixels
|
||
"once aligned properly (the true offset is **(7,7)**, and at the centre alignment
|
||
it reads a misleading 78–84 %)". P5 builds directly on this, so it was worth
|
||
attacking.
|
||
|
||
Re-ran the RE agent's own metric on the exported PNGs — over every pixel where the
|
||
base sprite is visible, the fraction where the focus sprite's alpha ≥ the base's —
|
||
scanning the whole offset space, not just the stated answer:
|
||
|
||
| alignment | ptbtn01 | ptbtn02 | ptbtn03 | ptbtn04 | ptbtn05 |
|
||
|---|---|---|---|---|---|
|
||
| **(7,7)** | **100.00 %** | **100.00 %** | **100.00 %** | **100.00 %** | **100.00 %** |
|
||
| geometric centre | 80.58 % | 79.20 % | 79.58 % | 79.45 % | 79.45 % |
|
||
| pivot-to-pivot | 80.58 % | 86.59 % | 87.40 % | 79.45 % | 84.58 % |
|
||
|
||
**The refutation fails, and more strongly than the original claim.** Over a
|
||
15×14 offset scan, `(7,7)` is a *unique isolated cell* at 100 % on every one of
|
||
the five buttons — every neighbouring offset, including (6,6) and (7,6), falls
|
||
below 90 %. The centre and pivot alignments reproduce the 78–84 % band the RE
|
||
agent reported as misleading. A claim that survives a search of its whole
|
||
parameter space is worth more than one checked at a single point, so this is
|
||
recorded as strengthened, not merely unrefuted.
|
||
|
||
### `(7,7)` is not a constant to apply — it is what the declared positions already say
|
||
|
||
An earlier version of this analysis had the port disagreeing with the capture by
|
||
6 px. **That was my arithmetic error and it is worth writing down**, because it is
|
||
the mistake this format invites: I computed each element's top-left as
|
||
`pos - pivot`, which gives base→focus deltas of (13,13) and a 29 % coverage — a
|
||
confident wrong number.
|
||
|
||
`pos` **is** the top-left. `screen_view.gd:121` is
|
||
`Rect2(pos - pivot*(s - 1), natural*s)`: the pivot is the anchor scale grows
|
||
about and it *cancels at 100 %*, which is exactly the "can be got wrong
|
||
invisibly" the comment there warns about. Getting it wrong invisibly is what
|
||
happened.
|
||
|
||
With `pos` as the top-left, base − focus is `(542,162) - (535,155)` = **(7,7)**
|
||
directly, on four of the five buttons. So P5 draws each focus element at its own
|
||
declared `pos` and needs no offset constant at all. Nothing to author.
|
||
|
||
### The one real find: `ptbtn04` is 1 px off the grid on the disc
|
||
|
||
The focus records sit on a clean 80 px pitch — 155, 235, 315, 395, 475. The
|
||
**base** records do not: 162, 242, 322, **401**, 482, i.e. spacings 80, 80, **79**,
|
||
**81**. So `ptbtn04`'s declared base→focus delta is **(7,6)**, while the art
|
||
itself aligns at (7,7) — the coverage scan puts `ptbtn04` at 100 % on (7,7) and
|
||
below 90 % on (7,6), the same as every other button.
|
||
|
||
This is 1 px of authoring jitter on the disc, not a decode error, and it has one
|
||
consequence worth stating: **do not derive the focus placement from the base by a
|
||
constant.** Draw the focus record at its own declared `pos`. A port that
|
||
"simplified" this to base + (7,7) would put `ptbtn04`'s focus art 1 px off, and
|
||
would look right on the other four.
|
||
|
||
### Verified against a capture, not against our other renderer
|
||
|
||
Diffing two oracle frames isolates what focus changes without any instrument in
|
||
the path: `live-main-menu.png` vs `live-main-menu-options-focused.png` differ in
|
||
one tight cluster of 6 338 px at **x 506..702, y 398..445**. `ptbtn04` is the
|
||
`OPTIONS` button, and the union of its focus record under the `pos`-as-top-left
|
||
reading — ring `ptbtneff01` at (500,396) 42×46 plus label `ptbtn04f` at (535,395)
|
||
172×56 — is **x 500..706, y 395..451**. Those agree on all four edges to within a
|
||
few px of near-transparent sprite border.
|
||
|
||
Under the `pos - pivot` reading the same record predicts x 433..604, y 367..422,
|
||
which matches nothing in the capture — and *no* button matches that cluster. The
|
||
capture, not our renderer, is what settles it.
|
||
|
||
### An instrument that failed its own control, and was therefore discarded
|
||
|
||
To locate the buttons independently I wrote a masked normalised-cross-correlation
|
||
template matcher and ran it as PROTOCOL requires — **through a control first**:
|
||
match each *base* sprite against the *plain* capture, where the declared position
|
||
is known and the answer must be a (0,0) delta.
|
||
|
||
It returned deltas of (13,5), (6,−19), (12,21), (−16,22), (6,8) at NCC
|
||
**0.096–0.206** — noise, with three of five pinned to the ±22 search boundary.
|
||
The control fails, so the instrument is dead rather than tuneable, and **none of
|
||
its output is used above.** The button art is dark, low-contrast and further
|
||
crushed by the capture's γ ≈ 1.49 ramp, which is the likely cause; a matcher for
|
||
this corpus would have to work on gradients rather than luminance. Filed so the
|
||
next iteration does not rebuild the same broken tool.
|
||
|
||
### What P5 still needs, and has not got
|
||
|
||
* **Initial focus is not stable across boots** (Q5: 2× `TUTORIAL`, 2× `NEW GAME`).
|
||
That is a value to author, with a `why` naming Q5 — it is not written yet.
|
||
* **The ring's own animation is unread.** `ptbtneff01`'s two keyframes go
|
||
`rotation_deg` 0 → **360** at t=120 with no second timed keyframe, i.e. a full
|
||
turn. Whether it spins continuously while focused, or turns once and holds, is
|
||
the group-loop question again — answered "groups hold" for build-in animations,
|
||
but a 360° hold and a 0° hold are the same pose, so *this* group cannot be told
|
||
apart by its rest pose. Not guessed; raised below.
|
||
|
||
---
|
||
|
||
## P5 — navigation, 2026-08-29
|
||
|
||
The gate is *"a human clicks through it"*, and the artifact is a walk that
|
||
proves the wiring rather than the intent: `up` (which wraps 01→05), five `down`,
|
||
Ⓐ into `EXTRAS`, `down`, Ⓑ back — landing on the main menu with focus restored
|
||
to `EXTRAS`.
|
||
|
||
```sh
|
||
xvfb-run -a godot --path port -- --menu \
|
||
--script=up,down,down,down,down,down,accept,down,cancel --shots=/tmp/p5
|
||
```
|
||
|
||
Ten PNGs, one per step, each taken after the screen it produced had settled.
|
||
Contact sheet handed over as `share` id `1788002507-ef4468a0a33a`.
|
||
|
||
### The scripted walk goes through the input system, not around it
|
||
|
||
`--script` posts `InputEventAction` through `Input.parse_input_event` and lets
|
||
it arrive at `_unhandled_input` exactly as a d-pad's press would. Calling
|
||
`MenuFlow.move()`/`accept()`/`cancel()` directly would have been shorter and
|
||
would have proved nothing: the thing most likely to be broken is the wiring
|
||
between a press and the cursor, and a direct call is precisely the part that
|
||
skips it. The same reasoning says the settle wait must be real — a shot taken
|
||
before the screen stops moving photographs a fade and calls it a menu.
|
||
|
||
### What is authored here, and what is derived
|
||
|
||
Split deliberately, because P5 is where the two are easiest to blur:
|
||
|
||
| | where | why |
|
||
|---|---|---|
|
||
| the ORDER of the items | **derived** — each screen file's `buttons`, filled by the exporter from the button-role elements sorted by resting Y | it is on the disc |
|
||
| where an item goes | **authored** — `authored/flow.json` | HANDOFF Q4 *measured* the destinations; they are not in the file |
|
||
| which item opens focused | **authored** | Q5 measured that it is **not stable boot to boot** |
|
||
| what Ⓑ does | **authored** | Q5, measured — except on the main menu, see below |
|
||
| ⬅➡ do nothing | **authored**, written as an explicit no-op | so that *"the game ignores it"* and *"we never wired it"* are different lines of code |
|
||
|
||
Four of the five main-menu destinations are `goto: null` with a `blocked` note.
|
||
That is **not** an unknown: `DIFFICULTY`, the save-slot list, the lesson list and
|
||
the settings menu were all measured, and they live in archives this export does
|
||
not carry. `blocked` and `none` are kept apart so a later reader does not
|
||
"discover" a gap that was a milestone boundary.
|
||
|
||
`EXTRAS` is the only main-menu destination inside `GP_TITLE`, and therefore the
|
||
only Ⓐ-into-a-submenu this gate can actually walk.
|
||
|
||
### The one navigation rule with nothing behind it
|
||
|
||
Ⓑ on the **main menu** → title. HANDOFF Q5 states it, and `flow.json` marks it
|
||
*authored — likely but UNPROVEN*, because the title also self-returns after
|
||
~8–10 s idle and a single unrecorded observation cannot separate the two. The
|
||
port implements it anyway — a menu with no way out is worse than a menu with a
|
||
plausible one — and says in the file that it did. Asked of the Decoder this
|
||
iteration; see `BLOCKED.md`.
|
||
|
||
Independent corroboration that the main menu is different from its submenu:
|
||
the main menu's footer advertises only `Ⓐ : OK`, while `EXTRAS`' footer
|
||
advertises `Ⓑ : Back`. That is on the disc, in `ptmsg.png` vs `ptmsg2.png`, and
|
||
it is visible in both the port's render and the captures.
|
||
|
||
### A press during a fade is dropped
|
||
|
||
**Authored, and not measured.** Nobody has watched what the game does with a
|
||
button pressed mid-transition. Dropping invents less than queueing does: it
|
||
cannot manufacture a press the game might have discarded. `flow.json` says so
|
||
under `navigation.input_during_transition`.
|
||
|
||
---
|
||
|
||
## `--headless` cannot draw, and the port hung instead of saying so, 2026-08-29
|
||
|
||
`docs/port/PORT-MISSION.md` and the loop prompt both name `godot-headless` as
|
||
how this project runs unattended. It does not work, and the way it failed was
|
||
the worst available shape.
|
||
|
||
**Measured, not assumed.** Under `--headless` Godot's dummy renderer never emits
|
||
`RenderingServer.frame_post_draw`. Every capture path in `boot.gd` awaits it —
|
||
`--capture` since P1, `--film` since P3, `--shots` as of this milestone — so all
|
||
three blocked forever. Isolated by the difference between two runs:
|
||
|
||
```
|
||
godot --headless --path port --quit # prints, exits 0
|
||
godot --headless --path port -- --screen=… --capture=… # no output at all, killed at 40 s
|
||
```
|
||
|
||
The second produces **zero bytes of output** before it is killed, because
|
||
Godot's stdout is block-buffered and never flushes. So the observable behaviour
|
||
of an unattended headless capture was: silence, forever. In a loop, a job that
|
||
waits reads as a job still working — this is the failure mode that costs a whole
|
||
iteration and leaves nothing behind to say what happened.
|
||
|
||
Two changes, and deliberately not one:
|
||
|
||
* `--capture`, `--film` and `--shots` **refuse at startup** under `--headless`,
|
||
naming the flag and printing the `xvfb-run` line that does work. Refusing
|
||
early rather than at the first frame means the run does not die halfway
|
||
through a filmstrip with some frames written.
|
||
* `--script` **no longer waits for a drawn frame when it is not going to
|
||
photograph one.** Navigation is checkable where nothing draws, and that is
|
||
worth keeping: `godot --headless --path port -- --menu --script=…` now walks
|
||
the menus and exits 0 in about four seconds, which is a cheap regression check
|
||
that needs no X server at all.
|
||
|
||
The Xvfb path is unchanged and is what produced the P5 artifact.
|
||
|
||
---
|
||
|
||
## Refutation — the focus ring IS drawn rotated, and it is not at 0° in either capture
|
||
|
||
Attempted against the Decoder's `7eeae30` (*"re(ui): the focus ring SPINS, the
|
||
game draws it, and the leaf owns the f record"*), point 2: that in the
|
||
OPTIONS-focused capture the ring's bright head sits in a different angular
|
||
position from the sprite's own, caught mid-spin. **It survives**, and the
|
||
evidence is stronger than what was claimed.
|
||
|
||
Chosen for refutation because it is exactly what PROTOCOL says to aim at: a
|
||
claim the port is about to build on, resting on an estimator (a brightest-region
|
||
centroid) whose own control the Decoder reported as ±19.8°.
|
||
|
||
### The test, and why it needs no absolute registration
|
||
|
||
`live-main-menu.png` has `ptbtn01` focused; `live-main-menu-options-focused.png`
|
||
has `ptbtn04` focused. Both draw **the same sprite**, `ptbtneff01.png` — the
|
||
export confirms the two focus records name the same file. So the two captures
|
||
contain two instances of one 42×46 image, 240 px apart in design space, and the
|
||
question *"is it drawn rotated"* becomes *"are these two crops the same image
|
||
at a different angle"* — which needs no crop offset and no reference to our own
|
||
renderer.
|
||
|
||
Method: sample each ring into a 360-bin **angular luminance profile** over the
|
||
annulus band (r = 9…15 px, bilinear, 0.5 px radial step) and circularly
|
||
cross-correlate. A rotation about the centre shifts that vector and changes
|
||
nothing else.
|
||
|
||
### The instrument was run through two controls before it was believed
|
||
|
||
| control | result |
|
||
|---|---|
|
||
| rotate a capture's own ring by a known 0/30/90/150/210/270/330° and recover it | **0° error on all seven**, peak corr 1.000 |
|
||
| the same estimator on a ring-free 60×64 patch of the *same* capture | peak corr **0.369** — it does not manufacture a match |
|
||
|
||
### The measurement
|
||
|
||
On one shared centre for all three images, so a centroid difference cannot
|
||
masquerade as a rotation:
|
||
|
||
| pair | best shift | peak corr | corr at 0° |
|
||
|---|---|---|---|
|
||
| capture A vs capture B | **134°** | 0.968 | −0.064 |
|
||
| sprite (unrotated) vs capture A | **76°** | 0.969 | −0.295 |
|
||
| sprite (unrotated) vs capture B | **210°** | 0.948 | −0.181 |
|
||
|
||
210 − 76 = 134: the three measurements are internally consistent, which nothing
|
||
in the method forced them to be. Sweeping the centre by ±2 px moves the A-vs-B
|
||
answer over 117…161° while the peak correlation stays 0.9+ across the middle of
|
||
that range, so the **magnitude is ~134° ± ~15°** and the precision claim stops
|
||
there.
|
||
|
||
Evidence sheet — sprite, capture A, capture B, each cropped at the declared
|
||
`42×46+500+156` / `+500+396` — handed over as `share` id
|
||
`1788002507-afe1ad843789`. The phase difference is obvious by eye; the numbers
|
||
are here so it is not only obvious by eye.
|
||
|
||
### The two things this settles for the port
|
||
|
||
1. **The game draws `rotation_deg` on an element the English boot path shows.**
|
||
This is a second, independent confirmation on a different screen and a
|
||
different element from the `ptloop` sweeps, and it moves HANDOFF **ask 4**
|
||
(*should the port draw rotation*) off "changes nothing at rest" — it changes
|
||
the main menu's focus marker, in every frame.
|
||
|
||
2. **0° is not a pose the running game shows.** `screen_view.gd` currently draws
|
||
the ring at its `rest` pose, which is `rotation_deg 0`, and both captures put
|
||
it at 76° and 210°. So the port's focus marker is **known** to be wrong, not
|
||
suspected — and the comment in `screen_view.gd` now says which two numbers it
|
||
is wrong against.
|
||
|
||
### Registration, as a by-product
|
||
|
||
The ring's annulus centroid lands at (32.94, 36.63) and (33.30, 38.90) in
|
||
windows whose design-space prediction under a **zero crop offset** is
|
||
(33.0, 37.0). Within ~0.4 px on the better-thresholded of the two. That
|
||
corroborates `ORACLE-CAPTURES.md`'s *"1279×675, top-left aligned"* directly, on
|
||
a feature nobody chose for the purpose.
|
||
|
||
⚠️ Do not read the earlier P5-groundwork note *"button text bands land at design
|
||
y + 23"* as a crop offset — it is an offset **within** the button sprite, and
|
||
the two were nearly confused here.
|
||
|
||
### What the port did NOT do about it
|
||
|
||
It did not start spinning the ring. The period is a **guess with two unknowns**
|
||
and both belong to the Decoder:
|
||
|
||
* the keyframes are `t=120, rot 0` then an **untimed** `rot 360`. Under HANDOFF
|
||
Q1's replicated reading (*"`+36` is the time the NEXT pose is reached"*) that
|
||
is one revolution in 120 units = **2.0 s** — but this port's `pose_at`
|
||
implements the *other* reading, and switching it is a change to every screen's
|
||
animation timing, not a P5 change;
|
||
* *"groups hold"* (settled 2026-08-28) predicts the ring stops at 360° = 0°.
|
||
Both captures show it elsewhere. That is either a spin that loops, or two
|
||
captures both taken inside the first two seconds of focus. **The port cannot
|
||
tell those apart**, and a wrong answer here is a visible continuous rotation
|
||
on whichever button the player is sitting on.
|
||
|
||
Filed in `BLOCKED.md` and asked over the message channel. What settles it is two
|
||
frames of one focused button a known time apart.
|
||
|
||
---
|
||
|
||
## P5 end to end — and the title does not say `PRESS Ⓐ`, 2026-08-29
|
||
|
||
The gate walk above starts on a screen. This is the whole thing, unattended, in
|
||
one run — the sequence PORT-MISSION names as the objective:
|
||
|
||
```sh
|
||
xvfb-run -a godot --path port -- --boot --play \
|
||
--script=accept,down,down,down,down,accept,cancel,cancel --shots=/tmp/e2e
|
||
```
|
||
|
||
```
|
||
screen publisher_logo … settles at t=235 (3.917 s)
|
||
-> developer_logos at 4.70 s
|
||
-> video ADV at 8.60 s
|
||
video ended at 151.91 s
|
||
-> title at 151.91 s
|
||
boot sequence complete after 156.30 s, holding on title
|
||
menu on title
|
||
script[1] accept (A) -> main_menu
|
||
…
|
||
script[6] accept (EXTRAS) -> extras
|
||
script[7] cancel (B) -> main_menu focus restored to ptbtn05
|
||
script[8] cancel (B) -> title
|
||
script complete after 166.76 s on title
|
||
```
|
||
|
||
Publisher wordmark → developer logos → `ADV` → title → Ⓐ → main menu →
|
||
navigate → Ⓐ → `EXTRAS` → Ⓑ (focus restored) → Ⓑ → title. Contact sheet shared.
|
||
|
||
Two smaller things this run found, both fixed here:
|
||
|
||
* the boot step's `why` in `authored/flow.json` still said *"nothing takes the
|
||
title's place until P5 gives it somewhere to go"*. P5 has. Rewritten to say
|
||
what is actually true — `--boot` still **stops** on the title, and `--play`
|
||
**hands the held title over**; the stop is not a bug and the handover is not
|
||
another boot step.
|
||
* an empty focus printed as a line that trailed off, which reads like a value
|
||
went missing rather than like there is none. The title is a screen with no
|
||
`buttons` that still takes Ⓐ, so it prints
|
||
`(none -- this screen has no focusable item)`.
|
||
|
||
Also confirmed on the way: entering a submenu **directly** (`--menu=extras`) and
|
||
pressing Ⓑ enters the parent at its authored initial focus, not at a restored
|
||
one — there is no history to restore, and `MenuFlow.cancel` only claims a
|
||
restored focus when the stack agrees about where it is going.
|
||
|
||
### 🔴 The port's title does not tell the player to press Ⓐ
|
||
|
||
Found by running the objective end to end, which is the only thing that would
|
||
have found it: the boot's last step is `title` (build 4), and **build 4 has no
|
||
`PRESS Ⓐ BUTTON` plate**. P5 has now made Ⓐ the only way off that screen.
|
||
|
||
This is not a guess about the art. Both states are captured off the running
|
||
game and they differ by exactly that plate:
|
||
|
||
| | capture |
|
||
|---|---|
|
||
| title **without** the plate | `title-builds/live-title-build4-no-plate.png` |
|
||
| title **with** the plate | `title-builds/live-title-press-a.png` |
|
||
|
||
And the plate is already exported — `press_start`, `GP_TITLE` build 2 (HANDOFF
|
||
Q2), sitting in `export/screens/title/` unused by anything.
|
||
|
||
**This is P3's gate, not P5's, and P5 is what exposed it.** Recording rather
|
||
than fixing, for two reasons:
|
||
|
||
1. Which state an idle post-boot title shows — build 4 alone, build 4 with the
|
||
plate over it, or build 4 *then* the plate after a delay — is **behavioural**,
|
||
and the port has no oracle for a sequence. The game demonstrably has both
|
||
states; nothing here says which one follows the intro movie. That is the
|
||
Decoder's.
|
||
2. Showing it would mean **drawing two builds at once**, which this port has
|
||
never done — every mode loads exactly one screen. That is a real change to
|
||
`ScreenView`, not a line in `flow.json`, and it should not be smuggled in
|
||
under a navigation milestone on the strength of "it looks more right".
|
||
|
||
Filed in `BLOCKED.md`. Not blocking: P5's gate is Ⓐ into a submenu and Ⓑ back,
|
||
and both work.
|
||
|
||
## P6 — menu audio, 2026-08-29
|
||
|
||
The disc's menu sound reaches Godot as Ogg Vorbis: three cues and one music bed.
|
||
Nothing in `port/` has heard of XMA, `sound.pak` or `Static.slb`, and nothing in
|
||
it reassembles anything — `sylpheed_formats::media` does that and the exporter
|
||
converts what it hands back.
|
||
|
||
### The cue offsets moved OUT of the exporter, into `authored/`
|
||
|
||
The previous iteration left `crates/sylpheed-export/src/audio.rs` holding the
|
||
three `Static.slb` offsets as a Rust `const CUES`. That is wrong under MISSION
|
||
§3 and the fix is the first thing this iteration did.
|
||
|
||
Those offsets are **measured**, not decoded. `Static.slb` has no `RIFF`, no seek
|
||
chunk and no container: it is a packed run of whole 2048-byte XMA1 packets, and a
|
||
wave is defined *only* by `(offset, packet_count)`. Both numbers came from the
|
||
running game — Canary with `--xma_param_probe=true` prints a stream's packet
|
||
count and first 32 bytes when it is played, and searching those bytes in the bank
|
||
gives the offset (HANDOFF Q8).
|
||
|
||
A measured value compiled into the exporter is **a measurement wearing the
|
||
costume of a decoded field**. It reads as though the exporter derived it from the
|
||
disc; nobody deletes it when the real answer lands, because nobody can see that
|
||
there is anything to delete. So the table is `authored/audio.json` `se.*`, each
|
||
row carrying its own `why`, and the exporter holds no cue table at all.
|
||
|
||
`crate::video::MOVIES` stays a `const` in the exporter, and the contrast is the
|
||
point: Q9 **decoded** that mapping off the movie manifest on the disc. Same
|
||
shape, different provenance, different home.
|
||
|
||
### `name_match` is a field, and its absence means something
|
||
|
||
Q8 names `SE_UI_CURSOR` for the move cue by **name match against the authors' own
|
||
identifiers** — a plausible guess, not the measurement. For Ⓐ, Q8 is explicit
|
||
that the wave was *not* separated between `SE_UI_DECIDE` and
|
||
`SE_UI_SUB_WIN_OPN`, so no name is claimed at all.
|
||
|
||
`name_match` therefore travels beside every cue in `authored/audio.json` and in
|
||
`manifest.json`, and **an absent one means nobody claimed a name — never that
|
||
the binding is unknown.** The binding is the measured part. Collapsing the two
|
||
would turn "we did not separate two candidates" into "we do not know what this
|
||
sound is", which is a different and much weaker statement than the one the RE
|
||
agent actually made.
|
||
|
||
### The BGM is NOT a choice, and this port spent an iteration believing it was
|
||
|
||
The first draft of `authored/audio.json` picked `BGM_001`, wrote a careful `why`
|
||
explaining that the choice was arbitrary, and was **wrong**.
|
||
|
||
`docs/port/BLOCKED.md` carried the row that caused it: *"not on the disc … the
|
||
port is choosing a track, and that choice is authored."* The menu's music is
|
||
**`BGM_103`**, and it is in HANDOFF at **`9ca1eb5`** — the exact commit that page
|
||
says it was reconciled against. So this was not staleness. **The row was wrong
|
||
when it was written.**
|
||
|
||
What HANDOFF says is a negative *with a bound*, and the bound is the entire
|
||
content of it:
|
||
|
||
> the **tables** cannot say — `SOUNDS`, `FILES` and the bank headers name no
|
||
> screen. `GamePart_Title`'s phase handler `sub_821C5580` carries `li r5, 1103`
|
||
> into a sound call; cue 1103 is `BGM_103`; and `BGM_103.slb`'s two declared
|
||
> waves (3 876 864 / 3 930 112 B) are byte-for-byte the two streams the XMA probe
|
||
> saw decoding at the main menu. Static code, disc census and runtime all agree.
|
||
> **"The port does not have to choose a track."**
|
||
|
||
The failure is worth naming precisely, because "read HANDOFF more carefully" is
|
||
not the lesson — `BLOCKED.md`'s own staleness check passed, twice, and would pass
|
||
again. **A negative summarised without its reach reads as a bigger negative than
|
||
it is.** "The tables cannot say" became "it is not on the disc", and one word of
|
||
scope was the whole answer. A row in `BLOCKED.md` must quote the reach.
|
||
|
||
It also cost a second thing worth recording: the port would have shipped a menu
|
||
playing the wrong music with a confident `why` beside it saying the choice was
|
||
deliberate. That is exactly the shape of error this project's vocabulary exists
|
||
to prevent, produced *by* the machinery meant to prevent it.
|
||
|
||
### The bank name carries `.slb`, and that is how the mistake surfaced
|
||
|
||
`BGM_001` is not in `sound.pak`. `BGM_001.slb` is — `media::read_sound_bank`
|
||
looks up `name_hash(name)` against the TOC, and the TOC hashes the **file name**.
|
||
`Static.slb` worked from the first run only because the RE finding happens to
|
||
write it with its extension.
|
||
|
||
So the wrong track never played: the export failed loudly with *"BGM_001: not
|
||
present in sound.pak"*. That is luck, not design — had the draft picked a name
|
||
that happened to resolve, nothing would have complained. The `why` in
|
||
`authored/audio.json` now records both the correct name and why the short form
|
||
fails.
|
||
|
||
`export_bgm` now distinguishes the two cases it was conflating. A bank that is
|
||
**not in this disc's `sound.pak`** is a missing asset: the manifest takes a
|
||
warning and everything else still exports. Any other failure — a short read, a
|
||
malformed bank — still stops the run, because a partly-read bank produces a file
|
||
that plays.
|
||
|
||
### The two stems are summed. That part is not a choice
|
||
|
||
Q10 also measured that a bank's sub-waves are **two stems of one performance,
|
||
played together** — sample-synchronous, equal duration, on all 32 banks.
|
||
Concatenating them is explicitly wrong.
|
||
|
||
Emitting them as two files would be wrong for a second, independent reason:
|
||
MODDING rule 1 is *one logical asset, one file*, and handing a modder two stems
|
||
to line up by hand is precisely the reassembly the exporter exists to have
|
||
already done. `amix=normalize=0` sums at unity rather than halving, because
|
||
halving is a mix decision nobody made — and because a sum can clip, the peak is
|
||
**measured and reported** rather than silently corrected.
|
||
|
||
### The loop seam is ugly on purpose
|
||
|
||
🔴 **SUPERSEDED — see *"the menu BGM loop window"* below and `authored/audio.json`'s
|
||
`loop_start_why`.** The loop point exists: it is a **runtime** field set by
|
||
`XMASetLoopData`, and for `BGM_103` it is **[9.44 s, 71.31 s], cycling every
|
||
61.87 s**, which the exporter has trimmed to since. This section is kept because
|
||
it is what the port believed when it shipped the seam, and the reasoning below —
|
||
that inventing a loop point is worse than an ugly one — is why the wait was
|
||
cheap. Every claim in the paragraph that follows is dead.
|
||
|
||
No loop-point field has been identified [refuted]. `loop: "restart"` replays from sample 0,
|
||
so a listener hears the track's own fade-out and its trailing silence before the
|
||
music comes back.
|
||
|
||
Trimming to the fade would sound better and would be **worse**. It would invent a
|
||
loop point, and an invented one is indistinguishable from a decoded one a month
|
||
later — which is the failure mode this whole project is organised against. The
|
||
seam stays audible until a loop point is measured or a capture of the real menu
|
||
looping settles it.
|
||
|
||
### When a cue fires — two rules measured, one authored
|
||
|
||
* **Move** fires on a press that *actually moves the cursor*. `MenuFlow.move()`
|
||
already returned whether it did, which is why left/right stay silent by
|
||
construction rather than by a rule written twice (Q5: ⬅➡ do nothing, and Q8:
|
||
they play nothing).
|
||
* **Ⓐ and Ⓑ** fire when the press *does something*, and not when nothing is
|
||
bound. 🟡 **This half is authored and NOT measured** — nobody has watched the
|
||
game take a dead press. Silence invents less: a sound the game does not make is
|
||
a wrong fact you can hear, while a missing one is a gap. `blocked` counts as
|
||
doing something, because those destinations *were* measured off the running
|
||
game and are missing from this export, not from the game.
|
||
* The bed starts when the menu becomes live and **carries across submenus**.
|
||
`play_bed` is idempotent, because music that restarts every time you press Ⓑ is
|
||
the kind of wrong that reads as "the audio works".
|
||
|
||
### `--audio=` records the Master bus, because neither container has a sound card
|
||
|
||
`docs/port/AUDIO-VERIFICATION.md` §2. An `AudioEffectRecord` on the Master bus
|
||
captures the mixed output from inside a headless run with no device at all, and
|
||
that is the only thing that closes the loop the file opens: comparing an exported
|
||
Ogg against the disc proves the **asset** is right and says nothing about whether
|
||
the engine ever reached it.
|
||
|
||
The run prints `AudioServer.get_driver_name()` beside the file it wrote, because
|
||
"recorded under a dummy driver" is a weaker claim than "heard" and the write-up
|
||
has to be able to say which one it is making.
|
||
|
||
The WAV is saved in `_exit_tree` rather than beside each `quit()`. There are
|
||
eight of those, and the one that would get missed is an error path — exactly the
|
||
run whose audio somebody wants to look at.
|
||
|
||
### `check` now refuses silence and clipping
|
||
|
||
`sylpheed-export check` gained an `audio` pass, and two of its rules are content
|
||
checks rather than schema checks. That is deliberate. Silence is *the* audio
|
||
failure that looks like success — a file of the right duration, the right channel
|
||
count and the right size, full of zeroes — and it passes every structural check
|
||
there is. Clipping is the other one, and the BGM can produce it because it is a
|
||
sum at unity gain. The exporter measures both at export time; `check` refuses a
|
||
tree whose peak is ≤ −90 dBFS or ≥ 0 dBFS.
|
||
|
||
Neither is a judgement about whether the audio is the *right* audio. Nothing in
|
||
that binary can know that, and `BLOCKED.md` says which parts are still authored
|
||
guesses.
|
||
|
||
### A bug worth naming: the temp name ate the file extension
|
||
|
||
`run_ffmpeg` wrote to `.back.ogg.partial` — the temp-name-then-rename discipline
|
||
this project uses everywhere, and which `AUDIO-VERIFICATION.md` records as
|
||
already having caused a confident wrong number once.
|
||
|
||
ffmpeg picks its muxer **from the output filename**, so that is not a slightly
|
||
uglier temp name; it is a hard failure before a byte is written: *"Unable to
|
||
choose an output format for '.back.ogg.partial'"*. `video.rs` already had the
|
||
right shape (`.ADV.partial.ogv`) and this function was written from scratch
|
||
without looking at it. The extension goes last.
|
||
|
||
### Refutation — the three Q8 cue durations, checked end to end
|
||
|
||
**The claim:** HANDOFF Q8 publishes three cue lengths — move **0.533 s**
|
||
(8 192 B, 4 packets), back **0.344 s** (4 096 B, 2), confirm **1.016 s**
|
||
(12 288 B, 6). P6 is built directly on top of these, which by PROTOCOL's own rule
|
||
makes them the right thing to attack: refutation is cheapest where the other
|
||
agent is most confident, and most valuable where the port is about to build.
|
||
|
||
**Why they looked attackable.** The three do not share a rate. Seconds per
|
||
packet is 0.133, 0.172 and 0.169 — the move cue is 22 % off the other two. If a
|
||
packet were a fixed span of audio, at most one of these numbers could be right.
|
||
|
||
**Why that is not a refutation.** An XMA1 packet is 2 048 bytes of *bitstream*,
|
||
not a fixed span: it carries a variable number of 512-sample frames. At 48 kHz a
|
||
frame is 10.667 ms, and the three durations come to **50.0, 32.3 and 95.3
|
||
frames** — near-integers, which is what a variable-frames-per-packet encoding
|
||
looks like and is not what an arithmetic slip looks like.
|
||
|
||
**The measurement.** The exporter reads `(offset, packet_count)` through
|
||
`media::se_wave_riff`, decodes, and `ffprobe`s the finished Ogg:
|
||
|
||
| cue | Q8 claims | exported file measures |
|
||
|---|---|---|
|
||
| move | 0.533 s | **0.533 s** |
|
||
| back | 0.344 s | **0.344 s** |
|
||
| confirm | 1.016 s | **1.016 s** |
|
||
|
||
**Verdict: survives, exactly, at every published digit.** Recorded as a survival
|
||
rather than a pass, because that is what PROTOCOL asks for — a claim that has
|
||
survived an attempt is stronger than one nobody challenged, and the corpus should
|
||
say which it is.
|
||
|
||
⚠️ **Reach, stated so nobody over-reads it.** This is not independent of Q8: the
|
||
durations were derived from the same packet counts the exporter feeds in, so what
|
||
it confirms is that reading those `(offset, packets)` through
|
||
`sylpheed_formats::media` yields streams of exactly the claimed length — i.e.
|
||
that the *transcription* into `authored/audio.json` and the assembly path are
|
||
right. It does **not** confirm that these three waves are the sounds the game
|
||
plays on those three events; that is Q8's own measurement, taken by playing them,
|
||
and this port has no oracle to re-take it with.
|
||
|
||
The attempt did find something, just not here: see the BGM section above, where
|
||
the port's *own* `BLOCKED.md` row failed the same kind of check.
|
||
|
||
### The BGM bank has three sub-waves and HANDOFF says it has two
|
||
|
||
`media::sound_bank_riffs("BGM_103.slb")` returns **three**. HANDOFF Q10's census
|
||
says a music bank is *"exactly two waves of identical duration (32/32 banks on
|
||
the disc)"* — and that census is itself a correction, of an earlier reading that
|
||
called `BGM_001` three sub-waves and was refuted with "the 10 KB is the bank
|
||
header".
|
||
|
||
The third comes from `sylpheed-formats/src/slb.rs:380`, `to_xma_riffs`: when a
|
||
bank has a leading headerless packet region ahead of its first `RIFF`, that
|
||
region is emitted as a sub-wave. It exists because the voice path needs it —
|
||
`VOICE_D_453` decoded to 0.14 s without it. `docs/re/REFUTED.md` already records
|
||
the same region as what makes `BGM_106`–`BGM_109` "break the two-wave rule".
|
||
|
||
**The port sums all three and says so in the manifest.** That is not the
|
||
appealing answer — dropping sub-wave 0 would give a file matching the census, and
|
||
it would have been one line. It is the correct one: *which bytes belong together*
|
||
is the question `sylpheed_formats::media` owns, MISSION §2 names re-deriving it
|
||
here as the single easiest thing in this project to get subtly wrong, and "the
|
||
decoder returned something the corpus does not predict" is a finding to report,
|
||
not a number to quietly adjust. Adjusting it would also have destroyed the
|
||
evidence: a corrected export looks exactly like a correct one.
|
||
|
||
So the export ships the decoders' answer, the manifest carries a warning naming
|
||
the contradiction, `BLOCKED.md` has the row, and the Decoder has the pointer.
|
||
Until it comes back, **the menu plays a sum of three things where the census
|
||
predicts two**, and every one of those places says so.
|
||
|
||
### Clipping — and a comment of mine that argued for the thing that clipped
|
||
|
||
The BGM came out at **+1.8 dBFS**. The comment above the code that produced it
|
||
said `amix=normalize=0` sums at unity "because halving is a mix decision nobody
|
||
made".
|
||
|
||
That was wrong in both halves. Unity summing *is* a decision, and it is the one
|
||
that clips. And 1/n is not a taste call: it is the smallest constant that makes
|
||
an n-input sum of unity-scale signals provably clip-free, which is precisely the
|
||
reasoning `video.rs` already carried for its 0.4142-normalised 5.1 downmix — in
|
||
this same repository, written by this same port, and not looked at. It preserves
|
||
the stems' relative balance exactly, which is the only thing about the sum that
|
||
Q10 settles.
|
||
|
||
It is written as an explicit `volume=` rather than left to `amix`'s
|
||
`normalize=1` default, so the coefficient appears in the manifest's command line.
|
||
A default is a decision nobody made and it can move under an ffmpeg upgrade —
|
||
the same argument MISSION §6 makes about the downmix matrix.
|
||
|
||
**The `confirm` cue is a different case and is not "fixed".** It lands at
|
||
+0.18 dBFS, and it is a single wave off the disc with no arithmetic of ours in
|
||
it: the disc masters it near full scale and a lossy decode of a near-full-scale
|
||
signal overshoots by a fraction of a dB. Attenuating it would mean altering a
|
||
game asset to make one of our own numbers smaller. So `check` bounds the two
|
||
kinds differently — a `bgm` peak ≥ 0 dBFS is refused outright, because it is our
|
||
sum; an `se` is refused only above **+1.0 dBFS**.
|
||
|
||
🟡 That +1.0 is a **judgement and not a measurement**, and it is the weakest
|
||
number in P6. Nobody has measured the overshoot distribution across a corpus of
|
||
cues. If a cue ever trips it, the right response is that measurement, not a
|
||
looser bound.
|
||
|
||
## P6 gate — the audio is in the mix, and a null control says which part
|
||
|
||
No container here has a sound card, so "P6 works" cannot be answered by
|
||
listening. `docs/port/AUDIO-VERIFICATION.md` splits the question into three, and
|
||
these are the two that need no device.
|
||
|
||
### 1. The exported files, measured off the finished assets
|
||
|
||
```
|
||
se back -> audio/se/back.ogg (0.344 s, peak -5.7 dBFS)
|
||
se confirm -> audio/se/confirm.ogg (1.016 s, peak +0.2 dBFS)
|
||
se move -> audio/se/move.ogg (0.533 s, peak -1.4 dBFS)
|
||
bgm main_menu -> audio/bgm/main_menu.ogg
|
||
(87.744 s, peak -7.7 dBFS, bank BGM_103.slb, 3 sub-waves)
|
||
```
|
||
|
||
`sylpheed-export check export` passes: 16 screens validate, and every audio entry
|
||
carries a peak and a duration inside its bounds. The three cue durations match
|
||
HANDOFF Q8 at every published digit — see the refutation record above.
|
||
|
||
### 2. The engine, recorded off the Master bus
|
||
|
||
```
|
||
godot --path port -- --menu --script=down,down,accept,cancel --audio=…/p6.wav
|
||
→ recorded 6.037 s of Master bus (driver Dummy)
|
||
peak 0.0 dBFS, RMS −21.1 dBFS
|
||
```
|
||
|
||
**Non-silent is not the claim.** A WAV of the right duration full of the *bed*
|
||
would look exactly like this, and the cues could be missing entirely. So the cue
|
||
was isolated with a **null control**: the same scripted walk with ⬅ in place of ⬇.
|
||
Left/right are measured no-ops (Q5) and fire nothing, so the two runs differ by
|
||
exactly two move cues and nothing else — same screens, same transitions, the same
|
||
Ⓐ and Ⓑ cues in both, the same bed.
|
||
|
||
| | RMS |
|
||
|---|---|
|
||
| walk with two ⬇ presses | −21.9 dBFS |
|
||
| walk with two ⬅ presses (null) | −22.1 dBFS |
|
||
| **difference** | **−34.6 dBFS** |
|
||
|
||
The difference is not spread over the run. It is **one burst beginning at
|
||
t = 1.10 s and lasting 0.55 s** — two overlapping 0.533 s move cues — with 22 of
|
||
237 windows above −70 dBFS and silence everywhere else, including across the Ⓐ
|
||
and Ⓑ presses, which cancel because both runs make them. That is the cue reaching
|
||
the bus, separated from the music that was playing over it.
|
||
|
||
### The control that proved nothing, kept because it nearly passed
|
||
|
||
The first attempt paired `--script=down,down` against `--script=left,left`. The
|
||
difference was **bit-identical zero**, which reads as "the cues never reached the
|
||
bus" and would have been reported as a bug.
|
||
|
||
It was neither. Both runs recorded **1.115 s** while the first press lands at
|
||
~1.17 s: the control ended before the event it was controlling for. A null result
|
||
from an instrument that was not running is not a null result — PROTOCOL's "run
|
||
your own instrument through a control" applies to the control too.
|
||
|
||
### What this does NOT establish
|
||
|
||
* **That it sounds right.** Everything above is correspondence and separation,
|
||
not judgement. A ten-second human listen still answers something no measurement
|
||
here does.
|
||
* **That the bed is at a sane level against the cues.** 🔴 The Master bus peaks at
|
||
**0.0 dBFS** in the four-step run — the `confirm` cue is +0.2 dBFS on its own,
|
||
so any music under it puts the mix on the ceiling. Per-file levels are the
|
||
disc's and are fine; the **runtime** mix has no headroom. The port has not set a
|
||
bus balance, because nothing measures one and an invented balance is the same
|
||
class of mistake as an invented loop point. Recorded here rather than fixed
|
||
quietly.
|
||
* **That "Dummy driver" means heard.** It does not, and the run prints the driver
|
||
name so a write-up cannot forget to say so.
|
||
|
||
### One bug, in two dialects, both about a temp filename
|
||
|
||
The temp-name-then-rename discipline this project uses everywhere broke twice in
|
||
this milestone, in two different tools, for the same underlying reason: **tools
|
||
dispatch on the extension, so a temp name must preserve it.**
|
||
|
||
* `run_ffmpeg` wrote `.back.ogg.partial` → *"Unable to choose an output format"*,
|
||
a hard failure before a byte was written.
|
||
* `boot.gd` wrote `p6.wav.part` → `save_to_wav` **appends** `.wav` when the path
|
||
does not end in it, producing `p6.wav.part.wav`; the rename then failed to find
|
||
its source, its return value was not checked, and the run printed a success
|
||
line naming a file that did not exist.
|
||
|
||
The second is the more dangerous shape, and it is the one this project has
|
||
already warned itself about: a confident line of output pointing at nothing. The
|
||
rename's return is now checked and the failure is loud.
|
||
|
||
## P3, reopened — the boot title was missing the `PRESS Ⓐ` plate, 2026-08-29
|
||
|
||
P3 passed its gate with a boot that ended on build 4 alone. `BLOCKED.md` carried
|
||
that as 🔴 from the start: both states were captured, so the art was never the
|
||
question — the *sequence* was, and it is behavioural, so the port had no oracle
|
||
for it.
|
||
|
||
It is answered. `docs/re/title-plate-delay-measured.md`
|
||
(`auto/no-disc-and-menu-captures` at `fb536df`, **not on `main`** at the time of
|
||
writing) measures two independent boots: the title presents **without** the
|
||
plate, and the plate arrives **2.13 s** later, the two runs agreeing to 6 ms.
|
||
|
||
### Two builds at once, as two `ScreenView`s
|
||
|
||
`ScreenView` draws one screen. The obvious change was to teach it about a
|
||
subordinate overlay screen; the change made was to put a **second `ScreenView`
|
||
in the same `SubViewport`**, after the first.
|
||
|
||
That is what "two builds at once" actually is. Each build has its own timeline,
|
||
its own textures and its own hold — the plate's group runs independently of the
|
||
title's, which is the entire content of the finding — and Node2D siblings already
|
||
paint in tree order. The alternative would have put an `if overlay` in every
|
||
method that walks elements, and would have expressed the same information less
|
||
directly. The export's `paint_order` still means what it always meant: an
|
||
ordering *within* a build.
|
||
|
||
### The delay is timed from where build 4 stops animating
|
||
|
||
Not from where the title first appears. This is the finding rather than a detail:
|
||
measured from first-draw the two oracle runs differ by **0.48 s**, because the
|
||
build-in itself ran 1.64 s and 2.13 s and the emulator's frame pacing during an
|
||
animation is not the game's clock. Measured from settle they differ by 6 ms.
|
||
|
||
So `_boot_done` — the moment the sequencer already had for "this screen has
|
||
reached its hold" — is the landmark, and the overlay is due `after_settle_seconds`
|
||
later. A number taken from the wrong instant here looks exactly like a
|
||
measurement.
|
||
|
||
### The overlay is attached to the BOOT STEP, not to the `title` screen
|
||
|
||
What was measured is the boot title. Whether the plate is there when the title is
|
||
reached *again* — by Ⓑ from the main menu, or after the attract movie — is not
|
||
measured, and hanging the overlay on the screen would quietly claim that it is.
|
||
So it lives on the boot step in `authored/flow.json`, and `_drop_overlay` takes
|
||
it away with the screen it belongs to. `BLOCKED.md` carries the gap.
|
||
|
||
### Refutation — the RE agent's instruction contradicts the RE agent's measurement
|
||
|
||
**The claim under test**, quoted from the finding's *"What the port should
|
||
author"*: draw build 4, *"when build 4 has settled, wait **2.13 s**, composite
|
||
build 2 over it"*.
|
||
|
||
**It does not reproduce the measurement it came from**, and the gap is 3.97 s.
|
||
Build 2 is not a static plate: it has a group, and this port plays groups.
|
||
`press_start` has one element, `ptbtn00`, and its `fade_argb` reads
|
||
|
||
```
|
||
t=214 0x00ffffff pos (383, 560) invisible
|
||
t=236 0x00ffffff pos (383, 550) still invisible, having slid 10 px up
|
||
t=238 0xffffffff full alpha
|
||
t=244 0xffffffff holds
|
||
— 0x00ffffff the exit, untimed
|
||
```
|
||
|
||
At the measured 60 units/s that is **3.967 s** from the group's start to full
|
||
alpha. Compose the instruction with the group and the plate is first *visible* at
|
||
settle + 2.13 + 3.97 = **settle + 6.10 s**. What was measured — the glyph counter
|
||
leaving its no-plate value of 154 — is the plate becoming visible at **settle +
|
||
2.13 s**.
|
||
|
||
Neither obvious reconciliation works:
|
||
|
||
| reading | plate visible at | measured |
|
||
|---|---|---|
|
||
| both groups start together | 3.97 s (build 4 settles at **4.350 s**) — i.e. 0.38 s *before* settle | settle + 2.13 s |
|
||
| build 2's group starts at settle | settle + 3.97 s | settle + 2.13 s |
|
||
| build 2's group starts at settle + 2.13 s (the instruction) | settle + 6.10 s | settle + 2.13 s |
|
||
|
||
To land on the measurement, build 2's group has to start **2.51 s** after build
|
||
4's, which is not a landmark of anything.
|
||
|
||
**Verdict: the instruction is refuted as written; the measurement is untouched.**
|
||
The measurement is an observation of the running game and this port has no
|
||
standing to doubt it. What is refuted is the step that turns it into an
|
||
authoring rule, and that step is an interpretation.
|
||
|
||
**So the port ships the instruction, not its own arithmetic**, prints the
|
||
discrepancy on every boot, and files the row. This is the same call as the BGM
|
||
sub-waves and for the same reason: reconciling two of the RE agent's numbers is
|
||
a decoding question, and a port that quietly picks the one that looks right
|
||
destroys the evidence — a corrected boot looks exactly like a correct one.
|
||
|
||
The first thing to check is about the instrument rather than the game: is *"title
|
||
settled"*, the glyph counter first reading 154, the same instant as the port's
|
||
last-element settle (t=261, 4.350 s into the group)? If that landmark is earlier,
|
||
the gap closes with nothing else moving.
|
||
|
||
### Refuting the port's own claim: things in this export DO pulse
|
||
|
||
`BLOCKED.md` has carried this since P2, under the port's own raised question
|
||
about whether groups loop:
|
||
|
||
> no element's alpha reverses direction anywhere in this export, so nothing
|
||
> pulses, which removes the obvious reason to expect a loop without disproving
|
||
> one.
|
||
|
||
**`ptbtn00` reverses.** `0x00` → `0xff` → `0x00`, in the table above, in the
|
||
export, the whole time. The claim was never checked against `press_start`; it was
|
||
checked against the screens P2 happened to be animating. The RE agent has now
|
||
measured the running game pulsing this exact element at a mean 2.24 s.
|
||
|
||
So the reason to expect a loop is back — and the port still does not draw one,
|
||
because **no reading of this group produces 2.24 s**: the whole group is 268
|
||
units = 4.47 s, and from its first keyframe 54 units = 0.90 s. The plate is drawn
|
||
arriving and then holding at its settle (t=238, alpha `0xff`), which is what
|
||
every other screen does and what the static oracle capture
|
||
`live-title-press-a.png` shows. Which instant a repeat restarts from is filed,
|
||
not guessed.
|
||
|
||
### `--boot --capture=` — one frame instead of six hundred
|
||
|
||
The boot had no artifact of its own except `--film`, a PNG every 0.25 s for the
|
||
whole 156 s run, to answer one question: is the plate on top of the title at the
|
||
end. `--capture` was a `--screen`-only flag taken in `_ready`, which for a boot
|
||
run is 150 s too early. It is now deferred to the end of the sequence when
|
||
`--boot` is given.
|
||
|
||
### P3 gate — the boot ends on two builds
|
||
|
||
```
|
||
godot --path port -- --boot --capture=…/p3-plate.png
|
||
→ boot sequence complete after 155.86 s, holding on title
|
||
overlay press_start due at 157.99 s (+2.13 s after settle)
|
||
overlay press_start raised at 158.00 s, 1 element(s), settles at t=238
|
||
⚠ plate raised at settle+2.13 s but its own group reaches full alpha 3.97 s
|
||
later, so it is first VISIBLE at settle+6.10 s -- the measurement is
|
||
settle+2.13 s.
|
||
boot ends on title + press_start at 161.99 s
|
||
drew 16: ptbase2, ptloop01, …, ptcopyright
|
||
overlay press_start at t = 261.00 units, drew 1: ptbtn00
|
||
```
|
||
|
||
The PNG shows the title logo with **`PRESS Ⓐ BUTTON`** under it — build 4 and
|
||
build 2 in one frame, which this port had never drawn.
|
||
|
||
Two things the run made obvious and that are now fixed:
|
||
|
||
* **The capture reported only the base build's elements.** The first composited
|
||
capture printed `drew 16` and no mention of the plate, which reads as though
|
||
the overlay had not drawn at all. The overlay gets its own line; folding its
|
||
elements into the first list would have reported a screen that does not exist.
|
||
* **`--screen=<a> --overlay=<b>`** raises the same composite immediately, by the
|
||
same code path, with no delay. It exists because the only other way to see two
|
||
builds was a 156 s boot of which 137 s is the intro movie — and under Xvfb's
|
||
software Theora decode that is several minutes to answer "is the plate on top
|
||
of the title". It applies **no** delay: the delay is a measurement and lives in
|
||
`authored/flow.json`. The boot-mode narration is suppressed there, because a
|
||
log line that describes a sequence it is not running is worse than no log line.
|
||
|
||
## P5 — the focus ring spins, 2026-08-29
|
||
|
||
The ring was drawn at 0° and the file said so: *"THIS IS KNOWN TO BE WRONG, and
|
||
is drawn anyway because the right answer is a guess."* What was missing was the
|
||
**period**, and it is now measured — `docs/re/focus-ring-spin-measured.md`
|
||
(`auto/no-disc-and-menu-captures` at `4fa3099`): a continuous spin, from eight
|
||
evenly spaced autocorrelation peaks over nine revolutions, **with no angle
|
||
estimated anywhere** — both angle estimators failed their own controls and were
|
||
not used.
|
||
|
||
### The period comes off the disc; the RE agent supplied only that it repeats
|
||
|
||
`ptbtneff01` declares two keyframes that differ in **nothing but**
|
||
`rotation_deg`, 0 → 360, the first timed at `t = 120` and the second untimed.
|
||
The port turns once per **120 units**. Nothing is authored: the number is on the
|
||
disc, and what the measurement adds is that the turn **repeats** rather than
|
||
stopping at 360 = 0, which "groups hold" could not distinguish because those are
|
||
the same pose.
|
||
|
||
`ScreenView.spin_period_units` is the rule, and it is structural and narrow:
|
||
exactly two keyframes, differing only in rotation, by a full 360, first timed and
|
||
second untimed. **Disc-wide check over this export: 16 of 212 elements match, and
|
||
all 16 are focus rings** — `ptbtneff01` on the five main-menu buttons and
|
||
`ptbtneff02` on the three `EXTRAS` buttons, in both locales, every one declaring
|
||
`t = 120`. Zero false positives.
|
||
|
||
That check is the point rather than a formality. The measurement was taken on
|
||
**one** button of **one** screen; a rule that also caught something else would be
|
||
extrapolating it to elements nobody watched.
|
||
|
||
⚠️ It is a rule about **shape**, not a decoded field. Nothing on the disc says
|
||
"this loops". The day a loop flag is decoded, this goes.
|
||
|
||
### Verified on the port's own render, with the RE agent's own control
|
||
|
||
Captures at `--time=` 2.0 … 4.0 s on the settled main menu, `ptbtn01` focused:
|
||
|
||
| | |
|
||
|---|---|
|
||
| t=2.0 vs t=4.0 (one full period apart), **whole frame** | **0.0000 / 255** — bit-identical |
|
||
| t=2.5, 3.0, 3.5 against t=2.0, inside the ring's box | 3.60, 3.71, 3.58 / 255 |
|
||
| sum of box luminance across **eight** phases | spread **0.027 %** of the mean |
|
||
|
||
The last row is deliberately the RE agent's own observable: they separated
|
||
rotation from a brightness pulse by showing total annulus brightness is conserved
|
||
while per-bin brightness moves. The port's render conserves it to 0.027 % (theirs
|
||
was 0.4 % over 16 s, with capture noise in it). A filmstrip of the four
|
||
quarter-period phases shows the bright head at top, right, bottom, left.
|
||
|
||
### Two things it does not settle
|
||
|
||
* **Direction.** The port turns 0° → +360°, the sign the disc declares. No signed
|
||
angle was ever measured — the estimator that would have given one failed its
|
||
control and was not used.
|
||
* **Phase across a focus change.** The port drives the ring off the **screen**
|
||
clock, so moving the cursor does not restart the turn. The alternative — the
|
||
record's group restarting when the record is instantiated — is the stronger
|
||
claim, and the oracle run held focus on one button throughout, so nothing
|
||
separates them. Two frames straddling a focus change would.
|
||
|
||
## P3, corrected — the plate needs no authored delay at all, 2026-08-29
|
||
|
||
Last iteration the port refuted the RE agent's authoring instruction (*"when
|
||
build 4 has settled, wait 2.13 s, composite build 2"*) with arithmetic off the
|
||
disc, shipped the instruction anyway rather than pick between two of their
|
||
numbers, and printed the discrepancy on every boot.
|
||
|
||
**The refutation held, and the answer that came back is better than either option
|
||
the port offered: author nothing.** `5b0a6e6`.
|
||
|
||
### The premise that failed was the port's, and it will bite again
|
||
|
||
> 🔴 **`rest.t` is not when a screen settles.** It is the last *hold* keyframe
|
||
> before the exit.
|
||
|
||
Checked here rather than taken on trust. `title`'s `ptlogo1`:
|
||
|
||
```
|
||
t=26 (-116,-7) 150% a=0x00 the pre-roll
|
||
t=42 (179,186) 101% a=0xe0 it has arrived
|
||
t=251 (184,193) 100% a=0xff 5 px and 31 alpha steps later, 3.5 s on
|
||
```
|
||
|
||
It **stops moving at t=42** and then creeps for 209 units. `rest.t = 251` is the
|
||
end of that creep, not the arrival. The title's visible build-in is over at
|
||
**`t = 118`**, where `pteff01`, `pteff02` and `ptlogoall_eff` finish together.
|
||
|
||
Every reconciliation the port computed last iteration was wrong by exactly that
|
||
error: reading `rest.t` put build 4's arrival at 4.350 s instead of 1.967 s, and
|
||
the "2.51 s, which is not a landmark of anything" that looked so damning is
|
||
`(4.350 − 1.967) + 0.13` — the error itself, wearing a decimal point.
|
||
|
||
### One clock, and the interval is declared
|
||
|
||
| | units |
|
||
|---|---|
|
||
| build 4's last build-in ramp | `t = 118` |
|
||
| `ptbtn00` reaches `a = 255` | `t = 238` |
|
||
| **difference** | **120 units = 2.000 s** |
|
||
|
||
Measured: **2.138 s** and **2.132 s**. The 6.7 % is presentation rate — 120 units
|
||
in 2.135 s is 56.2 units/s, the emulator running 28.1 fps against a nominal 30,
|
||
and the corpus had independently measured the idle title at 28.5 fps *before*
|
||
these runs.
|
||
|
||
So `authored/flow.json` carries `"clock": "shared"` and **no delay**, `boot.gd`
|
||
raises the overlay when the step's screen loads rather than at its settle, and
|
||
`overlay.time_units = view.time_units` — assigned, not accumulated, because two
|
||
independently advanced clocks drift by a frame here and there and the whole
|
||
content of the finding is that 120 units is a fixed interval on one timeline.
|
||
|
||
⚠️ **The general hazard, stated by the RE agent and worth repeating where the
|
||
port will read it: discount a wall-clock number off that oracle by ~6 %.** It is
|
||
Canary's presentation rate baked into whatever it measures. A port at a true
|
||
30 Hz that authored 2.13 s would be visibly late.
|
||
|
||
### Refutation — two of the RE agent's numbers for the same 120 units disagree by 2 %
|
||
|
||
Both findings measure the same declared quantity: **120 keyframe units of wall
|
||
clock, during a static hold, in Xenia Canary.**
|
||
|
||
| | | implied presentation |
|
||
|---|---|---|
|
||
| plate: settle → plate, two runs | 2.138, 2.132 s → mean **2.135 s** | 28.10 fps |
|
||
| ring: one revolution, seven spacings | 2.18 2.16 2.18 2.16 2.16 2.20 2.20 → mean **2.177 s** | 27.56 fps |
|
||
| **disagreement** | **0.042 s = 1.97 %** | |
|
||
|
||
That is **seven times** the plate finding's own run-to-run agreement of 6 ms, and
|
||
it lands on the argument that finding uses to justify trusting itself: *"the
|
||
build-in is where frames are dropped; the static hold is not. A model in which
|
||
the game's own timing varied would have to move both."* Two static-hold
|
||
measurements are exactly what should agree under that model.
|
||
|
||
A second, smaller arithmetic slip in the same place: the ring page reconciles
|
||
2.177 s against a band of "27.6–28.8 fps", saying the measurement *"sits at the
|
||
top of that band"*. It does not sit in it. 60 rendered frames at 27.6 fps is
|
||
2.1739 s; the mean needs **27.56 fps** and the two slowest spacings (2.20 s)
|
||
need **27.27 fps**. Four of the seven spacings are above the band's top.
|
||
|
||
**Verdict: the containment claim is refuted; the spin, the period and the
|
||
reconciliation are untouched.** Either the presentation rate genuinely differed
|
||
between the two sessions — which the plate page's own corroboration argues
|
||
against for static holds — or the ring's revolution is not exactly 120 units.
|
||
The corpus should say which, because they are the same claim measured twice.
|
||
|
||
🟢 **Nothing in the port moves either way.** `spin_period_units` uses the
|
||
declared 120 units at 60 units/s = **2.000 s of port time**, which is the
|
||
true-30 Hz value both readings agree the disc means. This is a corpus
|
||
consistency problem, not a port one — which is why it is filed rather than
|
||
worked around.
|
||
|
||
### The corrected boot ended one build too early, and the capture showed it
|
||
|
||
Moving the plate onto the shared clock also moved the boot's exit, and the first
|
||
capture taken afterwards was **visibly darker** than the one before it. The cause
|
||
is `pteff00`, the title's black fade quad: it ramps `0xff000000` → `0x00000000`
|
||
over t=16…261, so at t=243 — where the run was quitting, `overlay.settle_time()`
|
||
after the overlay was raised — the frame is still ~7 % black.
|
||
|
||
The plate arrives at t=238; **build 4 is not finished until t=261**. The boot now
|
||
ends at the later of the two, and says which in the log:
|
||
|
||
```
|
||
-> title at 145.79 s
|
||
overlay press_start raised at 145.79 s, 1 element(s), settles at t=238
|
||
boot ends at 150.14 s, once both builds have arrived (t=261)
|
||
```
|
||
|
||
Worth recording because of how it presented: nothing failed, no warning was
|
||
printed, and the only symptom was a frame slightly darker than the previous
|
||
run's. A gate artifact that silently drifts is the failure mode this project
|
||
keeps meeting — and it was caught only because there was a previous capture to
|
||
compare against.
|
||
|
||
## P7 — the new-game intro, 2026-08-29
|
||
|
||
`S00A.wmv` has been in `export/video/` since P4 (MISSION §6 put both movies in
|
||
scope at once). What P7 needed was for something to *play* it and for the run to
|
||
end somewhere defined.
|
||
|
||
### The port skips two measured screens, and says so on screen
|
||
|
||
The real chain is **`NEW GAME` → `DIFFICULTY` → `SELECT DATA` → Ⓐ on a save slot
|
||
→ ~4.5 s → `S00A`** (HANDOFF Q4 measured the screens, Q9 decoded the movie and
|
||
then measured its onset off the running game at 0.96–1.000 with a strictly
|
||
monotone playhead over 25 consecutive 0.5 s samples).
|
||
|
||
`DIFFICULTY` and `SELECT DATA` are measured destinations that are **not
|
||
`GP_TITLE` builds**, so no screen file exists to go to. The port therefore jumps
|
||
from `NEW GAME` to the one thing in that chain it has.
|
||
|
||
That is a gap, not a sequence, and the whole design here is about not letting it
|
||
read as one:
|
||
|
||
* `MenuFlow.accept` returns a **new kind**, `video`, rather than folding this
|
||
into `blocked`. The caller has to announce the skip, and a distinct kind is
|
||
what forces it to.
|
||
* The runtime prints it every time:
|
||
`(NEW GAME) -> the real chain is DIFFICULTY -> SELECT DATA, then the movie.
|
||
Neither screen is in this export.`
|
||
* `authored/flow.json` carries `skipped_chain` as **data**, so the names of what
|
||
is missing live beside the decision rather than inside a GDScript string.
|
||
|
||
A port that quietly jumped from `NEW GAME` to the intro would be showing a
|
||
sequence the game does not have, with nothing on screen saying so. That is the
|
||
exact failure this project keeps meeting from the other direction.
|
||
|
||
### What happens after the movie is authored, and had to be
|
||
|
||
The game goes into **mission 1**. Gameplay is out of scope (PORT-MISSION §7), so
|
||
"returns to a defined state" is a decision, and P7's gate says as much. The port
|
||
returns to the **title**: the boot's own end state, so a run that finishes the
|
||
intro lands somewhere a player can start again from. Nothing measured says the
|
||
game does this, and `after_video.kind` is `"authored"`.
|
||
|
||
### The 4.5 s gap is left empty on purpose
|
||
|
||
Q9 measures the movie starting ~4.5 s after Ⓐ on the save slot. What is on screen
|
||
for those 4.5 s was never observed — the run that would have shown it hit the
|
||
documented `sub_823070B0` cache crash after `SELECT DATA`.
|
||
|
||
`GP_TITLE` **does** carry a loading screen (below), and 4.5 s is about the right
|
||
shape for one. That is precisely why it is in `BLOCKED.md` and not in
|
||
`flow.json`: a plausible filler that nobody watched is the kind of thing that is
|
||
indistinguishable from a measurement a month later.
|
||
|
||
### A script timeout that would have failed every movie
|
||
|
||
`--script`'s per-step timeout is 20 s, to stop an unattended run waiting forever
|
||
on a screen that never settles. `S00A` is **93.9 s**, so the first scripted
|
||
new-game run would have been killed at step 1 and reported as "never settled".
|
||
|
||
Raising the constant would have been wrong in the other direction: a movie stuck
|
||
at frame 0 would then hang the job, and a job that waits is worse than a job that
|
||
fails, because it does not look like a failure.
|
||
|
||
So the test is **liveness, not duration**: while `get_stream_position()`
|
||
advances, the deadline moves with it; a stalled movie still trips the same 20 s.
|
||
|
||
### Found while looking: `GP_TITLE`'s four unnamed builds are LOADING screens
|
||
|
||
`build_00`, `build_01`, `build_12`, `build_15` have never had names. Every
|
||
element in all four is `pgloading_*` — `pgloading_processing.png`,
|
||
`pgloading_circle1`, `pgloading_delta`, `pgloading_ring` — and `LOADING` is one
|
||
of the three screen names the RE agent read out of the title part's state
|
||
function.
|
||
|
||
Two variants: 0/1 carry 7 elements, 12/15 carry 10.
|
||
|
||
**They are not renamed here.** The archive's own pairing (adjacent for 2/3, `+3`
|
||
for 4…9 and for 10/13, 11/14) suggests 0 is 1's twin and 12 is 15's, but which
|
||
member of each pair is which **locale** is an inference, and a name is exactly
|
||
the kind of thing that stops being questioned once written. Handed to the RE
|
||
agent, who can answer it from a capture in one look. `BLOCKED.md` has the row.
|
||
|
||
⚠️ **And one of them is a second casualty of the `rest.t` problem.**
|
||
`pgloading_eff00.prm` on entries 12/15 is a full-screen black quad whose group
|
||
runs `0xff000000` at t=38 → `0xff000000` at t=48 → `0x00000000` untimed: black,
|
||
held, *then* clear. Its `rest.t` is **38**, where it is fully opaque. A port that
|
||
draws that screen at its declared rest draws **a black rectangle over the entire
|
||
loading screen**. The title's case only dimmed a frame; this one hides
|
||
everything. Filed with the `settle_time()` row it belongs to.
|
||
|
||
### Refutation — attempted on the fade-quad census; it survives, with a caveat
|
||
|
||
**The claim** (HANDOFF, on transitions): *"in `GP_TITLE` exactly the six screen
|
||
builds carry it while the six overlays do not"*, where "it" is the full-screen
|
||
black `.prm` quad *whose keyframe group is the transition*.
|
||
|
||
**The test**, over the whole export: count builds carrying a full-screen
|
||
primitive with black in its keyframes.
|
||
|
||
```
|
||
16 builds exported; 12 carry one.
|
||
Of the 12 `is_build` bundles (excluding the 4 authored splashes): 8 carry, 4 do not.
|
||
carry: title, title_jp, main_menu, main_menu_jp, extras, extras_jp,
|
||
build_12, build_15
|
||
do not: press_start, press_start_jp, build_00, build_01
|
||
```
|
||
|
||
8 and 4, not 6 and 6. But the two extras are `build_12` / `build_15`, and their
|
||
quad is a **different shape**:
|
||
|
||
| | transition quad (`pteff00.prm`, title) | loading quad (`pgloading_eff00.prm`) |
|
||
|---|---|---|
|
||
| | `0xff000000` t=16 | `0xff000000` t=38 |
|
||
| | `0x00000000` t=261 | `0xff000000` t=48 |
|
||
| | `0xff000000` untimed | `0x00000000` untimed |
|
||
| shape | black → clear → **black** | black → held → **clear** |
|
||
|
||
The transition quad returns to black on exit; the loading quad does not. Read
|
||
strictly — the quad *whose group is the transition* — the claim holds.
|
||
|
||
**Verdict: survives.** The refinement is worth recording anyway, because the
|
||
naive test over-counts by two and somebody will run the naive test. There are
|
||
**two kinds** of full-screen black `.prm` in `GP_TITLE`, and only one of them is
|
||
a transition.
|
||
|
||
## P7 gate
|
||
|
||
```
|
||
godot --path port -- --menu --script=accept --audio=…/p7.wav
|
||
```
|
||
|
||
```
|
||
menu on main_menu, focus ptbtn01
|
||
script[1] accept
|
||
(NEW GAME) -> the real chain is DIFFICULTY -> SELECT DATA, then the movie.
|
||
Neither screen is in this export.
|
||
-> video S00A at 1.18 s (/work/export/video/S00A.ogv)
|
||
video ended at 94.93 s
|
||
-> title (authored: authored)
|
||
menu on title, focus (none -- this screen has no focusable item)
|
||
script complete after 99.28 s on title
|
||
recorded 98.453 s of Master bus (driver Dummy)
|
||
```
|
||
|
||
The movie ran **93.75 s** against a declared 93.9 s, the run ended on the title,
|
||
and the Master bus recorded 98.453 s: `pcm_s16le`, 44.1 kHz stereo, RMS
|
||
**−22.2 dBFS**.
|
||
|
||
**What this does not show, stated because it would be easy to imply otherwise:**
|
||
the recording contains the menu bed *and* the movie together, and this run did
|
||
**not** separate them. So it establishes that the engine reached an output for
|
||
98 s of a run whose middle 94 s was a movie — not that `S00A`'s own audio track
|
||
is in the mix. Separating them wants the P6 null-control method (a paired run
|
||
that differs only in the movie), and that is not done here.
|
||
|
||
🔴 **Peak 0.0 dBFS again.** The same runtime-headroom problem P6 filed: per-file
|
||
levels are the disc's and are fine, the Master bus has no headroom, and the port
|
||
has set no bus balance because nothing measures one.
|
||
|
||
### One more file read while it was being written
|
||
|
||
`ls` reported the recording as **3 702 828 B**; `ffprobe` on the finished file
|
||
reports **17 367 084 B / 98.452608 s** — a factor of 4.7. `ffprobe` is right and
|
||
the `ls` caught it mid-flight.
|
||
|
||
`AUDIO-VERIFICATION.md` opens by naming this failure and the port has had the
|
||
temp-name-then-rename discipline since P6, which is what makes it worth writing
|
||
down rather than shrugging off: **the discipline protects a reader who opens the
|
||
path, and it does not protect a reader who stats it at the wrong moment.** Size
|
||
on disk is not a measurement of a file somebody else is still writing. Ask the
|
||
decoder, not the directory entry.
|
||
|
||
## Modding — rule 4 was never implemented, 2026-08-29
|
||
|
||
`docs/port/MODDING.md` is explicit that modding is *"a design constraint on the
|
||
exporter today — not a milestone to add later"*, and its rule 4 is base-and-
|
||
overrides: a mod replaces a file by **shadowing its path**, so a modder edits
|
||
nothing under the derived tree and re-exporting is always safe.
|
||
|
||
**Nothing read `data/mods/` at all.** The directory has existed since the
|
||
monorepo merge with a `.gitkeep` in it and no code path anywhere — exporter or
|
||
runtime — that looked at it. Eight milestones shipped past that.
|
||
|
||
### One resolver, and every read goes through it
|
||
|
||
`ExportTree.resolve(rel)` returns the mod tree's copy when one exists and the
|
||
derived tree's otherwise. `read_json`, `texture`, `video` and `MenuAudio` all
|
||
call it, so a mod can replace **a screen's JSON, a sprite, a cue, the music bed
|
||
or a movie** — every asset kind the port reads.
|
||
|
||
`MenuAudio` was reading `tree.root.path_join(...)` directly and had to be
|
||
changed. Left alone it would have made audio the one asset kind a mod could not
|
||
touch, for no reason a modder could have guessed — which is the failure mode
|
||
rule 4 exists to prevent.
|
||
|
||
There is deliberately no manifest of what a mod contains and no registration
|
||
step: **the path is the registration**, which is the whole of the rule.
|
||
|
||
⚠️ **One tree, not a stack.** Several mods layering over each other needs a load
|
||
order, and a load order needs a rule nobody has asked for. Said out loud in
|
||
`data/mods/README.md` rather than answered.
|
||
|
||
### A modded run must not look like an unmodded one
|
||
|
||
Every shadowed file is printed the first time it is read:
|
||
|
||
```
|
||
mod: sprites/title/main_menu/ptbtn01.png <- /work/data/mods/sprites/…/ptbtn01.png
|
||
```
|
||
|
||
MODDING says *"did I break it?"* is answered by disabling a mod. That is a fine
|
||
last resort and a poor only resort, so the log names the replacement instead.
|
||
|
||
**The first version of this got it wrong in an instructive way**: it printed a
|
||
summary in `_ready`, before a single asset had been read, and so always said
|
||
`(nothing shadowed yet)`. A report structurally incapable of reporting anything
|
||
is worse than no report, because it looks like an answer. It now announces each
|
||
shadow at the moment it happens.
|
||
|
||
### Gate
|
||
|
||
A synthetic 203×43 magenta PNG — nothing disc-derived — dropped at
|
||
`data/mods/sprites/title/main_menu/ptbtn01.png`:
|
||
|
||
| | |
|
||
|---|---|
|
||
| pixels changed between the two renders | **8 501** of 921 600 (0.92 %) |
|
||
| bounding box of the change | x 542…744, y 162…204 — **203×43**, the sprite's own size |
|
||
| `sylpheed-export check export` afterwards | 16 screens still validate |
|
||
|
||
The changed region is exactly the sprite and nothing else moved.
|
||
|
||
### `data/mods/` was not gitignored, and that is a hole in a hard rule
|
||
|
||
*"Never commit game assets"* has been enforced on `export/` and `data/base/`
|
||
since P0. But **a mod is usually an edited game asset**, and `data/mods/` was
|
||
fully tracked — so the one directory a user is invited to put modified sprites in
|
||
was the one directory git would happily take them from.
|
||
|
||
`.gitignore` now excludes everything under it except the README.
|
||
|
||
### The naming split is not mine to resolve
|
||
|
||
`MODDING.md` describes the tree as `data/base/`; `PORT-MISSION.md` §3, the
|
||
exporter, `ExportTree` and `.gitignore` all say `export/`. Both are mission
|
||
files, and PROTOCOL is clear that **only the human changes a mission**, so this
|
||
is raised rather than picked. `.gitignore` has ignored both names on purpose
|
||
since P0.
|
||
|
||
It matters here for one concrete reason: MODDING's layout has `base/` and `mods/`
|
||
as **siblings**, and today they are not — the tree is `export/` at the repo root
|
||
while mods are `data/mods/`. The resolver takes `SYLPHEED_MODS` or defaults to
|
||
`data/mods/`, which is what exists; if the tree is ever renamed to `data/base/`
|
||
the sibling rule becomes natural and that default can go.
|
||
|
||
## Refutation — the paint-order key, and the reach of its tie-break
|
||
|
||
**The claim** (HANDOFF Q3): paint order is *"a `u16` layer key at `+0x0A`,
|
||
**decoded**"*, with the tie-break filed 🟡 as *"eight candidates refuted; costs
|
||
one element's blend on one screen"*.
|
||
|
||
**First pass: 2 of 16 screens did not match** a stable sort by layer key — both
|
||
loading screens, `build_12` and `build_15`.
|
||
|
||
**That was my test, not the claim.** `pgloading_eff00.prm` carries **no layer key
|
||
at all** — `layer: null`, `layer_source: "none"`: it is a primitive with no
|
||
sprite header, and the exporter's implied-name fallback produces nothing either.
|
||
My sort put a keyless element first; the decoders put it **last**.
|
||
|
||
Completing the rule as *"stable sort by layer key, elements with no key last"*
|
||
gives **16 of 16**. And last is right: `pgloading_eff00` is the full-screen black
|
||
quad, and HANDOFF's own sentence is that the fade quad paints last.
|
||
|
||
**Verdict: survives, with the rule completed.** Worth recording because the
|
||
published statement does not say where a keyless element goes, and there is at
|
||
least one in the archive.
|
||
|
||
🟡 **But the tie-break's reach looks understated.** Census over this export:
|
||
|
||
```
|
||
elements sharing a layer key with another element: 105, across 12 of 16 screens
|
||
```
|
||
|
||
HANDOFF characterises the cost as *"one element's blend on one screen"*. 105
|
||
elements on 12 screens is a much larger surface than that. Most of those ties are
|
||
probably invisible — two elements that share a key and never overlap cannot show
|
||
a difference — but *probably* is doing the work in that sentence, and nothing has
|
||
measured which. The port is unaffected either way: it draws
|
||
`ui_layout::derived_paint_order` verbatim and derives no order of its own.
|
||
|
||
## Correction — the runtime "clipping" I flagged 🔴 twice was overstated
|
||
|
||
P6 and P7 both filed 🔴 *"the runtime mix has no headroom"* on the strength of a
|
||
peak reading of 0.0 dBFS off the Master bus. Measured properly:
|
||
|
||
| | samples at full scale | of total | longest clamped run |
|
||
|---|---|---|---|
|
||
| P6 walk (5.944 s) | 43 | 0.0082 % | 10 samples — **0.23 ms** |
|
||
| P7 new-game run (98.453 s) | 24 | 0.00028 % | 11 samples — **0.25 ms** |
|
||
|
||
That is not a headroom defect. It is the disc's own `confirm` cue, mastered near
|
||
full scale (+0.18 dBFS after a lossy decode), touching the ceiling for a quarter
|
||
of a millisecond on a transient — and possibly only in the recording's 16-bit
|
||
conversion, since Godot mixes in float and `AudioEffectRecord` saves `s16`.
|
||
|
||
**Nothing is changed, and that is the point.** Attenuating the mix to buy
|
||
headroom would be an unmeasured decision about level — the same class of thing
|
||
this port refused for the BGM loop point and the stem balance. Refusing it there
|
||
and taking it here would be inconsistent, and it would trade an inaudible
|
||
0.25 ms clamp for an audible change nobody measured.
|
||
|
||
**A peak reading is not a clipping measurement.** One sample at 0 dBFS and two
|
||
seconds of square wave give the same number, and I reported the first as though
|
||
it were the second — twice, in red, in two milestones' write-ups.
|
||
|
||
## The P1 regression harness had been broken since the monorepo merge, 2026-08-29
|
||
|
||
`tools/port/verify-screen` is the P1 gate's regression detector: Godot's drawing
|
||
of a screen against `sylpheed-cli screen render` of the same build. It had not
|
||
been run since P1, across four milestones that changed the renderer — rotation,
|
||
the focus record, the spinning ring, two builds composited at once.
|
||
|
||
It could not have been run. **It resolves its reference binary to a path that
|
||
`build-reference-cli` stopped being able to produce.** That script greps
|
||
`crates/sylpheed-export/Cargo.toml` for
|
||
|
||
```
|
||
sylpheed-formats = { git = "…Syplheed-Reborn.git", rev = "…" }
|
||
```
|
||
|
||
and the monorepo merge (`65cefa7`) replaced that line with
|
||
`{ path = "../sylpheed-formats" }`. The grep returns nothing, the script exits 1,
|
||
and the binary left at `reference-cli/sylpheed-cli` is whatever predated the
|
||
merge — here, **three hours older than the sources** and built from a revision
|
||
nothing in the tree points at any more.
|
||
|
||
Running the diff against it would have compared the port to a decoder from
|
||
another era and called the result a regression check. `DECISIONS.md` already
|
||
carries *"The reference renderer was stale for three diff runs"* from P2. This
|
||
would have been the fourth, and the mechanism was different: not a forgotten
|
||
rebuild, but a **build step that could no longer succeed and a consumer that
|
||
only checked whether the file existed**.
|
||
|
||
### The fix is a deletion, not a repair
|
||
|
||
The revision-keying solved a two-repo problem: `/reborn`'s `target/` was a live
|
||
mount of the other agent's checkout and moved mid-run, so a pixel disagreement
|
||
against it had a free variable in it. **The monorepo removed that problem by
|
||
construction** — the exporter, the reference and the port now read one decoder,
|
||
the working tree's. So `verify-screen` builds `sylpheed-cli` from the workspace.
|
||
`SYLPHEED_CLI` still overrides for anyone who wants to pin one deliberately.
|
||
|
||
### The baseline, all 16 screens
|
||
|
||
```
|
||
build_00/01 max 3 over3 0 OK
|
||
press_start(_jp) max 1 over3 0 OK
|
||
title max 6 over3 790 DIFFERS
|
||
main_menu(_jp) max 4 over3 0 DIFFERS
|
||
extras(_jp) max 3 over3 0 OK
|
||
publisher_logo(_r) max 1-2 over3 0 OK
|
||
developer_logos(_r) max 2 over3 0 OK
|
||
title_jp max 155 over3 20498 DIFFERS
|
||
build_12/15 max 0 over3 0 OK
|
||
```
|
||
|
||
**No new drift.** Four milestones of renderer change and the only screen with a
|
||
substantial disagreement is `title_jp` — which is the *same* one P1 recorded and
|
||
left open: `ptlogo_eff2` is the single drawn element in the whole export at a
|
||
scale that is not a whole multiple of 100 % (125 %), and the two renderers pick
|
||
different source texels there. `ui_layout::blit` samples at the destination
|
||
pixel's top-left corner, a GPU at its centre. **The port has still not changed to
|
||
match**, because matching would mean reproducing a half-pixel bias on purpose to
|
||
make a number smaller. Only an oracle capture settles it.
|
||
|
||
`title`'s 790 pixels at ≤ 6/255 are the same class, one texel wide, on the logo's
|
||
scaled edges. `main_menu` and `main_menu_jp` say DIFFERS on a max of 4 with
|
||
**zero** pixels over the bar — a couple of pixels differing in a single channel.
|
||
|
||
### `max` alone could not tell 2 pixels from 25 000
|
||
|
||
The script reported only the largest difference anywhere in the frame, so
|
||
`main_menu` (two pixels) and `title_jp` (2.8 % of the frame) produced the same
|
||
verdict. It now also reports how many pixels are over the bar.
|
||
|
||
**The bar itself is not raised.** Tuning a threshold until things match is the
|
||
failure the script's own header warns about; adding a second number is
|
||
information, not a loosened bound. ⚠️ The count is thresholded on **greyscale
|
||
luma** while `max` is a per-channel maximum, so they are not two views of one
|
||
measurement — a per-channel check counts 957 on `title` where the luma count
|
||
says 790.
|
||
|
||
### What this harness cannot see, stated because the OK rows look reassuring
|
||
|
||
It renders `--pose=rest`. That is deliberate — it holds both renderers to the
|
||
same declared pose so the test is *port vs reference* and not *rest vs timeline*
|
||
— but it means **none of this iteration's or the last four's visible work is
|
||
under test**: not the spinning focus ring, not the plate composited over the
|
||
title, not any timeline behaviour, not audio. Sixteen OK rows are a statement
|
||
about the resting composite and nothing else.
|
||
|
||
And it remains what its header says: a consistency check between two renderers
|
||
that share their assumptions. Both have been wrong together three times —
|
||
`pteff05`, scale-0, `rest()` — and each time only a capture caught it.
|
||
|
||
## Refutation — "builds 0/1 and 10/11 are the loading screen" is false in the index space this export uses
|
||
|
||
**The claim**, from the RE agent 2026-08-29, answering the port's ask to name
|
||
`GP_TITLE`'s unnamed bundles: *"builds 0/1 and 10/11 are the loading screen,
|
||
decoded from their own `pgloading_*` element names."*
|
||
|
||
**In this export, entries 10 and 11 are the splash screens**, and it is not close:
|
||
|
||
| entry | elements |
|
||
|---|---|
|
||
| 10 | `palogo_eff0`, **`palogo_sqex`**, `palogo_sqex_eff` |
|
||
| 11 | `palogo_eff0`, **`palogo_gamearts`**, `palogo_seta`, `palogo_anima` … |
|
||
| 12 / 15 | `pgloading_eff00`, `pgloading_loop1`, `pgloading_str` … |
|
||
|
||
Entry 10 is the **SQUARE ENIX** wordmark and 11 the developer logos — which the
|
||
same agent identified, in the answer to the port's ask 1, as *"entries 10/13 are
|
||
the SQUARE ENIX publisher wordmark, the first thing the boot shows"*.
|
||
|
||
**Verdict: the finding is almost certainly right and the index space is wrong.**
|
||
Over the twelve bundles `is_build` accepts — entries 0,1,2,3,4,5,6,7,8,9,12,15 —
|
||
ordinals 10 and 11 are entries **12 and 15**, which are exactly the two dressed
|
||
loading variants. So "0/1 and 10/11" is the `is_build` ordinal, and this export
|
||
addresses by **pak entry index**.
|
||
|
||
**Why this is worth a section rather than a shrug.** `authored/screen_names.json`
|
||
is keyed by entry index, and the exporter's own comment says why: *"keyed by
|
||
ENTRY, not by the enumeration ordinal — widening the enumeration to reach the
|
||
splash renumbers the ordinals, and a name that moves when the rule changes is not
|
||
a name."* Someone reading that message and writing keys `"10"` and `"11"` would
|
||
**name the publisher wordmark and the developer logos as loading screens**, and
|
||
the export would validate, and the boot would still run.
|
||
|
||
Two enumerations of the same archive differ by exactly the four bundles the port
|
||
had to add an allow-list to reach. That is the sharpest possible demonstration of
|
||
why the exporter switched, and it has now nearly caused the error it switched to
|
||
prevent. Reported; the names are still the RE agent's to give.
|
||
|
||
## The intro's missing dialogue was an export gap, not a transcode bug, 2026-08-29
|
||
|
||
A human play-test heard music under the boot intro and no voices. The obvious
|
||
reading is that the 5.1→stereo fold dropped the centre channel, and it is wrong.
|
||
|
||
**`ADV.wmv` carries music and effects only.** On this disc a cutscene's voice is
|
||
a *separate asset*: one continuous XMA stream in `sound.pak`, bound to the movie
|
||
by the manifest in `tables.pak` (`ADV` → `VOICETRACK = VOICE_ADV`). Nothing was
|
||
dropped — `grep -rn voice crates/sylpheed-export/src/` returned nothing, because
|
||
the exporter had never been asked for it. The transcode was correct the whole
|
||
time, which is why every measurement on it passed.
|
||
|
||
That is worth stating plainly because the failure *looked* exactly like a codec
|
||
bug, and `docs/port/AUDIO-VERIFICATION.md` is full of ways to measure a
|
||
transcode against its source. Every one of them would have come back clean.
|
||
|
||
### The binding is resolved, and must never be matched by name
|
||
|
||
`audio::export_voice` takes exactly one route:
|
||
`media::resolve_movie_voice_region(source, movie, VoiceLang::English)`, which
|
||
walks movie → cue token (manifest) → sound id (registry) → a `[start, end)` byte
|
||
region of the continuous stream. The cheap route — read `VOICE_<movie>.slb` —
|
||
was not taken, and the reason is a measurement:
|
||
|
||
| movie | resolved region | inside the bank named after it? |
|
||
|---|---|---|
|
||
| `ADV` | 433 930 240…437 044 592 | yes |
|
||
| `S00A` | 452 798 464…455 499 120 | yes |
|
||
| `RT01A` | 437 044 592…437 345 648 | **no — it is inside `VOICE_ADV.slb`** |
|
||
|
||
⚠️ **Name-matching is correct on exactly the two movies this port ships, and
|
||
wrong on the radio cutscenes.** It would have exported clean, verified clean
|
||
against both in-scope movies, and returned the wrong recording the moment
|
||
anybody widened the export. This is the failure mode MISSION §2 names — one
|
||
playable thing is not one archive entry — in its most convincing disguise: the
|
||
spot-checks a person would actually run are the ones it passes.
|
||
|
||
### Three choices, and why none is a guess
|
||
|
||
* **One file per movie**, per MODDING rule 1, and the region's chunks are
|
||
**summed** — see the correction below, because the first version of this
|
||
paragraph said the opposite and was wrong.
|
||
* **Mono**, folded from the stream's **own declared channel count**, probed with
|
||
`ffprobe` rather than assumed. This is not pedantry: `pan` silently ignores a
|
||
channel the input does not have — measured this iteration on the 5.1 fold
|
||
below, where `FLC`/`FRC`/`SL`/`SR` vanished with no warning at all — so a
|
||
stereo matrix applied to a mono voice track is not an error, it is a −6 dB
|
||
attenuation that nothing reports. A track that is already mono is passed
|
||
through untouched.
|
||
* **No sync offset, and no length clamp.** The voice plays from the video's
|
||
first frame, so nothing is authored. The decoded length is recorded in the
|
||
manifest *beside the movie's own length* rather than trimmed to it: the voice
|
||
has no shared container to disagree with, so a length mismatch is the only
|
||
symptom a resolution error would ever show, and clamping would delete it. That
|
||
decision is the reason the error below was caught in the same hour it was made.
|
||
|
||
### Correction, within the hour — the chunks are stems, and I had concatenated them
|
||
|
||
The first version of `export_voice` joined the region's chunks end to end and
|
||
produced **359.201 s of voice for a 137.437 s movie**, and **255.460 s for a
|
||
93.779 s one**. Both ratios sit near 3, and both regions decode to 3 chunks.
|
||
|
||
The manifest said so on the first run, because the length was recorded against
|
||
the movie's instead of being clamped to it. A clamp — which is what
|
||
`sylpheed-viewer` does, and what `media`'s own doc comment invites with *"trimmed
|
||
by the caller's length clamp"* — would have produced a file of exactly the right
|
||
duration containing the wrong audio, and every check in
|
||
`docs/port/AUDIO-VERIFICATION.md` would have passed it.
|
||
|
||
Decoding each chunk and timing it (`crates/sylpheed-export/examples/voice_chunks.rs`):
|
||
|
||
| movie | movie length | chunk 0 | chunk 1 | chunk 2 |
|
||
|---|---|---|---|---|
|
||
| `ADV` | 137.437 s | 84.553 | **137.324** | **137.324** |
|
||
| `S00A` | 93.779 s | 68.072 | **93.694** | **93.694** |
|
||
| `RT01A` | — | 0.009 | **34.034** | — |
|
||
|
||
Chunks 1 and 2 are **equal to six decimals and each span the whole movie**. That
|
||
is HANDOFF Q10's decoded shape — *two stems of one performance, played together;
|
||
do not concatenate* — showing up on a second asset kind. They are summed at
|
||
`1/n`, exactly as `export_bgm` sums a music bank.
|
||
|
||
⚠️ **Chunk 0 is dropped and its status is open.** Its duration matches nothing:
|
||
84.6 s under a 137 s movie, 9 ms under `RT01A`. `docs/re/REFUTED.md` records
|
||
`to_xma_riffs`'s hybrid branch emitting a **leading headerless packet region**
|
||
ahead of the real `RIFF` waves, and `docs/port/BLOCKED.md` already carries that
|
||
as an open row against `BGM_103`, where `media` returns three sub-waves against a
|
||
census of two. **This is the same signature on an independent asset kind** — good
|
||
corroboration, not proof, and the port is not entitled to close it. So the
|
||
selection rule is written in terms of the measurement (*keep the longest
|
||
duration and everything tying with it*), and every dropped chunk is named in the
|
||
manifest with its length.
|
||
|
||
This is the media-assembly trap MISSION §2 names, and it caught me: I wrote a
|
||
doc comment asserting concatenation, gave the reason, and had it wrong. What
|
||
saved it was refusing to clamp — the one decision in the first version that was
|
||
made for the right reason.
|
||
|
||
### What a `None` means
|
||
|
||
A movie whose region does not resolve is **genuinely unvoiced** — the honest
|
||
answer for most `hokyu_*` resupply cutscenes — and gets a manifest warning, not
|
||
a substitute. The corpus already paid for the alternative: resolving unbound
|
||
movies through a shared demo line played the *wrong recording*.
|
||
|
||
This is **decoded, not authored**, so it runs outside the `authored/audio.json`
|
||
block in `main.rs`. Nothing new goes in `authored/`; there is nothing here we
|
||
decided.
|
||
|
||
## Refutation, of my own exporter — MISSION §6 pins a downmix matrix, and the exporter ships a different one
|
||
|
||
**The claim under test is the port's**, not another agent's, and it has been in
|
||
`video.rs` since P4: that the 5.1 fold is normalised by
|
||
`1/(1 + √½ + √½) = 0.4142` because *"the unnormalised form was measured too and
|
||
**clips**: peak 0.0 dBFS."*
|
||
|
||
That sentence rests on a peak reading. `docs/port/BLOCKED.md` records this port
|
||
withdrawing a 🔴 runtime-clipping flag on precisely the grounds that **a peak
|
||
reading is not a clipping measurement** — one sample at full scale and two
|
||
seconds of square wave give the same number. So the justification for deviating
|
||
from a matrix a human pinned was produced by an instrument this port has already
|
||
declared unfit for the question.
|
||
|
||
### Measured properly, over the whole of both movies
|
||
|
||
Decoded to 32-bit float so nothing is pre-clamped, then counted: samples at or
|
||
over full scale, how many exceed it by more than 1 dB, and the longest
|
||
consecutive run.
|
||
|
||
| | peak | RMS | ≥ full scale | > +1 dB | longest run |
|
||
|---|---|---|---|---|---|
|
||
| `ADV`, MISSION §6 matrix | **+4.26 dBFS** | −14.55 | **4 406** / 13 187 900 | 1 874 | 16 samples (0.333 ms) |
|
||
| `ADV`, exporter's matrix | −3.39 dBFS | −22.21 | 0 | 0 | — |
|
||
| `S00A`, MISSION §6 matrix | **−1.34 dBFS** | −18.73 | **0** | 0 | — |
|
||
| `S00A`, exporter's matrix | −8.99 dBFS | −26.39 | 0 | 0 | — |
|
||
|
||
**The claim survives, and the reasoning behind it does not.** The pinned matrix
|
||
genuinely overloads `ADV`: not one stray sample but 4 406 of them, 1 874 more
|
||
than a full dB over, wanting 4.26 dB more headroom than the container has. That
|
||
is a different animal from the 43 samples and 0.25 ms transient I withdrew a flag
|
||
over, and the number that separates them is the **magnitude**, not the count.
|
||
|
||
But the same table refutes the *scope* of the fix. **`S00A` never clips under the
|
||
pinned matrix** — it peaks at −1.34 dBFS. The exporter attenuates it by 7.65 dB
|
||
to solve a problem it does not have, because 0.4142 is derived from a theoretical
|
||
worst case (every channel correlated at full scale at once) that neither movie
|
||
comes near.
|
||
|
||
### Control, before believing any of it
|
||
|
||
The pinned matrix names `FLC`, `FRC`, `SL` and `SR`, and a 5.1 source has none of
|
||
them. ffmpeg neither errors nor warns — measured at `-loglevel warning`, the
|
||
output was empty. So the literal string was decoded alongside its three-term 5.1
|
||
reduction (`FL = 1.0·FL + 0.707·FC + 0.707·BL`) and the two outputs compared:
|
||
**bit-identical**, 52 751 600 bytes. The reduction is what runs, and it is the
|
||
matrix §6 intends. *That silence is itself the trap the mono fold above guards
|
||
against.*
|
||
|
||
### Not changed, and deliberately so
|
||
|
||
MISSION §6 is a **human decision of 2026-08-29**, and the level of a mix is
|
||
exactly the kind of thing §6 reserves — *"adjust it deliberately, as a commit"*.
|
||
Three options, and choosing between them is not mine:
|
||
|
||
1. **Keep the pin.** `ADV` clamps on 4 406 samples. Rejected on the measurement.
|
||
2. **Keep the exporter's 0.4142.** Preserves the two movies' relative loudness
|
||
exactly, costs 7.65 dB, and is safe by construction for any movie a modder
|
||
drops in.
|
||
3. **One measured constant, `1/1.6339 = 0.612`.** The smallest single scalar
|
||
under which no in-scope movie clamps: +3.39 dB over today, still one constant
|
||
so relative loudness is untouched. Tuned to two files, but the exporter's own
|
||
`check` refuses any export whose peak reaches 0 dBFS, so a third movie that
|
||
needed more headroom would fail loudly rather than clamp quietly.
|
||
|
||
Per-file normalisation is **not** on that list: it would put `ADV` 4.26 dB below
|
||
`S00A` and change how two cutscenes sit against each other and against the menu
|
||
bed, which is an aesthetic decision with nothing measured behind it.
|
||
|
||
What changes today is only that the deviation is **visible**: `video.rs` now
|
||
cites MISSION §6 by name and says it departs from it, and the export carries a
|
||
manifest warning with these numbers. Before this, a reader of the manifest could
|
||
not tell that a pinned human decision had been overridden at all — the command
|
||
line was recorded faithfully, and recording the command you ran does not disclose
|
||
that it is not the command you were given.
|
||
|
||
### The voice reaches the output, and a null control says so quantitatively
|
||
|
||
`+ voice ADV` in the log proves only that `play_voice` found a stream and called
|
||
`play()`. Whether the audio arrives at the Master bus is a different question,
|
||
and `docs/port/AUDIO-VERIFICATION.md` §2 exists because it is.
|
||
|
||
The control needed **no test-only code**: MODDING rule 4 already shadows any
|
||
exported asset by path, so 140 s of silence dropped at
|
||
`data/mods/audio/voice/ADV.ogg` mutes the dialogue and changes nothing else. Two
|
||
`--boot --skip-at=25 --audio=…` runs, then `astats` over the same 14 s of movie:
|
||
|
||
| | peak | RMS |
|
||
|---|---|---|
|
||
| `ADV.ogv`'s own audio (the bed) | −6.239 | −24.941 |
|
||
| the exported voice alone | −7.614 | −27.965 |
|
||
| **run with the voice muted** | **−6.251** | **−25.126** |
|
||
| **run with the voice playing** | **−5.415** | **−22.913** |
|
||
|
||
The muted run reproduces the bed to **0.01 dB peak / 0.19 dB RMS**, which is what
|
||
makes the other row worth reading. And the mixed run is not merely *louder*: two
|
||
incoherent sources at −24.941 and −27.965 dBFS predict a sum at **−23.184**, and
|
||
the run measures **−22.913** — **0.27 dB** out. The voice is in the mix, at the
|
||
level its own file says it should be.
|
||
|
||
⚠️ **Under the Dummy driver.** Per AUDIO-VERIFICATION, *"recorded under a dummy
|
||
driver"* is a weaker claim than *"heard"*, and no measurement here says the
|
||
recording is the **right** dialogue for this cutscene — only that the file the
|
||
exporter resolved is the one reaching the output at the expected level. The two
|
||
runs are also not sample-aligned (they differ by 1.7 s of wall clock), which is
|
||
why the `RMS trough` column is omitted: it moved by 40 dB between runs on window
|
||
placement alone, and peak and RMS are the two numbers that survive that.
|
||
|
||
### Ⓐ *does* skip the intro in this build, so the play-test's report is not this bug
|
||
|
||
`--skip-at=25` on a `--boot` run: `video skipped at 25.02 s`, `video ended at
|
||
25.02 s`, title at 25.02 s. The press goes through `Input.parse_input_event` and
|
||
arrives at `_unhandled_input` exactly as a pad's would, so **the wiring from press
|
||
to skip is live**. What that does not cover is a real key event from a focused
|
||
window, which is the difference between this run and the human's — and, separately,
|
||
**whether the game permits skipping an attract movie at all** is HANDOFF Q9 and
|
||
still 🟡. If the answer is no, this path is deleted rather than debugged.
|
||
|
||
|
||
## Refutation of my own two-stem reading — and it had already been adopted elsewhere
|
||
|
||
Two hours after writing that a voice region's equal-length chunks are *"HANDOFF
|
||
Q10's decoded two-stem shape"*, the Decoder asked me to decode the leading chunk
|
||
— it has no XMA1 decoder in its container — and the decoder run refuted the
|
||
claim I had made.
|
||
|
||
**Equal duration was a shape match, and I carried Q10's *music* census across to
|
||
voice on the strength of it.** The content does not support it:
|
||
|
||
| | |
|
||
|---|---|
|
||
| `S00A` chunk 2 | **digital silence** — 4 497 300 samples, peak −inf |
|
||
| `ADV` chunk 2 | **0.60 × chunk 1** (best-fit scalar), residual **26.8 dB** below the target |
|
||
|
||
About 95 % of `ADV`'s second chunk is a −4.4 dB copy of the first. Two chunks of
|
||
equal length, one silence and the other a scaled near-duplicate, are not two
|
||
stems of one performance. ⚠️ **The claim had already travelled** — it is quoted in
|
||
the Decoder's `voice-region-leading-chunk.md` — which is the failure PROTOCOL
|
||
names: a wrong belief moving faster than its correction, through two documents
|
||
that share a source.
|
||
|
||
### What it cost, and what changed
|
||
|
||
Summing chunk 1 with silence at `1/n` put `S00A`'s dialogue **6.02 dB down for
|
||
nothing**: the exported file peaked at −16.2 dBFS against a source chunk peaking
|
||
at −4.2. `export_voice` now drops a **digitally silent** chunk before the sum.
|
||
That is arithmetic, not a content judgement — a silent input contributes nothing
|
||
to a mix and counting it in the normalisation is simply my error.
|
||
|
||
**What `ADV`'s near-duplicate chunk 2 is remains open and it is still summed.**
|
||
Whether the game plays both is a decoding question; 26.8 dB of residual is not
|
||
nothing, and dropping a chunk because it correlates with another would be
|
||
answering it.
|
||
|
||
### The leading chunk, decoded — structure, and not one word about content
|
||
|
||
The Decoder's ask was *"cutscene dialogue or mission dialogue"*. `ADV` region
|
||
+ 1392, 394 packets: **84.553 s, stereo, 48 kHz, peak −2.48 dBFS, RMS −24.80**,
|
||
with **6 silent gaps over 0.4 s below −50 dB totalling 45.3 s** — 54 % silence,
|
||
the same duty cycle as the two full-length chunks (54 %, 55 %). So it is
|
||
**speech-structured audio**: not a header, not padding, not noise.
|
||
|
||
🔴 **Which is as far as a measurement goes.** *Cutscene or mission* is an
|
||
identification and this agent has no ears and no oracle. Envelope
|
||
cross-correlation against the full-length chunks peaks at 0.768 **at the last lag
|
||
in the search range**, which is where a statistic lands when it has found
|
||
nothing, and it is not evidence. The Decoder's 🟡 stands, and its own leading
|
||
hypothesis — an in-mission `VOICE_D_*` line — is untouched by any of this. The
|
||
byte-span test it already built settles it the moment those regions are
|
||
enumerated; nobody has to listen.
|
||
|
||
### Taken from the same message: `bank_header_len`, not `riffs.len()`
|
||
|
||
The Decoder's census warns that eight bank-header regions also yield three
|
||
chunks, so the chunk count cannot say which structure you are in. **This exporter
|
||
never used the count** — it selects on decoded duration, which is why it already
|
||
handles both cases: `RT01A`'s 10 300 B leading chunk decodes to 9 ms and falls
|
||
out on its own. But a duration tie is an *observation* and `bank_header_len` is
|
||
*decoded*, so the rule switches the day `c1f3608` reaches `main`.
|
||
`sylpheed-formats` is a path dependency and merging another agent's topic branch
|
||
is not the port's to do.
|
||
|
||
## The mono fold I warned about, in the comment directly above the code that did it
|
||
|
||
`export_voice`'s first version folded to mono by averaging every **declared**
|
||
channel, and the doc comment above it said, in as many words, that *"`pan`
|
||
silently ignores a channel the input does not have — so a stereo matrix applied
|
||
to a mono voice track is not an error, it is a −6 dB attenuation that nothing
|
||
reports."*
|
||
|
||
It then did exactly that. Per-channel `astats` on both voice streams:
|
||
|
||
| | channel 1 | channel 2 |
|
||
|---|---|---|
|
||
| `ADV` chunk 1 | peak +0.000 dBFS | **peak −inf** |
|
||
| `S00A` chunk 1 | peak −4.207 dBFS | **peak −inf** |
|
||
|
||
The voice is a **mono recording carried in a nominally stereo stream**, and
|
||
averaging it with silence cost **5.94 dB** — which is most of why `S00A`'s
|
||
exported dialogue sat at −16.2 dBFS against a source chunk peaking at −4.2 (the
|
||
other 6.02 dB was summing a silent *chunk*, corrected in the same iteration).
|
||
|
||
**Checking the declared channel count is not checking the content, and only the
|
||
content is the fold.** `live_channels` now measures which channels carry signal
|
||
and averages only those. `sylpheed-viewer`'s `pan=mono|c0=c0` reaches the right
|
||
answer here for a reason it does not state; this reaches it for a stated one, and
|
||
would still be right if a stream ever did carry two live channels.
|
||
|
||
Worth recording as a pattern rather than a bug: **three defects this iteration
|
||
were all the same shape** — a silent chunk in a sum, a silent channel in a fold,
|
||
and a `pan` matrix naming channels that do not exist. Each is an input that
|
||
contributes nothing being counted in a divisor, and none of them is visible in
|
||
anything but a level.
|
||
|
||
## The leading chunk is the TAIL of the full one — measured, and it is why the region over-covers
|
||
|
||
The Decoder settled by byte-span analysis that a voice region's leading chunk is
|
||
**the movie's own dialogue, 17 of 17** — killing its own standing hypothesis that
|
||
it was an in-mission `VOICE_D_*` line — and asked whether dropping it is
|
||
therefore a truncation. It has no XMA1 decoder; this container does.
|
||
|
||
Envelope cross-correlation, sliding with overhang allowed at both ends and
|
||
normalised over the overlap only. ⚠️ **This corrects an earlier number of mine**:
|
||
a first pass scored 0.768 and I called it nothing, correctly — that search only
|
||
tried lags where the shorter chunk fitted *wholly inside* the longer one, and it
|
||
peaked on the boundary of its own range.
|
||
|
||
| | best *r* | at lag | overlap |
|
||
|---|---|---|---|
|
||
| `ADV` chunk 0 → chunk 1 | **0.998** | **+52.8 s** | 84.5 s |
|
||
| `S00A` chunk 0 → chunk 1 | **0.932** | **+25.6 s** | 68.0 s |
|
||
| control — `ADV` chunk 0 against itself | 1.000 | 0.0 s | — |
|
||
| control — `ADV` chunk 0 against `S00A` chunk 1 | **0.289** | — | 28.2 s |
|
||
|
||
**Both lags put chunk 0 flush against the end of chunk 1**: 52.8 + 84.55 =
|
||
137.35 s against chunk 1's 137.324, and 25.6 + 68.07 = 93.67 against 93.694.
|
||
|
||
Confirmed in the sample domain — lag refined to ±1 sample on the loudest second,
|
||
then a scalar best-fit over the whole overlap: `ADV` +52.8000 s, gain 0.833,
|
||
residual **16.70 dB** below the target; `S00A` +25.6320 s, gain 0.365, residual
|
||
**23.15 dB**. 98–99.5 % of the energy is a scaled copy: the same material at a
|
||
different gain, not bit-identical, which is what a lossy decode at two gains
|
||
should look like.
|
||
|
||
**So dropping chunk 0 removes a duplicate, and is not a truncation** — the
|
||
exporter's existing behaviour is right for a better reason than the one it gave.
|
||
🟡 **The manifest note has NOT been rewritten to say so.** The structural claim —
|
||
that the region over-covers because it re-presents its own tail, and that this
|
||
accounts for the whole 2.6× — is the Decoder's to write down; this page reports
|
||
the measurement and says which is which. The note stays hedged until its page
|
||
carries the conclusion, and the hedge is true either way.
|
||
|
||
⚠️ **The 504 464 B constant was deliberately not converted.** The Decoder found
|
||
the region anchor sitting that far after the true predecessor trailer on all 17
|
||
and pointedly declined to call it missing dialogue. Converting it needs a
|
||
byte↔time mapping, and the numbers above are the reason there isn't one: chunk 1
|
||
is 1 118 268 B and chunk 2 is 1 171 516 B for **the same 137.324 s**, so bytes per
|
||
second is not constant even inside a single region. Any figure in seconds off
|
||
that constant would be invented.
|
||
|
||
## Third reading of a voice region, and this one is decoded: three presentations of one take
|
||
|
||
`export_voice` has now read the same bytes three ways in one session, and each
|
||
reading was ended by a measurement rather than by an argument:
|
||
|
||
1. **Concatenate the chunks** — 359 s of dialogue for a 137 s movie.
|
||
2. **Sum them as HANDOFF Q10's two stems** — refuted here: `S00A`'s second
|
||
full-length chunk is digital silence, `ADV`'s is 0.60 × the first with 26.8 dB
|
||
of residual.
|
||
3. **Keep one stream.** ✅ This one is decoded, and not by me.
|
||
|
||
The Decoder settled the shape disc-wide without a decoder, by counting stream
|
||
starts inside every inter-descriptor span: **258 spans hold one stream, 28 hold
|
||
three, and nothing holds two or any other number.** The 95 movie-voice regions
|
||
decompose 70 + 8 + 17, and the 8 are independently the same 8 its first census
|
||
flagged. So a region carries **three presentations of one take** — which is
|
||
exactly `359 = 84.55 + 137.32 + 137.32`, the first clipped by its crate's own
|
||
1.5 MB predecessor guard.
|
||
|
||
It also cross-checked my correlation by a route needing no decoder: if the
|
||
leading chunk is the tail of a full stream, the whole leading stream should be
|
||
one complete take, and `ADV`'s 504 464 + 808 304 = 1 312 768 B at chunk 0's byte
|
||
rate is **137.323 s against my measured 137.324**. Two instruments, no shared
|
||
assumption.
|
||
|
||
**So summing was wrong for a third reason:** a take plus a 0.60 × copy of itself
|
||
is ~4 dB louder and coloured, not a mix of parts. The exporter keeps one stream
|
||
and performs no arithmetic on it.
|
||
|
||
🟡 **Which stream is a recommendation, not a decoded field.** The selector is the
|
||
**highest byte rate** among the equal-duration survivors, on the Decoder's
|
||
advice. Nothing on the disc says which presentation the game plays, and on `ADV`
|
||
this picks the **quieter** of the two — −8.3 dBFS against 0.0. That is in the
|
||
manifest in those words so the choice is visible and reversible; it is the one
|
||
part of this that a capture could still overturn.
|
||
|
||
`check` moves `voice` off the strict peak bound as a consequence. It sat with
|
||
`bgm` because it was a sum this exporter produced; it is now a single wave off
|
||
the disc, mastered near full scale — `ADV`'s louder presentation measures
|
||
**+0.0003 dBFS at source** — and refusing that would be refusing the disc's own
|
||
mastering.
|
||
|
||
### The 504 464 B constant: I refused the conversion, and refusing was right
|
||
|
||
The Decoder asked whether I would spend a decode converting its anchor offset to
|
||
seconds, and I declined because bytes per second is not constant even inside one
|
||
region. It has since found the stronger reason and withdrawn the ask: **the
|
||
constant is structural, not proportional** — identical on all 17 regions despite
|
||
their differing durations. A proportional prediction lands within 8 bytes on
|
||
`ADV`, which is a coincidence, and is **4 305 B out on `S00A`**. A seconds figure
|
||
off that constant would have been invented, and it would have looked corroborated
|
||
on the first movie anybody checked.
|
||
|
||
❔ **Why the disc stores three presentations at all is unanswered**, by either of
|
||
us.
|
||
|
||
## The transcode cache had never hit, because the wipe ran first
|
||
|
||
`video::transcode` has carried a cache since P4. It writes a `.cmd` sidecar with
|
||
the exact ffmpeg command, the source's byte count and its channel count, and
|
||
skips the encode when all three still match. Its doc comment says why: *"without
|
||
it every re-export pays ~4 minutes to produce a byte-identical file, and an
|
||
exporter nobody re-runs is worse than a cache."*
|
||
|
||
**It had never hit once.** `main.rs` clears the output tree wholesale — and the
|
||
`remove_dir_all` runs immediately before the check, deleting the sidecar and the
|
||
output it stamps. The cache tested a file it had just erased.
|
||
|
||
This session ran the exporter **six times** and paid the full Theora encode every
|
||
one of them, producing five byte-identical files. Roughly 48 minutes. Nothing
|
||
reported it, and nothing could have: a cache is silent when it works and silent
|
||
when it does not, and the only symptom is a wall-clock cost that looks like the
|
||
job simply being slow.
|
||
|
||
⚠️ **It is worth being specific about how this hid**, because the ingredients are
|
||
ordinary. The cache is correct. The wipe is correct. Each carries a doc comment
|
||
explaining itself, and neither mentions the other. The defect exists only in
|
||
their ordering, which is stated in neither, and the cost is invisible in every
|
||
artefact the export produces — the tree is byte-identical either way.
|
||
|
||
### The fix keeps the wholesale guarantee rather than trading it away
|
||
|
||
The obvious repair — stop wiping — would break what the wipe is for: *a screen
|
||
that stops being exported stops existing, rather than lingering as a stale file
|
||
that still validates.* So the wipe now spares exactly `video/`, and
|
||
`prune_videos` deletes anything in it this run did not claim. Everything else is
|
||
still cleared outright.
|
||
|
||
That is a **cache, not a hand-edit**, and the distinction matters against
|
||
MISSION §3: nothing in `export/` is authored, the sidecar is derived from the
|
||
command the exporter itself computed, and any change to the command, the source
|
||
size or the channel count re-encodes. A modder who edits an `.ogv` by hand gets
|
||
it overwritten on the next export, exactly as before.
|
||
|
||
🟡 **Not measured yet:** that a cached run reproduces the same tree. The claim is
|
||
structural — the skip is keyed on the whole command string — but "the second run
|
||
produces the same bytes" is checkable and has not been checked.
|
||
|
||
## `settle_time()` — the answer arrived, and it refutes my own 🔴 more than it confirms it
|
||
|
||
The Decoder took the port's top ask and measured the boot on a cold profile with
|
||
no shader cache (`auto/no-disc-and-menu-captures` at `4bd4779`,
|
||
`docs/re/boot-settle-times-measured.md`). It confirms the *principle* I filed:
|
||
the title's `rest.t` is 251 units = **4.183 s** where its art is finished at about
|
||
2 s, so `rest.t` is not when a screen arrives.
|
||
|
||
**But my row said more than that**, and the extra part is wrong. It said
|
||
*"everything the boot sequencer paces off that landmark is therefore late"*, and
|
||
named `publisher_logo` and `developer_logos`. So I measured the port the way the
|
||
game was measured — **visible span, not arrival-to-arrival** — with `--film` at
|
||
4 fps and a per-frame greyscale mean:
|
||
|
||
| | port, visible span | game, three cold boots | |
|
||
|---|---|---|---|
|
||
| publisher wordmark | **4.25 s** | 4.297 / 4.604 / 4.370 | 0.05 s under the lowest |
|
||
| developer logos | **3.50 s** | 3.508 / 3.503 / 3.366 | **dead on** |
|
||
| black hold between | ≈0.25 s | 0.2 – 0.3 s | inside |
|
||
| title settled → plate | 2.000 s (declared 120 units) | 2.247 s | inside, at ~28 fps presentation |
|
||
|
||
**The splashes are not late. They match.** ⚠️ And the reason my earlier reading
|
||
said otherwise is worth keeping: I had compared the port's *transition
|
||
timestamps* — 4.68 s and 3.94 s, arrival to arrival — against the game's
|
||
*visible spans*. Those differ by the exit ramp plus the black hold, about 0.6 s,
|
||
which is the whole of the discrepancy I was about to chase. This corpus has been
|
||
bitten by exactly this before, in the plate delay: *"timed from where build 4
|
||
stops animating, not from where it first appears — measured the other way the two
|
||
runs differ by 0.48 s against 6 ms."*
|
||
|
||
So the port paces the boot correctly, and **`rest.t` is a wrong landmark whose
|
||
blast radius is much smaller than I claimed**: on the screens the sequencer
|
||
actually advances off, `rest.t` plus the 24-unit exit ramp lands where the game
|
||
lands. What it still affects is `_script_settled`, which waits longer than it
|
||
needs to before photographing — a slow test, not a wrong frame.
|
||
|
||
### `dwell_seconds` stays `null`, and the question is now closed rather than open
|
||
|
||
`authored/timing.json` says of it: *"If a capture ever times the real boot, this
|
||
is where that number goes."* A capture has now timed the real boot, and the
|
||
answer is that **nothing goes there** — the disc's own keyframe groups reproduce
|
||
the game's dwells to 0.05 s and 0.01 s. The field stays `null` for a measured
|
||
reason instead of an absence of one.
|
||
|
||
### Taken from the same page, and not taken
|
||
|
||
* ✅ **The 120-unit plate delay stands.** The Decoder ran a refutation of it that
|
||
failed instructively: its probe's `title_static` mark gave 3.203 s, which on a
|
||
cold boot looks like a real effect. It was the instrument — the mark fires
|
||
during the crossfade out of the attract movie, with the glyph count still 0.
|
||
Re-measured from content: 2.247 s. **The port changes nothing**, and the
|
||
declared 120 units is what it keeps.
|
||
* 🔴 **No Ⓐ→menu dwell is authored.** It measured 3.763 s and contains a 1.53 s
|
||
guest load stall — the third independent reproduction of that stall, this one
|
||
on a cold cache, so it is not a warm-cache artefact. It is emulator time, not a
|
||
game constant.
|
||
* 🟡 **Menu build-in 0.531 s and Ⓑ→title 0.482 s are not authored either**, and
|
||
that is the Decoder's own caveat rather than my caution: they rest on one run,
|
||
where the plate delay and the load stall are each cross-checked against
|
||
independent prior evidence. The port is within ~0.1 s of both with its existing
|
||
24-unit exit ramp, so authoring them would replace a disc-derived number with a
|
||
provisional measured one and gain nothing measurable.
|
||
|
||
## The voice presentation is now unambiguously the port's choice, and the recommendation behind it was withdrawn
|
||
|
||
The Decoder has withdrawn "highest byte rate": its sentence read *"the
|
||
highest-rate, highest-gain one is chunk 1"*, and those two criteria select
|
||
**different streams** — `ADV` chunk 1 is 1 118 268 B at 0.0 dBFS, chunk 2 is
|
||
1 171 516 B at −8.3. The rule named one and the parenthetical named the other. I
|
||
implemented the rule faithfully and got the quieter presentation.
|
||
|
||
What the file can still say is decoded and does not adjudicate it: the `fmt `
|
||
chunk is a 32-byte `XMAWAVEFORMAT` whose `+0x20` is a declared
|
||
`PsuedoBytesPerSec` — 8 142 and 8 530 on `ADV`'s two, matching the computed rates
|
||
to 0.02 % — but `wEncodeOptions` (`0x10d6`), channel count and channel mask are
|
||
**byte-identical across the presentations**. Nothing in the header ranks them.
|
||
|
||
⚠️ One more observable, measured here and not in that page: **the two
|
||
presentations differ in channel layout.** `ADV` chunk 1 is mono-in-stereo —
|
||
channel 2 digitally silent — while chunk 2 is **dual-mono**, both channels
|
||
identical at −8.318574. So they are not two encodes of one file differing only in
|
||
rate.
|
||
|
||
Also recorded, because it cost the Decoder time: **`sylpheed-cli audio info` is
|
||
not to be trusted on these.** Its "16 channels / 4310 Hz / 2-bit" is
|
||
`wBitsPerSample`, `wEncodeOptions` and the channel fields read at the wrong
|
||
offsets — its XMA1 reader is misaligned. That is a tool in this repository
|
||
reporting confident nonsense, and it is the second time a renderer or reader of
|
||
ours has been believed before it was checked.
|
||
|
||
## Refutation of my dual-mono inference — the measurement stands, the generalisation does not
|
||
|
||
I argued that `highest_rate` had no case because `ADV`'s higher-rate presentation
|
||
is **dual-mono** while its louder one is mono-in-stereo, so the extra bytes buy a
|
||
duplicated channel rather than fidelity. The Decoder tested that disc-wide, as a
|
||
refutation attempt, and **it fails**.
|
||
|
||
Over the 28 three-stream cues, the stream-3 / stream-2 size ratio runs:
|
||
|
||
| min | median | max | sd | within 15 % of 1.0 |
|
||
|---|---|---|---|---|
|
||
| 0.0778 | 1.2565 | 2.9163 | 0.5057 | **12 of 28** |
|
||
|
||
Declared rates scatter with them — `S06A` is 5 661 against 16 513 B/s. **A 37×
|
||
spread is not a duplicated channel.**
|
||
|
||
**The channel measurement itself stands**: `ADV` chunk 1 really is mono-in-stereo
|
||
and chunk 2 really is dual-mono at −8.318574, and that is this port's own decode,
|
||
which the Decoder could not re-run and did not dispute. What fails is the step
|
||
from *one asset* to *the format*.
|
||
|
||
### What this changes, and what it does not
|
||
|
||
Nothing in the export changes. `loudest` is a **per-asset content** rule — it
|
||
reads the peak of the actual streams in front of it — so a scattering structural
|
||
ratio cannot undermine it, and `ADV`'s dialogue at +0.3 dBFS instead of −8.7 is
|
||
plainly the better outcome either way.
|
||
|
||
What changes is the *reason*, in four places: `authored/audio.json`'s
|
||
`presentation_why`, the selector comment in `audio.rs`, `BLOCKED.md`'s row, and
|
||
this page. The honest statement is narrower and slightly less satisfying:
|
||
**`highest_rate` was never refuted — it was never argued for, and neither is
|
||
`loudest`.** Which is exactly why the entry is marked *chosen* rather than
|
||
*measured*, and why one capture deletes it.
|
||
|
||
⚠️ **This is the third claim of mine in two iterations that generalised a
|
||
single-asset observation** — after "the chunks are two stems" and "everything the
|
||
sequencer paces off `rest.t` is late". All three were true of the thing I looked
|
||
at. The pattern is not carelessness about the measurement; it is reaching for the
|
||
rule the measurement would imply if it held everywhere, and writing that down in
|
||
the same breath. The corpus catches it because someone else runs the census.
|
||
|
||
### Two things in that data that are not mine, recorded so they are not lost
|
||
|
||
* **`S12B`'s three streams are byte-size identical** (14 396 each).
|
||
* **`BIRD_224` is three-stream and is not a movie cue** — so the three-stream
|
||
shape is not exclusive to cutscenes, which narrows how it was described to this
|
||
port earlier. Neither affects `export_voice`, which only resolves movies.
|
||
|
||
## Two rows of the P1 baseline were comparing blank frames and reporting OK
|
||
|
||
`docs/port/BLOCKED.md` has carried a 🔴 since P3: *"the loading screen's fade quad
|
||
rests OPAQUE BLACK … it will bite whoever first draws a loading screen."* It had
|
||
already bitten, in the one place nobody looks — the regression harness.
|
||
|
||
`build_12` and `build_15` render as **pure black in both renderers**: mean 0,
|
||
max 0, on the Godot side and on `sylpheed-cli`'s. The difference between two
|
||
blank frames is zero, so `verify-screen` scored them `max 0 over3 0 OK` — the
|
||
strongest verdict it has. **Two of sixteen rows were comparing nothing against
|
||
nothing**, and the committed baseline reads as sixteen passes.
|
||
|
||
That is worse than a missing test. A missing test is visible in the count.
|
||
|
||
### The cause, isolated by a control rather than by reading
|
||
|
||
`build_00` and `build_01` are the *plain* loading variant — the same screen minus
|
||
three elements. They render: **mean 1.913, max 214.5**. `build_12`/`build_15` add
|
||
`pgloading_baseeff`, `pgloading_loop5` and **`pgloading_eff00`**, a 1280×720
|
||
primitive whose `rest` is `0xff000000` — opaque black — at `t=38`, inside its own
|
||
opening black hold (`0xff000000` at 38, `0xff000000` at 48, clear on the untimed
|
||
final). It carries `layer_source: "none"`, so paint order puts it **last**, over
|
||
everything.
|
||
|
||
Same screen, one element different, one renders and one does not. That is the
|
||
diagnosis, and it did not require an opinion about `rest`.
|
||
|
||
### The rule I was about to write, and the census that killed it
|
||
|
||
The obvious reading is that `rest.t = 38` is wrong because it precedes the
|
||
element's last timed keyframe at 48 — so "`rest.t` before the last timed
|
||
keyframe" would flag the pathology. **I ran the census before writing the rule,
|
||
and it does not survive: 152 of 212 elements in this export have `rest.t` earlier
|
||
than their last timed keyframe.** It is the norm.
|
||
|
||
What actually distinguishes this element is its *content*, and the reach of that
|
||
is one:
|
||
|
||
| screen | full-frame primitive | rest.t | last | rest fade |
|
||
|---|---|---|---|---|
|
||
| `build_12` / `build_15` | `pgloading_eff00` | 38 | 48 | **`0xff000000`** |
|
||
| `extras` / `extras_jp` | `pteff00` | 64 | 74 | `0x00000000` |
|
||
| `main_menu` / `main_menu_jp` | `pteff00` | 70 | 80 | `0x00000000` |
|
||
| `title` / `title_jp` | `pteff00` | 261 | 269 | `0x00000000` |
|
||
| `title` / `title_jp` | `pteff02` | 46 | 236 | `0x40000000` |
|
||
|
||
**`pgloading_eff00` is the only element in the whole export whose resting pose is
|
||
a fully opaque full-frame quad — 1 of 212.** Every other full-frame primitive
|
||
rests clear or at 25 %. One instance is not a rule about `rest`, and keying the
|
||
renderer on "an opaque full-frame quad at rest is probably wrong" would be a
|
||
content heuristic of exactly the kind this port refuses elsewhere.
|
||
|
||
### So nothing in the renderer changed, and the harness did
|
||
|
||
The screens stay black. Nothing draws a loading screen, and the honest position
|
||
is that either `rest` is mis-identified for this one element — a decoding
|
||
question, asked — or the screen really does begin fully black and `--pose=rest`
|
||
is simply the wrong thing to photograph it at.
|
||
|
||
What changed is that **a blank pair can no longer score.** `verify-screen` now
|
||
checks both frames for ink first and reports
|
||
`BLANK -- both renderers drew nothing; this row proves nothing`. It is not a
|
||
failure — the port may legitimately have nothing to draw — and `status` is
|
||
untouched, so an unrelated `DIFFERS` still fails the run. The corrected baseline:
|
||
|
||
```
|
||
build_00/01 max 3 OK press_start(_jp) max 1 OK
|
||
title max 6 DIFFERS main_menu(_jp) max 4 DIFFERS
|
||
extras(_jp) max 3 OK title_jp max 155 DIFFERS
|
||
publisher_logo(_r) max 1 OK developer_logos(_r) max 2 OK
|
||
build_12 / build_15 BLANK ← previously OK
|
||
```
|
||
|
||
Fourteen rows, not sixteen. No new drift among the fourteen.
|
||
|
||
## Refutation attempt — the loading-screen variants, and it survived
|
||
|
||
The Decoder's `ui-title-build-map.md` says entries 0/1 are the plain loading
|
||
variant at 7 elements and 12/15 the dressed one at 10, the three additions being
|
||
`pgloading_eff00`, `pgloading_loop5` and `pgloading_baseeff`. Checked against this
|
||
export, which addresses by pak entry:
|
||
|
||
| entry | elements |
|
||
|---|---|
|
||
| 0, 1 | 7 — `eff01 eff02 line loop1 loop3 loop4 str` |
|
||
| 12, 15 | 10 — the same seven **plus** `baseeff`, `eff00`, `loop5` |
|
||
|
||
**Exact, in both the count and the identity of the three.** The claim survives,
|
||
and it paid for itself immediately: the two variants differing by exactly the
|
||
black quad is what made `build_00` a control for `build_12` and turned "the
|
||
loading screen is black" into "this one element blacks it out".
|
||
|
||
## 🔴 The voice export is known incomplete — the game decodes all three streams at once
|
||
|
||
The Decoder booted with Canary's `--xma_param_probe=true` — the cvar whose own
|
||
comment exists to say which sub-wave a movie's `.slb` the game decodes — and the
|
||
answer is that **it does not pick one. It decodes all three, concurrently, in
|
||
three separate XMA contexts.**
|
||
|
||
| ctx | packets | byte_size | disc payload (RIFF − 60) |
|
||
|---|---|---|---|
|
||
| 0 | 632 | 1 294 336 | 1 294 396 |
|
||
| 1 | 546 | 1 118 208 | 1 118 268 |
|
||
| 2 | 572 | 1 171 456 | 1 171 516 |
|
||
|
||
Three-way, byte-exact. **So "three presentations of one take, pick one" is
|
||
refuted by the running game**, and the question I had been arguing about —
|
||
*which* presentation — has no answer, because its premise was wrong.
|
||
|
||
### This one was not caught by a census, and could not have been
|
||
|
||
The last three claims of mine that overreached were all killed by counting
|
||
something. This one survived every count available: the streams really are
|
||
equal-duration, one really is silence, one really is 0.60 × another with the
|
||
residual 26.8 dB down. Every measurement was right and the frame around them was
|
||
wrong, and **no amount of looking harder at the file would have moved it** — the
|
||
file says `ChannelMask = 0x0002` on all three. It took the running game.
|
||
|
||
That is the mission's own sentence arriving in practice: *the Port has no oracle
|
||
— if it needs to know what the game does, it asks.* I did ask, repeatedly, and
|
||
each time for the wrong thing: which stream, rather than whether the premise held.
|
||
|
||
### What changed, and what deliberately did not
|
||
|
||
**The behaviour is held.** Reverting to the `1/n` sum is not obviously less
|
||
wrong: an equal-gain sum of channel pairs is **not** a downmix — MISSION §6 makes
|
||
exactly that point when it pins an explicit matrix for the movies' 5.1 fold
|
||
rather than letting ffmpeg default — and the sum cost `S00A` 6.02 dB when one
|
||
stream was silence. Swapping one guess for another on the strength of a message
|
||
is what produced this entry twice already.
|
||
|
||
**What changed is that the wrongness is now loud.** ⚠️ *This failure sounds like
|
||
success*: a single stream decodes to clean, audible dialogue, so nothing a
|
||
listener hears reveals that two streams are missing. So it is stated in three
|
||
places a reader cannot miss — a top-level `manifest.json` warning per movie, the
|
||
console line (`1 of 3 streams [refuted] -- KNOWN INCOMPLETE`), and the entry's own `why` —
|
||
and 🟡 became 🔴 in `authored/audio.json` and `BLOCKED.md`.
|
||
|
||
🟡 **"They are 5.1" is the Decoder's hypothesis and is not established.** Three
|
||
concurrent stereo streams is six channels and N stereo streams is how XMA carries
|
||
multichannel on the 360, which would explain the differing byte rates, the
|
||
near-silent stream, and why cues are 1-stream or 3-stream and never 2. Against
|
||
it: all three declare `ChannelMask = 0x0002` identically, which is odd for
|
||
distinct channel roles. Nothing here builds on it.
|
||
|
||
**What settles it, and it is asked:** a recording of the game's own output over
|
||
`ADV`, through the PulseAudio null sink (`AUDIO-VERIFICATION` §3). Candidate
|
||
combinations of the three decoded streams can then be correlated against what the
|
||
game actually played — which turns the channel-role question from a decode into a
|
||
fit against an oracle. Twenty seconds over dialogue is enough.
|
||
|
||
### The measurements survive; only their meaning moved
|
||
|
||
`S00A`'s silent stream and `ADV`'s 0.60 × relationship are untouched and now read
|
||
as facts about **channels**: 0.60 × with the residual 26.8 dB down is what a
|
||
correlated channel pair at a lower level looks like, and a silent channel is an
|
||
unused one. Nothing measured here is retracted. What is retracted is every
|
||
sentence that called them *presentations*.
|
||
|
||
## 🔴 The oracle capture does not contain the intro — a controlled negative
|
||
|
||
The Decoder took the capture I asked for — `adv-game-output-6ch.wav`, 70.2 s,
|
||
6 ch, 48 kHz, shared as `1788018994-16f9d19d90b8`, taken at `68aa192` — described
|
||
as *"the FULL mix, the movie's own WMA track plus the three XMA streams"*.
|
||
|
||
**It contains none of them.** Envelope cross-correlation, sliding with overhang,
|
||
normalised over the overlap, minimum 30 s of overlap so a short window cannot win
|
||
on an edge:
|
||
|
||
| capture ch | against | best *r* | runner-up | margin | |
|
||
|---|---|---|---|---|---|
|
||
| c0 | `ADV` bed | 0.361 | 0.359 | **+0.003** | no match |
|
||
| c1 | `ADV` bed | 0.407 | 0.403 | +0.004 | no match |
|
||
| c0 | voice stream 1 / 2 / 3 | 0.42 / 0.26 / 0.27 | — | ≤ +0.006 | no match |
|
||
| c1 | voice stream 1 / 2 / 3 | 0.47 / 0.34 / 0.34 | — | ≤ +0.006 | no match |
|
||
| c0 | `BGM_103` (menu bed) | 0.271 | 0.260 | +0.011 | no match |
|
||
| c0 | `S00A` | 0.351 | 0.349 | +0.002 | no match |
|
||
|
||
**The margin is the number that matters**, not *r*. A match has a *peak*; these
|
||
have a *plateau* — best and second-best differ by 0.001–0.016 across every
|
||
pairing, which is what a statistic does when no alignment exists.
|
||
|
||
### Three controls, because a negative from an uncontrolled instrument is worthless
|
||
|
||
1. **The instrument finds matches on this data.** `bed` vs `bed` → r = 1.000,
|
||
margin **+0.115**. Voice stream 2 vs stream 3 → r = 1.000, margin **+0.300**.
|
||
2. **My reference really is the movie.** The `.ogv` transcode against the disc's
|
||
own `ADV.wmv` → r = 1.000, margin **+0.114**. So a failure to match is not my
|
||
transcode.
|
||
3. **Time drift is ruled out.** A stretched playback would break a long
|
||
correlation while still matching locally, with the best lag climbing
|
||
monotonically. Five-second windows of the capture slid over the whole bed give
|
||
best lags of **4.95, 15.30, 119.35, 50.75, 29.35, 83.95 s** — scattered across
|
||
the movie, not monotonic, and every margin ≤ 0.017.
|
||
|
||
### What I can say, and what I will not
|
||
|
||
**Said:** this capture cannot answer the channel-role question, and it is not the
|
||
intro's audio. **Not said:** what it *is*. It is 70 s of something, all six
|
||
channels carrying signal at RMS ≈ −27 dBFS, matching nothing this port exports.
|
||
Diagnosing it is the Decoder's side of the wall and I have handed it back rather
|
||
than guessing.
|
||
|
||
⚠️ **One measurement on the file that may help them.** Split as 5.1, **channels 3
|
||
and 6 are byte-identical** — same MD5, not merely the same peak and RMS to six
|
||
decimals. An exact duplicate pair inside a six-channel "surround" output is
|
||
consistent with the Decoder's own warning that the 6-channel frame is Xenia's
|
||
hardcoded `kFrameChannelsDefault`, not the guest's request. It weakens, further,
|
||
any reading of that file as evidence of a 5.1 game mix.
|
||
|
||
### The voice export stays exactly as it is
|
||
|
||
Still one stream of three, still marked 🔴 in the manifest, the console line and
|
||
`authored/audio.json`. **The capture changed nothing**, which is the correct
|
||
outcome for a measurement that failed: the question is open, and it was open
|
||
before. What would have been wrong is treating a 70 s recording as an oracle
|
||
because it was expensive to obtain.
|
||
|
||
|
||
### Resolved the same day — it was the capture path, and the duplicate pair was the thread
|
||
|
||
The Decoder found the cause and **withdrew the capture**: PulseAudio was
|
||
remapping between two mismatched channel maps, and a 6-channel remap **silently
|
||
drops and duplicates**. Its control needs no emulator and no disc — six channels,
|
||
six different tones, the same sink and the same `parec` invocation — and came
|
||
back `400 / 3200 / 200 / 800 / 800 / 200` for an input of
|
||
`400 / 800 / 200 / 1600 / 3200 / 6400`. **Two source channels were gone
|
||
entirely.** Setting the sink's `channel_map` to the guest's own returns all six.
|
||
|
||
So the negative was right and, more usefully, **the byte-identical pair I
|
||
reported was the thread that unravelled it.** That is worth recording precisely,
|
||
because it was nearly not reported at all: it began as an idle check of two
|
||
channels whose peak *and* RMS matched to six decimals, and the only reason it
|
||
became evidence is that a coincidence at six decimals is cheaper to hash than to
|
||
explain.
|
||
|
||
**Withdrawn with the file**, both the Decoder's: *"all six channels carry
|
||
signal"*, and the non-zero-surround observation offered as weak support for a 5.1
|
||
guest mix. Unaffected: the three-XMA-context concurrency result, which is read
|
||
from the emulator's own log rather than the audio path, on two independent boots.
|
||
|
||
### What the port took from it: `tools/port/check-capture`
|
||
|
||
A capture now has to pass a provenance check before anyone analyses it, and it is
|
||
one command. It splits the file, hashes every channel and fails on any duplicate
|
||
pair. Documented in `docs/port/AUDIO-VERIFICATION.md` §5.
|
||
|
||
**Run through its own controls, both directions**, because a checker nobody
|
||
controlled is the thing this whole incident is about:
|
||
|
||
* six distinct tones → **PASS**;
|
||
* the remap's own output pattern → **FAIL**, naming all four duplicate pairs;
|
||
* the corrupt game capture → **FAIL** on `ch2 == ch5`.
|
||
|
||
⚠️ **The known-bad control is the part worth reading.** All six of its channels
|
||
report a peak of **−18.063656 dB — identical to six decimals — while containing
|
||
three duplicate pairs.** A level check cannot see this failure at all. That is
|
||
why the tool hashes rather than measures, and it is why the corrupt capture's
|
||
"plausible per-channel levels" were never evidence of anything.
|
||
|
||
The tool says so itself: it is **necessary, not sufficient.** Passing means no
|
||
channel was duplicated; it says nothing about whether the right thing was
|
||
recorded. A capture should survive both that and §1's correlation against a known
|
||
source before anything is concluded from it — and the one that was analysed here
|
||
would have failed the cheap check in thirty seconds.
|
||
|
||
The corrupt file is withdrawn from the exchange (`share drop`), so the next agent
|
||
cannot pick it up and repeat the work.
|
||
|
||
## 🔴 Take 2 is clean, my instrument was not, and the negative had to be re-earned
|
||
|
||
The Decoder's second capture passes `check-capture` — I re-ran it myself rather
|
||
than cite theirs — carries a screen log, and was recorded with the sink's
|
||
`channel_map` set equal to Canary's own. It is a good file.
|
||
|
||
⚠️ **One provenance discrepancy, minor but worth stating:** the message gives
|
||
253.3 s; the file is **318.539 s**. The screen log runs to 316 s and is
|
||
consistent with the file, so this is a mis-stated number rather than a bad
|
||
capture — but a length quoted in a provenance claim should match the artefact.
|
||
|
||
### The retraction that came out of measuring it
|
||
|
||
Take 2 also showed no alignment with the bed or the voice streams. Before
|
||
reporting a second negative I asked whether the method could do the job at all,
|
||
by building a **synthetic mix** — the bed plus the three voice streams — and
|
||
hunting the bed inside it.
|
||
|
||
**It failed. r = 0.415, against the `r > 0.8` bar my earlier negatives were
|
||
judged against.**
|
||
|
||
So the instrument that produced *"the capture contains no ADV audio"* could not
|
||
have found ADV audio in a mix even when it was certainly there. That conclusion
|
||
was right — the Decoder's tone control proved take 1 corrupt independently — but
|
||
**it was right by luck, and I reported it as measurement.** The three controls I
|
||
was pleased with tested the wrong things: that the method finds a *clean* signal
|
||
in a *clean* reference, which was never the task.
|
||
|
||
### The rebuilt instrument, calibrated in both directions
|
||
|
||
Band-limit so the target dominates, then judge on **lag and margin**, not on
|
||
absolute *r* — the `r > 0.8` bar is correct clean-against-clean and meaningless
|
||
for a component in a mix.
|
||
|
||
| hunting | band | against | *r* | lag | margin |
|
||
|---|---|---|---|---|---|
|
||
| the bed | 40–180 Hz | mix containing it | 0.663 | **0.0 s** ✓ | **+0.111** |
|
||
| the bed | 40–180 Hz | voice-only mix | 0.262 | wrong ✗ | +0.005 |
|
||
| voice stream 2 | 300–3000 Hz | mix containing it | 0.810 | **0.0 s** ✓ | **+0.248** |
|
||
| voice stream 2 | 300–3000 Hz | the bed alone | 0.358 | wrong ✗ | +0.005 |
|
||
|
||
A 20–50× separation in the discriminating statistic. Documented as
|
||
`AUDIO-VERIFICATION.md` §6.
|
||
|
||
### And now the negative, supported
|
||
|
||
Every one of take 2's six channels, against both targets, sits in the
|
||
**known-absent** regime:
|
||
|
||
| | bed (40–180 Hz) | voice stream 2 (300–3000 Hz) |
|
||
|---|---|---|
|
||
| margins | +0.000 … +0.014 | +0.001 … +0.017 |
|
||
| lags | −58 … +255 s, scattered | −72 … +183 s, scattered |
|
||
|
||
**Take 2 contains neither the movie's WMA bed nor the cutscene voice**, on an
|
||
instrument that demonstrably finds both when they are present.
|
||
|
||
### What that leaves, and it is not mine to answer
|
||
|
||
Two captures, differently configured, the second provably free of the channel-map
|
||
fault, with a screen log saying the movie was on screen — and **neither carries
|
||
either audio source.** That points away from a one-off setup error. The
|
||
possibilities I can see are a capture path that still loses the guest's mix, or
|
||
the guest not emitting these sources at all during the movie, and **only one side
|
||
of that wall can tell them apart.** Handed back with the numbers.
|
||
|
||
⚠️ **If it is the second, it reaches the port directly**: the export's movie audio
|
||
comes from the `.wmv`'s WMA track, and if the game never plays that track, then
|
||
`ADV.ogv`'s audio is wrong in a way no amount of transcode fidelity would fix. I
|
||
am not asserting that — it is a question about what the game does — but it is the
|
||
reason this is worth another boot rather than being written off.
|
||
|
||
## Every music bank was summed at 1/3 when only two sub-waves are music — 3.52 dB, since P6
|
||
|
||
The Decoder's message about `BGM_102` came with declared durations from the
|
||
corrected XMA1 `PsuedoBytesPerSec`, and checking my export against them turned up
|
||
a defect of mine that had been shipping since P6.
|
||
|
||
`export_bgm` summed every sub-wave `media` returned and scaled by `1/n`. Decoded
|
||
and timed, the three banks are identical in shape:
|
||
|
||
| bank | sub-wave 0 | sub-wave 1 | sub-wave 2 |
|
||
|---|---|---|---|
|
||
| `BGM_103` | **10 300 B → 0.009 s, peak −inf** | 3 876 924 B → 87.744 s | 3 930 172 B → 87.744 s |
|
||
| `BGM_102` | **10 300 B → 0.009 s, peak −inf** | 1 151 036 B → 37.482 s | 1 269 820 B → 37.482 s |
|
||
| `BGM_001` | **10 300 B → 0.009 s, peak −inf** | 4 466 748 B → 173.809 s | 4 673 596 B → 173.809 s |
|
||
|
||
**Sub-wave 0 is digitally silent in all three**, and 10 300 B is 10 240 + a
|
||
60-byte RIFF wrapper — 10 240 B being exactly what the Decoder's disc-wide census
|
||
identifies as the bank header. So it is not a stem. Counting it in the divisor
|
||
put every real stem at 1/3 instead of 1/2: **3.52 dB of attenuation on all the
|
||
menu music this port has shipped since P6.**
|
||
|
||
Dropping it is **arithmetic, not a decoding decision** — a silent input
|
||
contributes nothing to a sum, and this is the same rule `export_voice` already
|
||
applies. Measured after the fix: `main_menu.ogg` goes **−7.69 → −4.20 dBFS**,
|
||
**+3.49 dB** against 3.52 predicted, the remainder being Vorbis.
|
||
|
||
⚠️ **This is the third instance of one defect in this pipeline** — a silent chunk
|
||
in the voice sum, a silent channel in the mono fold, and now a silent sub-wave in
|
||
the music sum. Each was invisible in every check except a level, and each time
|
||
the divisor was computed from *how many inputs there are* rather than *how many
|
||
carry signal*. That is the shape to look for, not the individual bug.
|
||
|
||
### It also closes a 🔴 that has been open since P6
|
||
|
||
`docs/port/BLOCKED.md` carried *"`media::sound_bank_riffs` returns three
|
||
sub-waves where HANDOFF Q10's census says two"* as a disagreement the port shipped
|
||
deliberately. The census was right; the third was never a stem. The export now
|
||
reports **2 sub-waves** and the warning is gone — closed by measurement on my
|
||
side, corroborating the Decoder's `c1f3608` from a different direction (decoding
|
||
it, rather than counting headers).
|
||
|
||
### The declared-rate method, cross-checked a third time — and one correction
|
||
|
||
Their declared lengths against my decodes: `BGM_103` 87.750/87.749 vs **87.744**;
|
||
`BGM_102` 37.487 vs **37.482**; `BGM_001` 173.821 vs **173.809**. Agreement to
|
||
**5–12 ms** on three banks. The method is good for lengths.
|
||
|
||
🟢 **Refutation attempt, and the conclusion survives while the reasoning does
|
||
not.** The Decoder wrote that `BGM_001` reads *"173.821 s declared against your
|
||
decoded 167.663 s — a gap of 6.158 s"*, explaining it as *"declared is the
|
||
encoded stream, decoded is where the audio stops."* **A full decode of
|
||
`BGM_001` yields 173.809 s of PCM, not 167.663 s.** The 167.663 figure is where
|
||
the music *fades out*, measured from the audio; the stream then continues, silent,
|
||
to its declared end. So declared and decoded agree to 12 ms and the trailing
|
||
silence is *inside* the decode, not the difference between two methods. The
|
||
cross-check stands — better than stated, since it is now three banks rather than
|
||
a coincidence — and the sentence explaining it should go.
|
||
|
||
## Take 2 was starved, my correlator was fine, and `check-capture` was incomplete
|
||
|
||
The Decoder diagnosed take 2: a **starved** capture. Verified here independently
|
||
rather than taken on trust — 35.6 % of frames silent on all six channels, 10 482
|
||
alternating runs, median burst 13.5 ms and gap 3.9 ms, a 17.4 ms period at 57 Hz.
|
||
Their untruncated original reads 39.3 % and 10 595 runs; the difference is
|
||
exactly the truncation, and every other number agrees.
|
||
|
||
**So my rebuilt correlator was working correctly on a file that could not carry
|
||
the signal.** ✅ And the alarming reading it produced — *"the game may not play
|
||
the `.wmv`'s WMA track, so `ADV.ogv`'s audio has been wrong since P4"* — **is not
|
||
supported by this capture, and is not refuted either.** It is withdrawn as a
|
||
concern arising from evidence, and nothing is changed on account of it in either
|
||
direction. That matters more than it looks: it was the most expensive-to-act-on
|
||
hypothesis in the port, and it came from a file that could not speak to it.
|
||
|
||
### The real deliverable: my own checker passed the starved file
|
||
|
||
`check-capture` tested only for duplicated channels, so it cleared a recording
|
||
that was 36 % holes. A provenance check that passes the artefact it was built in
|
||
response to is not a check.
|
||
|
||
It now measures starvation too. ⚠️ **Two thresholds I invented were both wrong,
|
||
and the controls caught both** — which is the part worth recording:
|
||
|
||
1. **Counting exact-zero frames.** Real audio crosses zero constantly; a clean
|
||
voice track scored 5 947 "gaps" of median 0.0 ms and was called starved. **A
|
||
gap is a run, not a sample.** Only runs ≥ 1 ms count.
|
||
2. **Gap count and median length.** A genuine music bed shows **454 gaps at a
|
||
median of 1.4 ms** — quiet 16-bit passages really are zero for milliseconds —
|
||
so neither statistic separates it from a starved file.
|
||
|
||
What separates them is the **rate**:
|
||
|
||
| | gaps/s | median gap | all-channel silence |
|
||
|---|---|---|---|
|
||
| the starved capture | **32.9** | 3.9 ms | 35.6 % |
|
||
| a real music+SFX bed | **3.3** | 1.4 ms | 1.1 % |
|
||
| a voice track, 53 % pauses | **0.03** | — | — |
|
||
|
||
Bar set at **20 gaps/s** — 1.6× below the bad case, 6× above the worst good one,
|
||
**derived from the controls rather than chosen and then justified.** Controlled
|
||
in both directions: real stereo bed PASS, six distinct tones PASS, starved
|
||
capture FAIL. It also now reports a `data` chunk declaring 0 bytes, which is what
|
||
a file copied while still being written looks like.
|
||
|
||
### 🔴 The voice channel roles are not obtainable this session
|
||
|
||
Both capture routes are closed and the Decoder has said so plainly. The monitor
|
||
sink is starved **by construction** — it advances at wall-clock rate and
|
||
substitutes silence, so every moment the emulator runs slow is a hole, and
|
||
deleting the holes warps the timebase rather than repairing it. The route that
|
||
works is an internal tap at `SDLAudioDriver::SubmitFrame`, and that needs a
|
||
Canary rebuild whose cost they have measured: the build root `build-canary`
|
||
targets does not exist in that container, the warm tree is configured against the
|
||
same missing path, so it is a full reconfigure and compile on a box with ~700 MB
|
||
free and a history of parallel builds OOM-killing the host.
|
||
|
||
**A whole session for one probe.** That is a human's call and neither agent
|
||
should start it mid-loop. Until then the port keeps authoring with the known
|
||
recorded: one stream of three, 🔴 in the manifest, the console line and
|
||
`authored/audio.json`.
|
||
|
||
## The settle run carries an unmeasured real-time factor — and the numbers it touches were already unauthored
|
||
|
||
The Decoder has withdrawn one of the two arguments propping up its settle-time
|
||
run. It had claimed the plate *pulse period* was an internal clock proving the
|
||
run was not slowed; re-examined, that estimate rests on **one interval at a
|
||
125 ms sample interval (±6.7 %)**, and re-running the trough-picking gives
|
||
**2.628 s** rather than the 2.369 quoted — an adjacent local minimum had been
|
||
counted as a separate trough. Against the corpus's 2.24 s that is **+17.3 %**. It
|
||
is too weak to show anything and **cannot resolve a real-time factor below
|
||
~7 %**.
|
||
|
||
**✅ Nothing in the port moves, and this is the second time in two iterations that
|
||
the right call was to have authored nothing.**
|
||
|
||
| number from that run | anchored by | did the port take it? |
|
||
|---|---|---|
|
||
| title → plate, 2.247 s | three prior readings (2.13 / 2.132 / 2.138) and the disc's declared 120 units | ✅ it is what the port already draws |
|
||
| menu build-in, 0.531 s | **nothing** | ❌ not authored |
|
||
| Ⓑ → title, 0.482 s | **nothing** | ❌ not authored |
|
||
| Ⓐ → menu, 3.763 s | contains a 1.53 s load stall | ❌ explicitly refused |
|
||
|
||
Checked rather than remembered: `grep` over `authored/` and `port/scripts/` finds
|
||
no `0.531` and no `0.482`. The only build-in reference in the tree is the plate
|
||
arithmetic — `t=118 → t=238`, 120 units — which is the **anchored** leg.
|
||
|
||
I declined those two because they were one-run figures the Decoder had itself
|
||
flagged, and because the port was already within ~0.1 s of both from the disc's
|
||
own keyframes. **That reasoning has now been joined by a second, independent one
|
||
I did not have at the time**: a few per cent of slowdown sits inside them
|
||
undetected. A provisional measurement adopted over a decoded number would have
|
||
imported an error nobody could see.
|
||
|
||
## `verify-dwell` — the comparison that refuted my own 🔴, made repeatable
|
||
|
||
Last iteration I measured the port's visible spans against the oracle's dwells by
|
||
hand, and it refuted a red flag I had filed myself: `rest.t` *is* the wrong settle
|
||
landmark, but *"everything the sequencer paces off it is therefore late"* was
|
||
false, and I nearly went and re-paced screens that already matched.
|
||
|
||
That check existed once, in a transcript. It is now `tools/port/verify-dwell`.
|
||
|
||
```
|
||
screen port oracle (3 cold boots) verdict
|
||
publisher wordmark 4.25 s 4.297 / 4.604 / 4.370 agrees
|
||
developer logos 3.75 s 3.508 / 3.503 / 3.366 agrees
|
||
```
|
||
|
||
⚠️ **The trap it exists to prevent is in its header, because it is the whole
|
||
point:** a port's *transition timestamps* and the oracle's *visible spans* are
|
||
not the same quantity — they differ by the exit ramp plus the black hold, about
|
||
0.6 s, which was the entire discrepancy I was about to chase. This corpus has
|
||
been bitten by the identical confusion before, on the plate delay, where the two
|
||
readings differ by 0.48 s against 6 ms.
|
||
|
||
**The bar is the oracle's own run-to-run spread**, plus one film interval. Three
|
||
cold boots of the real game differ by 0.3 s; agreeing more tightly than the
|
||
oracle agrees with itself would not mean anything, and a tighter bar would be a
|
||
number chosen to look impressive. The developer-logo span read 3.50 s on the
|
||
hand-run and 3.75 s here — one film interval apart, both inside the bar, which is
|
||
the tool reporting its own resolution honestly rather than hiding it.
|
||
|
||
**The oracle's numbers are quoted in the script as a test fixture and labelled as
|
||
the oracle's**, with the RE document they come from. Nothing in the port derives
|
||
them and nothing may.
|
||
|
||
## The `PRESS Ⓐ` plate pulses — authored per element, because the census forbids a rule
|
||
|
||
The human listed pulsation as first-class and the port drew nothing at all: the
|
||
plate's focus record `ptbtn00f` was never reached, because `press_start` has no
|
||
`buttons` and nothing is focused.
|
||
|
||
**That it loops is measured**, not assumed. The corpus timed the pulse four times
|
||
— 2.12 / 2.19 / 2.34 / 2.31 s — and you cannot measure a period unless the thing
|
||
repeats.
|
||
|
||
### The rule I was going to write, and the census that forbade it
|
||
|
||
The spinning ring is a **rule** in the renderer (`spin_period_units`) and it
|
||
earns that: 16 of 212 elements match its shape and **all 16 are focus rings**,
|
||
zero false positives. So I looked for the analogous shape for a pulse — a group
|
||
whose keyframes vary **only** in alpha, whose first and last alpha are equal, a
|
||
closed cycle. `ptbtn00f` fits it exactly: `0 → 6 → 74 → 80 → 80 → 74 → 6 → 0`.
|
||
|
||
**Censused before writing it: 82 of 212 elements match.** `ptcopyright`,
|
||
`palogo_sqex`, `ptmsg`, `ptlogo_back2`, and every `_eff` fade-in-hold-fade-out on
|
||
every screen. **A renderer rule on that shape would make the copyright notice
|
||
pulse.**
|
||
|
||
Narrowing to focus records leaves exactly **one** distinct element (plus its JP
|
||
twin). A rule justified by n = 1 is a special case wearing a rule's clothes. So
|
||
the pulse is a **lookup** in `authored/timing.json`, keyed `<screen>/<element>`,
|
||
and the census is recorded beside it so nobody widens it later.
|
||
|
||
### The period is the element's own group — and the alternative is stated
|
||
|
||
**129 units**: its last timed keyframe is t=105, and the final untimed keyframe
|
||
is reached `exit_ramp_units` (24) later. **No new constant** — 24 is the same
|
||
authored value every other element's exit already uses. That is 2.150 s at
|
||
60 units/s, or 2.295 s at the ~28.1 fps the emulator presents, against
|
||
measurements of 2.12–2.34 s. It sits inside the spread at either rate.
|
||
|
||
⚠️ **It is a choice, and the alternative cannot be ruled out**: the cycle could
|
||
restart at the group's first keyframe (t=6) rather than at 0, giving 123 units =
|
||
2.050 / 2.189 s — **also inside the measured spread**. Nothing available
|
||
separates them. t=0 is taken because it is where every other group in this port
|
||
starts, which is consistency and not evidence, and `authored/timing.json` says so.
|
||
|
||
A fifth reading is recorded and **not averaged in**: the Decoder re-picked its
|
||
troughs and got 2.628 s having previously reported 2.369 from the same run, then
|
||
withdrew the estimate as too weak to resolve better than ~7 %.
|
||
|
||
### Verified the way the ring was — bit-identity one period apart
|
||
|
||
20 authored periods is 2.15 × 20 = **43.00 s = exactly 172 film frames**, so
|
||
frames N and N+172 must be the same frame:
|
||
|
||
| | max difference |
|
||
|---|---|
|
||
| f_055 vs f_227 (43.00 s) | **0/255** |
|
||
| f_060 vs f_232 (43.00 s) | **1/255** |
|
||
| f_070 vs f_242 (43.00 s) | **1/255** |
|
||
| f_079 vs f_251 (43.00 s) | **0/255** |
|
||
| **control** — f_070 vs f_243 (43.25 s) | **58.7/255** |
|
||
|
||
The control is what makes the rest mean anything: a quarter-second off the period
|
||
differs by 58.7, on the period by 0–1. Measured on the held boot title, where the
|
||
glow-box mean swings **26.0 ↔ 37.7** — a real pulse, not a static glow.
|
||
|
||
## ✅ The oracle finally speaks: the exported voice IS the game's centre channel
|
||
|
||
The Decoder's fourth capture is the first faithful one — `--gpu=null` takes the
|
||
guest from 0.70× to 0.96× real time so Xenia stops padding, an ALSA `file` tee in
|
||
front of a paced slave removes PulseAudio's wall clock entirely. **59.7 s, 0.35 %
|
||
silence, one gap in the whole file, six distinct channel hashes.** Checked here
|
||
independently: it passes `check-capture`, and its header sizes verify exactly
|
||
(RIFF 34 369 572 / data 34 369 536 against 34 369 580 actual).
|
||
|
||
The correlator was already calibrated, so the fit was a re-run rather than a
|
||
rebuild. **Controls first, on this instrument: known-present margin +0.248,
|
||
known-absent +0.005.**
|
||
|
||
### Speech band, 300–3000 Hz
|
||
|
||
| | FL | FR | **FC** | LFE | RL | RR |
|
||
|---|---|---|---|---|---|---|
|
||
| stream 1 (leading) | +0.013 | +0.006 | +0.012 | +0.009 | +0.012 | +0.005 |
|
||
| **stream 2 — the one exported** | +0.238 | +0.171 | **+0.305** | +0.011 | +0.035 | +0.006 |
|
||
| stream 3 | +0.240 | +0.173 | **+0.307** | +0.009 | +0.054 | +0.006 |
|
||
|
||
**`r = 0.989` on FC, margin +0.305 — above the known-present control.** And the
|
||
bed, in the low band, is the mirror image: FL 0.763 / FR 0.838 / RL 0.805 / RR
|
||
0.817, all agreeing on the same lag, and **FC 0.317**.
|
||
|
||
### What is established, and what is not
|
||
|
||
✅ **The capture contains `ADV`'s audio**, at lag ≈ +6.6–6.7 s — agreed by two
|
||
independent bands and by six channels.
|
||
|
||
✅ **The dialogue is in the centre channel and the bed is in the four corners.**
|
||
FC carries the voice and not the bed; FL/FR/RL/RR carry the bed and not the
|
||
voice. That is a textbook film mix, and it is measured rather than inferred from
|
||
a header — which matters, because the header says `ChannelMask = 0x0002` on all
|
||
three streams and would never have told us.
|
||
|
||
✅ **The port's exported voice file is the material the game plays in FC.** The
|
||
`loudest` choice in `authored/audio.json` — which I recorded as an unjustified
|
||
choice — selects the dialogue. A mono voice file played into the mix is the
|
||
right approximation of a centre channel.
|
||
|
||
⚠️ **Streams 2 and 3 are indistinguishable to this instrument**, and that is
|
||
expected: I measured months-of-iterations ago that stream 3 is 0.60 × stream 2
|
||
with the residual 26.8 dB down. They are the same take at two levels, so they
|
||
correlate identically with everything. **This does not say `loudest` picked the
|
||
*right* one of the two — only that whichever it picked is the dialogue.**
|
||
|
||
⚠️ **Stream 1 is not detectable in this window**, margins +0.005…+0.013. Consistent
|
||
with it being the tail of stream 2 (measured earlier at r = 0.998) and with a
|
||
59.7 s window that starts before the tail.
|
||
|
||
🔴 **The `1 of 3 streams [refuted]` warning stays.** Nothing here explains what the other
|
||
two contribute to the game's output, and the export still ships one. What changed
|
||
is its character: it is no longer *"one of three, contents unknown"* but *"the
|
||
centre-channel dialogue, plus two streams whose relationship to it is measured
|
||
and whose role is not."*
|
||
|
||
⚠️ **And the reach: 59.7 s of a 137 s movie**, one run, at 0.96× real time. The
|
||
`--gpu=null` route costs video, so this capture has no screen provenance — its
|
||
provenance is the XMA probe showing `ADV`'s three streams decoding during the
|
||
run, which for an audio question evidences the thing recorded rather than what
|
||
was on screen.
|
||
|
||
## The stripping control passes — `S00A` is obtainable, and the gate is cleared
|
||
|
||
The Decoder made this the gate on `S00A`, and it is the right call: `ADV` plays
|
||
itself on boot so it can be captured with `--gpu=null` at 0.96× real time, but
|
||
`S00A` starts ~4.5 s after Ⓐ on a save slot, which needs a **driven** run, which
|
||
needs screens, which rules out `--gpu=null`. So `S00A` is necessarily the 0.70×
|
||
rendered route with ~10 % additive padding — and is only worth a boot if
|
||
stripping that padding is exact.
|
||
|
||
**It is.** A real music+SFX bed (137.37 s, carrying 454 genuine zero runs of its
|
||
own) had 1 149 holes inserted at 8.37/s to +9.9 % length, matching the observed
|
||
ALSA profile, then was stripped and correlated in the low band:
|
||
|
||
| | *r* | lag | margin |
|
||
|---|---|---|---|
|
||
| original vs itself — **ceiling** | 1.000 | 0.0 s | +0.141 |
|
||
| **padded** vs original — what padding costs | **0.436** | −12.2 s | **+0.006** |
|
||
| **stripped** vs original — recovered | **1.000** | **0.0 s** | **+0.142** |
|
||
| stripped vs original-also-stripped | 1.000 | 0.0 s | +0.143 |
|
||
|
||
**Two things worth reading off that table.**
|
||
|
||
First, **padding at that profile destroys correlation completely** — r 0.436,
|
||
margin +0.006, which is the known-absent regime. That independently confirms, on
|
||
a file whose contents I control, that the earlier captures were unusable for the
|
||
reason claimed rather than for some other reason.
|
||
|
||
Second, **recovery does not require stripping both sides.** The stripped capture
|
||
matches the *unstripped* source at the ceiling. That matters operationally: the
|
||
port's reference assets never need touching.
|
||
|
||
⚠️ **What the control does not license.** Stripping removes genuine silence too
|
||
and cannot tell the two apart. On this material the genuine runs total 0.71 s in
|
||
137 s and cost nothing measurable; on material that is mostly silence they would.
|
||
And the whole thing rests on the **substituted-versus-additive** distinction — it
|
||
is valid for Xenia's ALSA padding, which inserts, and it is vandalism on a
|
||
PulseAudio monitor capture, which substitutes. `tools/port/strip-padding` says so
|
||
in its header before it says anything else, because running it on the wrong
|
||
artefact would look like it worked.
|
||
|
||
Its output is **byte-identical** to the control's own stripping, so the tool and
|
||
the experiment are the same operation rather than two implementations that agree.
|
||
|
||
## The correctness harness the docs promised for eight milestones did not exist
|
||
|
||
`tools/port/verify-screen`, line 20, since P1: *"Use `tools/port/verify-capture` for
|
||
the correctness question."* **There was no such file.** The port has had a harness
|
||
comparing itself to `sylpheed-cli` — two renderers sharing its assumptions — and
|
||
none comparing it to the game, while its own documentation said otherwise.
|
||
|
||
`docs/re/captures/ORACLE-CAPTURES.md` is blunt about why that matters: two
|
||
renderers agreeing proves nothing, and this corpus has been bitten three times —
|
||
the dropped `pteff05` background, the scale-0 rect, `rest()` — each invisible to a
|
||
render-vs-render diff and obvious against a capture.
|
||
|
||
`tools/port/verify-capture` now exists. **Five screens, against framebuffer
|
||
captures of the real game:**
|
||
|
||
| screen | RMSE | differing | note |
|
||
|---|---|---|---|
|
||
| `main_menu` | 14.79 | **0.25 %** | focus state may differ |
|
||
| `extras` | 15.29 | 0.46 % | focus state may differ |
|
||
| `title` | 21.07 | 1.82 % | `ptloop` sweeps never stop |
|
||
| `publisher_logo` | 10.77 | 1.00 % | |
|
||
| `developer_logos` | 9.37 | 0.39 % | |
|
||
|
||
**No screen shows a large connected blob** — the shape a missing or misplaced
|
||
element makes, and the shape all three historical failures made. The differences
|
||
are scattered, and the two largest have stated causes.
|
||
|
||
### 74 % of `main_menu`'s difference is the oracle's own focus signature
|
||
|
||
The corpus ships `live-main-menu.png` and `live-main-menu-options-focused.png` —
|
||
the same screen with a different button lit. Their difference *is* what focus
|
||
changes, measured by the oracle against itself. Of the port's 2 159 differing
|
||
pixels, **1 599 — 74.1 % — fall inside that signature.** So the bulk of the
|
||
disagreement is a state mismatch (the port focuses `NEW GAME`, authored, because
|
||
HANDOFF Q5 measured initial focus as unstable), not a rendering defect.
|
||
|
||
## Refutation attempt — the tone curve survives in its stated reach and not past it
|
||
|
||
`ui-render-tone-curve.md` models the relationship as
|
||
`capture = 255·(render/255)^γ`, γ ≈ 1.34–1.49, **measured on dark flat patches
|
||
(render ~0–60), with "nothing constrains midtones or highlights"** written into
|
||
its own reach.
|
||
|
||
**I tried to fit that γ and got contradictory answers three times, and the
|
||
contradictions were mine.** Binning every structurally matched pixel of
|
||
`main_menu` by render level gives the relationship directly:
|
||
|
||
| render | capture | implied γ | pixels |
|
||
|---|---|---|---|
|
||
| 8 | 4.04 | 1.20 | 183 026 |
|
||
| 16 | 7.89 | **1.26** | 227 630 |
|
||
| 24 | 15.57 | 1.18 | 100 945 |
|
||
| 32 | 26.15 | 1.10 | 87 474 |
|
||
| 40 | 38.07 | 1.03 | 86 094 |
|
||
| 48 | 53.96 | **0.93** | 85 255 |
|
||
| 64 | 78.52 | 0.85 | 6 509 |
|
||
| 96 | 130.44 | **0.69** | 1 682 |
|
||
|
||
✅ **The claim survives where it was measured.** In the darks the capture really
|
||
is darker than the render and γ > 1.
|
||
|
||
🔴 **It is not a single power law.** The implied exponent falls monotonically and
|
||
**crosses 1.0 near render ≈ 44** — above that the capture is *brighter*. One
|
||
exponent cannot express a curve that crosses unity, which is precisely why my
|
||
whole-frame fits kept returning γ = 1.00: the darks want more than 1 and the
|
||
midtones want less, and they cancel.
|
||
|
||
**So the corpus's stated reach was not a hedge, it was the finding.** ⚠️ And the
|
||
exponent in the darks measures **1.18–1.26 here against the page's 1.49 for this
|
||
screen** — a disagreement I am recording rather than resolving, since they fit
|
||
selected flat patches and I binned every matched pixel.
|
||
|
||
### The tool reports the curve, not a best exponent
|
||
|
||
Two earlier versions of `verify-capture` reported a best-fit γ and were wrong
|
||
both times — once by fitting across a 74 % structural mismatch, once by
|
||
extrapolating past a reach the measurement's own authors had written down.
|
||
**Extrapolating a measurement past its stated reach is how this tool got it wrong
|
||
twice**, and the answer was not a better fit but a different instrument: a table
|
||
somebody can argue with.
|
||
|
||
## Identifying the capture's focused button — and my harness was posing the port wrong
|
||
|
||
The Decoder attached an honest caveat to its reproduction of the tone-curve
|
||
refutation: its bins included the focus-state mismatch, so it was *"not a clean
|
||
second opinion"*. That is removable, and removing it found a defect of mine.
|
||
|
||
### The method, with a known-answer control
|
||
|
||
`--menu=main_menu --script=down,down,down,down` walks focus through all five
|
||
buttons and shoots each. Compare every one against a capture; the minimum
|
||
identifies the focused button. **The control is the capture whose answer is in
|
||
its own filename:**
|
||
|
||
| render focus | vs `live-main-menu-options-focused` | vs `live-main-menu` |
|
||
|---|---|---|
|
||
| `ptbtn01` NEW GAME | 6 351 | **531** |
|
||
| `ptbtn02` LOAD GAME | 7 087 | 7 094 |
|
||
| `ptbtn03` TUTORIAL | 6 230 | 6 237 |
|
||
| `ptbtn04` OPTIONS | **1 292** | 6 364 |
|
||
| `ptbtn05` EXTRAS | 6 073 | 6 080 |
|
||
|
||
✅ **The control picks `OPTIONS`, by 4.7×** — the answer the filename gives.
|
||
✅ **So the test is trustworthy, and `live-main-menu.png` has NEW GAME focused, by
|
||
11.5×.**
|
||
|
||
**Which is what the port already focuses.** `authored/flow.json`'s
|
||
`initial_focus: ptbtn01` was chosen because HANDOFF Q5 measured focus as
|
||
*unstable* across boots — four boots gave TUTORIAL, TUTORIAL, NEW GAME, NEW GAME
|
||
— and it is one of the two observed states. It is now also the state of the
|
||
committed capture. ⚠️ That is corroboration, **not** a decode: Q5's instability
|
||
stands, and this identifies one frame rather than a rule.
|
||
|
||
### The defect: `verify-capture` rendered menus with no focus at all
|
||
|
||
`--screen=` draws no focus record, so the harness had been comparing `main_menu`
|
||
to the oracle **in a state the oracle was never in**. Rendered properly, with
|
||
`--menu=`:
|
||
|
||
| | before | after |
|
||
|---|---|---|
|
||
| `main_menu` | 0.25 % differing, RMSE 14.79 | **0.06 %**, RMSE 13.21 |
|
||
| `extras` | 0.46 %, RMSE 15.29 | **0.20 %**, RMSE 13.38 |
|
||
|
||
A 4× improvement on `main_menu` that was **entirely my harness posing the port
|
||
wrong**, not the port drawing wrong. Worth stating plainly: the first run of a
|
||
new correctness harness reported a discrepancy, and three quarters of it was the
|
||
harness.
|
||
|
||
### And the Decoder's caveat resolves to nothing — measured, not assumed
|
||
|
||
Re-deriving the transfer curve on the correctly-posed pair gives 1.20 / 1.26 /
|
||
1.18 / 1.10 / 1.03 / 0.94 at render 8…48 — **unchanged** from the mismatched run.
|
||
So the focus-state contamination it flagged really did not move the trend. Its
|
||
reproduction stands as a second opinion after all, and that is now a measurement
|
||
rather than a hope.
|
||
|
||
## `tools/port/which-focus` — the Decoder asked for a detector, and it carries its own control
|
||
|
||
`S00A` is blocked on knowing which button a screenshot has focused.
|
||
`newgame_path.sh` assumed NEW GAME is focused at boot, drove on that assumption,
|
||
and landed in a **tutorial mission** — because HANDOFF Q5 measured focus as
|
||
*unstable across boots*. And counting presses cannot substitute: ⬆ from the first
|
||
item wraps to the last, so no fixed number of presses lands on a known item from
|
||
an unknown start.
|
||
|
||
The Decoder's own attempt — a per-row brightness statistic — **failed the
|
||
control**, picking NEW GAME on the capture whose filename says OPTIONS. The
|
||
render-difference method passes it, so it is now a script that agent can run.
|
||
|
||
### It runs the control on every invocation, not once when it was written
|
||
|
||
```
|
||
control -- live-main-menu-options-focused.png (answer is in the filename):
|
||
OPTIONS 1285 <- picked
|
||
EXTRAS 6073
|
||
...
|
||
-> OPTIONS, margin 4.7x CONTROL PASSED
|
||
```
|
||
|
||
If that fails, the tool **refuses to report a result at all**. A control that
|
||
does not execute is not a control, and this one cannot be skipped.
|
||
|
||
### Three checks, and one of them independently reproduces a corpus measurement
|
||
|
||
| input | verdict | margin |
|
||
|---|---|---|
|
||
| `live-main-menu-options-focused` — **known answer** | OPTIONS | 4.7× |
|
||
| `live-main-menu` — the question | **NEW GAME** | 11.4× |
|
||
| `live-extras` — **known from the corpus** | MISSION SELECT | 4.2× |
|
||
| `live-title-press-a` — **no menu at all** | *refuses* | 1.0× |
|
||
|
||
The `extras` row is a second known answer I did not plant: `authored/flow.json`
|
||
already records *"MEASURED: EXTRAS opens focused on MISSION SELECT
|
||
(live-extras.png)"*, and the tool reaches it independently.
|
||
|
||
The title row is the negative control. A frame with no menu in it gives a margin
|
||
of 1.0× and the tool says *"this frame does not decide it. Do not act on this."*
|
||
|
||
⚠️ **And that refusal now carries a non-zero exit code.** The first version
|
||
printed the warning and exited 0 — so a caller scripting it, which is the entire
|
||
point, would have read a refusal as an answer. That is the same defect as a
|
||
checker claiming a check it skipped, and it is the fifth instance of that shape
|
||
between the two of us this session.
|
||
|
||
**What it is not:** it identifies focus in *one frame*. It says nothing about
|
||
what *selects* focus; Q5's instability stands.
|
||
|
||
## The title's 1.82 % — three of my own explanations refuted, and the format has no blend mode
|
||
|
||
`title` is the port's largest disagreement with the oracle, and last iteration I
|
||
attributed it to the moving `ptloop` sweeps *without checking*. That attribution
|
||
is wrong, and so were the two hypotheses I formed after it.
|
||
|
||
**❌ Not the sweeps.** `ptloop01`/`ptloop02` are **399×180 at (441, 270)** — small
|
||
and central — and their exported keyframes hold `pos`, `scale` and `rotation`
|
||
constant, varying only alpha. The difference peaks at **x ≈ 1088**, nowhere near
|
||
them.
|
||
|
||
**❌ Not an over-held element.** `--screen` holds every element at its own
|
||
`rest.t`, so I added `--no-hold` to render the other answer. Playing the title's
|
||
groups past their rest **fades the whole screen to black by t = 5.2 s** —
|
||
30.97 % differing against 1.82 % held. Holding at rest is right.
|
||
|
||
**❌ Not a timing offset.** Sweeping the build-in: 24.05 % at t=1.6, falling
|
||
monotonically to **1.68 % at t=4.18** and 1.82 % settled. The capture is at the
|
||
settled end; there is no earlier moment that fits better than marginally.
|
||
|
||
### What it actually looks like: a horizontal redistribution
|
||
|
||
Signed difference (port − capture), by cell:
|
||
|
||
| | x=0 | x=320 | x=640 | x=960 |
|
||
|---|---|---|---|---|
|
||
| y=0 | +1.1 | **−13.1** | −6.6 | **+16.0** |
|
||
| y=169 | +2.1 | **−8.3** | +4.2 | **+9.9** |
|
||
| y=338 | +5.8 | −4.5 | −0.0 | +3.2 |
|
||
| y=507 | +3.9 | +3.3 | +2.0 | +2.3 |
|
||
|
||
**The port is darker centre-left and brighter right, and it nearly cancels** —
|
||
whole-frame means 63.8 against 62.5. That is not a level error and not a tone
|
||
ramp; it is brightness in the wrong *place*. And it falls in exactly the rows
|
||
spanned by the two wide elements `ptlogo_back2` (1118×262 at 71,126) and
|
||
`ptlogo_back2eff` (1133×280 at 64,117), with the column profile falling off past
|
||
x≈1152 against their right edges at 1189 and 1197.
|
||
|
||
### 🔴 The export carries no blend mode, so the port cannot draw one
|
||
|
||
`ptlogo_back2eff`'s exported keys are `declared, id, index, keyframes, kind_raw,
|
||
layer, layer_source, pivot, rest, role, sprite`. **There is no blend field**, in
|
||
this element or in `FORMAT.md` at all. The port composites everything with normal
|
||
alpha.
|
||
|
||
If the game draws `_eff` layers **additively**, a wide gradient sprite would
|
||
produce precisely this signature — surplus where the sprite is bright, deficit
|
||
where the underlying art is brighter than the sum — and **nothing in the export
|
||
would reveal it.** That is a decoding question, not a port one, and it is asked
|
||
rather than assumed. ⚠️ It is a hypothesis I have not tested; I am recording it
|
||
because the three I could test are dead.
|
||
|
||
### A separate `rest.t` casualty, recorded and not acted on
|
||
|
||
`pteff02` is a full-frame primitive whose group runs `0x40000000` (25 % black) at
|
||
t=46 → `0xd4000000` at 76 → `0xcc000000` at 118 → **`0x00000000` at 236**. Its
|
||
`rest.t` is **46**, so the port holds a **25 % black veil the screen's own
|
||
timeline removes**. This is the third instance of `rest.t` naming a hold that is
|
||
not the settled state — after the loading screen's opaque quad and `ptlogo1`'s
|
||
creep. ⚠️ **It does not explain the residual** — removing a darkening veil would
|
||
make the port brighter still, and it is already brighter where it disagrees — so
|
||
it is recorded rather than fixed.
|
||
|
||
### And a new diagnostic
|
||
|
||
`--no-hold` plays a screen past its rest instead of clamping each element at
|
||
`rest.t`. Added because the question *"is the held pose what the idle game
|
||
shows"* could not be asked otherwise. ⚠️ Its first version set the flag thirty
|
||
lines before `view` exists and silently rendered nothing — caught because the
|
||
comparison loop found no files, not because anything reported an error.
|
||
|
||
## 🔴 The exporter dropped nested `.rat` leaf geometry on 45 elements — and it is the title's 1.82 %
|
||
|
||
The Decoder overturned one of my three eliminations, and it was the one I was
|
||
most confident about. I ruled out the `ptloop` sweeps because *"399×180 at
|
||
(441, 270), keyframes hold position constant"* — **that is the parent's record.
|
||
The geometry is in the leaf, and the exporter never opened it.**
|
||
|
||
| | parent (what the export shipped) | **leaf `ptloopNN.rat`** |
|
||
|---|---|---|
|
||
| `ptloop01` | scale (100, 100), rot 0, pos (441, 270) fixed | **scale (100, 600), rot +30°**, x sweeping **−639 → −39 → 1521** |
|
||
| `ptloop02` | scale (100, 100), rot 0, pos (441, 270) fixed | **scale (100, 800), rot −45°**, x sweeping **1721 → 1111 → −839** |
|
||
|
||
Two ~1080 and ~1440 px quads leaning opposite ways and sweeping across the
|
||
frame, against two 400 px sprites drawn upright and static in the middle. **That
|
||
is exactly the signature I measured** — darker centre-left, brighter right,
|
||
nearly cancelling — and the Decoder's GPU capture puts their centres at x ≈ 467
|
||
and 992, which are the two cells where my signed difference peaked.
|
||
|
||
`ui_layout`'s own doc comment had said so: *"the rotated quads come from its two
|
||
**nested** `.rat` leaf records, which the census never opened."* **Neither did
|
||
this exporter.** It opened a leaf in exactly one place — `highlight_name`, for
|
||
focus records — and nowhere else.
|
||
|
||
### It is not two elements. It is 45
|
||
|
||
| screen | elements with a dropped leaf |
|
||
|---|---|
|
||
| `main_menu`, `extras`, `press_start` (+ JP twins) | every button — `ptbtn01.rat` … `ptbtn13.rat`, `ptbtn00.rat` |
|
||
| `title`, `extras` | `ptloop01.rat`, `ptloop02.rat` |
|
||
| `build_00/01/12/15` | `pgloading_loop1/3/4/5.rat` |
|
||
| `title_jp` | `ptlogo_eff2.rat` — **two** elements |
|
||
|
||
⚠️ The buttons are the benign case and `screen.rs` already knew it: *"a BASE
|
||
record's leaf duplicates the parent's placement and the two can differ by a unit
|
||
(`ptbtn04`: parent y=401, leaf y=402). There the parent wins."* The `ptloop` case
|
||
is the opposite — the parent carries **no geometry at all** and the leaf carries
|
||
all of it. 🟡 And `title_jp`'s `ptlogo_eff2` is the element `DECISIONS` has
|
||
recorded since P1 as the single largest render disagreement in the export, *"the
|
||
one drawn element at a scale that is not a whole multiple of 100 %"*. It has a
|
||
two-element leaf. That is a lead, not a conclusion.
|
||
|
||
### Emitted, deliberately not drawn
|
||
|
||
`Element::leaf` now ships the decoded leaf, and one `read_leaf` closure serves
|
||
both it and the focus path — a second copy is how the case would go missing
|
||
again.
|
||
|
||
🔴 **`ScreenView` ignores it**, and that is the honest state. Parent and leaf each
|
||
carry their own alpha ramp on a **different span** — parent `0 → 255` over
|
||
t=70…238, leaf `255 → 0x80 → 255` over t=150…600 — so **how the two compose is a
|
||
decoding question**, and drawing the leaf on a guess would replace a visible
|
||
1.82 % gap with an invisible wrong one. `verify-screen` confirms nothing moved:
|
||
`title` still max 6 / over3 790, `main_menu` max 4, `title_jp` max 155.
|
||
|
||
✅ **Additive blending is refuted** — the Decoder tested `T8aD +0x04` bit `0x02`
|
||
as an additive selector and *"every measure worsens"*. My blend-mode hypothesis
|
||
from last iteration is dead, and the export carries no blend field because **none
|
||
has been found**: the per-draw capture records primitive type, index count,
|
||
shader hashes, texture bindings and vertex attribute 0, and **no
|
||
`RB_BLENDCONTROL`**.
|
||
|
||
🔵 **And this makes the port's biggest oracle gap the same item as the rotation
|
||
question already standing with the human.** `sylpheed-cli screen render`
|
||
deliberately does not rotate, which is why *both* renderers show it — the
|
||
Decoder measures its own residual as tiles running −38.6 then +33.8 and
|
||
cancelling, the same shape as mine. It is a **shared decode gap, not a defect in
|
||
my compositor**, and MISSION's *"Needs a human decision — rotation"* now has a
|
||
number attached: **1.82 % of the title's pixels, in a signature that can be
|
||
recognised.**
|
||
|
||
## The leaf composition is decoded and implemented — and it does **not** close the 1.82 %
|
||
|
||
The Decoder decoded the rule I refused to guess: **draw the leaf on its own
|
||
timeline; do not multiply the parent's alpha in.** Multiplying is *refuted*, not
|
||
merely unsupported — at the fitted time the parent has expired (its group returns
|
||
to 0 at t=250 and holds), so `leaf × parent / 255` predicts zero for both quads
|
||
and the sweeps would be invisible. They are drawn.
|
||
|
||
The fit is worth repeating because of its shape: the game's own composed alpha is
|
||
observable in the per-draw vertex colours (`C3FFFFFF`/`B6FFFFFF` = **195** and
|
||
**182**), fitting *only those two numbers* against the two leaf ramps gives one
|
||
consistent time **t = 355**, and the same t then **predicts** quad centres at 981
|
||
and 478 against **992.0** and **467.2** measured. No x entered the fit.
|
||
|
||
Implemented: `_draw_leaf` runs the leaf unclamped — like the spinning ring, and
|
||
for the same reason. Held at its own `rest.t` the leaf sits at **x = 1521**,
|
||
entirely off the right edge, so `holding` would delete the sweeps rather than
|
||
settle them.
|
||
|
||
### 🔴 And it changes nothing measurable. The title is still 1.82 %
|
||
|
||
| t | units | differing |
|
||
|---|---|---|
|
||
| 4.35 s | 261 | 1.82 % |
|
||
| **5.917 s** | **355** | **1.81 %** |
|
||
| 7.00 s | 420 | 1.79 % |
|
||
|
||
**At t=355 my interpolation puts the leaf's top-left at x ≈ −324 — off-screen
|
||
left — where the Decoder's model puts the quad's centre at 981.** Those cannot
|
||
both be right, and the gap is not something to tune away: it is a disagreement
|
||
about how the leaf's keyframes become a placed quad, most likely in the pivot and
|
||
the rotation about it. **Handed back with both numbers.**
|
||
|
||
So the honest state: the exporter no longer drops the data, the composition rule
|
||
is implemented as decoded, and **the port's largest oracle gap is exactly where
|
||
it was.** Fixing the export was necessary and was not sufficient.
|
||
|
||
### Two flagged elements deliberately not drawn
|
||
|
||
`authored/rendering.json` narrows drawing to `ptloop01`/`ptloop02` — what the
|
||
decode covers — while the exporter keeps flagging all 15.
|
||
|
||
* **`title_jp/ptlogo_eff2`** — parent 125 % at (412,96), leaf 100 % at (402,66).
|
||
The same shape, and the element `DECISIONS` has recorded since P1 as the
|
||
largest render disagreement in the export. The Decoder said plainly *"I have
|
||
not tested it."* Drawing it would extend a decode past the case it was fitted
|
||
on — the failure this port has made four times this session.
|
||
* **`pgloading_loop5`** — leaf scale **(0, 0)**. A zero scale is one of the three
|
||
historical failures this corpus names, so it is exactly where a guess costs
|
||
most.
|
||
|
||
⚠️ **And neither can be adjudicated here.** `title_jp` has no oracle capture, and
|
||
`verify-screen` compares against `sylpheed-cli`, **which draws no leaves at all**
|
||
— so *any* leaf drawing increases that divergence whether it is right or wrong.
|
||
Its `max` went 155 → 232 when they were drawn, and **that number is not evidence
|
||
in either direction.** Recognising it as uninformative is the reason it did not
|
||
become a justification.
|
||
|
||
## The −324 was the old keyframe association, and the corrected one is available **today**
|
||
|
||
The Decoder found it and it is not geometry: my leaf path pairs each pose with
|
||
the *next* pose's time. Fed my pairing into its own interpolation, it reproduces
|
||
**−324 to the digit**. Corrected, t=355 gives top-left **781** and centre
|
||
**980.5** against **992.0** measured — so there was never a 1 300 px pivot
|
||
mystery to look for, and I am glad I said the two numbers instead of hunting one.
|
||
|
||
### 🔴 Correcting BLOCKED: this was never gated on a branch landing
|
||
|
||
`docs/port/BLOCKED.md` has carried the keyframe-record-layout row as *"NOT taken,
|
||
and it cannot be taken yet … `sylpheed-formats` is a path dependency, so this
|
||
reaches the port only when that branch lands on `main`."*
|
||
|
||
**That is wrong.** The corrected association is in this checkout, behind an
|
||
environment variable:
|
||
|
||
```rust
|
||
// Set `SYLPHEED_KF_TIME_SHIFT=1` to read `W[k-1]` as block `k`'s time
|
||
// ("the word is the time the NEXT pose is reached") instead of `W[k]`.
|
||
```
|
||
|
||
It has been switchable the whole time. **I read that file twice this session —
|
||
once for `rotation_deg`, once for the leaf note — and did not notice the switch.**
|
||
|
||
### The experiment, run: mixed, and not decisive for the reason that matters
|
||
|
||
Re-exported with `SYLPHEED_KF_TIME_SHIFT=1` and asked the oracle:
|
||
|
||
| screen | default | shifted |
|
||
|---|---|---|
|
||
| `main_menu` | 13.21 / 0.06 % | 13.81 / **0.10 %** — worse |
|
||
| `extras` | 13.38 / 0.20 % | 13.95 / **0.24 %** — worse |
|
||
| `title` | 21.07 / 1.82 % | **20.41** / 1.86 % — RMSE better, area worse |
|
||
| `publisher_logo` | 10.77 / 1.00 % | **9.05 / 0.75 %** — better |
|
||
| `developer_logos` | 9.37 / 0.39 % | **8.86 / 0.33 %** — better |
|
||
|
||
⚠️ **And it does not adjudicate the association, because the port's renderer is
|
||
built for the other one.** Under the shift the **untimed keyframe moves from last
|
||
to first** — the leaf reads `t=None` at x=−639, then t=150, t=540 — while
|
||
`pose_at` is written around *"the final keyframe carries no `t`, so it is given a
|
||
synthetic time `exit_ramp_units` after the last timed frame."* So this measures a
|
||
**renderer/association mismatch**, not the association. BLOCKED said as much:
|
||
the change touches `pose_at`, `settle_units`, `spin_period_units`,
|
||
`exit_ramp_units` and the plate.
|
||
|
||
**Export reverted to the default.** Adopting the shift is a real piece of work —
|
||
re-deriving the exit ramp, the settle, the spin period and the plate against a
|
||
layout where the untimed frame is the *first* — and doing it hastily at the end of
|
||
an iteration is how a 1.8 % gap becomes five wrong ones.
|
||
|
||
### ⚠️ The methodological point, which is the Decoder's and is the best thing here
|
||
|
||
Its rule matched because **alpha at t=355 sits inside a long segment where a
|
||
one-keyframe shift barely moves it, while x sweeps 1 560 px over the same span.**
|
||
It confirmed on the insensitive quantity; I was wrong on the sensitive one; and
|
||
neither of us saw it until the two were compared.
|
||
|
||
> **Check a new interpretation against the fastest-moving field you have, not the
|
||
> one that happens to agree.**
|
||
|
||
That is a different failure from the ones this session has collected — not an
|
||
uncontrolled instrument, but a control chosen where it could not fail.
|
||
|
||
❔ A residual **11.5 px** (980.5 against 992.0) is left over and is **not** to be
|
||
fitted. A rotation about a declared pivot rather than the centre would displace by
|
||
roughly that; if it still matters once the association is adopted, it gets
|
||
measured rather than derived.
|
||
|
||
## Re-running the P5/P6 gate after eight iterations of changes
|
||
|
||
The leaf path, the plate pulse, the BGM level, the voice export, the focus fix
|
||
and a new diagnostic flag have all landed since the gate was last actually run.
|
||
The mission's test is *"a human presses a d-pad and Ⓐ and moves through those
|
||
screens"*, and I had been verifying pieces of that against captures without once
|
||
re-running the whole walk.
|
||
|
||
**It works.** Nine steps, unattended:
|
||
|
||
| step | | |
|
||
|---|---|---|
|
||
| ⬇ ×4 | `ptbtn01` → `ptbtn05` | focus moves, every frame drawn |
|
||
| Ⓐ | `EXTRAS` → screen `extras`, focus `ptbtn11` | |
|
||
| Ⓑ | back to `main_menu`, **focus restored to `ptbtn05`** | HANDOFF Q5's rule, live |
|
||
| ⬆, Ⓐ | `ptbtn04` `OPTIONS`, destination outside this archive | prints the gap rather than pretending |
|
||
|
||
Filmstrip shared as `1788027380-788b1faafc3e`. Every shot is non-blank (frame
|
||
means 31.5–33.1).
|
||
|
||
### The sound is verified by a null control, not by a detector
|
||
|
||
I first tried an onset detector: count sharp level rises in the recording. It
|
||
found **one** onset in the walk — and **the same one** in the music bed alone,
|
||
which contains no cues. The 50 ms envelope cannot see a short cue over the bed.
|
||
⚠️ **The right response to a detector that cannot separate its control from its
|
||
subject is to stop using it**, not to widen the window until the numbers look
|
||
better. That is the shape this session has hit five times.
|
||
|
||
So the same technique that settled the voice: shadow the three cues with silence
|
||
through `data/mods/`, run the identical walk, and compare.
|
||
|
||
| | peak | RMS |
|
||
|---|---|---|
|
||
| walk, cues playing | **+0.0003 dBFS** | −18.36 |
|
||
| walk, cues silenced | **−4.74 dBFS** | −20.78 |
|
||
|
||
**Silencing three sound effects costs 4.74 dB of peak and 2.43 dB of RMS**, and
|
||
the walk's peak is *set by* a cue rather than by the music. The mod log confirms
|
||
exactly three files shadowed. No detector, no threshold, and the control is the
|
||
same run with one input changed.
|
||
|
||
⚠️ Recorded under the Dummy driver, as everything audio here is. It shows the
|
||
cues reach the Master bus at the right moments; it does not show they are the
|
||
cues the game plays — that is HANDOFF Q8, and `authored/audio.json` still carries
|
||
the offsets as measured rather than decoded.
|
||
|
||
## Pinned `formats-pin-2026-08-29c` — and the knob I tested last iteration was retired
|
||
|
||
🔴 **I tested the wrong switch.** `SYLPHEED_KF_TIME_SHIFT` is a **superseded
|
||
partial fix**: it got the association right but **left pose 0 untimed**, which is
|
||
exactly why the untimed keyframe appeared to "move from last to first". It does
|
||
not exist in the current parser. The real correction is the **default** in the
|
||
tagged crate, with the old reading behind `SYLPHEED_KF_TIME_LEGACY=1` — the
|
||
opposite polarity from what I had.
|
||
|
||
So last iteration's five rows measured a renderer/association mismatch **against
|
||
a knob nobody should use**. I suspected they were not decisive; I did not suspect
|
||
the knob itself was retired.
|
||
|
||
### The consequence is smaller than I budgeted for: there is no untimed keyframe
|
||
|
||
A placement group is an 8-byte header then `frames` × `{u32 time; 36-byte pose}`,
|
||
so pose 0's time is the group's lead-in word and **every pose is timed, including
|
||
the last.** Measured on the re-export: **866 keyframes, 0 untimed.**
|
||
|
||
`pose_at`'s premise — *"the final keyframe carries no `t`, so it is given a
|
||
synthetic time `exit_ramp_units` after the last timed frame"* — does not invert,
|
||
it **disappears**. The branch is now dead code rather than wrong code, which is
|
||
why nothing needed re-deriving to adopt this.
|
||
|
||
And the leaf reads as the Decoder's table says: t=0 x=−639, t=150 x=−39, t=540
|
||
x=1521. At t=355 that interpolates to **x = 781** — the top-left it predicted, and
|
||
the 1 300 px discrepancy is gone.
|
||
|
||
### Pinned by tag, which is what MISSION §2 is for
|
||
|
||
> *"The RE agent tags when it lands something you need and tells you over the
|
||
> message channel — that is how you stay current without floating."*
|
||
|
||
That is precisely what happened, so `crates/sylpheed-export/Cargo.toml` now pins
|
||
`formats-pin-2026-08-29c` by tag. ⚠️ **`BLOCKED.md` was wrong in both
|
||
directions** — it said the change "cannot be taken yet" *and* that it arrives
|
||
only when the branch lands on `main`. It arrives when the tag is pinned.
|
||
|
||
🔴 **The cost, stated rather than discovered later:** `sylpheed-cli` builds from
|
||
the **workspace** crate, so until this reaches `main` the exporter and the
|
||
reference renderer read **different decoders**, and `verify-screen` is comparing
|
||
two eras rather than detecting drift. `verify-capture` is unaffected — it
|
||
compares the port against oracle **captures** and never touches the CLI — and it
|
||
is the check that matters. Revert to the path dependency the day the tag is an
|
||
ancestor of `main`.
|
||
|
||
### What the oracle says
|
||
|
||
| screen | before | after |
|
||
|---|---|---|
|
||
| `publisher_logo` | 1.00 % | **0.75 %** |
|
||
| `developer_logos` | 0.39 % | **0.33 %** |
|
||
| `extras` | 0.20 %, region **736×525** | 0.19 %, region **398×295 at (441,230)** |
|
||
| `main_menu` | 0.06 % | 0.06 % |
|
||
| `title` | 1.82 % | 1.82 % |
|
||
|
||
The splashes improve outright. ⚠️ **`extras` is the interesting row**: the *area*
|
||
barely moved but its differing region **collapsed onto the sweep position**
|
||
(441, 270) — the residual is now localised to the one element still in question
|
||
rather than spread over the screen.
|
||
|
||
The title does not move. Its row is now posed at **t=355**, the Decoder's fitted
|
||
sweep time, because the leaf group ends at t=600 with the quads parked off-screen
|
||
at x=1521 — posing at the settle simply omits them. ⚠️ **t=355 is not the time
|
||
that minimises the difference**: t=390 measures **1.65 %**. Picking that would be
|
||
fitting the pose to the score, which is what this harness exists not to do.
|
||
|
||
## Refuted — my own "the single non-whole-multiple scale in the export"
|
||
|
||
`DECISIONS` has said since P1 that `ptlogo_eff2` is *"the single drawn element in
|
||
the whole export at a scale that is not a whole multiple of 100 % (125 %)"*.
|
||
**That census was parents-only.** Opening the 45 leaves finds **thirteen** distinct
|
||
non-whole-multiple scales — 75, 96, 99, 101, 103, 112, 125, 150, 204×208,
|
||
210×220, 250 — and 125 % is among the *rarest* at two occurrences.
|
||
`ptlogo1`/`ptlogo2` carry 101/103/112 on the **English** title.
|
||
|
||
The claim's real content was *"the only one **the port draws**"* — a fact about my
|
||
element set, not about the disc. Corrected.
|
||
|
||
🔴 **And `ptlogo_eff2` stays withheld, now for a stronger reason than caution.**
|
||
Its 125 % is a **pop**, not a steady scale: scale-0 → 125 % → scale-0 between
|
||
t=50 and t=107, ≈0.95 s. The leaf draws at 100 %, as **two superimposed copies**
|
||
at alpha 160 and 80, each rotating 360° over 960 units — 16 s per revolution.
|
||
**If parent scale gates the leaf it is a 0.95 s flash; if the leaf runs free it
|
||
spins for 16 s.** Nothing on the disc chooses, `title_jp` has no oracle capture,
|
||
and the Japanese-locale capture MISSION has parked is what would settle it.
|
||
|
||
## The 11.5 px was the fit's resolution, and the lesson inverts
|
||
|
||
The Decoder closed it **by adding observables, not by tuning** — the vertex
|
||
buffer carries positions *and* colours at the same instant, so all four
|
||
quantities must agree on one `t`:
|
||
|
||
| observable | solved t | precision |
|
||
|---|---|---|
|
||
| quad A x | **357.88** | ±0.12 units |
|
||
| quad B x | **357.58** | ±0.12 units |
|
||
| quad A alpha | 355.75 | ±1.54 units |
|
||
| quad B alpha | 354.09 | ±1.89 units |
|
||
|
||
Alpha moves only 0.27–0.33 levels per keyframe unit, so **one byte of
|
||
quantisation is worth 1.5–1.9 units, which at 4 px/unit is 6–8 px of sweep**.
|
||
That is the whole of the 11.5 px. At t = 357.7 the centres land within 0.70 px
|
||
and both alphas inside one level.
|
||
|
||
⚠️ **The lesson is the earlier one inverted, and this is the half worth keeping.**
|
||
Checking a wrong rule against alpha made it *look confirmed*. Here the same
|
||
insensitivity **manufactured a residual that did not exist**. So an insensitive
|
||
quantity does not merely fail to falsify — **it invents error.** Solve on the
|
||
fastest-moving field; check the slow one; never the reverse.
|
||
|
||
I was already looking for a pivot rule to explain 11.5 px when they wrote. There
|
||
was nothing to find.
|
||
|
||
### Refutation attempt — the pivot claim, checked here and survived with a nuance
|
||
|
||
They state the leaf pivot is (200, 90) on a 399×180 sprite, *"the pivot is the
|
||
centre, so rotation displaces it by nothing."* Checked against my own export:
|
||
pivot **[200, 90]**, sprite **399×180**, true centre **199.5, 90**.
|
||
|
||
✅ Survives. ⚠️ With one correction of no consequence: the sprite is **odd-width**,
|
||
so the pivot is the centre to within **half a pixel**, not exactly. Against their
|
||
−0.70/−0.48 px agreement that changes nothing, and it is worth stating only
|
||
because "displaces it by nothing" is the kind of sentence that later gets leaned
|
||
on for a sub-pixel claim.
|
||
|
||
`verify-capture` now poses the title at **t=357.7** rather than 355: RMSE
|
||
21.07 → **20.92**, differing 1.82 % → **1.81 %**. Marginal, and it is the right
|
||
pose for a stated reason rather than a better number.
|
||
|
||
### `ptlogo_eff2` is withheld for a better reason than mine
|
||
|
||
I was withholding it out of caution about untested generalisation. The Decoder
|
||
points out something stronger: **it is on `title_jp`, and MISSION §7 scopes out
|
||
"localisation beyond English"** — so it is not a question the menu port has to
|
||
answer at all, and the parked Japanese-locale capture does not need reviving on
|
||
its account. `authored/rendering.json` now says that first and the undecidability
|
||
second. **Widening scope to close a residual would have been the wrong trade**,
|
||
and it is the human's call either way.
|
||
|
||
## 🔴 The focus ring had silently stopped, and BLOCKED had listed it
|
||
|
||
`docs/port/BLOCKED.md` said the record-layout change touches five things:
|
||
`pose_at`, `settle_units`, `spin_period_units`, `exit_ramp_units` **and the
|
||
plate**. I checked `pose_at` and deleted `exit_ramp_units`, reported that, and
|
||
**did not work the rest of the list.**
|
||
|
||
`spin_period_units` required *"the first timed and the second untimed"*. Under
|
||
the corrected layout the ring reads `t=0 rot=0` and `t=120 rot=360` — **both
|
||
timed** — so the rule returned 0 and **the focus ring stopped spinning**. Nothing
|
||
reported it: a period of 0 is a legal *"this element does not spin"*.
|
||
|
||
Rewritten to take the **span** between the two poses. On the ring that is
|
||
120 − 0 = **120 units, the same number the old rule produced**, which is a small
|
||
piece of evidence that the corrected layout is self-consistent rather than merely
|
||
different.
|
||
|
||
**Verified the way P5 verified it — bit-identity one period apart**, on the ring's
|
||
own 60×60 box so the `ptloop` sweeps cannot confound it:
|
||
|
||
| separation | mean difference |
|
||
|---|---|
|
||
| **+120 units (one period)** | **0** |
|
||
| +120 units again | **0** |
|
||
| +30 units (quarter) | 8.61 |
|
||
| +60 units (half) | 8.88 |
|
||
|
||
⚠️ Getting there took three wrong instruments, and the sequence is the lesson.
|
||
A whole-frame `max` saturates on one rotating edge — adjacent frames scored 131
|
||
while their mean was 0.022. A live `--menu` filmstrip jitters by up to a frame,
|
||
which is ~3° of ring, and its cadence cannot be pinned. And a whole-frame
|
||
comparison is dominated by the sweeps, which move 480 px over one ring period.
|
||
**`--focus=<id>` was added so a `--screen` run can draw a focus record
|
||
deterministically**, which is what made the check reproducible at all.
|
||
|
||
## The plate's period is now the disc's 105, and it disagrees with the measurement
|
||
|
||
Under the corrected layout `ptbtn00f` runs **t=0 (alpha 0) → t=105 (alpha 0)** — a
|
||
closed cycle with every pose timed.
|
||
|
||
✅ **The ambiguity this entry carried is gone.** It used to say the cycle might
|
||
restart at t=6 rather than 0, giving 123 units, and that nothing separated the
|
||
two. There is now one reading.
|
||
|
||
🔴 **And the number is worse against the oracle, which is stated rather than
|
||
avoided.** 105 units is **1.750 s**; scaled by the factor the ring shows between
|
||
its declared 120 units and its measured 2.177 s (×1.089), **1.906 s** — about
|
||
**17 % below** every one of the corpus's four timings (2.12 / 2.19 / 2.34 /
|
||
2.31 s). The old 129 gave 2.34 s, at the top of that range, which is exactly why
|
||
it looked right.
|
||
|
||
**129 was the last timed keyframe plus `exit_ramp_units`, and that constant is
|
||
deleted.** A period built from a constant that no longer exists cannot stay, even
|
||
though it fitted better. So the port ships the disc's number **and says it is
|
||
wrong**, rather than keeping a number that agreed with the measurement for a
|
||
reason that has evaporated.
|
||
|
||
Verified: the plate is bit-identical 105 units apart (mean diff **0**) and differs
|
||
at 30 units (0.83). ❔ What would resolve the disagreement: whether the group
|
||
loops from its start at all, or holds at alpha 0 between cycles. Asked.
|
||
|
||
## The plate's period is 120, decoded — and it was falsified with my own ring number
|
||
|
||
The Decoder found it in the format: **a nested record is itself a RATC bundle,
|
||
and its header's `+0x08` is the loop length** — the same field
|
||
`ui_header_time_disc` already tests at the top level. A record's keyframes need
|
||
not fill it, and the slack is a hold at the final pose. **`ptbtn00f` is 105 units
|
||
of ramp inside a 120-unit cycle**, so the glow rests dark for 15 units. The five
|
||
`ptbtn0Nf` records fill their 120 exactly, which is what shows the slack belongs
|
||
to *that record* rather than to the format.
|
||
|
||
Disc-wide over 1 781 timed nested records: 92.3 % declare exactly their last
|
||
keyframe time, 7.7 % declare more, **0 declare less**. That last row is the
|
||
falsifier — a cycle cannot restart before its own last pose — and it never fires.
|
||
|
||
### The decisive test used this port's number, not theirs
|
||
|
||
Both candidates need the same emulator pacing factor, and **the ring measures it
|
||
independently**: declared 120 units → 2.177 s → **1.0885**.
|
||
|
||
| plate period | nominal | factor needed for the measured 2.12–2.34 s | |
|
||
|---|---|---|---|
|
||
| 105 units | 1.750 s | 1.211 … 1.337 | 🔴 excludes 1.0885 |
|
||
| **120 units** | 2.000 s | 1.060 … 1.170 | ✅ **contains it** |
|
||
|
||
**105 cannot reach the measured range under any pacing the ring also satisfies.**
|
||
Two different elements in different bundles, measured in separate runs, tied only
|
||
by both declaring 120.
|
||
|
||
⚠️ **My three readings of this number, in order, are the useful record:** 129
|
||
(`105 + exit_ramp_units`) fitted the measurement for a reason that later
|
||
evaporated; 105 (the group length) I shipped *knowing* it was 17 % short; 120 is
|
||
decoded. And the 123-vs-129 pair I once called unseparable **straddled the right
|
||
answer without containing it** — which is the sharpest argument I have seen
|
||
against treating "two candidates, nothing separates them" as if the truth must be
|
||
one of the two.
|
||
|
||
Verified: bit-identical **120 units** apart (mean diff 0), 0.061 at a quarter and
|
||
0.888 at half. Still authored, because the pinned tag does not expose `+0x08`
|
||
yet — **delete the entry the day a tag does.**
|
||
|
||
### Their `rest()` flag, checked rather than assumed
|
||
|
||
They warned that a focus record is exactly the kind of element `rest()`
|
||
mishandles, since a pulse's last hold is not its resting state. Censused: **34
|
||
focus-record elements in the export, and only 2 have a varying alpha** — both
|
||
`ptbtn00f`, EN and JP. Their `rest.alpha` is **80, identical to their peak**,
|
||
which is precisely the pathology described. The port does not hit it because the
|
||
plate is drawn through the loop path, and the other **32 are constant-alpha, so
|
||
`rest()` is safe for them**. Bounded, not hoped.
|
||
|
||
## ✅ A settled screen is ONE instant, and it collapsed three residuals at once
|
||
|
||
The Decoder's finding, applied: **`rest()` returns each element's last hold
|
||
keyframe chosen independently of every other element.** That is right for
|
||
anything that ends the screen settled and **exactly wrong for a transient** —
|
||
the title's `ptlogo_back2eff1` is a two-frame flash (0 until t52, 255 at t54–56,
|
||
0 by t58), so its last hold *is* the flash peak and `rest()` left it burning.
|
||
There are five of them, and `rest()` drew all five at once.
|
||
|
||
The settled instant is **the longest interval containing no keyframe time**, over
|
||
a bundle's **top-level** elements. Reproduced independently here before adopting:
|
||
title `[160, 236]`, midpoint **198** — the Decoder's number to the unit. ⚠️ The
|
||
top-level restriction is what makes it match: including the `ptloop` leaves gives
|
||
`[269, 540]` instead.
|
||
|
||
### Against the oracle
|
||
|
||
| screen | before | after |
|
||
|---|---|---|
|
||
| **`title`** | 20.92 RMSE, **1.81 %** | **14.61 RMSE, 0.26 %** |
|
||
| **`publisher_logo`** | 9.05, **0.75 %** | **2.17, 0.01 %** |
|
||
| **`developer_logos`** | 8.86, **0.33 %** | **3.05, 0.01 %** |
|
||
| `main_menu` | 0.08 % | 0.08 % — unchanged, window too narrow |
|
||
| `extras` | 0.19 % | 0.19 % — unchanged, window too narrow |
|
||
|
||
**Seven times fewer differing pixels on the title, seventy-five times fewer on
|
||
the publisher splash**, whose differing region is now a **13×18 box**. This is
|
||
the largest correctness gain the port has had, and none of it is mine: it is a
|
||
decode, computed from the keyframe table with no reference to any capture.
|
||
|
||
### ⚠️ It is applied only where the window is wide, and that bar is not invented
|
||
|
||
The widths in this export split with **nothing in between**: `press_start` 214,
|
||
`publisher_logo` 190, `developer_logos` 145, `title` 76 — then `main_menu` 12,
|
||
`extras` 12, the loading screens 8 and 4. A 12-unit "settle" on a menu that
|
||
builds in until t=70 is a gap between staggered ramps, not a settled pose.
|
||
|
||
The bar is **30 units**: the Decoder's disc-wide census puts the knee there (30 %
|
||
of bundles ≥ 30, 42 % under 10, the latter mostly `loop*` fragments meant to be
|
||
in motion), and this export's own screens sit **4× either side of it with nothing
|
||
between 12 and 46**. Two independent populations agreeing on where to cut is what
|
||
makes it a bar rather than a preference.
|
||
|
||
Checked unbroken: the boot pacing is unmoved (`developer_logos@4.26`,
|
||
`title@7.91`, developer agrees) and the scripted walk still runs end to end with
|
||
focus restored.
|
||
|
||
## Their census, and a framing of mine they sharpened
|
||
|
||
I reported *"34 focus-record elements in the export, only 2 with a varying
|
||
alpha"*. Disc-wide it is **210 varying, 202 with `rest()` at the peak**, across
|
||
1 130 focus records — 116 in `GP_DEBRIEFING_PILOTLOG`, 54 in `GP_MOVIE_THEATER`,
|
||
30 in `GP_HANGAR_ARSENAL`, 8 in `GP_LEADERBOARD`, and **2 in `GP_TITLE`**.
|
||
|
||
**My 2 is right because `GP_TITLE` has 2.** ⚠️ But *"only 2 have a varying alpha"*
|
||
reads as a fact about the format and is a fact about one pak — and the pathology
|
||
sits in exactly the screens a wider port reaches next. The sentence was true as
|
||
measured and false as remembered, which is the failure this corpus keeps
|
||
finding, and it was mine this time.
|
||
|
||
⚠️ **And they corrected a framing I had:** I called `rest.alpha == peak` "the
|
||
pathology". It is worse than that — **a pulsing element has no resting pose at
|
||
all.** The question `rest()` answers is *malformed* rather than mis-answered,
|
||
because the element's state is a phase, not a value. `pose_at(t)` with `t` inside
|
||
the record's own declared cycle is the only well-formed query on one.
|
||
|
||
🔴 Worth carrying for whenever this port grows: `GP_LEADERBOARD`'s
|
||
`py_ranking_btn01f` swings 255 → 127 → 255 with no two adjacent keyframes equal,
|
||
so `rest()` falls through to its longest-dwell rule and returns **244** — neither
|
||
peak nor trough. **A glow stuck at its peak is visibly wrong; one stuck at 244 of
|
||
a 127–255 range looks entirely plausible, and nothing reports it.**
|
||
|
||
✅ And a free second instance of the loop-length decode from a pak neither of us
|
||
was looking at: `py_ranking_btn01f`'s ramp ends at **t=90 inside a declared 120**
|
||
— 30 units of hold, the same shape as the plate's 105-in-120.
|
||
|
||
## Their "do not draw all five flashes" flag — checked, and it does not apply here
|
||
|
||
The Decoder armed a draw capture before the title exists and caught the build-in
|
||
on the console: `ptlogo_back2eff1` in frames 130–131, `eff2` at 133, `eff4` at
|
||
133–135 — and **`eff3` never drawn at all.** Not a miss: a flash's peak is 2
|
||
keyframe units, which at that run's pacing is **0.85 of a presented frame**, so
|
||
which flashes get sampled is a matter of phase. The console shows a *subset* on
|
||
any given play, and the warning was that drawing all five would read heavier than
|
||
the real thing.
|
||
|
||
**Checked rather than reasoned about.** Sweeping the port's build-in with
|
||
`--no-hold`:
|
||
|
||
| t | flashes drawn |
|
||
|---|---|
|
||
| 54, 56 | `eff1` |
|
||
| 58 | `eff2` |
|
||
| 60 | `eff2`, `eff3` |
|
||
| 62 | `eff3`, `eff4` |
|
||
| 64 | `eff4`, `eff5` |
|
||
| 66, 68 | `eff5` |
|
||
|
||
**The port draws them sequentially, never more than two at once**, and the two
|
||
only overlap where their declared windows abut. That is the stagger the disc
|
||
declares, not a pile-up — the pile-up was the `rest()` bug, and it is fixed.
|
||
|
||
⚠️ **So the difference from the console is presentation rate, not content.** At
|
||
60 fps each 2-unit flash gets ~2 frames; at the console's pacing it gets 0.85, so
|
||
some are skipped. **A frame-by-frame comparison of the build-in against a console
|
||
capture will therefore show flashes the console missed, and that is a fact to
|
||
know rather than a bug to fix.** It is also why the settled-frame comparison —
|
||
the one `verify-capture` makes — is unaffected: at t=198 none of the five is
|
||
drawn.
|
||
|
||
### Three things of theirs worth recording
|
||
|
||
✅ **My top-level restriction was verified, not merely accepted**: top-level
|
||
`[160, 236]` width 76, versus `[269, 540]` width 271 with the `ptloop` leaves
|
||
included — an instant *after every top-level element has exited*. Worth having on
|
||
their page, because the rule as described permits the wrong reading.
|
||
|
||
✅ **The 120-unit loop is confirmed from the guest's own vertex data**, not
|
||
inferred from pixels: the glow quad's per-vertex colour alpha *is* the element's
|
||
fade alpha, giving an observed range of **0…80 against a decoded peak of 80**,
|
||
exact and unfitted, over 20 cycle starts. Fitting the decoded ramp gives RMS
|
||
13.16 alpha levels against **38.18 for the same ramp reversed** — if the shape
|
||
carried no information those would be equal.
|
||
|
||
📌 **A trap noted for whenever this port grows draw-stream tooling:** a 2D draw's
|
||
identity is its **vertex geometry, not its bound texture**, because these sprites
|
||
sample large shared pages. Matching on texture dimensions told them first that no
|
||
flash is ever drawn, then that `ptbase2` and `pteff04` are drawn in frames 75–105
|
||
— which are the intro movie, whose YUV planes are 640×360 targeting 1280×720.
|
||
Two errors, opposite directions, one pass, neither loud.
|
||
|
||
## ✅ The `publisher_logo` residual was a missing black hold, and we had both dismissed it
|
||
|
||
I had carried this as *"0.03 s outside a composite bound, probably a property of
|
||
the bound rather than the game"*, and the Decoder agreed. **We were both wrong,
|
||
and the way it was settled is the point: I stopped reasoning about the bound and
|
||
filmed the transition.**
|
||
|
||
At 0.05 s the port fell straight out of the publisher's fade into the developer
|
||
logos — mean 5.06 → 0.32 at t=4.20, then **5.65 at t=4.25**. There was **no black
|
||
frame at all**, where the oracle measures a 0.17–0.23 s pure-black plateau
|
||
(HANDOFF Q7). The bound was fine. The port was missing a fifth of a second of
|
||
black, and had been since P3.
|
||
|
||
**Authored at 12 units**, because on the boot path there is nothing to read it
|
||
from: `publisher_logo` and `developer_logos` each carry a single `palogo_eff0` —
|
||
a 1280×720 primitive with **one keyframe at t=0**, static, not a transition ramp.
|
||
The menus' quad declares black for 12 units and 12/60 = **0.200 s** sits in the
|
||
middle of the measured range, so the number is the disc's where a screen has one.
|
||
|
||
Filmed after: **t=4.25, 4.30, 4.35, 4.40 all at mean 0** — four black frames,
|
||
0.20 s — then the developer logos at 4.45.
|
||
|
||
| | before | after |
|
||
|---|---|---|
|
||
| publisher interval | 4.26 s, **DIFFERS** | **4.47 s, agrees** |
|
||
| developer interval | 3.62 s, agrees | 3.73 s, agrees |
|
||
|
||
The settled-frame comparisons are untouched, as they should be — this is pacing,
|
||
not pixels.
|
||
|
||
⚠️ **The lesson is about the shape of the dismissal, not the number.** *"A 0.03 s
|
||
miss against a bound composed from two measured ranges plus jitter slack is more
|
||
likely a property of the bound"* is a **plausible** explanation, it was offered
|
||
and accepted by both of us, and it was wrong. The composite bound was the reason
|
||
the miss looked small — the underlying gap was 0.2 s — and a plausible
|
||
explanation for a small number is exactly how a real defect stays hidden. **The
|
||
film cost one command.**
|
||
|
||
## `ptlogo_back2eff3` — recorded, deliberately not acted on
|
||
|
||
The Decoder has reproduced across two independent build-ins that the console
|
||
**never draws `eff3`**: 0 draws against ~5 expected, while `eff1` gets 4, `eff2`
|
||
3 and `eff4` 6. Three explanations are ruled out — sampling phase (`eff3` is
|
||
non-zero over six units against a 2.23-unit step, and frames at t=60.1 and 62.3
|
||
sit inside it drawing `eff2` and `eff4` instead), a draw the log cannot see, and
|
||
a bad position guess (no quad anywhere is within ±30 of the expected 408 width;
|
||
the spectrum jumps 262 → 748).
|
||
|
||
❔ **But *why* is not established** — nothing in `eff3`'s record differs from its
|
||
neighbours: same kind `0x0`, same keyframe shape, same `u4`/`u8`, same scale.
|
||
|
||
🔴 **So the port keeps drawing it, and that is a decision rather than an
|
||
oversight.** Dropping an element the disc declares, on a measurement with no
|
||
mechanism behind it, is authoring a behaviour neither agent can derive — and
|
||
**nothing this port gates on would notice either way**: the flashes live only in
|
||
the build-in, and `verify-capture` compares the settled frame at t=198 where none
|
||
of the five is drawn. Acting would buy no measurable fidelity and cost an
|
||
unexplained exception in `authored/`.
|
||
|
||
**What would change it:** a mechanism in the record, or a gate that measures the
|
||
build-in against a capture. Until then the port is visibly wrong for two frames
|
||
during a build-in nobody compares, which is the cheaper of the two wrongs.
|
||
|
||
## The narrow settle windows are harmless, and I can now say why
|
||
|
||
Adopting the settle instant left `main_menu` and `extras` on per-element `rest()`
|
||
— their windows are 12 units, below the 30-unit bar — and I recorded that as a
|
||
gap. **It is not one.** `rest()` is malformed only for a **transient**, and the
|
||
transients are precisely on the screens whose windows are wide:
|
||
|
||
| screen | window | transients |
|
||
|---|---|---|
|
||
| `title` | 76 | `ptlogo1`/`ptlogo2` ×4, `pteff01`, `ptlogo_back2eff1…5`, `ptlogoall_eff` |
|
||
| `publisher_logo` | 190 | `palogo_sqex_eff` |
|
||
| `developer_logos` | 145 | `palogo_*_eff` ×3 |
|
||
| **`main_menu`** | **12** | **none** |
|
||
| **`extras`** | **12** | **none** |
|
||
|
||
The discriminator is *returning to dark far before the screen's own end* —
|
||
`ptlogo_back2eff1` is dark again at t=58 while the title runs to t=269 — as
|
||
opposed to the ordinary fade-in-hold-fade-out that every menu element has, where
|
||
`rest` at 255 **is** the settled pose and the final 0 is the exit.
|
||
|
||
⚠️ **I am not claiming that as a rule.** Two screens having narrow windows *and*
|
||
no transients could be coincidence; it is n = 2, and n = 2 is where I have gone
|
||
wrong before. What it does mean is that **nothing measurable is being left on the
|
||
table by the 30-unit bar today.**
|
||
|
||
## Refuted, mine — "the menu residual is localised on the `ptloop` sweeps"
|
||
|
||
I have written that twice. It came from reading the **bounding box** of the
|
||
differing pixels, which sat at (441, 230) — the sweep position. Tested by sweeping
|
||
the leaf's phase against the live-menu captures:
|
||
|
||
| `main_menu`, sweep phase | differing |
|
||
|---|---|
|
||
| t=60 (barely on screen) | 0.063 % |
|
||
| t=350 (mid-screen) | **0.183 %** |
|
||
| t≥600 (parked off-screen) | **0.061 %** |
|
||
|
||
✅ Two things fall out. **The capture shows no sweeps**: the port matches best
|
||
when they are off-screen and three times worse when they cross the middle. And
|
||
with them parked the residual's box is **834×358 at (445, 167)** — the *button
|
||
column*, which is where the focus signature lives, not the sweep position.
|
||
|
||
On `extras` the same test moves the box (398×295 at the sweep position at t=70,
|
||
736×525 spread at t=700) while the **magnitude barely changes: 0.192 % against
|
||
0.200 %**.
|
||
|
||
🔴 **So the box moves with the sweeps and the residual does not.** A bounding box
|
||
over scattered pixels tells you where the outermost differing pixels are, **not
|
||
where the difference is** — and I had been quoting it as if it localised a cause.
|
||
`verify-capture` prints that box, so this is a caution about reading my own tool.
|
||
|
||
### And a first piece of evidence on whether the sweeps loop
|
||
|
||
The disc gives one pass, t=0…600, ending parked off-screen at x=1521.
|
||
`ORACLE-CAPTURES.md` says the title's sweeps *"move continuously"*, which I had
|
||
taken as implying a loop. **The idle main-menu capture matches best with them
|
||
off-screen**, which is evidence they run once and park.
|
||
|
||
⚠️ One capture, one screen, and "best match" is a weak instrument for an absence
|
||
— but it is the first evidence either way, and it points against looping. The
|
||
loop-length field the Decoder decoded (`+0x08` of a nested record's header) would
|
||
settle it outright; it is not in the pinned tag.
|
||
|
||
## Refuted — "the developer splash is one composited quad, the bounding box of the three logos"
|
||
|
||
The Decoder observed the game submitting **one 525×259 quad at (378, 155)** on
|
||
the developer splash and read it as the bounding box of the three logos, warning
|
||
that drawing three sprites there draws something the console does not. **The
|
||
arithmetic does not support it**, and the port keeps drawing three.
|
||
|
||
| | bounding box |
|
||
|---|---|
|
||
| the **three logos** | **500×421 at (390, 164)** |
|
||
| `gamearts_eff` + `seta_eff` | **521×261 at (379, 154)** |
|
||
| the observed quad | **525×259 at (378, 155)** |
|
||
|
||
**A 259-tall quad cannot contain the three logos**, which span y 164…585:
|
||
`palogo_anima` alone starts at y = 449, thirty-five pixels below that quad's
|
||
bottom edge. The observed quad matches the union of the two `_eff` **glows** to
|
||
about four pixels in every dimension.
|
||
|
||
⚠️ And those two are **transients** — my own census flagged them, dark again by
|
||
t=45 — so a frame containing that quad is a **build-in** frame, not the settled
|
||
screen. Consistent with a draw capture that starts early, which is exactly what
|
||
theirs does.
|
||
|
||
I cannot see their draw stream, so I have sent the arithmetic rather than a
|
||
verdict. What I will not do is stop drawing an element on a claim whose stated
|
||
identification excludes that element from its own bounding box.
|
||
|
||
## The black hold is 9 units, not 12 — measured in draws rather than luminance
|
||
|
||
I authored 12 from HANDOFF Q7's luminance plateau of 0.17–0.23 s, supported by
|
||
the menus' transition quad declaring black for 12. The Decoder counted **submitted
|
||
quads** instead, which is the better instrument: luminance cannot separate the
|
||
outgoing fade's tail from true black.
|
||
|
||
Frames 21–125 submit `palogo_sqex` fading to alpha 7; **frames 126–129 submit no
|
||
sprite quad at all**; 130–153 fade the developer splash in from alpha 34. Four
|
||
presented frames at 2.284 units/frame — a rate derived from the **disc as its own
|
||
clock**, because that run ran at 13.1 fps against 28 elsewhere — gives **9.1
|
||
units = 0.152 s**, ±1 frame 6.9–11.4.
|
||
|
||
⚠️ **It disagrees with the luminance figure and the disagreement is the point.**
|
||
0.114–0.190 s against 0.17–0.23 s overlaps only at the top, and the true black is
|
||
**shorter** than 9 even so: both boundary frames still carry picture. My 12 was
|
||
also supported *by analogy* — a different screen's quad on a different path,
|
||
where the boot splashes carry no quad at all. **A number that fits by analogy
|
||
loses to one measured in place.**
|
||
|
||
`verify-dwell`'s bound moved with it. Both screens still agree: publisher 4.42 s,
|
||
developer 3.78 s.
|
||
|
||
## The title's sweeps loop — measured, and the field could not have told us
|
||
|
||
The disc gives one pass (`ptloop01` t=0…600, `ptloop02` t=0…720), each ending
|
||
parked off-screen, and the port ran them once. **The oracle says they loop**:
|
||
across two title dwells the sweep quad oscillates over its whole x range and
|
||
resets hard to the same start — one reset in the first dwell, two in the second.
|
||
|
||
🔴 **And the loop-length field could not have settled it, which corrects a hope I
|
||
had stated.** Both records declare exactly their last keyframe time — **slack
|
||
zero** — and *"loops at 600"* and *"runs once for 600 and stops"* write the
|
||
identical header. 92.3 % of records on the disc are in that state, so the field
|
||
discriminates only where there **is** slack, as the plate's 105-in-120 had.
|
||
|
||
Implemented and verified on the two sweeps' **least common multiple**, since they
|
||
have different periods: 600 and 720 realign at **3600 units = 60 s**.
|
||
|
||
| separation | mean difference |
|
||
|---|---|
|
||
| **+3600 units (LCM)** | **0** |
|
||
| +1800 units | 0.438 |
|
||
| +600 units (`ptloop01` only) | 0.100 |
|
||
|
||
⚠️ **Scoped to the title**, because that is where it is measured. The menus
|
||
declare the same 600/720 and nothing on the disc distinguishes them, but my own
|
||
weak evidence points the other way there — sweeping the phase against
|
||
`live-main-menu.png`, the port matches best with the sweeps **off-screen**
|
||
(0.061 %) and three times worse mid-screen (0.183 %), and if they looped the
|
||
sweep is on screen for roughly 73 % of the cycle. **Two weak signals in opposite
|
||
directions is a reason to scope, not to pick.**
|
||
|
||
## The menus' residual is the tone floor, not structure — and `extras` is not really 3× worse
|
||
|
||
`extras` sits at 0.19 % differing against `main_menu`'s 0.06 %, on two screens of
|
||
the same family, and that gap wanted explaining.
|
||
|
||
**Signed difference (port − capture), by cell:**
|
||
|
||
| | x=0 | x=320 | x=640 | x=960 |
|
||
|---|---|---|---|---|
|
||
| `extras` y=169 | **+12.13** | −3.64 | +2.43 | **+10.60** |
|
||
| `extras` y=338 | **+12.29** | +1.36 | +1.42 | **+9.05** |
|
||
| `main_menu` y=169 | **+11.63** | −0.68 | +3.01 | **+10.24** |
|
||
| `main_menu` y=338 | **+11.03** | +3.93 | +2.92 | **+8.84** |
|
||
|
||
**The two screens are nearly identical**, and the port is uniformly **+9 to +12
|
||
brighter in the dark outer columns** — which is exactly the transfer curve I
|
||
measured earlier: γ > 1 in the darks, capture darker than render. There is no
|
||
dipole, no displacement, no missing element.
|
||
|
||
So the 0.06 % / 0.19 % gap is **not a difference in fidelity**. The thresholded
|
||
count only sees pixels differing by more than 64 levels, which are text and
|
||
sprite **edges**; the two screens simply have different amounts of high-contrast
|
||
edge. The *level* disagreement, which is what a tone term produces, is the same
|
||
on both.
|
||
|
||
⚠️ **This is the bounding-box lesson again in a different costume.** I had two
|
||
numbers, 0.06 and 0.19, and took the ratio as meaningful. It is a count of
|
||
threshold crossings, and a count of threshold crossings is not a measure of how
|
||
wrong a screen is.
|
||
|
||
### A diagnostic trap of my own, worth writing down
|
||
|
||
My first pass at this reported **10 of 18 elements "transparent at rest"** on
|
||
`extras` — the buttons, the title, the frames — and looked exactly like a
|
||
missing-element bug. It was not. **`--screen=NAME` without `--time` renders at
|
||
t = 0**, and `pose_at` clamps `t` to `minf(t, settle_units)`, so t=0 stays t=0 and
|
||
every element is still at its first keyframe. Passing `--time=2.0` draws 18 of 18.
|
||
|
||
The tool was right and my invocation was wrong, and the failure looked like a
|
||
serious defect rather than an empty argument. Same family as the instrument traps
|
||
this session has collected — and mine was the one that reported a *worse* problem
|
||
than existed, which is the direction that wastes an iteration rather than hiding
|
||
one.
|
||
|
||
## Refutation attempt — their 239.8-unit figure, checked from my export
|
||
|
||
The Decoder converted the boot's black gap using the disc as its own clock:
|
||
*"`palogo_sqex` declares alpha ≥ 1 for **239.8 units** and is drawn in 105 frames
|
||
→ 2.284 units/frame."* That 239.8 comes from their reading of the record; I have
|
||
the same element in my export and can compute it independently.
|
||
|
||
`palogo_sqex` ramps 0 → 255 over t=15…30 and 32 → 0 over t=251…255. Under the
|
||
linear ramp the port already uses, alpha first reaches 1 at **t = 15.0588** and
|
||
last exceeds it at **t = 254.8750**:
|
||
|
||
**239.816 units.**
|
||
|
||
✅ **Survives, to four significant figures.** It matters more than a spot-check:
|
||
that number is the *denominator* of the units-per-frame conversion behind the
|
||
9-unit black hold I just authored, so an error in it would have propagated
|
||
straight into a constant I ship. Two derivations from different sides of the same
|
||
record agreeing to 0.02 % is what makes that constant safe to hold.
|
||
|
||
## 🔴 The loading screens are black at *every* instant — which proves the layer rule wrong for a layerless element
|
||
|
||
`build_12` and `build_15` have rendered blank since P3, and I had filed it as a
|
||
`rest()` casualty: `pgloading_eff00` is a 1280×720 quad whose `rest` is opaque
|
||
black. **It is not that**, and the corrected keyframe association makes it
|
||
provable rather than suspected.
|
||
|
||
Every element's declared alpha window on `build_12`:
|
||
|
||
| element | opaque / visible | gone by |
|
||
|---|---|---|
|
||
| **`pgloading_eff00`** (black quad) | **t=0 … 38 at alpha 255** | clears at t=48 |
|
||
| `pgloading_loop4` | t=8…32 | 38 |
|
||
| `pgloading_loop1` | t=16…32 | 40 |
|
||
| `pgloading_line` | t=18…26 | 32 |
|
||
| `pgloading_str` | t=22…28 | 34 |
|
||
| `pgloading_loop3` | t=24…26 | 34 |
|
||
| `baseeff`, `eff01`, `eff02`, `loop5` | t≈16…32 | 32–40 |
|
||
|
||
**The quad is fully opaque across the entire span in which any content is up, and
|
||
it only clears at t=48 — by which time every other element has faded to zero.**
|
||
Rendered at t = 20, 30, 36, 40, 42, 44, 46, 50 units with the timeline *playing*,
|
||
the frame is **mean 0 at every one**.
|
||
|
||
So this is not a bad choice of pose. **Under the port's current layer rule there
|
||
is no instant at which this screen shows anything**, and a loading screen that is
|
||
black for its whole life is not what the game does. That is a proof by
|
||
contradiction, not a preference.
|
||
|
||
### The rule under suspicion is mine, and it is narrow
|
||
|
||
`pgloading_eff00` carries `layer_source: "none"` — no layer key at all — and the
|
||
exporter sorts a layerless element **last**, i.e. on top. Its `paint_order` is
|
||
`[6, 7, 8, 9, 1, 5, 4, 2, 3, 0]` with element 0, the quad, drawn last.
|
||
|
||
⚠️ **Every other full-frame primitive in the export has a layer key** —
|
||
`main_menu`, `extras` and `title` all give their `pteff00` `0x00008030` — so this
|
||
rule only ever bites here, which is exactly why it survived eight milestones
|
||
behind two screens nobody draws.
|
||
|
||
❔ **Where a layerless element sorts is a decoding question and I am not
|
||
answering it.** If the game sorts it *first* — behind everything — the screen
|
||
renders and the quad becomes a backdrop rather than a cover, which is what a
|
||
1280×720 black rectangle at the bottom of a loading screen would sensibly be.
|
||
That reading is *consistent* with the contradiction above, which is not the same
|
||
as being established, and I have asked rather than flipped the sort.
|
||
|
||
✅ **What this does settle:** `verify-screen`'s `BLANK` verdict on those two rows
|
||
was the right call. It reports that both renderers drew nothing and that the row
|
||
proves nothing — and `sylpheed-cli` agrees with the port here precisely because
|
||
it shares the assumption under suspicion. Two renderers agreeing, again.
|
||
|
||
## Their `eff3` retraction — my refusal was right, and my refutation found the same bug
|
||
|
||
The Decoder has withdrawn *"the game never draws `eff3`"*. It draws all five, in
|
||
the declared stagger, in both title entries. **And the mechanism was the one my
|
||
developer-splash refutation had already caught one layer down**: a draw batches
|
||
several quads and the log dumps only the first 8 vertices, so min/max over a
|
||
line's vertex list *merges* them. `eff3` (788…1196) lies entirely inside `eff4`
|
||
(447…1196), so the union is exactly `eff4`'s extent and `eff3` vanished with
|
||
nothing anomalous to see. My `525×259` was `gamearts_eff` merged with `seta_eff`
|
||
by the identical mechanism.
|
||
|
||
⚠️ **The part worth carrying is theirs**: three explanations were reported "ruled
|
||
out", and all three were aimed at the wrong failure — the *"a draw the log cannot
|
||
see"* check counted draws with **no** geometry when the hiding place was draws
|
||
with **partial** geometry. **Refuting three wrong hypotheses is not evidence for
|
||
a fourth**, and a list of failure modes written by whoever built the instrument
|
||
is the least likely to contain that instrument's blind spot.
|
||
|
||
Nothing in the port changes: `eff3` was never dropped, and the developer splash
|
||
still draws three sprites.
|
||
|
||
## The forced backdrop: two of sixteen screens were black for their whole life
|
||
|
||
`build_12` and `build_15` — the two dressed loading screens — rendered as **pure
|
||
black at every instant of their declared timeline**. Not at rest, where a wrong
|
||
`rest.t` could explain it: at t = 20, 30, 36, 40, 42, 44, 46 and 50 units with
|
||
the timeline *playing*, mean 0 in every frame.
|
||
|
||
That is not a defect you can attribute to a pose. A screen that is black for its
|
||
entire life is impossible on its face, and it is the kind of impossibility that
|
||
survives a render-vs-render diff: `verify-screen` scored those two rows
|
||
`max 0 mean 0 over3 0 OK` — **the strongest verdict that script has, awarded for
|
||
comparing nothing against nothing.** Both renderers were black because both
|
||
share `implied_layer_key`. The blank guard now in `verify-screen` was written
|
||
after that, and it is what turned the pass into a row that says it proves
|
||
nothing.
|
||
|
||
### The rule, and whose it is
|
||
|
||
It is the **Decoder's**, decoded from the file rather than inferred from the
|
||
render:
|
||
|
||
> An element that covers the screen and is **fully opaque** at some instant
|
||
> cannot paint above anything visible at that instant. Where the elements
|
||
> visible during its opaque span are **all** of them, its position is forced to
|
||
> first.
|
||
|
||
`pgloading_eff00` is a full-screen quad at alpha 255 from t=0 to t=38, clearing
|
||
at t=48; every other element on those screens peaks around t=8–32 and is gone by
|
||
t=32–40 — entirely inside the opaque span. Under a layer-key sort it painted
|
||
over all nine of them, at every instant they existed. Hence black.
|
||
|
||
### What is implemented, and the two limits that are not negotiable
|
||
|
||
`forced_backdrop_first` in `crates/sylpheed-export/src/screen.rs`, as a post-pass
|
||
over `ui_layout::derived_paint_order`. Two restrictions are copied from the
|
||
Decoder verbatim because each one was found by a test that failed:
|
||
|
||
* 🔴 **Elements with no sprite only.** Applied to sprites, the rule claimed 22
|
||
`.t32` textures must sort first *against their own layer keys*. An element's
|
||
alpha says nothing about whether its **texture** covers the screen — most of a
|
||
sprite may be transparent. The assertion that caught this was one the Decoder
|
||
had nearly deleted as over-strict.
|
||
* 🔴 **Not a name heuristic.** `*base*` first / `*eff*` last matches 77 of 80 and
|
||
fails on exactly the three families that cross it: `palogo_eff0`,
|
||
`pgloading_eff00`, `pzeff00`. `palogo_eff0.prm` is named like an overlay and is
|
||
*measured* painting first. The name is not the rule; occlusion is.
|
||
|
||
⚠️ Reach: it assumes straight alpha-over. Blend mode is undecoded, and an
|
||
additive quad at alpha 255 would not occlude. It is a **lower bound on one
|
||
element's position**, not an ordering — 80 elements are forced, 50 are
|
||
constrained but not forced, and this says nothing about those 50.
|
||
|
||
### The controls
|
||
|
||
Both are the Decoder's prior measurements off the running game. No new oracle run
|
||
was made for this change, by either agent.
|
||
|
||
| primitive | measured | our opaque instants | outcome |
|
||
|---|---|---|---|
|
||
| `palogo_eff0.prm` | **first** | 256 (they measured 211) | ✅ forced first |
|
||
| `pteff00.prm` | **last** | **2** (they measured 2) | ✅ still last |
|
||
|
||
`pteff00` is the one that would break if this were implemented as "push every
|
||
layerless element down". It is the fade cover: opaque at t=0 and again at t=269,
|
||
its screen's entry and exit, and transparent for the 253 instants between. The
|
||
constraint never binds it, and it remains last on all four title-family screens.
|
||
|
||
The `palogo_eff0` count differs — 256 against 211 — because we take the opaque
|
||
span to the **screen's** last keyframe (255) and they stop at 210. It changes no
|
||
verdict here, since the element is opaque across the whole span either way, but
|
||
the two spans are not the same span and only one of them can be the screen's.
|
||
Filed in BLOCKED.
|
||
|
||
An element **holds its final pose to the end of the screen**; it does not vanish
|
||
at its own last keyframe. Reading `palogo_eff0`'s span as `0..=0` — it declares a
|
||
single keyframe — would make the splash's backdrop a one-instant event rather
|
||
than the thing on screen for the whole splash. Rendering `build_12` confirms the
|
||
hold directly: the frame is constant from t=30 to t=60 with the timeline running.
|
||
|
||
### What changed, measured
|
||
|
||
* `build_12`/`build_15`: mean 0 at every instant → ramps in over t=0…30 and
|
||
holds (mean 1.95, max 214.5). The two BLANK rows are gone from `verify-screen`.
|
||
* The splashes are unmoved against the **oracle**: `publisher_logo` 0.01 %,
|
||
`developer_logos` 0.01 % differing region, unchanged before and after.
|
||
⚠️ That is **non-regression, not confirmation** — `verify-capture` poses at the
|
||
settle instant, and the ordering does not necessarily bind there. The evidence
|
||
for the rule is the Decoder's two controls and the impossibility of a
|
||
permanently black screen, not this row.
|
||
* Six `verify-screen` rows now DIFFER: the six screens the rule touches. The
|
||
reference `sylpheed-cli` builds from the workspace `sylpheed-formats`, which
|
||
does not have the rule. **That disagreement is expected and must not be tuned
|
||
away** — it ends when a pinned tag carries the Decoder's change, at which point
|
||
this post-pass is deleted rather than kept in two places.
|
||
|
||
It also explains 36 builds the Decoder had filed as "coming out one colour":
|
||
`pzeff00.prm` is forced first in 32 of 32 of them. Those were wiped by our own
|
||
sort.
|
||
|
||
## Refutation attempt — the forced-backdrop rule's quantifier, and whether it misses a case
|
||
|
||
The rule fires only when **all** other elements are visible during the opaque
|
||
span. That is a strict quantifier, and a strict quantifier fails quietly: an
|
||
element that is a full-screen opaque backdrop but misses the bar by one dark
|
||
element would keep its layer-key position and go on hiding the screen, exactly
|
||
the defect the rule was written to fix. So the question worth asking of somebody
|
||
else's rule is not "is it right" but **"is it enough"** — and that one I can test
|
||
without an oracle, over every layerless full-screen element in `GP_TITLE`.
|
||
|
||
| screen | element | opaque instants | others visible | forced |
|
||
|---|---|---|---|---|
|
||
| `build_12`, `build_15` | `pgloading_eff00` | 39 | **9 / 9** | ✅ |
|
||
| `developer_logos` ×2 | `palogo_eff0` | 211 | **6 / 6** | ✅ |
|
||
| `publisher_logo` ×2 | `palogo_eff0` | 256 | **2 / 2** | ✅ |
|
||
| `title`, `title_jp` | `pteff00` | 2 | 3 / 23 | — |
|
||
| `main_menu` ×2 | `pteff00` | 2 | 7 / 15 | — |
|
||
| `extras` ×2 | `pteff00` | 2 | 5 / 17 | — |
|
||
| every screen | `pteff02` | **0** | — | — |
|
||
|
||
**The rule survives, and the margin is the reason.** Nothing sits near the
|
||
boundary. Every element that fires does so at **100 %** of the others; every
|
||
element that does not is at 13–47 %, and `pteff02` never reaches alpha 255 at all,
|
||
so no quantifier could fire on it. There is no borderline case in this archive
|
||
for a stricter or looser reading to disagree about — which is the answer I could
|
||
not have gotten by re-checking the six screens where it already fired.
|
||
|
||
It also reproduces the Decoder's **second** control number exactly: they report
|
||
`pteff00.prm` forced below **3 of 23**, and `title` measures 3 of 23 here. With
|
||
their opaque-instant count of 2 already matched, two of their three published
|
||
figures for that control now come out of an independent implementation
|
||
unchanged. The third — `palogo_eff0`'s 211 against our 256 — remains the span
|
||
disagreement filed in `BLOCKED.md`, and note that the 211 is the number our
|
||
`developer_logos` row *does* reproduce. That is worth saying plainly: **the
|
||
disagreement is not a constant offset**, so "they stop 45 instants early" is not
|
||
the explanation, and whatever it is differs per screen.
|
||
|
||
⚠️ What this does **not** test: whether the rule is right about the 50 elements it
|
||
calls constrained-but-not-forced, and whether alpha-over is the blend mode. Both
|
||
are the Decoder's to settle. It tests completeness within one archive, which is
|
||
the half I can reach.
|
||
|
||
## The 256/211 was never a disagreement — and my own census had already said so
|
||
|
||
The Decoder answered the span question, and the answer is that **both numbers are
|
||
right**. `palogo_eff0.prm` appears on *both* splashes: the publisher pair
|
||
(entries 10, 13) runs to t=255 and gives **256** instants, the developer pair
|
||
(11, 14) runs to t=210 and gives **211**. I computed the publisher; their page
|
||
quoted the developer.
|
||
|
||
Worth noting how that came out, because it is the one part I can claim: the
|
||
refutation census in the section above reported **256 on `publisher_logo` and 211
|
||
on `developer_logos`, in the same table**, and concluded *"the disagreement is not
|
||
a constant offset, so 'they stop 45 instants early' is not the explanation"*. The
|
||
resolution was already sitting in my own output before their reply arrived. What
|
||
I got wrong was **filing it as a disagreement at all** — I compared one of my two
|
||
numbers against one of theirs and did not check the other row of my own table
|
||
against it. A per-screen quantity needs the screen named next to it, and my
|
||
`BLOCKED.md` row named neither.
|
||
|
||
### The span convention, confirmed rather than assumed
|
||
|
||
> The span is `0 ..= max keyframe time over EVERY element in the build`, and an
|
||
> element **holds its final pose** past its own last keyframe.
|
||
|
||
That is exactly what `forced_backdrop_first` implements, so nothing changes in
|
||
the port. Two things they add that are worth having in writing:
|
||
|
||
* the hold **is not a convenience**: a group holds at its last keyframe rather
|
||
than looping, and the header's `+0x08` never falls short of the last keyframe —
|
||
the slack *is* that hold;
|
||
* `+0x08` and the elements' maximum are **interchangeable**, zero disagreements
|
||
disc-wide. We use the elements' maximum. It stays, and this paragraph is the
|
||
note that the two were *checked* equivalent rather than assumed so.
|
||
|
||
### The hold decides 55 % of verdicts, and the oracle picks it
|
||
|
||
Reported by them over 130 keyless full-screen primitives (their measurement, not
|
||
reproduced here — their page is `docs/re/structures/ui-forced-backdrop.md`, ⚠️ not
|
||
yet on `main` as of this commit):
|
||
|
||
| alternative convention | verdicts changed |
|
||
|---|---|
|
||
| span = the header's declared `+0x08` | 0 |
|
||
| span = the primitive's own last keyframe | **72** |
|
||
| elements **gone** after their last keyframe | **72** |
|
||
|
||
So the reading I very nearly shipped — span = the element's own last keyframe —
|
||
would have changed **55 %** of the verdicts disc-wide. My first implementation
|
||
did exactly that, and `palogo_eff0` is the case that catches it: a *single*
|
||
keyframe at t=0, opaque for one instant, nothing else up yet, so the rule calls
|
||
it **free** — against a game measured painting it first. The convention is not a
|
||
matter of taste; the oracle rules one out.
|
||
|
||
✅ None of our six verdicts rests on it. `pgloading_eff00` is first under all four
|
||
conventions and `pteff00` is free under all four; only `palogo_eff0` moves, and
|
||
only under the convention its own measured order excludes.
|
||
|
||
### The sharper form of the `verify-screen` failure
|
||
|
||
Theirs, and it is better than how I wrote it: those two solid-black frames
|
||
**were not two witnesses**. Both renderers read `implied_layer_key`, so their
|
||
agreement carried *no information* — a correlated failure is indistinguishable
|
||
from a confirmation. What caught it was not a second opinion but that the agreed
|
||
answer was **impossible on its face**. *"Is this result even possible?"* beats
|
||
*"do two implementations agree?"* whenever the two share an ancestor, and in this
|
||
project they nearly always do.
|
||
|
||
### The boot gate still holds
|
||
|
||
Re-run after the reorder, since the rule changed the paint order of the first two
|
||
screens in the boot path: `publisher_logo` → `developer_logos` → `ADV` (skipped at
|
||
8.12 s) → `title` + `press_start` overlay, plate at full alpha at t=236, complete
|
||
at 10.83 s holding on the title. No script errors.
|
||
|
||
## The clock freezes at settle — the port's settle window, seen from the other side
|
||
|
||
The Decoder measured `GP_TITLE` build 4 in the draw stream and found the
|
||
top-level clock **advances through the build-in, stops inside the settle window,
|
||
and holds**. The exit ramp is not on a timer; it plays when something makes the
|
||
screen leave.
|
||
|
||
**Their interval is `[160, 236]`. The exporter computes `title`'s settle window as
|
||
`[160, 236, 198]`.** Those are the same two numbers, and they were not obtained
|
||
the same way: mine is the longest keyframe-free interval over top-level elements,
|
||
read out of the file with no game running; theirs is where a captured clock stops
|
||
advancing. A heuristic I adopted because it collapsed three pixel residuals at
|
||
once turns out to name the exact interval the game holds in. That is the first
|
||
evidence for the settle instant that does not come from the port's own renderer.
|
||
|
||
The file agrees from a third direction: `ptcopyright` sits at alpha 255 from
|
||
t=160 to t=238 — it reaches full opacity precisely as the window opens.
|
||
|
||
### Refutation attempt — their declared spans, checked against the file
|
||
|
||
Their draw-stream argument cites what the file *declares*, which I can check
|
||
without a capture:
|
||
|
||
| their claim | the file | |
|
||
|---|---|---|
|
||
| `ptlogo1` declares an exit at **t=264** | last keyframe t=264, alpha 0 | ✅ exact |
|
||
| `ptcopyright` alpha ≥ 1 for **106 units**, t=138…244 | keyframes at t=138 and t=244, alpha 0 at both | ✅ endpoints exact |
|
||
|
||
Both survive. One quibble, and it is only that: the instants with alpha ≥ 1 number
|
||
**105**, t=139…243, not 106 — the endpoints they bracket with are themselves
|
||
alpha 0, so 106 is the keyframe span rather than the visible one. It changes
|
||
nothing in their argument, whose force comes from 1 050 frames against either
|
||
number.
|
||
|
||
### What this costs the port, and it is not nothing
|
||
|
||
`authored/timing.json` and `authored/flow.json` both said *"a screen's dwell is
|
||
its OWN keyframe group"* and *"the pacing is the disc's own"*. 🔴 **That is
|
||
refuted.** Build 4 declares about 120 presented frames and dwelled ~1 100 — nine
|
||
times its own timeline. The group is not the dwell.
|
||
|
||
The **decision** is unchanged and still right: hold zero extra rather than invent
|
||
a number. What was wrong is what the port claimed for it. Leaving when the group
|
||
ends is not reproducing the disc's pacing — it is leaving at the moment the game
|
||
starts waiting. Both files now say so, and so does `boot.gd`, where the comment
|
||
had inherited the same claim.
|
||
|
||
✅ The *structure* was already right, and this is the part the measurement
|
||
confirms: `_advance` is caused by the next screen arriving, never scheduled off a
|
||
timer, and the port's own comment already read *"a screen plays itself out because
|
||
something is taking its place"*. `exit_ramp_units` was deleted for an unrelated
|
||
reason and its absence is now doubly justified.
|
||
|
||
⚠️ **Only build 4 is measured, and build 4 is the one screen where the port is
|
||
unaffected** — it is the boot's end state and holds indefinitely. The two screens
|
||
this actually governs, `publisher_logo` and `developer_logos`, have no measured
|
||
dwell at all. The port's boot is **known too fast [refuted] on both, by an unmeasured
|
||
amount**. Nothing here is a number for them.
|
||
|
||
> 🔴 **WITHDRAWN, and this paragraph stood for days after the withdrawal was
|
||
> written.** "Known too fast on both" is false: the splash dwells are **declared
|
||
> on the disc** — publisher t=0…255, developer t=0…210 — corroborated over three
|
||
> cold boots to 1.1 %, and the port was already emitting each declared value plus
|
||
> the 9-unit black hold, *exactly*. See
|
||
> [Withdrawn — "the boot is known too fast [refuted]"](#-withdrawn--the-boot-is-known-too-fast-the-splash-dwells-are-declared-and-the-port-was-already-playing-them).
|
||
>
|
||
> Found 2026-08-30 by the Decoder's rule: **grep the corpus for the claim, not
|
||
> for the file you were working in.** I wrote the withdrawal as a new section and
|
||
> left the original assertion untouched, so a reader arriving here first got the
|
||
> dead answer with nothing to warn them — the same failure as a correction that
|
||
> never reaches the manifest, one layer up.
|
||
|
||
## 🔴 Withdrawn — "the boot is known too fast [refuted]". The splash dwells are declared, and the port was already playing them
|
||
|
||
Last iteration I took the Decoder's build-4 measurement — declared ~120 presented
|
||
frames, dwelled ~1 100 — and wrote into three files that the port's boot is
|
||
**"known too fast [refuted] on both splashes, by an unmeasured amount"**. That is
|
||
**withdrawn**. It was wrong, and the way it was wrong is the interesting part.
|
||
|
||
They then measured the splashes directly, over 3 cold boots:
|
||
|
||
| splash | declared | at 60 units/s | corpus wall clock |
|
||
|---|---|---|---|
|
||
| publisher (entries 10, 13) | t = 0…**255** | 4.250 s | 4.30 / 4.60 / 4.37 |
|
||
| developer (entries 11, 14) | t = 0…**210** | 3.500 s | 3.51 / 3.50 / 3.37 |
|
||
|
||
The developer agrees to **1.1 %**, two of its three runs to 0.3 %. And the port
|
||
emits **4.400 s and 3.650 s** — each declared value plus the 9-unit black hold,
|
||
exactly. ✅ **The pacing was right the whole time and no code changes.**
|
||
|
||
### What I actually did wrong
|
||
|
||
Not the arithmetic — the generalisation. Build 4 is the **title**, whose exit is
|
||
caused by something outside its timeline, so it holds. A splash's exit is caused
|
||
by nothing, so it plays its declared timeline and leaves. **The title is the
|
||
exception, not the rule.** I had one screen, it was the one screen in the boot the
|
||
port is structurally unaffected by, and I used it to overturn the two it governs.
|
||
|
||
I wrote at the time that a ratio from one screen is not a unit of pacing, and
|
||
declined to scale the splashes by nine. That refusal was right and is the only
|
||
reason this cost nothing but documentation. But refusing to apply the number
|
||
while adopting the *conclusion* it implied was half a caution: I still let one
|
||
screen's behaviour rewrite what the port claimed about two others. **The correct
|
||
move was to file build 4 as measured and leave the splashes alone**, which is
|
||
what the file now says.
|
||
|
||
### And the unit stays units
|
||
|
||
🔴 The Decoder's own container timed those same two dwells **15–20 % longer** than
|
||
both the declared values and the corpus — same disc, same declared timeline — and
|
||
three independent readings of that container's frame rate disagree with each
|
||
other. A seconds figure is one emulator's pacing on one run. The declared units
|
||
are on the disc. `authored/flow.json` `dwell` therefore takes **units**, and only
|
||
for a screen measured to wait beyond its group.
|
||
|
||
This also retires the "two timestamps would settle it" ask I filed: timestamps
|
||
were the wrong thing to author, and the measurement's own result says so.
|
||
|
||
## Refutation attempt — their two splash boundaries are not anchored the same way
|
||
|
||
Their draw stream reports *"publisher wordmark frames 6–119"* and *"developer
|
||
glows 123, wordmarks 140–209"*. Taking those spans against the declared groups:
|
||
|
||
| splash | declared units | their frames | units / frame |
|
||
|---|---|---|---|
|
||
| publisher | 255 | 6…119 = 114 | **2.237** |
|
||
| developer | 210 | 123…209 = 87 | **2.414** |
|
||
|
||
**Within one continuous boot, on one guest, those should be the same number.**
|
||
They differ by **7.9 %**, and the discrepancy runs the same direction as the
|
||
error split they have open — publisher +4.1 % where the developer is 1.1 %.
|
||
|
||
The file suggests why, and it is not the guest's clock: **the two boundaries are
|
||
anchored on different elements.** The developer span starts at its *glows*; the
|
||
publisher span is reported as starting at its *wordmark*. Those are 15 units
|
||
apart — every wordmark on both splashes is `alpha > 0` from t=16, every glow from
|
||
t=1 — and the publisher **has a glow**, `palogo_sqex_eff`, visible t=1…44,
|
||
structurally identical to the developer's three. So either frame 6 is the
|
||
publisher's glow and is mislabelled, or the publisher span genuinely starts 15
|
||
units later than the developer's and the two are not comparable quantities.
|
||
|
||
⚠️ I cannot tell which from here — it is their log, and the answer is one grep for
|
||
`palogo_sqex_eff` in it. Recording it because a 7.9 % internal inconsistency and
|
||
an unexplained 4.1 % error on the same screen are more likely one problem than
|
||
two. ⚠️ It also does **not** touch the corpus comparison, which is a separate
|
||
instrument (3 cold boots, not this draw log); their 4.1 % may still be real.
|
||
|
||
## Their corrected boundaries check out against the file — all six, exactly
|
||
|
||
The Decoder found the cause of the 7.9 % I reported, and it was worse than the
|
||
anchor mismatch I proposed: **the developer splash batches six quads into one
|
||
draw and their log dumps only the first two.** While the three glows are alive
|
||
they occupy that prefix, so the three wordmarks are invisible to the log until
|
||
the glows stop at t=45. *"Developer wordmarks first drawn at frame 140"* was the
|
||
logging prefix shifting, not the game. The anchor difference I found was a
|
||
symptom; the truncation was the cause. It is also what hid `palogo_anima`.
|
||
|
||
Their fix is to count `indices / 4`, which the 8-vertex dump cap cannot touch.
|
||
Every one of the six resulting calibration points matches this export:
|
||
|
||
| splash | their transition | their t | export |
|
||
|---|---|---|---|
|
||
| publisher | 1→2 quads | 15 | `palogo_sqex` joins at **t=16** |
|
||
| publisher | 2→1 | 45 | `palogo_sqex_eff` ends **t=44** |
|
||
| publisher | last drawn | 255 | group ends **t=255** ✅ |
|
||
| developer | 3→6 quads | 15 | three wordmarks join at **t=16** |
|
||
| developer | 6→3 | 45 | three glows end **t=44** |
|
||
| developer | last drawn | 210 | group ends **t=210** ✅ |
|
||
|
||
The two 15-vs-16 rows are a half-open boundary, not a disagreement: they name the
|
||
last frame at the old count, the export names the first instant at the new one.
|
||
|
||
✅ A second thing falls out that neither of us was looking for: their quad counts
|
||
are **1 and 2** on the publisher against **3 and 6** on the developer, and a
|
||
count restricted to *sprite-bearing* elements reproduces exactly that. So
|
||
`palogo_eff0` — the layerless forced backdrop — is **not in the batch they log**,
|
||
confirmed from the file. Their instrument and this export agree on which element
|
||
is the odd one out, having disagreed about it in every earlier iteration.
|
||
|
||
### Refutation attempt — does the drift actually explain the corpus's 4.1 %?
|
||
|
||
Their four segment rates recompute exactly (1.765 / 2.165 / 2.308 / 2.357; the
|
||
developer's two agreeing to 2.1 %, the run rising 33.5 %). The explanation is that
|
||
the publisher runs in the first seconds where the rate is furthest from its later
|
||
value. Testing what that predicts for the *corpus*:
|
||
|
||
| publisher ÷ developer | ratio |
|
||
|---|---|
|
||
| declared (255 ÷ 210) | 1.214 |
|
||
| **their drift predicts** | **1.369** |
|
||
| corpus, 3 cold boots | **1.278** |
|
||
|
||
**Sign confirmed, magnitude not.** The corpus ratio does sit above declared, which
|
||
is what the drift predicts and is real evidence. But their container's drift
|
||
would inflate it by 12.8 % where the corpus shows 5.3 % — roughly 2.4× too
|
||
strong. So drift of *some* size is doing the work; drift of *their* size is not.
|
||
|
||
⚠️ And the reason to be careful here is that the move is the one I just got wrong:
|
||
the 4.1 % is a property of the **corpus**, a different instrument (3 cold boots,
|
||
elsewhere), and the drift was measured in **their container**. Transferring it is
|
||
exactly what I did carrying build 4 onto the splashes. A general warm-up is
|
||
plausible for any emulator, so this is not baseless — but it is unconfirmed for
|
||
the corpus, and the magnitude gap is the evidence that the corpus's drift is not
|
||
theirs. It cannot be closed without the corpus's own frame log.
|
||
|
||
✅ Untouched by any of it: the declared **255** and **210**, and the port's
|
||
4.400 s / 3.650 s. Neither uses their draw log.
|
||
|
||
### The guard this puts on `keyframe_units_per_second`
|
||
|
||
⚠️ *"No single units-per-frame figure describes a run"* is a statement about
|
||
**emulator presentation pacing**, and a later reader could easily take it as
|
||
grounds to revisit the port's `60`. It is not. 60 is the **game's logical unit
|
||
rate** (HANDOFF Q1, measured), the port renders at its own frame rate and
|
||
converts through it, and guest pacing cannot reach it. `authored/timing.json` now
|
||
says so at the constant itself, where someone about to change it will read it.
|
||
|
||
## The n=1 disclosure, and the one port constant that rests on a single run
|
||
|
||
The Decoder disclosed that their `ARM=early` capture silently loses its trigger
|
||
**~40 % of the time** — two of five runs logged `ARMED EARLY` and produced no
|
||
draw log at all, indistinguishable in the session log from a run that armed. So
|
||
every draw-stream figure of theirs is **n = 1**.
|
||
|
||
That is worth more than the number it was attached to, because the port authors
|
||
constants from those runs. Auditing which: exactly one,
|
||
`black_hold_units = 9`. Everything else comes from the disc (the declared dwells
|
||
255/210, the settle window, the plate period), from the exporter, or from
|
||
multi-sample measurements (Q1's unit rate over seven frames, Q5's navigation).
|
||
|
||
**9 is not wrong, and three of its supports have moved.** Its conversion used a
|
||
105-frame count their own truncation fix has revised to 114; its second
|
||
corroboration (2.231) is the figure behind their retracted plate period; and a
|
||
run-average units-per-frame is the wrong shape for a 3–4 frame event now that the
|
||
rate is known to rise 33 % across a boot. Redone on their corrected local
|
||
segments, their two runs give **8.95** and **6.71** units.
|
||
|
||
⚠️ Those two were reconciled as replicating "within the ±1 both are quantised
|
||
to". **Overlapping error bars are not agreeing central values** — one frame is a
|
||
third of this quantity. The range is ~6.5–9.2 and the port sits at the top of it.
|
||
|
||
The value stays. Changing it would be my arithmetic on their instrument, and this
|
||
port does not author a number the corpus has not given; it is filed as a proposal
|
||
in `BLOCKED.md` with the one run that would settle it. ✅ And what is not in doubt
|
||
is that the hold is **real**: until this was implemented the port had no black
|
||
frame at all where the oracle measures a plateau.
|
||
|
||
### Their statistics, checked
|
||
|
||
Their per-boot excesses reproduce exactly: **+0.89 %, +8.24 %, +6.79 %**, spread
|
||
7.35 pp, wider than the 5.30 pp gap under test — so boot 1 alone essentially *is*
|
||
the declared ratio. Their concession is right and my 2.4× cannot carry the weight
|
||
I gave it.
|
||
|
||
One refinement, which cuts **against** their concession rather than for it: their
|
||
2.3 σ uses the *population* SD (3.178). At n = 3 the sample SD (3.892) is the
|
||
right estimator, giving **1.89 σ** — their run is *less* of an outlier than they
|
||
credited themselves with. Testing the other question, whether the corpus mean
|
||
differs from their prediction, gives t = 3.27 on 2 df, p ≈ 0.08. **Neither
|
||
framing reaches significance**, which is where both of us landed anyway.
|
||
|
||
📌 Their sharpening of the instrument point is the keeper: a truncated log and a
|
||
`--screen=NAME` render at t=0 both return a **complete, well-formed answer to a
|
||
different question**. That is why neither looks like an error — nothing inside
|
||
either view can tell you it is not the view you asked for.
|
||
|
||
## P6 gate — sound on the P5 walk, verified, and the tool I nearly shipped instead
|
||
|
||
`tools/port/verify-menu-audio`. Until now the evidence that P6's gate was met was
|
||
that `audio.play("move")` appears in `boot.gd` — evidence that a *call is
|
||
written*, not that a sound reaches the bus. Those differ, and this project has
|
||
the case to prove it: the black hold was implemented, called, and emitted nothing
|
||
for five milestones.
|
||
|
||
It needs no sound card. Godot records the Master bus to a WAV under the Dummy
|
||
driver. Three runs on `main_menu`: the walk (`down,down,accept,cancel,up`) and
|
||
two controls — `wait`×5 for the bed alone, and `left`×5 for **presses that reach
|
||
`_unhandled_input` and are bound to nothing** (Q5: left/right do nothing).
|
||
|
||
| check | result |
|
||
|---|---|
|
||
| a dead press is silent | **bit-identical** to the bed alone, 114 688 samples |
|
||
| `move` on the bus | r=0.201 at 1.85 s, bed-only 0.016, margin **+0.185** |
|
||
| `confirm` | r=0.945 at 2.14 s, bed-only 0.371, margin **+0.574** |
|
||
| `back` | r=0.660 at 2.42 s, bed-only 0.195, margin **+0.465** |
|
||
| cue order vs script order | **consistent** |
|
||
|
||
The order is the strongest line and it is free: the correlator is never told
|
||
where to look, so three different templates landing in script order is three
|
||
independent searches agreeing with the log. `move`'s absolute r is low because it
|
||
is the quietest cue under the loudest part of the bed; the margin over the same
|
||
template against the bed is what carries it.
|
||
|
||
🔴 **What it cannot conclude, and must never be read as:** that these are the cues
|
||
the *game* plays. That binding is HANDOFF Q8, the Decoder's, and nothing here
|
||
re-measures it. This tool cannot tell a correct cue from a confidently wrong one.
|
||
|
||
### The instrument I nearly shipped
|
||
|
||
The first version counted envelope bursts above a multiple of the bed level. It
|
||
reported **4 cues on one run and 0 on the next, from the same script** — its
|
||
answer was set by two hand-picked constants, the multiple and a minimum run
|
||
length, and the bed level is not constant across a run. I caught it only because
|
||
I ran it twice.
|
||
|
||
The replacement has no such constant: **the exported cue file is its own
|
||
template**, the search covers the whole recording, and the verdict is a margin
|
||
over that same template matched against the bed-only control.
|
||
|
||
⚠️ Cue *length* is deliberately not asserted. The audible part of a cue is far
|
||
shorter than its wave — 0.12–0.38 s against authored 0.344–1.016 s — because the
|
||
bed masks the tail. "Elevated for 0.13 s" is a fact about the bed, not the cue,
|
||
and I came close to filing that gap as a defect.
|
||
|
||
### A check that could not be made independent
|
||
|
||
I tried to verify Q8's cue durations against the exported waves. They agree
|
||
exactly — 0.533 / 1.016 / 0.344 — **and the agreement is worthless**: the
|
||
exporter decodes from Q8's own offsets and packet counts, so the duration is
|
||
determined by the claim under test. It confirms the export is faithful to the
|
||
authored table, nothing more. Recording it because "I checked and it matched" is
|
||
exactly what a correlated instrument feels like from the inside.
|
||
|
||
## Their `.tbm` self-refutation does not reach this archive — and it fixes my guard anyway
|
||
|
||
The Decoder downgraded 38 of the forced-backdrop rule's 80 verdicts from decoded
|
||
to inferred: those elements are `.tbm`, declaring fade `ffffffff`, and a solid
|
||
white quad painted first at alpha 255 would make the screen white. No screen is
|
||
white, so a `.tbm`'s white is a modulation **on a texture** and its element alpha
|
||
says nothing about coverage — the `.t32` mistake one file extension further out.
|
||
|
||
✅ **No verdict the port ships is affected.** All six forced elements here are
|
||
`palogo_eff0.prm` and `pgloading_eff00.prm`, `role: primitive`, `kind_raw 0x10`,
|
||
fade `0xff000000`. They fall in the 42 that stay decoded. And **no layerless
|
||
full-screen element anywhere in `GP_TITLE` has a non-black fade** — checked
|
||
across all 16 screens, so the downgrade cannot touch this archive.
|
||
|
||
But their diagnosis applies to my code regardless: *an element's alpha is not its
|
||
texture's opacity, and only an untextured primitive makes the two the same fact*.
|
||
My guard was `sprite.is_none()` — a **symptom** test, the same shape as the one
|
||
they say fixed their symptom and not their cause. It would keep admitting a
|
||
`.tbm` that this exporter happens not to emit a sprite for.
|
||
|
||
The guard is now the positive test, `role == "primitive"`. It changes no verdict
|
||
today — the six are identical before and after, 16 screens still validate — and
|
||
it is correct by construction if the corpus grows.
|
||
|
||
⚠️ Not adopted from their message: their reading that the blend question now
|
||
narrows to `pbafc.prm`. That is theirs to settle and the port draws no additive
|
||
quad either way.
|
||
|
||
## Coverage is now tested per instant, because scale animates
|
||
|
||
The Decoder found that `forced_backdrop` judged screen coverage from the declared
|
||
size alone, ignoring scale — and the disc carries its own counterexample.
|
||
`pbafc.prm` declares **844×600 at alpha `ff`**, which reads as a screen-filling
|
||
cyan wash; it is scaled **2 % × 3 %** and draws about **17×18 px**, strobing and
|
||
travelling x=178→291. A moving glint. A rule reading its declared size would call
|
||
it screen-covering.
|
||
|
||
The port had the same gap and it is closed. `scale_at` interpolates scale on the
|
||
same linear ramp as the fade, and coverage is folded **into the opaque-instant
|
||
test** rather than checked once: an instant counts only where the element is both
|
||
alpha 255 *and* covering. That is the rule's own wording — "covers the screen
|
||
**and** is fully opaque **at some instant**" — where the previous code tested the
|
||
two halves at different times.
|
||
|
||
The static size prefilter is now deliberately *not* a rejection: an element scaled
|
||
**above** 100 could cover the screen from a smaller declared size, so rejecting on
|
||
declared size would have replaced one version of the bug with its mirror.
|
||
|
||
✅ **No verdict moves.** Six forced elements before and after; 16 screens validate;
|
||
the oracle figures are identical to the digit (`publisher_logo` 0.01 %,
|
||
`developer_logos` 0.01 %, `main_menu` 0.07 %, `extras` 0.19 %, `title` 0.26 %).
|
||
Their claim that all 80 forced instances sit at scale 100 reproduces on the
|
||
GP_TITLE subset, and more strongly: **no layerless full-screen element anywhere in
|
||
this archive has a non-100 scale on any keyframe.**
|
||
|
||
It is in for the reason they gave, which is the right one: the data that would
|
||
break it demonstrably exists on this disc. That is a better argument than a
|
||
failure would have been, because it does not require the bug to happen first.
|
||
|
||
### Their blend-robustness argument, checked
|
||
|
||
They classify the blend mode **undecodable with reach** and argue the rule does
|
||
not depend on it, for a black quad:
|
||
|
||
| | drawn **first** | drawn **last** |
|
||
|---|---|---|
|
||
| alpha-over, α=255 | correct | blanks the screen |
|
||
| additive, α=255 | correct — adds nothing | correct |
|
||
|
||
The table holds. An additive black quad contributes nothing at any position, so
|
||
both orders are correct under it; only alpha-over distinguishes them, and it
|
||
picks *first*. **"First" is right under both hypotheses, "last" under one** — so
|
||
`forced_backdrop`'s verdict is robust to a question neither of us can close.
|
||
|
||
It also explains a detail of the original bug that I had not accounted for:
|
||
"layerless sorts last" was *wrong* under alpha-over and merely *pointless* under
|
||
additive, which is why those screens came out **solid black** rather than
|
||
**empty**. The symptom was diagnostic of the blend mode all along.
|
||
|
||
⚠️ Not evidence that the blend is alpha-over, and I am not recording it as such.
|
||
It is the reason the port can stop waiting on it. `pbafc.prm` remains the sole
|
||
additive candidate and is outside the rule at 17×18 px; the port draws no
|
||
additive quad either way.
|
||
|
||
## P7 gate — the new-game intro plays and returns, and a defect I nearly invented
|
||
|
||
`--menu=main_menu --script=accept` walks the P7 path: Ⓐ on NEW GAME announces the
|
||
two measured screens this export skips, opens `S00A`, plays it to its natural end
|
||
at **93.33 s** against the manifest's 93.9, and returns to the title. Nine film
|
||
frames across the movie are distinct and non-black (mean 10 → 140). The gate asks
|
||
for "plays, then returns to a defined state"; that is both halves.
|
||
|
||
### The near-miss, which is the part worth keeping
|
||
|
||
Checking that the movie's audio actually reached the Master bus, I correlated the
|
||
recording against the exported `S00A.ogv` audio and got **r = −0.0068 at the
|
||
movie's known start**, with the correlator passing its own positive control at
|
||
**r = 1.0000**. A working instrument returning zero at the right place. I was one
|
||
step from filing *"the movie's own audio never reaches the bus"* — which would
|
||
have been a serious P4/P7 defect, and the inverse of the human's original report
|
||
that the intro "plays music but no voice".
|
||
|
||
It is false. Re-run on the **boot** path, where no menu bed is playing:
|
||
|
||
| against the bed-free recording | r | at |
|
||
|---|---|---|
|
||
| `ADV` voice | **0.8855** | movie start 7.6 s |
|
||
| `ADV` movie audio | **0.4178** | movie start 7.6 s |
|
||
|
||
Both present, at the same start. The movie's audio is simply quieter than the
|
||
voice mixed on top of it, and in the P7 run the **menu bed masked it below
|
||
detection** while the louder voice survived. The bed carries across into the
|
||
movie — `play_bed` is documented as carrying across submenus — so the P7 run was
|
||
never a clean measurement.
|
||
|
||
🔴 **The lesson is about the control, not the bed.** I validated the correlator on
|
||
clean data and then ran it on masked data. A positive control proves the
|
||
instrument works *on the material it was given*; it says nothing about whether
|
||
the instrument can see through an interferer that was not in the control. **A
|
||
negative result needs its own positive control under the same masking**, and this
|
||
is the third time in this corpus that an unvalidated negative nearly became a
|
||
finding — after "10 of 18 elements transparent at rest" and the burst counter that
|
||
read 4 cues on one run and 0 on the next.
|
||
|
||
`tools/port/verify-video-audio`'s header already warned that a fidelity
|
||
comparison needs cross-correlation alignment *and* an agreed downmix. It was
|
||
right, and I would add the third condition it did not know to state.
|
||
|
||
⚠️ What is **not** settled: whether the bed *should* carry into the movie and on
|
||
to the title after it. It does, it is authored that way, and nobody has watched
|
||
the game do either. That is a separate question from this one and is not filed as
|
||
a defect — only as unmeasured.
|
||
|
||
✅ Also checked, from their `compose` finding: `GP_TITLE` declares **115 `.t32`,
|
||
45 `.rat`, 18 `.prm` and no `.tbm` at all**, and every non-primitive element has a
|
||
resolvable sprite. The "draws no pixels for an unresolvable element" hazard —
|
||
the shape that hid `pteff05` from both renderers — does not reach this archive.
|
||
|
||
## `ScreenView.skipped` was correct and unread since P1 — now it says so itself
|
||
|
||
The draw loop has always tracked what it could not draw, with the comment *"a
|
||
silently missing element looks like art"*. **Nothing ever printed it.** For eight
|
||
milestones the port could drop an element on every frame and report it to nobody.
|
||
|
||
That is the same shape as the black hold — implemented, called, emitting nothing
|
||
until somebody filmed it — and as `verify-screen` scoring two blank frames `OK`.
|
||
A fact that needs someone to remember to look at it is a fact that goes
|
||
unnoticed, so `_note_structural` **prints from inside `ScreenView`** rather than
|
||
returning a value for a caller to surface. Routing it through a caller is exactly
|
||
what did not happen.
|
||
|
||
Only **structural** skips are reported — `(no sprite in the export)` and
|
||
`(sprite failed to load)`. `(transparent at rest)` is ordinary animation, true of
|
||
every element at some instant, and reporting it would bury the two that mean
|
||
something under the one that never does.
|
||
|
||
✅ **Nothing is being skipped today**: 0 across every screen, on the boot path and
|
||
per-screen. This is a guard, not a fix. The export corroborates it — no missing
|
||
sprite PNG, no element at alpha 0 on every keyframe, none at scale 0.
|
||
|
||
### 🔴 And the first version of that scan was a false pass
|
||
|
||
My first run reported *"0 structural skips"* on ten screens. **`screen_view.gd`
|
||
did not parse.** I had inserted a line at three tabs inside a four-tab block —
|
||
the Python `assert old in s` passed because a three-tab string is a *substring*
|
||
of a four-tab line — which orphaned a `continue`. Godot loaded nothing, printed
|
||
nothing, and `grep -c` faithfully counted zero.
|
||
|
||
A count of zero from a dead script is indistinguishable from a count of zero from
|
||
a clean one, and I had already written the sentence claiming the clean reading.
|
||
The scan now counts the screen summary line as a **positive control**: if the
|
||
script did not run, `summary-lines=0` says so, and the zero cannot be read as a
|
||
pass. That is the third time this session that a well-formed answer to a
|
||
different question nearly became a finding.
|
||
|
||
⚠️ Note the mechanism, because it will recur: matching indented code by substring
|
||
is unanchored, and it silently matched a *shallower* indent than the one in the
|
||
file.
|
||
|
||
## Refutation attempt — "the element declared first paints first"
|
||
|
||
All six of the port's forced-backdrop elements sit at **element index 0**. So on
|
||
those six screens the rule's verdict is indistinguishable from a far simpler
|
||
hypothesis I had not tested: *the first-declared element is painted first.* If
|
||
that held, `forced_backdrop` would be an elaborate way to reproduce the file's own
|
||
ordering, and my six verdicts would be no evidence for it at all.
|
||
|
||
**It is refuted, on 8 of 16 screens.** Index 0 is *not* painted first on
|
||
`build_00`/`build_01` (position 2), `extras` (7), `title` (13), `title_jp` (18)
|
||
and — decisively — `main_menu`, where index 0 is **`pteff00`, painted last**,
|
||
position 15 of 16. `pteff00` is the Decoder's own *measured* control: the game
|
||
puts the first-declared element on top of that screen.
|
||
|
||
So declaration order is not paint order, the six coincidences are coincidences,
|
||
and the rule is not redundant.
|
||
|
||
⚠️ What survives as a real limitation: **those six screens, taken alone, cannot
|
||
distinguish the two hypotheses.** The evidence separating them comes from
|
||
elsewhere in the archive. Worth stating because it is the exact weakness in the
|
||
Decoder's `pfbase.tbm` upgrade — *"element 0 of the save/load frame, and the
|
||
measured order starts [0, 1, 2, …]"*. An order that equals the trivial order is
|
||
weak evidence for **any** rule, since every rule preserving declaration order
|
||
agrees with it. ✅ Their claim survives, but on evidence they did not cite: it is
|
||
`main_menu`'s `pteff00` that rules out the trivial reading, not the save/load
|
||
frame itself.
|
||
|
||
## The menu bed plays under the cutscene, nobody decided that, and it stays
|
||
|
||
`MenuAudio.stop_bed()` exists and is **called from nowhere**. So the music that
|
||
starts when the main menu goes live runs through the cutscene and on past it —
|
||
and since `authored/audio.json` sets `loop: "restart"`, it then loops. Both
|
||
follow from the source and the authored data alone; no measurement is needed to
|
||
establish them.
|
||
|
||
The port therefore emits **two unrelated music tracks at once** during `S00A`:
|
||
the movie carries its own music and effects, and the menu bed is underneath it.
|
||
|
||
### It is not being fixed, and that is the decision
|
||
|
||
`PORT-MISSION`'s rule is to leave an unmeasured detail **plainly wrong rather
|
||
than plausibly invented**, and this is the textbook case for it. Music over a
|
||
cutscene is wrong in a way any listener catches in one second. Ducking or
|
||
stopping the bed would sound entirely right — and would be a guess about a game
|
||
nobody has watched. **The audible version gets fixed; the plausible version ships
|
||
forever.**
|
||
|
||
So `_play_video` now *announces* it, the way `skipped_chain` already announces
|
||
the two screens NEW GAME jumps over:
|
||
|
||
```
|
||
-> video S00A at 1.13 s (…)
|
||
🔴 the menu bed is STILL PLAYING under this movie -- unmeasured,
|
||
left audible on purpose (BLOCKED.md: does menu music duck?)
|
||
```
|
||
|
||
It fires on the menu path and correctly stays silent on the boot path, where no
|
||
bed has started. `stop_bed` is **kept**, not deleted: the day a capture says
|
||
whether the game's menu music ducks under a movie, it is the one line to change.
|
||
|
||
⚠️ This is the mirror of `ScreenView.skipped` from the previous iteration — a
|
||
fact recorded and never surfaced, against a capability provided and never used.
|
||
Both were invisible for the same reason: **nothing fails when they are missed.**
|
||
|
||
### 🔴 And my correlator is not trustworthy on music under music
|
||
|
||
Chasing this I ran the envelope correlator over the P7 recording repeatedly and
|
||
got answers that moved with the window and the template: the bed at r=0.42 with
|
||
one template and no peak at all above 0.4 with another; a post-movie window
|
||
search whose range **excluded the correct answer** and duly reported the bed's
|
||
own loop as unidentifiable. I was drafting *"46 s of unexplained audio after the
|
||
movie"* when the explanation was the authored `loop: "restart"` sitting in a file
|
||
I had not re-read.
|
||
|
||
The instrument is sound where it was validated — discrete SFX against a bed, with
|
||
margins of +0.5 over a negative control. It is **not** sound for music under
|
||
music at comparable level, where every candidate scores 0.15–0.42 and nothing
|
||
separates. ⚠️ A margin is only meaningful against a control **at the same SNR**,
|
||
and I did not have one here. That is the fourth near-miss of this kind, and the
|
||
first where I would have invented an *anomaly* rather than a defect.
|
||
|
||
Nothing in this section rests on that correlator. The finding is `stop_bed` having
|
||
no caller, which is a fact about the source.
|
||
|
||
## `wait:<seconds>`, and the bed's loop seam is 3.4 seconds of silence
|
||
|
||
The port could not be asked to **run for a stated duration**. A bare `wait`
|
||
script step is `pass` — it returns as soon as the screen settles — so nothing
|
||
happening after the settle point was observable from a harness at all. The music
|
||
bed made that concrete: an 87.7 s track whose restart nobody had watched, on a
|
||
harness whose longest menu run was under seven seconds.
|
||
|
||
`--script=wait:105` fixes that, and the first thing it found was the answer.
|
||
|
||
### The bed loops, exactly where it should
|
||
|
||
Recording the Master bus over 132 s with nothing but the menu playing — no movie,
|
||
no voice, a clean signal — the bed's `t=2…17 s` template matches twice:
|
||
|
||
| | r | pass begins |
|
||
|---|---|---|
|
||
| first pass | **0.947** | 0.0 s |
|
||
| second pass | **0.885** | **87.8 s** |
|
||
|
||
The track is **87.7 s**. So `loop: "restart"` does what `authored/audio.json`
|
||
says: replays from sample 0 at the track's end, no trimming, no loop point.
|
||
✅ First end-to-end observation of P6's looping behaviour.
|
||
|
||
### And the seam is measurably as bad as it was authored to be
|
||
|
||
`loop_why` predicted *"the listener hears the track's own fade-out and the silence
|
||
after it before the music comes back"*. Measured off the bus:
|
||
|
||
| window around the seam | RMS |
|
||
|---|---|
|
||
| −8 … −4 s | 2057 |
|
||
| −4 … −2 s | 714 |
|
||
| −2 … −0.3 s | 431 |
|
||
| +0.3 … +2 s | 2164 |
|
||
|
||
and **36 consecutive 50 ms windows below peak 300, from 84.40 s to 87.80 s** —
|
||
about **3.4 seconds of near-silence** before the music returns. That is long
|
||
enough to read as *the music stopped*, not as a loop.
|
||
|
||
The claim was right and is now a number. ⚠️ It does **not** license trimming to
|
||
the fade: that would still invent a loop point, and an invented one is
|
||
indistinguishable from a decoded one a month later. The measurement is recorded
|
||
to make the cost of the missing loop point concrete, in `authored/audio.json`.
|
||
|
||
### 🔴 My first `wait:` was wrong by 39 %, and the way it was wrong matters
|
||
|
||
It used `create_timer`, which counts down on the frame delta. In an **idle**
|
||
scene this container throttles and the delta it reports is not the time that
|
||
passed: a requested 30 s took **41.7 s** of wall clock while the port reported
|
||
30. Measured against `date` either side of the process, with a no-wait control
|
||
to subtract 1.21 s of startup.
|
||
|
||
Now polled on `Time.get_ticks_msec()`: 30 s requested, **31.38 s** wall, +4.6 %.
|
||
|
||
⚠️ **This is idle-specific and is not a general clock fault**, which I checked
|
||
before writing any of it down. Over a whole boot, where things are animating, the
|
||
port's clock tracks wall clock to **within 4 %** — 10.43 s wall against 10.82 s
|
||
reported. So the port's *animation* timing is sound and the earlier splash-dwell
|
||
agreement (4.400 s and 3.650 s against declared 255 and 210 units) **stands**; I
|
||
had briefly believed it did not.
|
||
|
||
What is genuinely unsound is `_elapsed` **while idle**: it reported 23.21 s across
|
||
30 real seconds of waiting. Every timing the port prints during animation is
|
||
fine; a timing that spans a wait is not.
|
||
|
||
📌 The reason to care: the only reason to hold a screen is to observe something on
|
||
a **real** clock — an audio loop, a timeout, an idle return. A timer that
|
||
silently ran 39 % long would have put every such observation at the wrong instant,
|
||
and the bed-loop result above would have been the first casualty. It survived only
|
||
because the seam was read off the **recording's** clock, which the bed's own known
|
||
length calibrates.
|
||
|
||
## Two harness bugs, and the defect the second one was hiding
|
||
|
||
### 1. `--capture` with `--script` photographed the frame *before* the script
|
||
|
||
`--capture` fired in `_ready` and quit. With `--script` that is **before the
|
||
first press**: at t=0.133 s, with 10 of 16 elements still transparent. Two runs
|
||
differing by two `down` presses came out **bit-identical**, and I read that as
|
||
*"runtime focus never changes"* — a confident wrong finding that `--shots`
|
||
contradicted within a minute.
|
||
|
||
Fixed: with a script, the capture defers to the end of the run, through the same
|
||
`_capture_to` member the boot path already used. Verified — the two runs now
|
||
differ at max 235, and the capture lands at t=82 units instead of t=8.
|
||
|
||
### 2. `--boot --capture=` wrote no file at all — ✅ FIXED, see below
|
||
|
||
`_finish_boot()` is reachable only from the overlay-quit branch, but the boot
|
||
quit first: line 412 exits when `_film == "" and _overlay_spec.is_empty()`, and
|
||
`_overlay_spec` is **cleared when the overlay is raised**. So a plain `--boot`
|
||
ended at 10.99 s, 1.2 s before its own scheduled 12.21 s, and the capture never
|
||
happened.
|
||
|
||
Confirmed pre-existing by stashing my changes and re-running. Fixed by also
|
||
requiring `_overlay_quit_at < 0.0`. The boot now runs to 12.19 s, prints *"boot
|
||
ends on title + press_start"*, and writes the file.
|
||
|
||
⚠️ The flag has a doc comment explaining that it exists so the boot has an
|
||
artifact of its own instead of a 600-PNG filmstrip. **It has been producing
|
||
nothing.** A flag that silently writes no file is the same failure as
|
||
`ScreenView.skipped` and `stop_bed`: provided, plausible, and never exercised.
|
||
|
||
### 3. 🔴 And the artifact it now produces shows the plate is missing
|
||
|
||
The boot's end frame is **bit-identical** to `--screen=title` at the same instant
|
||
— max difference 0. The `PRESS Ⓐ` plate is not in the port's end state.
|
||
|
||
`ptbtn00`'s own fade explains it exactly:
|
||
|
||
| t | 0 | 214 | **236** | **238** | 244 |
|
||
|---|---|---|---|---|---|
|
||
| alpha | 0 | 0 | **255** | **255** | 0 |
|
||
|
||
The plate is visible for **8 units — 0.133 s** — and the boot captures at
|
||
**t=246.54**, two and a half units after it has gone.
|
||
|
||
That is not an accident of frame timing; the code chose it. `_overlay_quit_at`
|
||
takes `max(view.settle_time(), overlay.settle_time())`, and its comment says why:
|
||
*"the plate arrives at t=238 and build 4 is still fading up from black until
|
||
t=261 … quitting when the plate lands photographs a title that has not finished
|
||
presenting."* Both halves are true, and together they mean **the two states
|
||
cannot both be in one frame.** The port picked the title, and the consequence —
|
||
that the plate is in no artifact at all — was never written down.
|
||
|
||
⚠️ I am **not** moving the trigger. The earlier reasoning is sound and the
|
||
measurement it protects is real; picking the other instant would trade a missing
|
||
plate for a visibly dark title, which is the swap that was already made once and
|
||
regretted. What settles it is what the *game* does with the plate after t=244 —
|
||
filed.
|
||
|
||
📌 Worth naming: defect 3 was **invisible while defect 2 existed**. A capture flag
|
||
that writes nothing cannot show you a missing element. The broken tool was hiding
|
||
the thing the tool was built to find, and neither was noticed because the absence
|
||
of a file looks exactly like not having run the command.
|
||
|
||
### What was *not* wrong, and how I nearly recorded that it was
|
||
|
||
Runtime focus works. Per-step `--shots` across `down,down` differ at max 232–233,
|
||
with the differing boxes tracking down the button column. My contrary reading came
|
||
from analysing 410 `f_NNN` files after asking for `--shots=…/s` — **the filenames
|
||
did not match the flag I passed, and that was visible in my first `ls`.** A
|
||
verbatim re-run produces six correctly-named per-step shots. I do not know where
|
||
those 410 files came from, which is itself the point: I drew a conclusion from a
|
||
file set whose provenance I never checked.
|
||
|
||
## The `PRESS Ⓐ` plate: four bugs in a row, and a number I have been misquoting
|
||
|
||
Last iteration I filed that the plate was visible for 8 units and simply missed by
|
||
the boot's capture instant. That was wrong in the direction that matters: **the
|
||
plate could not be drawn at any instant at all**, and three separate faults had to
|
||
be removed before it appeared. The fourth is a correction to figures I have quoted
|
||
to the Decoder repeatedly.
|
||
|
||
### 1. `--time=` was silently ignored on half the screens
|
||
|
||
`pose_at` did `if holding: t = settle_instant`. The requested instant was
|
||
**discarded** on every screen with a settle window ≥ 30 units — `title`,
|
||
`press_start`. The flag parsed, the log printed the time asked for, and the pose
|
||
came from somewhere else entirely.
|
||
|
||
`ScreenView.frozen` now marks an explicitly pinned instant and skips both clamps.
|
||
|
||
### 2. The settle window picked an interval where nothing is visible
|
||
|
||
`press_start`'s keyframes are 0, 214, 236, 238, 244. The widest keyframe-free gap
|
||
is **0…214** — the dead stretch *before* the plate exists, where `ptbtn00` is
|
||
alpha 0 throughout — so its settle instant was **t=107**, and every question about
|
||
that screen was answered there.
|
||
|
||
🔴 **A gap in which nothing is visible is not a settled state.** The exporter now
|
||
rejects those intervals. `press_start` becomes [214, 236] — 22 units, under the
|
||
runtime's 30-unit bar — so it falls back to each element's own hold, which is the
|
||
plate, opaque, as the disc declares it.
|
||
|
||
⚠️ It disturbs no window the settle instant was measured on: `title` keeps
|
||
[160, 236], the interval the Decoder's draw stream independently found the game's
|
||
clock freezing in.
|
||
|
||
### 3. An authored entry of mine was suppressing the decode
|
||
|
||
Even then the plate stayed dark, because `authored/timing.json`'s
|
||
`looping_focus_records` entry for `press_start/ptbtn00` made `_draw` take the
|
||
focus path — which draws the focus record **instead of** the base sprite:
|
||
|
||
| | with the entry | without |
|
||
|---|---|---|
|
||
| `press_start` t=236 | max **0** | max **252.5** |
|
||
| t=240 | max 0 | max 252.5 |
|
||
| t=250 | max 15.3 | max 252.5 |
|
||
|
||
I authored that entry to give the plate a glow. It substituted a dim glow at the
|
||
wrong phase for the element's own bright sprite, on the screen whose entire
|
||
content is that sprite. **Deleted** — an authored guess that overrides decoded
|
||
data with a worse answer is removed, not tuned. The glow is not claimed either
|
||
way; drawing both would be a rendering rule nobody has measured.
|
||
|
||
✅ The boot's end artifact now contains the plate: mean **95.7** in its region
|
||
against **33.6** for the title art alone, and the overlay reports `drew 1:
|
||
ptbtn00` where it reported `drew 0`.
|
||
|
||
### 4. 🔴 `verify-capture` has been measuring a different pose than it reported
|
||
|
||
It passed `--time=5.9617` for the title — t=357.7 units, the Decoder's refined
|
||
sweep fit — and **that value was never applied**, because of fault 1. Every title
|
||
figure this tool has printed, including the **0.26 %** I have quoted to the
|
||
Decoder more than once, was measured at the **settle instant, t=198**, under a
|
||
note claiming t=357.7.
|
||
|
||
Honouring the flag made it visible: t=357.7 is past the title's own group, which
|
||
ends at t=269, so the whole screen posed at its faded-out final keyframes and the
|
||
disagreement went to **30.97 %**. The instant was only ever meant for the `ptloop`
|
||
leaf, which runs to t=600 and is looped separately by `loop_leaf`. Applying it to
|
||
the whole screen was always wrong and was harmless only while it was ignored.
|
||
|
||
The splashes had the same shape: `--time=99` was an idiom for "settled" that
|
||
worked only because it was discarded. Both rows now pose by omission, and the
|
||
tool's note says what it does.
|
||
|
||
| | before | now |
|
||
|---|---|---|
|
||
| `title` | 0.26 % *(labelled t=357.7, actually t=198)* | **0.21 %** at t=198, labelled t=198 |
|
||
| `publisher_logo` | 0.01 % | 0.01 % |
|
||
| `developer_logos` | 0.01 % | 0.01 % |
|
||
| `main_menu` | 0.07 % | 0.08 % |
|
||
| `extras` | 0.19 % | 0.19 % |
|
||
|
||
The agreements were real; the **stated pose was not**. Corrected with the
|
||
Decoder, since they have those numbers.
|
||
|
||
### The flag audit that started it
|
||
|
||
All 16 flags `boot.gd` parses were exercised for an observable effect after last
|
||
iteration's two silent ones. `--pose=rest` (max 111 against the timeline),
|
||
`--play` (enters the menu), `--no-hold` (max 255 on two screens) all pass. `--time`
|
||
was the one that did not, and it took a screen whose content is a single late
|
||
spike to make the failure visible.
|
||
|
||
## The title's residual is the sweep phase, and the sweeps fit at ~400 units, not 357.7
|
||
|
||
Last iteration I found `verify-capture` was passing the Decoder's refined sweep
|
||
fit as `--time=5.9617` and having it silently discarded, so the value had **never
|
||
been tested**. Asking for it also destroyed the frame — t=357.7 is past the
|
||
title's own group end at t=269 — which is why nobody noticed.
|
||
|
||
`--leaf-time=<seconds>` separates the two clocks: the screen sits at its settled
|
||
pose, the `ptloop` leaf is placed at whatever phase is under test. That makes the
|
||
fit testable for the first time.
|
||
|
||
### Controls first
|
||
|
||
* The renderer is **deterministic** — three runs at one leaf phase are
|
||
bit-identical, max difference 0. So variation across phases is signal, not noise.
|
||
* The sweeps are **detectable** — two phases differ over **0.3953 %** of the frame
|
||
at a 10 % threshold. A comparison at this scale can see them.
|
||
|
||
### The fit
|
||
|
||
Sweeping the leaf across its full 600-unit span against
|
||
`live-title-build4-no-plate.png`, structural disagreement at a 25 % threshold:
|
||
|
||
| leaf phase | differing |
|
||
|---|---|
|
||
| 240 units | 0.1410 % |
|
||
| 320 units | 0.3395 % |
|
||
| **357.7 — the Decoder's fit** | **0.2532 %** |
|
||
| 390 | 0.0129 % |
|
||
| **395–402** | **0.0124 %** |
|
||
| 405 | 0.0127 % |
|
||
| 440 | 0.2033 % |
|
||
|
||
A sharp basin at **390–415 units**, an order of magnitude below everything
|
||
around it, and **20× better than t=357.7**.
|
||
|
||
### What that means, and what it does not
|
||
|
||
✅ **The title's 0.21 % residual is the sweep phase, not structure.** At the
|
||
fitted phase the disagreement falls to **0.0124 %** — the same order as the
|
||
splashes' 0.01 %. The port's title rendering is structurally right; the sweeps
|
||
were simply somewhere else in their loop.
|
||
|
||
🔴 **The port does not adopt 400 units, and `verify-capture` is not re-posed to
|
||
it.** That would be tuning until they match, which this repository's own tooling
|
||
header warns against. The port loops the leaf freely — there is no phase
|
||
parameter to set — and 400 units is a property of *that capture's instant*, not
|
||
of the game.
|
||
|
||
⚠️ Reach: this assumes the port's leaf geometry and sprite are otherwise correct.
|
||
A systematic error in how the sweeps are drawn could be absorbed by shifting the
|
||
phase, and one capture cannot separate those. What makes the result worth having
|
||
anyway is the **sharpness** — a 20× drop over 40 units is not something a
|
||
geometry error would produce at a wrong phase.
|
||
|
||
⚠️ And it does not tell the Decoder their 357.7 is wrong *as a measurement of
|
||
whatever they measured it on*. It says the phase that matches this capture is
|
||
~400. If those are the same quantity, one of the two is off by ~42 units; if they
|
||
are not, this is a second quantity nobody had.
|
||
|
||
## A second capture closes the sweep-geometry question, and the plate matches at 0.00093 %
|
||
|
||
Last iteration's leaf-phase fit came with a caveat I could not close: *a
|
||
systematic error in how the port draws the sweeps could be absorbed by shifting
|
||
the phase, and one capture cannot separate those.* A second capture can, and
|
||
`live-title-press-a.png` — the title **with** the plate — was sitting in the
|
||
corpus unused.
|
||
|
||
### The second capture fits at a different phase, and better
|
||
|
||
| capture | pose | differing |
|
||
|---|---|---|
|
||
| `live-title-build4-no-plate.png` | settled, leaf at ~400 units | **0.0124 %** |
|
||
| `live-title-press-a.png` | t=237, everything | **0.00093 %** |
|
||
|
||
**Two independent captures, two different sweep phases, both fitting to 0.01 % or
|
||
better.** A geometry error in how the sweeps are drawn would leave a floor in
|
||
*both*, and at a phase-independent level. Neither has one. ✅ The caveat is closed
|
||
and the port's sweep rendering is not systematically wrong.
|
||
|
||
The two phases are also consistent with each other rather than merely different:
|
||
the plate is opaque only at t=236–238, so a frame containing it is early
|
||
(t≈237) and a frame without it is either earlier or later. The no-plate capture
|
||
fits at leaf ~400 — 6.7 s in, well past the plate's window — and its filename
|
||
says it is build 4 alone. Both readings agree on where each frame sits.
|
||
|
||
⚠️ I nearly drew a further inference — that the no-plate capture *dates* the
|
||
plate's disappearance and therefore answers the BLOCKED question about whether
|
||
the plate stays up. It does not: the filename says `build4-no-plate`, so the
|
||
capture was taken **of build 4 alone, deliberately without the overlay**. It
|
||
carries no information about how long a plate lasts. That row stays open.
|
||
|
||
### Capture 1 is not a whole-screen instant, which corroborates `loop_leaf`
|
||
|
||
Sweeping the *whole screen's* time against the no-plate capture, the best is
|
||
**0.1483 % at t=230**, degrading sharply past 240 as the group fades out — an
|
||
order of magnitude worse than the leaf-only fit's 0.0124 % at phase ~400, which
|
||
the main timeline cannot reach without fading everything.
|
||
|
||
So that capture is **not** "the screen at instant t". It is the screen **settled**
|
||
with the sweeps **still looping** — which is exactly what `authored/rendering.json`'s
|
||
`loop_leaf_on_screens: ["title"]` models. That decision was authored from the
|
||
leaf's zero slack; this is the first independent evidence for it.
|
||
|
||
### 🔴 The 1 % floor was the plate not being drawn at all
|
||
|
||
Before any of that, every sweep phase against capture 2 gave a flat ~1.0 %, with
|
||
the residual a row of glyph-sized blobs on the plate's own position.
|
||
|
||
`--screen=X --overlay=Y` pushed the **raw elapsed clock** into the overlay — 9
|
||
units at the moment `--capture` fires. `press_start` is transparent until t=214.
|
||
So the one flag whose entire purpose is *put the plate on the title* drew nothing
|
||
and reported `drew 0`, and the frame read as a title with no plate.
|
||
|
||
A static overlay now poses at **its own arrival**. The `--boot` path is untouched:
|
||
there the shared clock is the finding — the 120 units between build 4's last ramp
|
||
and the plate's `a=255` is a fixed interval on one timeline.
|
||
|
||
⚠️ My first patch for this was wrong and I nearly committed the comment for it:
|
||
I wrote that *"nothing outside a boot sequence drives the overlay's clock"*. It is
|
||
driven — from `view.time_units`, every frame. The symptom was identical either
|
||
way, and only re-reading the log after the fix failed showed the cause was the
|
||
opposite of what I had written down.
|
||
|
||
### The new row
|
||
|
||
`title_plate` joins `verify-capture` at **0.00%** — two orders below every other
|
||
row, which makes it the most sensitive regression detector in the harness.
|
||
|
||
⚠️ Its instant is **fitted, not measured**. t=237 is where this capture's content
|
||
places it, found by sweeping. Choosing which frame to compare against is what
|
||
every row here does, but the 0.00093 % is a floor for *that pose*, not a general
|
||
statement of accuracy.
|
||
|
||
## `--focus=` did nothing on the menu path, and the corpus had an untested focus capture
|
||
|
||
Two unused live captures were sitting in `docs/re/captures/title-builds/`.
|
||
`live-main-menu-options-focused.png` is the menu with **OPTIONS** focused — the
|
||
only capture of a *known* focus state — and it was untestable, because
|
||
`--focus=` **silently did nothing on the `--menu` path**.
|
||
|
||
The flag parsed, was stored in `_force_focus`, and was applied to
|
||
`view.focused_id` at startup — and then `_menu_enter` overwrote it with the
|
||
authored initial focus on every entry. Every run logged `focus ptbtn01` whatever
|
||
was asked for, and all five buttons produced the same frame. It is now pushed
|
||
into the **menu model**, not just the view, so navigation continues from where it
|
||
was forced rather than jumping back on the first press.
|
||
|
||
### The port's focus rendering is right, measured against the oracle
|
||
|
||
Rendering each of the five buttons focused, against each capture:
|
||
|
||
| focused | vs `live-main-menu-options-focused` | vs `live-main-menu` |
|
||
|---|---|---|
|
||
| `ptbtn01` NEW GAME | 0.7352 % | **0.0705 %** |
|
||
| `ptbtn02` LOAD GAME | 0.8204 % | 0.8378 % |
|
||
| `ptbtn03` TUTORIAL | 0.7220 % | 0.7365 % |
|
||
| **`ptbtn04` OPTIONS** | **0.1355 %** | 0.7449 % |
|
||
| `ptbtn05` EXTRAS | 0.7029 % | 0.7236 % |
|
||
|
||
Each capture picks out exactly one button, by **5×** and **10×**. This is the
|
||
first time the port's focus rendering has been checked against the game at all —
|
||
the harness's own `main_menu` row uses an *authored* focus, so it could never
|
||
have caught a focus error.
|
||
|
||
### What that settles, and what it does not
|
||
|
||
✅ The port draws focus on the right button, distinguishably.
|
||
✅ `live-main-menu.png` shows **NEW GAME** focused, so the authored
|
||
`initial_focus: ptbtn01` matches the one frame it can be checked against.
|
||
|
||
⚠️ **It does not overturn HANDOFF Q5**, which measured initial focus as *unstable
|
||
boot to boot* across four runs. One capture showing `ptbtn01` is consistent with
|
||
instability, not evidence against it. The value stays **authored**, with the
|
||
agreement recorded beside it. Reading this as "initial focus is settled" would be
|
||
exactly the over-generalisation from a single observation that this corpus keeps
|
||
having to withdraw.
|
||
|
||
`main_menu_options` joins `verify-capture` at 0.13 %.
|
||
|
||
⚠️ Still unused: `live-attract-title-press-a-band.png`, a 1279×**120** strip
|
||
rather than a full frame. It needs a banded comparison the harness does not do,
|
||
so it is named here rather than quietly left out.
|
||
|
||
## The last unused capture, placed — and its residual is the oracle's, not the port's
|
||
|
||
`live-attract-title-press-a-band.png` was the one live capture nothing consumed.
|
||
Following last iteration's rule — *an unused capture is a signal about the
|
||
harness* — it turned out the same way as the previous two: nothing about the
|
||
capture was unusable, the harness simply could not compare a **band**. It is
|
||
1279×**120**, not a full frame.
|
||
|
||
### Placing it
|
||
|
||
Sliding it down the render, structural difference against the port:
|
||
|
||
| y | differing |
|
||
|---|---|
|
||
| 300 | 51.99 % |
|
||
| 500 | 19.61 % |
|
||
| 515 | 9.14 % |
|
||
| **520** | **0.354 %** |
|
||
| 525 | 8.87 % |
|
||
| 555 | 24.02 % |
|
||
|
||
**y = 520**, a 25× drop over five pixels. Measured, not guessed. Sweeping the
|
||
instant at that offset puts it at **t = 236–238** — the plate's own opaque
|
||
window, the same instant the full-frame `title_plate` row fits.
|
||
|
||
### 🔴 The 0.354 % is not the port's error
|
||
|
||
Three comparisons separate it:
|
||
|
||
| | differing |
|
||
|---|---|
|
||
| port's band vs `live-title-press-a`'s same band | **0.000 %** |
|
||
| `live-title-press-a`'s band vs the attract band *(oracle vs oracle)* | **0.301 %** |
|
||
| port's band vs the attract band | 0.354 % |
|
||
|
||
The port reproduces one capture's band **exactly**. The two captures differ from
|
||
*each other* by 0.301 %, which is nearly the whole residual.
|
||
|
||
And that oracle-to-oracle difference is two thin horizontal strips — **248×5 px**
|
||
and **206×1 px** — which is the shape of a sub-pixel edge difference or capture
|
||
noise, not of a state difference.
|
||
|
||
⚠️ I had started writing that the *attract-returned* title differs from the boot
|
||
title, which would have been a finding about the game inferred from 0.3 % of a
|
||
band. It is two hairlines. The connected-component breakdown is what stopped it,
|
||
and I would not have run it if the number alone had looked small enough to
|
||
dismiss or large enough to report — it was in the range where you have to look.
|
||
|
||
So `title_band` joins the harness at 0.35 %, and its job is to **stay near the
|
||
oracle-to-oracle gap**, not to reach zero. A row whose target is not zero has to
|
||
say so, or the next person tunes toward it.
|
||
|
||
✅ **All eight live captures in the corpus are now used.** Three were sitting idle
|
||
and all three were blocked by the harness, not by the capture: an overlay posed at
|
||
t=9 that drew nothing, a `--focus=` overwritten on every menu entry, and a banded
|
||
comparison that did not exist.
|
||
|
||
## `MODDING.md` had five rules and no check. Now it has one, and all five pass
|
||
|
||
`MODDING.md` opens by calling modding *a constraint on the exporter **today**,
|
||
not a later feature*. Nothing verified it. That is the shape this port keeps
|
||
finding — a rule stated, believed and unexercised: the black hold implemented and
|
||
never called, `ScreenView.skipped` written and never read, `stop_bed` provided and
|
||
never used, `--focus` parsed and overwritten on every menu entry.
|
||
|
||
`tools/port/check-modding` covers all five. Every one passes today, so it is a
|
||
**guard, not a fix**: its value is that the next thing to break one says so.
|
||
|
||
| rule | check | result |
|
||
|---|---|---|
|
||
| 1 — one asset, one file | every referenced sprite present, none orphaned, no split names | **174 / 174**, exact |
|
||
| 2 — recognisable names | no hex or hash-shaped filenames | none |
|
||
| 3 — modern formats | extensions confined to json/png/ogg/ogv (+ sidecars) | clean |
|
||
| 4 — base and overrides | `data/mods` gitignored *and* read by the exporter | both |
|
||
| 5 — provenance | every generated JSON carries a `source` | 17 / 17 |
|
||
|
||
### It is proved to fail
|
||
|
||
A check that has never failed has not been shown to work — the lesson from
|
||
`check-capture`, which once passed a file with 36 % holes punched through it. Three
|
||
controls, each failing correctly with a non-zero exit:
|
||
|
||
* a `.cmd` sidecar with its header stripped → rule 3;
|
||
* a `bogus.bmp` in the sprite tree → rule 3;
|
||
* one orphaned PNG → rule 1, *"174 referenced, 175 present"*.
|
||
|
||
### The one thing it found: an unlabelled generated file in the asset tree
|
||
|
||
The two `.cmd` encode-cache sidecars sat beside the `.ogv`s in the modder-facing
|
||
tree with no line saying what they were — a bare ffmpeg command next to a video
|
||
reads as something to edit or delete. They now carry a header stating that they
|
||
are generated, are not assets, and that the way to change a video is an override
|
||
under `data/mods/`.
|
||
|
||
Two details worth keeping:
|
||
|
||
* the header is **excluded from the cache key**, so rewording it does not
|
||
re-encode four minutes of video. A cache that punishes documentation gets
|
||
documented once and never again.
|
||
* the sidecar is now refreshed whenever its **text** differs, not only when a
|
||
re-encode happens. It used to be written inside the `!fresh` branch — which
|
||
meant a header change could never reach an existing export, because nothing
|
||
that reads the header triggers the write that updates it. The explanation would
|
||
have been correct in the source and absent on disc. Confirmed: two consecutive
|
||
exports, 20 s and 19 s, header present, no re-encode.
|
||
|
||
### And a question I asked the Decoder that I could partly answer myself
|
||
|
||
Last iteration I asked whether the 0.301 % between two of their captures implies a
|
||
**capture-path floor on every comparison in the corpus**. It does not, and I had
|
||
the evidence already: the port matches `live-title-press-a.png` at **0.00093 %**
|
||
full-frame and **0.000 %** across the band. A general floor could not coexist with
|
||
either number. So the 0.301 % is specific to the attract band capture, and my
|
||
0.01–0.2 % rows are not sitting on a hidden floor. ⚠️ What that does *not* settle
|
||
is why those two frames differ — still theirs, and still worth an answer.
|
||
|
||
## Five authored values had no reader — including the one I asked for measurements into
|
||
|
||
Applying the prior from the last six findings — *a rule or capability nothing
|
||
exercises turns out broken or inert when someone looks* — to `authored/` itself.
|
||
Grepping every authored key for a reader in the exporter or the runtime:
|
||
|
||
| key | file | status |
|
||
|---|---|---|
|
||
| `dwell` | `flow.json` | 🔴 **no reader** — now wired |
|
||
| `ramp` | `timing.json` | no reader — now asserted |
|
||
| `left_right` | `flow.json` | no reader — now asserted |
|
||
| `input_during_transition` | `flow.json` | no reader — now asserted |
|
||
| `stems` | `audio.json` | no reader (`stems_why` is carried; the sum is hardcoded) |
|
||
|
||
Everything else — `se`, `bgm`, `voice`, `boot`, `screens`, `navigation.wrap`,
|
||
`draw_leaf_for`, `loop_leaf_on_screens`, `keyframe_units_per_second`,
|
||
`black_hold_units`, `archives`, `also_export`, `presentation`, `loop_mode`,
|
||
`initial_focus`, `skippable`, `then_video`, `after_video` — is read.
|
||
|
||
### 🔴 `dwell` is the one that mattered
|
||
|
||
Its own text says *"when a capture times the real boot, the extra hold per screen
|
||
goes here."* **A number placed there did nothing.** Two iterations ago I asked
|
||
the Decoder for measurements destined for exactly that slot; had they arrived,
|
||
they would have been filed into a value with no reader and the boot would have
|
||
been unchanged, silently — and I would have reported the boot as matching.
|
||
|
||
It is wired now, and **stays empty**. Nothing is authored into it: the splash
|
||
dwells are declared on the disc and measured to agree. Wiring the slot so that a
|
||
future number has an effect is the opposite of adopting one.
|
||
|
||
⚠️ **I wired it to the wrong branch first, and it did nothing — silently.** Holding
|
||
longer after settle changes nothing, because the screen still leaves when
|
||
`exit_time() + black_hold` arrives and the extra hold is absorbed. A dwell has to
|
||
delay the **departure**. I found it only because I tested the control:
|
||
+120 units moved the transition 4.46 s → **6.43 s**, +1.97 s, with the video
|
||
following by the same amount. Reproducing the exact defect I was removing, inside
|
||
the fix for it, is the strongest argument I have for testing that a wire carries
|
||
current rather than that it exists.
|
||
|
||
### The other three are asserted, not implemented
|
||
|
||
`ramp`, `left_right` and `input_during_transition` describe behaviour the port
|
||
**hardcodes**. That is fine for a record and dangerous for a switch, and they are
|
||
written like switches — setting `left_right` to `"move"` would change nothing and
|
||
warn nobody.
|
||
|
||
Rather than invent the missing implementations, `_check_authored_invariants`
|
||
**asserts the value the port was built against**, naming the file. Changing one
|
||
now produces an error instead of silence.
|
||
|
||
That is precisely the distinction `left_right`'s own `why` claims to be making —
|
||
*"written out rather than left unhandled so that 'the game ignores it' and 'we
|
||
never wired it' are different lines of code"* — and which was not in fact being
|
||
made, because nothing read the value that was supposed to make it.
|
||
|
||
✅ The validator is **called**, not merely defined. A validator nobody invokes is
|
||
the same defect it exists to catch, and this file now documents six other
|
||
instances of exactly that.
|
||
|
||
Verified: clean boot with no invariant errors and unchanged timings; setting
|
||
`left_right: "move"` produces the error; all five MODDING rules still pass; the
|
||
oracle rows are unmoved.
|
||
|
||
## `FORMAT.md` declared the port's own export invalid, and a failed export is not atomic
|
||
|
||
Continuing the audit that has now found seven unexercised rules: `FORMAT.md` is
|
||
the **open format spec** — written for a stranger reading the tree with no access
|
||
to the disc or this exporter. So the question is whether what it promises is what
|
||
`sylpheed-export check` enforces.
|
||
|
||
Five documented requirements, each broken in a copy of the tree:
|
||
|
||
| broken | caught |
|
||
|---|---|
|
||
| `unresolved` removed from a screen | ✅ |
|
||
| `peak_dbfs` removed | ✅ |
|
||
| `peak_dbfs` = −120 (silence) | ✅ |
|
||
| `duration_s` removed | ✅ |
|
||
| `peak_dbfs` = 0.0 on an `se` | **passes** |
|
||
|
||
### The last row is the doc's error, not the code's
|
||
|
||
`FORMAT.md` said flatly that check *"refuses a tree whose peak is ≤ −90 dBFS or
|
||
**≥ 0 dBFS**"*. The implementation is kind-dependent and deliberate: a `bgm` is a
|
||
sum **we** produced, so a peak at or above full scale is our arithmetic and is
|
||
refused outright; an `se` or `voice` is a single wave off the disc, mastered near
|
||
full scale, whose lossy decode overshoots by a fraction of a dB, and those are
|
||
allowed to +1.0.
|
||
|
||
🔴 **And the doc was wrong about the port's own export.** It ships `confirm` at
|
||
**+0.18 dBFS** and the `ADV` voice at **+0.31** — both above 0. A consumer
|
||
implementing a validator from `FORMAT.md` would have rejected a valid tree, and
|
||
the file that exists to let someone check our work without trusting us would have
|
||
been the thing that misled them. Corrected, with the +1.0 marked as the judgement
|
||
it is.
|
||
|
||
✅ Verified both directions: a `bgm` forced to 0.0 is refused with *"a SUM we
|
||
produced clips"*; an `se` at 0.0 passes.
|
||
|
||
### 🔴 A failed export leaves a tree that is not an export tree
|
||
|
||
Found by accident, and worth more than the way it was found. Testing the new
|
||
`stems` assertion, the exporter `bail!`ed part-way — and left `export/` **with no
|
||
`manifest.json` at all**. Every subsequent tool then reported *"has no
|
||
manifest.json — is that an export tree?"*, which reads as a broken harness rather
|
||
than as the aftermath of a deliberate failure.
|
||
|
||
⚠️ It cost me a wrong reading immediately: the first run of the requirement audit
|
||
above reported every case as "no manifest", and I nearly recorded that the
|
||
validator was checking nothing. It was checking a tree that had been half-written.
|
||
|
||
The exporter writes the manifest last, which is the right order — a manifest is a
|
||
claim about a tree, and a manifest for a tree that was never finished would be
|
||
worse. So this is **filed rather than fixed**: the behaviour is defensible and the
|
||
message is not, since "is that an export tree?" describes the symptom and hides
|
||
the cause. What a stranger needs to be told is *the last export failed; re-run it*.
|
||
|
||
## `check-all`, a verdict that ignored its own statistic, and a claim of mine that was wrong
|
||
|
||
Eleven tools under `tools/port/` and **nothing ran them together**, so each had to
|
||
be remembered individually. That is the ninth instance of this port's recurring
|
||
shape — correct, documented, unexercised — one level up: the checks were the thing
|
||
nobody was running.
|
||
|
||
`tools/port/check-all` runs the four that assert (`check`, `check-modding`,
|
||
`check-capture-controls`, `verify-menu-audio`), prints the oracle table, and
|
||
handles `verify-screen` specially. All eleven were exercised first and **none had
|
||
rotted**; `which-focus` independently picks NEW_GAME at a **93.8× margin**, which
|
||
is a second instrument agreeing with the capture fit's 10×.
|
||
|
||
Two things it is careful about:
|
||
|
||
* the six exploratory tools are **not** listed as passes. They produce artifacts
|
||
for a person to look at and have no verdict; counting them would invent six.
|
||
* `verify-capture` is **reported, not asserted** — it always exits 0. Its header
|
||
is right that the numbers are not a target, but *not a target* is not *not a
|
||
regression detector*, and nothing would notice `title_plate` moving off 0.00 %.
|
||
Named as a gap rather than papered over; a real fix needs stored baselines, and
|
||
what a baseline means when the pose is fitted is a decision, not a chore.
|
||
* the `verify-screen` allowance **expires on its own condition**. It is allowed to
|
||
fail only while `formats-pin-2026-08-29d` is not an ancestor of `origin/main`;
|
||
the day it lands, `check-all` fails instead. A suppression with no expiry is
|
||
just a hidden failure.
|
||
|
||
### 🔴 The verdict ignored the statistic added to inform it
|
||
|
||
`verify-screen` computes `over3` — how many pixels exceed the bar — because *"a
|
||
single `max` cannot tell 2 pixels from 25 444"*, its own words. **The verdict was
|
||
then decided on `max` alone.** So `main_menu` (max 4, `over3` **0**) read DIFFERS
|
||
while `extras` (max 3, `over3` 0) read OK: one unit on one pixel separating two
|
||
frames that are equivalent at the bar.
|
||
|
||
⚠️ Not fixed by raising the bar, which this file rightly forbids. The bar is still
|
||
3. A frame with **no** pixel over it now gets its own verdict, `ROUNDING`, instead
|
||
of being lumped in with a real disagreement. Tenth instance: the fix was
|
||
implemented, documented, and never wired to the thing it was for.
|
||
|
||
### 🔴 And "six expected DIFFERS [refuted]" — which I have told the Decoder more than once — was wrong
|
||
|
||
The true count was **ten**, now **eight** after the rounding fix:
|
||
|
||
| screens | count | explained |
|
||
|---|---|---|
|
||
| the forced-backdrop six | 6 | ✅ the pin: two decoder eras |
|
||
| `main_menu`, `main_menu_jp` | 2 | ✅ now `ROUNDING`, not a disagreement |
|
||
| **`title`, `title_jp`** | **2** | 🔴 **not explained** |
|
||
|
||
`title` differs on **790** pixels and `title_jp` on **20 498**, and neither is the
|
||
forced-backdrop rule — those screens have no forced element. I had a blanket
|
||
allowance covering two disagreements I had never accounted for.
|
||
|
||
**My hypothesis for them is refuted.** `authored/rendering.json` notes that the
|
||
consistency harness compares against a renderer that draws no `.rat` leaves, so
|
||
the port's `ptloop` sweeps looked like the obvious cause. Emptying `draw_leaf_for`
|
||
and `loop_leaf_on_screens` changes the numbers **not at all** — 790 and 20 498
|
||
either way. `verify-screen` poses at `rest`, where the leaves evidently do not
|
||
draw. Filed as open.
|
||
|
||
⚠️ `title_jp` is a localisation screen and out of scope (MISSION §7). `title` is on
|
||
the boot path and is not.
|
||
|
||
## The `title` disagreement, localised — and the question I filed for it was the wrong one
|
||
|
||
Two iterations ago I filed `title`'s 790-pixel disagreement with `sylpheed-cli` as
|
||
needing the Decoder: *"which elements does `sylpheed-cli` draw on build 4 at
|
||
rest"*. That was a hypothesis dressed as a question, and it is wrong. I could
|
||
answer it myself, and did.
|
||
|
||
### What is established
|
||
|
||
* **The pixels cluster in one place**: x ≈ 938–1162, y ≈ 172–310, as blobs of
|
||
20–66 px. That is `ptlogo_back2eff1`'s position, `pos=[938, 194]` — one of the
|
||
six `ptlogo_back2eff*` glows, all of which carry a sprite and **no declared
|
||
size**, so the texture supplies it.
|
||
* 🔴 **Both renderers draw it.** The region reads mean **95.60** in the port and
|
||
**95.08** in the CLI. So the premise of my filed question — that one draws an
|
||
element the other does not — is **refuted**. A set difference of element lists
|
||
would have answered nothing.
|
||
* **It is not a placement offset.** Rolling the port's frame by every combination
|
||
of ±1 px makes it *worse* by two orders of magnitude — 790 pixels aligned
|
||
against ≥ 175 406 for the best shift. The images are registered; they differ in
|
||
content on 0.086 % of the frame.
|
||
|
||
### What is not established, and why I am not guessing
|
||
|
||
The mechanism. My next hypothesis was edge antialiasing, and **the test failed its
|
||
own control**: the edge mask classified **92 % of the frame** as edge, so the
|
||
25.2 % of differing pixels landing on it is *below chance* and the instrument is
|
||
dead. A number from it would have been noise wearing a decimal point.
|
||
|
||
### The ask is downgraded rather than left standing
|
||
|
||
`BLOCKED.md`'s row asked for the wrong thing on a refuted premise, which is worse
|
||
than no row: it would have spent someone else's time confirming a difference that
|
||
is not there. Corrected.
|
||
|
||
⚠️ And the residual value is genuinely low. This is **two of our own renderers**
|
||
disagreeing on 0.086 % of one frame, on a screen where the port matches the
|
||
**oracle** at 0.21 % and its plate variant at 0.00093 %. `verify-screen`'s own
|
||
header says a DIFFERS means *"we moved apart, go find out which of us moved"* —
|
||
here neither moved from the game. It stays visible as a DIFFERS rather than being
|
||
allowed, because an allowance is how the two `title` rows hid inside "six
|
||
expected" in the first place.
|
||
|
||
## Auditing `BLOCKED.md` found three stale rows, and the undated ones were all three
|
||
|
||
`BLOCKED.md` opens by warning that it goes stale *"within the hour. It has."*
|
||
Nothing audits it. So I read every live row against a running port instead of
|
||
against my memory of it.
|
||
|
||
### Three were wrong
|
||
|
||
**`P3/P5 — the title screen`** was the worst, and had been for weeks. Every
|
||
factual claim in it is false: the boot does not end on a plateless build 4,
|
||
`press_start` is not unused, and the port has drawn two builds at once since the
|
||
plate-delay work. Verified this iteration — `boot ends on title + press_start`,
|
||
`overlay press_start … drew 1: ptbtn00`, plate region mean **95.70** against 33.6
|
||
for the bare title.
|
||
|
||
🔴 And a row **four lines below it** was already marked *answered and TAKEN* for
|
||
the same question. Two rows on one question, one struck and one live claiming the
|
||
opposite — and the live one was the stale one. A reader taking the page at face
|
||
value would have believed the struck row was the old news.
|
||
|
||
**`P4 — is an attract movie skippable`** led with *"the port could not tell which
|
||
bug that is"*. It can and it did: `DECISIONS.md` records **Ⓐ *does* skip the
|
||
intro in this build**, and every boot run since prints `video skipped at …`.
|
||
Corrected to 🟡 — (a) answered, (b) still needs a capture.
|
||
|
||
**`P6 looping`** was not wrong, only thin. It now carries the measured cost: the
|
||
bed loops at 87.8 s against the track's 87.7, and the seam is **3.4 seconds of
|
||
near-silence**. The missing loop point now has a price expressed as a number
|
||
rather than as the adjective "ugly".
|
||
|
||
### The undated rows are the ones that rotted
|
||
|
||
Checking which sections record the HANDOFF commit they derive from — the standing
|
||
instruction, and the thing this page's own header demands — splits the file
|
||
exactly:
|
||
|
||
| | sha |
|
||
|---|---|
|
||
| every prose section added since 2026-08-29 | ✅ all |
|
||
| **every row in the two tables** | 🔴 **none** |
|
||
|
||
And all three stale rows were in those tables. That is about as close to a
|
||
controlled experiment as this page will produce: the rows with no date are the
|
||
rows nobody could tell had aged.
|
||
|
||
⚠️ I have **not** back-dated them. Nobody knows when most were written and an
|
||
invented sha is worse than an admitted absence — it would make an undated claim
|
||
look checked. Both tables now say so at the top, and carry the date they were last
|
||
audited against a running port.
|
||
|
||
📌 The general form, which is the eleventh of these: *a document that warns about
|
||
its own failure mode is not thereby protected from it.* This page has said "it
|
||
goes stale, it has" since P1, in a header everyone reads and nobody acts on. The
|
||
warning was doing the work of a check.
|
||
|
||
## The record already answered last iteration's question, under headings that name it
|
||
|
||
Last iteration I investigated `title`'s and `title_jp`'s disagreement with
|
||
`sylpheed-cli`, ruled out leaves and placement offsets, watched an edge test fail
|
||
its own control, and filed the mechanism as **unknown** — to the Decoder as well
|
||
as here.
|
||
|
||
**Both were already explained in this file**, under
|
||
[`title`: a tie in the paint order](#title-a-tie-in-the-paint-order--neither-renderer-is-wrong)
|
||
and
|
||
[`title_jp`: nearest-neighbour sampling phase](#title_jp-nearest-neighbour-sampling-phase--the-cli-is-the-one-i-would-call-wrong).
|
||
Headings that name the two screens in question.
|
||
|
||
### Both still hold, checked rather than assumed
|
||
|
||
* **`title`** — the CLI uses a paint order *measured* off the running game; the
|
||
port derives one. Every disagreement is **inside a tie**. Verified against the
|
||
current export: `title` still ties on `0x8083` (the `back2` glow group, 5
|
||
elements), `0x80a0` (7) and `0x8010` (2), and the export declares
|
||
`paint_order_ties` in `unresolved`. The old entry costs it at **904 px in the
|
||
glow band, all 4–6/255**; I measured **790 px at x≈938–1162, y≈172–310, max 6**
|
||
— same band, same magnitude, count moved with the export.
|
||
* **`title_jp`** — `ptlogo_eff2` at **125 %**, where the CLI samples the source at
|
||
the destination pixel's top-left and a GPU samples at its centre. The entry
|
||
claims it is the *only* drawn element in the export at a non-integer scale
|
||
ratio. 🟡 A whole-export census finds **26** such keyframes — until it is
|
||
restricted to elements **visible at `rest`**, which is the pose `verify-screen`
|
||
uses. Then there is exactly **one**: `title_jp/ptlogo_eff2`. The claim survives,
|
||
and it survives narrowly enough to be worth the check.
|
||
|
||
### 🔴 The failure is navigability, not memory
|
||
|
||
6 502 lines, 111 `##` sections, 243 `###`, and **no index**. So "has this been
|
||
decided already?" had no cheap answer, and the expensive answer — re-deriving it
|
||
— looked like diligence. It also cost the Decoder a message reporting an open
|
||
question that was closed.
|
||
|
||
`tools/port/index-decisions` generates a contents block, and `check-all` runs
|
||
`--check`. ⚠️ Two things about the design, both learned the hard way in the same
|
||
half-hour:
|
||
|
||
* it carries **no line numbers**. The first version did, and writing the index
|
||
shifted every line below it — a fixpoint that failed its own `--check` on the
|
||
first run. Worse, appending a section would silently invalidate all of them.
|
||
* it is **checked**, because a stale index is worse than none: it would answer
|
||
*"is this already decided?"* with a confident no. That is exactly the failure
|
||
it was built to prevent, and building an unchecked one would have reproduced it
|
||
one level up — which this port has now done often enough that I looked for it
|
||
deliberately.
|
||
|
||
📌 The twelfth instance, and a new species. The first eleven were *"correct,
|
||
documented, unexercised."* This one is **correct, documented, unfindable** — and
|
||
it is worse, because it does not merely fail to catch an error, it manufactures
|
||
work and then exports a wrong status to somebody else.
|
||
|
||
## 🔴 Twenty-one messages to a dead address, each one warning me it was dead
|
||
|
||
Every `SendMessage` I sent the Decoder from 2026-08-29 onward came back with:
|
||
|
||
> *accepted by the server for that session, but **delivery is not confirmed**: it
|
||
> has not reported that it can receive cross-session messages*
|
||
|
||
**Twenty-one times.** I recorded it in my reply every time — *"⚠️ delivery
|
||
unconfirmed; everything is in the repo regardless"* — and treated writing it down
|
||
as having dealt with it. It was not a caveat. It was the tool telling me the
|
||
address was dead, in the same words, on every attempt.
|
||
|
||
### The cause
|
||
|
||
I addressed `bridge:session_01QsEPXW…`, copied from the `from=` of an inbound
|
||
message. That is a **session ID**, and it died when the Decoder restarted.
|
||
`PROTOCOL.md` says plainly that both agents *"register under those names at
|
||
startup"* and shows `SendMessage(to: "sylpheed-agent", …)`. **Names survive a
|
||
restart; session IDs do not.** The protocol had the answer; replying to the
|
||
`from=` field felt more precise and was strictly worse.
|
||
|
||
### Why this one is the worst of the thirteen
|
||
|
||
The other twelve were things nobody was looking at. This one **had an alarm, and
|
||
the alarm fired every single time**. I did not miss it — I read it, wrote it into
|
||
my own status line, and carried on. Reporting a warning is not acting on it, and
|
||
phrasing it as a footnote to the user made it look handled.
|
||
|
||
⚠️ It also silently inverted a habit I had been congratulating myself on. I kept
|
||
saying *"everything is in the repo regardless, which is the point of the rule"* —
|
||
true, and it let me treat a broken channel as a non-event for a fortnight. The
|
||
repo did carry the findings. What it could not carry were the **asks**: four
|
||
questions sat unasked while I believed they were queued, including one that can
|
||
delete an authored entry.
|
||
|
||
### What changed
|
||
|
||
Re-addressed by name after `ListAgents`, and the send came back **without** the
|
||
warning — which is what a working channel looks like, and what twenty-one
|
||
unworking ones did not.
|
||
|
||
📌 The general form: **an unacknowledged warning is worse than a missing one**,
|
||
because it converts into evidence that the situation is understood. The fix is not
|
||
"read the warnings" — I read them all. It is that a warning repeating unchanged
|
||
across attempts is a *state*, not a footnote, and the second identical one should
|
||
have been treated as a failure rather than a fact about the world.
|
||
|
||
## The forced-backdrop pass is load-bearing on two screens, not six
|
||
|
||
I have said "six forced elements" since implementing the rule, and checked after
|
||
every change that **no verdict moved**. That measured the pass's *stability*, never
|
||
its *necessity*. Removing it entirely answers the other question:
|
||
|
||
| screens | order without the pass |
|
||
|---|---|
|
||
| `publisher_logo`, `publisher_logo_r`, `developer_logos`, `developer_logos_r` | **byte-identical** |
|
||
| `build_12`, `build_15` | first element becomes `pgloading_loop5` — the black screen returns |
|
||
|
||
**Four of the six are redundant.** `palogo_eff0`'s layer key is `0x00000000`,
|
||
`layer_source: implied` — lower than the lowest sprite key on those screens
|
||
(`0x0000a100`) — so the crate already sorts it first and the occlusion rule merely
|
||
agrees.
|
||
|
||
🔴 **The two that matter have no key at all.** On `build_12`/`build_15`,
|
||
`pgloading_eff00` carries `layer: null`, `layer_source: none` — the only two
|
||
elements in the export with neither a read nor an implied key. Their position rests
|
||
**entirely** on the occlusion constraint, with nothing to fall back on. That is the
|
||
port's single strongest dependency on a rule it did not decode, and it was hiding
|
||
inside a count of six.
|
||
|
||
### Which is also what makes the rule worth having
|
||
|
||
An agreement on four screens where a key already existed is not evidence — it is
|
||
the rule reproducing the crate. The two screens where it is load-bearing are the
|
||
two the rule was found on, and the argument for it there is not the key (there is
|
||
none) but that a permanently black screen is impossible on its face.
|
||
|
||
⚠️ So the honest statement, replacing the one I have been repeating: the rule
|
||
**decides** two screens and **confirms** four. I will not describe it as six again.
|
||
|
||
### The layer-key census this came out of
|
||
|
||
| `layer_source` | count | which |
|
||
|---|---|---|
|
||
| `sprite` — read from the file | 160 | everything with a texture |
|
||
| `implied` — the crate's, measured per name | 16 | `pteff00`, `pteff02`, `palogo_eff0`, `pgloading_eff00` |
|
||
| `none` — no key exists | **2** | `pgloading_eff00` on the two loading screens |
|
||
|
||
✅ Every layerless primitive's position is `implied` or absent, never read — and
|
||
`FORMAT.md` requires a consumer to be able to tell, which `layer_source` delivers.
|
||
The Decoder's own page says `pteff00`'s place on top *"is still a **measured**
|
||
per-name entry, not a decoded one"*; the port inherits that through
|
||
`layer: 0xfffffffe, layer_source: implied` and declares it. Nothing is being
|
||
passed off as decoded that is not.
|
||
|
||
## Re-running the Decoder's necessity census: every figure reproduces, and what that is worth
|
||
|
||
They took the stability-vs-necessity correction disc-wide and published the
|
||
instrument with it — *"so you can re-run it rather than trust it"*. I did, from a
|
||
worktree at their branch, across all 33 archives their census names:
|
||
|
||
| | theirs | my re-run |
|
||
|---|---|---|
|
||
| forced instances | 80 | **80** |
|
||
| **decided** by the rule | 62 | **62** |
|
||
| merely agreed | 18 | **18** |
|
||
| decided, by extension | 38 `.prm` / 24 `.tbm` | **38 / 24** |
|
||
| decided rows listing a keyless element | all | **all 62** |
|
||
|
||
Exact, on every figure. ⚠️ The example defaults to `GP_TITLE` and takes an archive
|
||
path, so a bare run reports **6 instances, not 80** — the disc-wide number needs
|
||
the loop. Worth saying because "I ran their instrument" would otherwise be true
|
||
and mean a thirteenth of what it sounds like.
|
||
|
||
### 🔴 What this verification is not
|
||
|
||
**I ran their code.** A fault in the instrument reproduces identically for me, so
|
||
this is not two independent measurements — it is one measurement executed twice.
|
||
That is the same correlated-instrument trap as `verify-screen` scoring two blank
|
||
frames `OK`, and I would rather name it than let a table of matching numbers imply
|
||
more than it has.
|
||
|
||
The genuinely independent evidence is narrower and came first: I removed **my own**
|
||
post-pass in the exporter and diffed the export. Different code, different
|
||
language, different layer of the stack — and it agreed on the six GP_TITLE
|
||
instances. Their crate-side run agrees with that. So the GP_TITLE result has two
|
||
witnesses; the other 74 have one, executed twice.
|
||
|
||
### The consequence for the port
|
||
|
||
Both of the port's decided screens are inside the 62, and **every one of the 62 is
|
||
keyless** — so the impossibility argument ("a screen black at every instant of its
|
||
own timeline cannot be right") is carrying all of them alone, with no key beneath
|
||
it anywhere. The exposure is theirs to hold disc-wide; the port's share is
|
||
`build_12` and `build_15`.
|
||
|
||
⚠️ 24 of the 62 are `.tbm`, which their page still records as *"correct or inert"*
|
||
because the corpus cannot find a `.tbm`'s pixels. None are in `GP_TITLE` — checked
|
||
again: 115 `.t32`, 45 `.rat`, 18 `.prm`, no `.tbm` — so nothing the port ships
|
||
depends on that half. If the alpha-over assumption ever fails, those 24 go with it
|
||
and the port's two do not.
|
||
|
||
## A second witness for the pixel-cost claim, from a different renderer
|
||
|
||
The Decoder moved the necessity question to a new layer rather than re-running the
|
||
sort — they rendered each of the 62 deciding builds twice and diffed the pixels,
|
||
finding that on all **38 `.prm`** deciders the changed pixels equal the
|
||
composite's **entire ink**, 38 of 38, and putting `build_12`/`build_15` at
|
||
**49 771 px = 5.40 %** each.
|
||
|
||
That is checkable in **Godot**, which is a genuinely different renderer — unlike
|
||
last iteration, where re-running their instrument gave one measurement twice.
|
||
|
||
| `build_12`, `--pose=rest` | ink |
|
||
|---|---|
|
||
| with the rule, threshold > 0 | 59 530 px (6.46 %) |
|
||
| with the rule, threshold > 1 | 48 368 px (5.25 %) |
|
||
| **without the rule** | **0 px at both** |
|
||
|
||
✅ **The strong form holds.** Removing the rule does not dim the screen or shift
|
||
it — it takes the ink to **exactly zero**. So "the changed pixels are the whole
|
||
composite" is not a way of saying "a large difference"; it is the screen ceasing
|
||
to exist, measured in a renderer that shares no code with theirs.
|
||
|
||
Their 49 771 sits between my two thresholds, 2.9 % above the `> 1` count. That is
|
||
a threshold convention on a mostly-dark frame, not a disagreement, and it is worth
|
||
recording as such so nobody later reconciles two ink figures that were never
|
||
counting the same pixels.
|
||
|
||
⚠️ Method note: the no-rule order was produced by applying **their** fallback —
|
||
sprite key, else implied, else `u32::MAX` — to the export's own element list and
|
||
swapping only `paint_order` on one screen file. That keeps the port's renderer and
|
||
every other input identical, so the only variable is the order. `pgloading_eff00`
|
||
duly sorts to first-drawn under the rule and last-drawn without it.
|
||
|
||
### Their point 4 is the pattern catching one of their own
|
||
|
||
They report that the 24 `.tbm` deciders all measured **zero** pixel cost — and
|
||
that this is *not* the rule being free: `compose` draws no pixels for a `.tbm` at
|
||
all, so their position cannot change a pixel **by construction**. The control
|
||
asked whether the composite had ink, which it always does. **A control that could
|
||
not fail.**
|
||
|
||
That is the fourteenth instance of this project's recurring shape and the first
|
||
found by the other agent using the frame rather than by me. It leaves the `.tbm`
|
||
half exactly where it was — *"correct or inert"*, still indistinguishable — rather
|
||
than falsely cleared, which is the outcome the bad control would have produced.
|
||
|
||
✅ Nothing the port ships is affected: no `.tbm` in `GP_TITLE`, and the port's two
|
||
deciders are `.prm`.
|
||
|
||
## Reconciling the two ink figures, and what "has its own key" is resting on
|
||
|
||
The Decoder reconciled our `build_12` ink counts and corrected a mistake I would
|
||
otherwise have made in their favour.
|
||
|
||
| | theirs | mine | gap |
|
||
|---|---|---|---|
|
||
| RGB > 0 | 49 771 | 59 530 | 9 759 px |
|
||
| RGB > 1 | 48 043 | 48 368 | **325 px — 0.67 %** |
|
||
|
||
🔴 **Their 49 771 was never a threshold figure.** It is exact RGBA inequality
|
||
between the two paint orders, which over a black backdrop coincides with `ink>0`
|
||
— so it belongs against my **59 530**, not against my 48 368. Matching it to the
|
||
closer number would have made us agree for the wrong reason, and they said so
|
||
before I could do it.
|
||
|
||
Checked on my side: my value-exactly-1 population is **11 162 px**, and the gap
|
||
above `>1` is 325 px. So **9 434 of the 9 759-px gap — 96.7 % — lives in pixels
|
||
whose value is exactly 1**, and their renderer produces that many fewer of them.
|
||
Their claim holds structurally, not just in headline.
|
||
|
||
✅ Two conventions worth carrying, both theirs:
|
||
|
||
* **`>0` is not portable between renderers on a mostly-dark frame; `>1` is.** Any
|
||
cross-renderer ink figure needs its threshold named. This corpus has spent real
|
||
effort on residuals at the 0.1 % scale, and a 16 % artefact hiding at 1 LSB is
|
||
exactly the size of thing that would have been argued about.
|
||
* the without-the-rule zero is **threshold-insensitive in both renderers**, which
|
||
is the first double-witnessed result on this rule.
|
||
|
||
### One refinement on the 18 confirmations
|
||
|
||
They sharpened my summary: the 18 are worse than "the crate agreeing with
|
||
itself" — 14 have their own key so the rule never fires decisively, and 4 are
|
||
builds where every element is forced, so the tie-break gives the same order
|
||
regardless. None is evidence in either direction. Agreed.
|
||
|
||
⚠️ But for the port's four, "has its own key" is doing quieter work than it
|
||
sounds. `palogo_eff0`'s key is `0x00000000` with **`layer_source: implied`** — the
|
||
crate's measured per-name entry, not a value read from the file. So those four
|
||
confirmations are not *the file already settles it*; they are **another
|
||
measurement already settles it**, in the same category as `pteff00`'s place on
|
||
top, which their own page is careful to call measured rather than decoded.
|
||
|
||
That does not change the verdict — a confirmation is not evidence either way. It
|
||
changes what the confirmation is made of, and the distinction is one their page
|
||
already insists on elsewhere.
|
||
|
||
## Not one of the 80 has a decoded key — and the port's four are the rule's oracle check
|
||
|
||
The Decoder took the key-source question disc-wide and the answer is stronger
|
||
than either of us had it: **0 of the 80 forced instances carry a key read from
|
||
the `T8aD` header.** 14 have an `implied_layer_key`, 66 have nothing.
|
||
|
||
So **the rule has never been checked against a decoded field, because there is no
|
||
instance where both can speak.** That is what a keyless-element fallback
|
||
necessarily looks like — but it removes a check a reader would assume exists.
|
||
|
||
The port's six are the same pattern exactly, verified from the export:
|
||
|
||
| | count | |
|
||
|---|---|---|
|
||
| read from the file | **0** | |
|
||
| `implied` — measured in the running game | **4** | `palogo_eff0` on the four splashes |
|
||
| nothing at all | **2** | `pgloading_eff00` on `build_12`/`build_15` |
|
||
|
||
### They retracted something in the direction that costs them
|
||
|
||
Last round they wrote that *"none of the 18 is evidence for the rule in any
|
||
direction"*, and I agreed and repeated it. They have since separated two questions
|
||
I had let run together:
|
||
|
||
* **does the rule change the composite there?** No — the sort already had a key.
|
||
That is the necessity count and it stands.
|
||
* **does the rule get the right answer there?** **Yes** — and this is the only
|
||
place it can be asked. `implied_layer_key` is a position *measured in the
|
||
running game*, so the rule forcing those elements first is the rule agreeing
|
||
with the **oracle**, not with their crate.
|
||
|
||
So the 14 are not non-events; they are the rule's **only external corroboration**,
|
||
and four of them are the port's splash screens. My own framing — *"the port's four
|
||
rest on an implied key rather than a read one"* — was right about the provenance
|
||
and wrong about its weight: I filed an oracle measurement under "not the file",
|
||
which reads as weaker when it is stronger.
|
||
|
||
⚠️ ✅ The revised state, which I expect to be stable: **38 `.prm` deciders with a
|
||
measured pixel cost, two double-witnessed · 24 `.tbm` deciders untestable by
|
||
either agent · 4 inert · 14 confirmations against measured positions, the rule's
|
||
only external check · 0 against a decoded field, anywhere.** The impossibility
|
||
argument carries all 62.
|
||
|
||
📌 Closing this thread on my side. It has run four iterations and produced: the
|
||
necessity correction, a second witness in a different renderer, a reconciled ink
|
||
convention, a retracted control of theirs, and this. It has also stopped being
|
||
about the port — nothing in `build_12`/`build_15` has moved since the rule landed,
|
||
and the remaining questions are the Decoder's to hold. Continuing would be
|
||
refining someone else's page.
|
||
|
||
## A withholding reason that was false, and the measurement beside it that was not
|
||
|
||
`authored/rendering.json` names three leaf records the exporter flags and the port
|
||
does **not** draw. Auditing the reasons rather than the behaviour:
|
||
|
||
### 🔴 `build_12,15/pgloading_loop5` — the reason was wrong
|
||
|
||
It read *"leaf scale (0,0). A zero scale is one of the three historical failures
|
||
this corpus names."* That describes **t=0 and t=30 and nothing after them.**
|
||
|
||
What the leaf actually holds, read from the export: one element,
|
||
`pgloading_ring`, with a sprite, whose scale ramps **0 → 250 → 800 → 1000** while
|
||
its alpha rises to full at t=55 and falls to nothing by t=130. An expanding,
|
||
fading ring — a loading pulse, not a degenerate record.
|
||
|
||
⚠️ **And it is visible at the instant this port poses.** `build_12`'s settle window
|
||
is [40, 48], so the pose lands near **t=44**, where the ring interpolates to
|
||
**scale 140 at alpha 143**. Withholding it is not declining to draw *nothing*; it
|
||
is declining to draw *something*, and the one-line reason concealed which.
|
||
|
||
✅ **It stays withheld**, on the reason that always applied and is already written
|
||
below it in the same file: there is no way to adjudicate it here. The loading
|
||
screens have no oracle capture — the Decoder records them as unreachable from the
|
||
title path — and `verify-screen` compares against a renderer that draws no leaves.
|
||
Drawing it would put unadjudicable content on a screen, which is the test
|
||
`ptlogo_eff2` also fails. Behaviour unchanged: `build_12` still renders 59 530 px
|
||
of ink.
|
||
|
||
📌 The two entries in that list were written to different standards. The
|
||
`title_jp/ptlogo_eff2` reason is four paragraphs, states the scale as a *pop*
|
||
(0 → 125 % → 0 over 0.95 s), works through both readings and says why neither can
|
||
be chosen. The `pgloading_loop5` reason was one line and false. Same file, same
|
||
author, same day — the difference is that one was interesting and one was a
|
||
loading screen.
|
||
|
||
### ✅ The measurement in the same paragraph checks out
|
||
|
||
That paragraph also claims *"its max went 155 → 232 when they were drawn"* for
|
||
`title_jp`. Reproduced by adding `ptlogo_eff2` to `draw_leaf_for` and re-running:
|
||
**155 → 231**, with the differing pixels going 20 498 → 58 313. One off a number
|
||
recorded days ago, which is rounding rather than drift.
|
||
|
||
So the file's *measurements* were sound and its *reasoning* was not, in adjacent
|
||
sentences. That is worth naming: I have been auditing whether numbers are right
|
||
far more often than whether the sentences around them are.
|
||
|
||
## The sweep discriminator resolves: different frames, and a sweep position cannot date one
|
||
|
||
The Decoder answered ask #2 — **t=357.7 was never fitted against a PNG.** It comes
|
||
from `title-draw-capture-vertex-colours.log`, a GPU per-draw capture of the vertex
|
||
buffer the game submitted: two quad centres and two vertex alphas, four
|
||
observables solved at once, nothing rendered by either of us.
|
||
|
||
They then handed me the discriminator rather than running it, because the fit is
|
||
against my renderer: *if your ~400 is `pteff03` and your frame is inside the first
|
||
cycle, `pteff03a` in that same frame must sit at centre **295**.*
|
||
|
||
Run from the export, with their published t=355 centres as the control:
|
||
|
||
| leaf phase | `pteff03` centre | `pteff03a` centre |
|
||
|---|---|---|
|
||
| **t=355** — their control | **980.5** (published 981) | **477.7** (published 478) |
|
||
| **t=400** — my fit | 1160.5 | **294.9** |
|
||
|
||
✅ **294.9 against a predicted 295.** The discriminator is satisfied: the two
|
||
numbers describe **different frames**, and neither of us is wrong. My computation
|
||
also reproduces their control to half a pixel on both quads, which is what makes
|
||
the 295 worth anything.
|
||
|
||
### Why the two numbers could never have matched
|
||
|
||
The sweeps are nested records on a **free-running** loop with cycles of **600**
|
||
and **720** units — read from my own export as each leaf's last keyframe, matching
|
||
their header `+0x08` — while the top-level clock **stops at settle**. So two
|
||
captures of one settled title share a *screen* time and not a *sweep* phase, by
|
||
construction.
|
||
|
||
🔴 **The rule to carry: a sweep position cannot date a frame.** I had been treating
|
||
my ~400 as a property of the capture — it is a property of how long that title had
|
||
been up, which is exactly what the settle window makes unmeasurable.
|
||
|
||
And the two are not comparable in kind. Their 357.7 is a **joint** fit where both
|
||
leaves agree; mine poses one phase. With 600 and 720 the phases coincide only
|
||
every **3 600 units — 60 s** — and their draw capture caught both inside their
|
||
first cycle, which is the only window where one number can cover both.
|
||
|
||
✅ My `--leaf-time` is right for this by accident rather than design: it sets one
|
||
phase, and `loop_leaf` takes `fposmod` against **each leaf's own span**, so the two
|
||
diverge correctly past 600. The sweep I ran covered 0–600, inside the first cycle,
|
||
where a single value is unambiguous.
|
||
|
||
### Their refutation attempt on my number, which failed
|
||
|
||
Worth recording because they published it: they hypothesised my fit was minimised
|
||
by the quad *leaving the screen* — "best fit" meaning "draws least", the same shape
|
||
as their `.tbm` control that could not fail. At t=400 quad B is **fully** on screen
|
||
and quad A is 319 of 400 px wide. The number is fitting something present, and it
|
||
survives.
|
||
|
||
## Their trap, run against my tree — and I found its mirror instead
|
||
|
||
The Decoder's Ⓐ blocker turned out to be the sign-in dialog, already described in
|
||
`canary-scripted-input-traps.md` §3 and in `boot_menu.sh`'s header. Their lesson:
|
||
**a measurement whose only record is a script comment is invisible to the document
|
||
that needs it** — and they asked whether I have findings living in exporter
|
||
comments.
|
||
|
||
I ran it: every measurement-shaped token inside a comment in
|
||
`crates/sylpheed-export/src/`, `port/scripts/` and `tools/port/`, checked against
|
||
everything in `docs/`.
|
||
|
||
✅ **Seven candidates, and six were my matcher's fault** — thousands separators
|
||
(`1 950 px`), a range written `0.70-0.82 %` in one place and `0.1355 %` in the
|
||
other, precision differing between `9.1` and `9.14`. The findings themselves are
|
||
all in `DECISIONS.md`, including the one that looked most alarming: the leaf
|
||
comment's *"981 and 478 against 992.0 and 467.2 measured off the capture"* is
|
||
recorded, and the **11.5 px** residual has its own paragraph saying it is not to
|
||
be fitted.
|
||
|
||
### 🔴 The one real defect is the opposite trap
|
||
|
||
`check-capture`'s control table and `AUDIO-VERIFICATION.md` both record the voice
|
||
control, and they had **drifted**: **53.3 %** in the tool, **53.2 %** in the doc,
|
||
twice each.
|
||
|
||
Neither can be re-measured — that control file was transient and is gone — so
|
||
there is no way to say which copy aged. The Decoder lost a finding because it had
|
||
**one** record, in a script comment. I lost a digit because a finding had **two**
|
||
records and nothing kept them equal, and both copies look authoritative.
|
||
|
||
Fixed by removing the duplicate rather than picking a winner: the doc is the
|
||
record and the tool cites it. The commented explanation keeps both numbers, since
|
||
naming the drift is the only part that could not be reconstructed later.
|
||
|
||
### ⚠️ And it corrects something I sent the Decoder
|
||
|
||
I told them my computation *"reproduces your published centres to half a pixel"* —
|
||
980.5/477.7 against 981/478. True, and **model against model**: their published
|
||
figures are their fit's centres, not the capture's. Against the **capture** this
|
||
corpus already records 992.0 and 467.2, an **11.5 px** residual, in a paragraph
|
||
that says it is not to be fitted.
|
||
|
||
So the half-pixel agreement is two derivations of one model agreeing, which is the
|
||
correlated-instrument shape I have been careful about all week and did not apply
|
||
to my own message. The discriminator result is unaffected — 294.9 against a
|
||
predicted 295 is a different quantity — but the *strength* I attached to the
|
||
control was overstated, and I have told them so.
|
||
|
||
## The plate pulses — measured, and the port was wrong on the boot's end state
|
||
|
||
Ask #1 came back the other way. Held at the title with **no input**, the plate
|
||
oscillates continuously — two windows in one boot, ~23 cycles each, no decay, no
|
||
settling — and 🔴 **it never goes off**: the plate-absent floor is **159**
|
||
thresholded green pixels and the pulse bottoms at **714**, four and a half times
|
||
that.
|
||
|
||
That reading is what makes the mechanism recoverable. A glow alone cannot do it:
|
||
`ptbtn00f` ramps 0 → 80 → 0. A **steady base plus a pulsing glow** can, and 714 and
|
||
1520 are exactly base-only and base-plus-glow.
|
||
|
||
And the port already had the base right. `ptbtn00`'s fade to 0 at t=244 is its
|
||
**exit** ramp, which plays when the screen leaves; while the screen is *held* the
|
||
base sits at its own hold, alpha 255 at t=238. What was missing was the glow.
|
||
|
||
### The defect was in the renderer, not in the deleted entry
|
||
|
||
`_draw` treated a **looping** record like a **focused** one — drawing it *instead
|
||
of* the base sprite. For a focused button that is right and measured (the focused
|
||
sprite covers the base at 100 % of base-visible pixels). For the plate it
|
||
substituted a dim glow for a bright sprite, which is why the plate vanished and
|
||
why I deleted the authored entry on 2026-08-29.
|
||
|
||
I deleted the right thing for the wrong reason. The entry was correct; the branch
|
||
it landed in was not. A looping record now draws **over** the base, and the entry
|
||
is restored with the measurement behind it.
|
||
|
||
| | |
|
||
|---|---|
|
||
| plate region, base only | **95.68** |
|
||
| base + glow at peak | **115.52** |
|
||
| measured period over 10 cycles | **1.980 s** |
|
||
| declared 120 units at 60 u/s | 2.000 s — **ratio 0.990** |
|
||
|
||
✅ The pulse appears on the **boot** path, where the overlay runs on the shared
|
||
clock. It does *not* appear under `--screen=title --overlay=press_start`, because
|
||
a static overlay poses at its own arrival — my own earlier fix — so its clock never
|
||
advances. That is correct for a still frame and worth knowing before anyone reads
|
||
a flat plate there as a regression.
|
||
|
||
### What is authored, and what is refused
|
||
|
||
**120 units, not seconds**, on the Decoder's own instruction: their run measured
|
||
2.530 and 2.540 s and an earlier corpus run 2.24 s — one declared number through
|
||
two emulator pacings (×1.27 and ×1.12 against a nominal 2.000 s). Hardcoding 2.5 s
|
||
would author a loaded container's clock.
|
||
|
||
⚠️ Their three limits are carried into `authored/timing.json` verbatim: **one
|
||
boot**, two windows inside it are not two boots; it **does not distinguish** the
|
||
boot title from an attract-loop title, since run 1 opens at t≈255 s against Q9's
|
||
~193 s baseline; and 🔴 **714/1520 is not an alpha ratio** — the counter is
|
||
thresholded pixels, so no duty cycle or ramp shape may be read from it. The port
|
||
draws the record's own declared ramp and infers nothing about its shape.
|
||
|
||
Oracle rows unmoved; every asserting check passes.
|
||
|
||
## A static overlay now advances, and a refutation attempt on the pulse floor
|
||
|
||
### The static overlay was frozen at its arrival, which was my fix overshooting
|
||
|
||
`--screen=X` animates X. `--screen=X --overlay=Y` **froze** Y, because the fix for
|
||
the original defect — the raw elapsed clock pushed in, 9 units at capture, plate
|
||
drawing nothing — replaced a frozen-too-early overlay with a frozen-at-arrival
|
||
one. One command animating one build and freezing the other is an inconsistency,
|
||
and the plate pulse is what made it visible: the plate oscillated on the boot path
|
||
and sat flat here, which reads as a regression and is not one.
|
||
|
||
Now **offset, not pinned**: the overlay begins at its own settle and takes the same
|
||
delta the main view takes. Verified — the static path pulses over the same range as
|
||
the boot path, 95.85 → 115.41 against 95.68 → 115.52. Still frames are unaffected
|
||
(`--time` freezes both) and `title_plate` holds at **0.00 %**.
|
||
|
||
📌 Both halves of this were mine, a week apart: the original bug, then the
|
||
over-correction for it. The over-correction was invisible until a *third* change —
|
||
the pulse — gave it something to be wrong about. A fix that overshoots leaves no
|
||
symptom until something else needs the part it disabled.
|
||
|
||
### 🟡 Refutation attempt — their pulse floor of 159 green pixels
|
||
|
||
Their measurement gives a plate-absent floor of **159** thresholded green pixels,
|
||
a pulse minimum of **714** and a peak of **1520**, the floor measured on
|
||
`live-title-build4-no-plate.png` — a capture I hold. So it is checkable.
|
||
|
||
**It is not reproducible from the published description.** Across the plate region
|
||
(513×48 at +383+551) my counts are 3–5× theirs at every threshold:
|
||
|
||
| green > | no-plate | press-a | ratio |
|
||
|---|---|---|---|
|
||
| 100 | 676 | 9 369 | 1 : 13.9 |
|
||
| 150 | 528 | 5 732 | 1 : 10.9 |
|
||
| 180 | 462 | 4 908 | 1 : 10.6 |
|
||
| 200 | 411 | 4 272 | 1 : 10.4 |
|
||
| **theirs** | **159** | **1 520** | **1 : 9.6** |
|
||
|
||
No threshold produces both numbers, so their region must be a tighter crop than
|
||
mine. Neither the region nor the threshold is stated on the page.
|
||
|
||
✅ **The finding survives in the part that matters.** The *ratio* is stable at
|
||
1 : 10.4–10.9 across a wide threshold band and brackets their 1 : 9.6, so "the
|
||
plate-present frame carries about ten times the green of the plate-absent one" is
|
||
robust to how it is counted. What cannot be checked is the absolute figures — and
|
||
those are what the "never goes off" conclusion is stated in.
|
||
|
||
⚠️ This is the convention we agreed two iterations ago, applied to their own new
|
||
page: **a cross-renderer pixel figure needs its region and its threshold named.**
|
||
I raised it there about *my* numbers drifting between two files; the same rule
|
||
makes a published measurement unverifiable when a reader has the capture and not
|
||
the crop. Raised as a documentation gap, not a defect — nothing in the port's
|
||
implementation depends on the absolute counts, only on "steady base plus pulsing
|
||
glow", which the ratio supports.
|
||
|
||
## Their pulse floor reproduces exactly once the predicate is named — 159, to the pixel
|
||
|
||
I challenged their 159 / 714 / 1520 as unreproducible: my counts over the plate
|
||
region were 3–5× theirs at every threshold. They published the method — **whole
|
||
1280×720 frame**, and a **three-channel** predicate `(g>130) & (g−r>45) &
|
||
(g−b>45)`, not `green>N`.
|
||
|
||
Applied to the capture I hold:
|
||
|
||
| | theirs | mine |
|
||
|---|---|---|
|
||
| floor, `live-title-build4-no-plate.png` | **159** | **159** — exact |
|
||
| pulse minimum | 714 | `live-title-press-a.png` gives **753**, 5.5 % apart |
|
||
| "never goes off" ratio | 714 / 154 = **4.6×** | 753 / 159 = **4.7×** |
|
||
|
||
✅ **The floor reproduces to the pixel**, and the load-bearing claim now has an
|
||
independent witness: a capture from a different session, counted by me, lands at
|
||
753 against their pulse minimum of 714. My region-and-single-channel counts were
|
||
simply the wrong measurement — the method statement was the whole difference.
|
||
|
||
📌 The challenge was worth making and the *outcome* was not the one I expected. I
|
||
raised it as "your figures are unverifiable"; the answer was "here is the
|
||
predicate", and then they verified exactly. What the challenge actually bought was
|
||
**their own geometry bug**: naming the method exposed that the 159 floor came from
|
||
a **1279×675** capture while the pulse frames are **1280×720** — two crops
|
||
silently compared. They have replaced it with a same-run, same-geometry floor of
|
||
**154**.
|
||
|
||
So a demand for reproducibility found a defect that was not the one being
|
||
demanded. ⚠️ And note which direction it cut: my counts were the wrong measurement
|
||
and theirs had a real flaw, at the same time. "One of us must be right" was never
|
||
the shape of it.
|
||
|
||
### Their retraction, and whether the port banked it
|
||
|
||
They retract citing a faulting run's dumped `logged_profile_slot_0_xuid = ""` as
|
||
evidence no profile was signed in: **Xenia dumps the config *file*, before
|
||
command-line overrides** — a run launched with `--mute=true` dumps `mute=false`,
|
||
four for four. So that dump cannot say what any run did.
|
||
|
||
✅ Checked: the port's docs never cite it. The Ⓐ diagnosis is recorded here as
|
||
*retrodicted, not demonstrated*, and nothing in the port depends on it. Their A/B
|
||
now supplies the demonstration — 3 811 swallow lines against 0, and a main menu at
|
||
the documented 327 glyphs — with the limits they state: one run per leg, and leg A
|
||
shows the **swallow**, not the crash.
|
||
|
||
⚠️ Also worth carrying: their first A/B pair was **void**. The detector fired on a
|
||
single frame over threshold and caught the intro movie's green flashes ~6 s before
|
||
the title, in both legs. The presses were real — each skipped the rest of the
|
||
movie, which is Q9's behaviour — but the pair tested nothing. A detector that can
|
||
fire on one frame will fire on the wrong one.
|
||
|
||
## My rendered pulse, counted in their units — and #4 refutes the voice value without fixing it
|
||
|
||
### The pulse lands in the right range, and I am not tuning the depth
|
||
|
||
Their predicate makes my render comparable to their measurement for the first
|
||
time. Counting the boot's held title with `(g>130) & (g−r>45) & (g−b>45)` over the
|
||
whole frame:
|
||
|
||
| | theirs | mine |
|
||
|---|---|---|
|
||
| plate-absent floor | 154 | — |
|
||
| pulse minimum | 714 | **805** |
|
||
| pulse peak | 1 520 | **1 420** |
|
||
|
||
✅ Same range, and both ends far above the floor — so **"never goes off" holds in
|
||
the port's own render**, not just in their capture. The trace oscillates cleanly:
|
||
`1413, 1359, 930, 857, 805, 830, 1182, 1413, …`
|
||
|
||
⚠️ My pulse is **24 % shallower** (615 against 806). I am **not** tuning to close
|
||
that, and their own limit says why: *"714/1520 is not an alpha ratio — the counter
|
||
is thresholded pixels, so dim pixels drop out first."* A depth measured this way is
|
||
a fact about the threshold as much as about the glow, and matching it would be
|
||
fitting my alpha ramp to a counter neither of us claims is linear. The port draws
|
||
the record's declared ramp.
|
||
|
||
### #4 refutes the voice value from the output side
|
||
|
||
They recorded 148 s of the game's own output over the boot intro — ALSA tee,
|
||
`--gpu=null`, **0.15 % silence**, with provenance from the XMA probe rather than a
|
||
screenshot. **Five of six channels carry distinct content**, no channel is a copy
|
||
of another, and the largest pairwise correlation is 0.70 between FL and FR.
|
||
|
||
🔴 So `presentation: "loudest"` — keeping **one** stream — cannot be right. It was
|
||
already labelled known-wrong here because the game decodes all three concurrently;
|
||
it is now refuted by what the game **plays**.
|
||
|
||
✅ **And it stays unfixed, on their instruction.** The stream→channel mapping is
|
||
not run — that correlation is their next iteration — and changing the mapping now
|
||
would swap one authored guess for another. **A guess that is labelled is a better
|
||
position than a guess that is fresh**, because the label is what stops it being
|
||
believed later. `authored/audio.json` records the refutation and keeps the value.
|
||
|
||
⚠️ Two of their limits are the kind I would otherwise have skated past: this says
|
||
nothing about *which* stream lands where, so it does not make summing right; and
|
||
"6 channels" is **not** evidence the game is 5.1 — that count is Xenia's hardcoded
|
||
`kFrameChannelsDefault`. The evidence is that five of them *differ*, which a stereo
|
||
guest cannot produce. The number of channels in a capture is a property of the
|
||
capture.
|
||
|
||
## The voice export now carries every qualifying stream — and a unity sum was refused by our own check
|
||
|
||
#4 is answered and it reframes the question the port had been asking. **`ADV.wmv`
|
||
carries one audio stream and it is WMA Pro 5.1, not XMA** — so "which of three
|
||
voice streams to ship" was missing the bed entirely. The movie's own track is the
|
||
bed; the three streams are *additional*.
|
||
|
||
Solving `capture = 0.600 × movie + residual` per channel, the gain is 0.600
|
||
uniformly (−4.44 dB), and the residual is **three signals at three positions** —
|
||
front pair (r 0.918), rear pair (r 0.929), and a centre whose partner LFE is empty
|
||
to −115 dB. 🔴 The load-bearing number is **LFE reproducing to −115.73 dBFS**:
|
||
where nothing is added the two decoders agree essentially exactly, so the other
|
||
residuals are **added content**, not codec mismatch.
|
||
|
||
`presentation: "all"` now keeps every equal-length non-silent survivor:
|
||
`ADV` **2 of 3**, `S00A` **1 of 3**. The third `ADV` chunk is the leading one this
|
||
port had already measured to be the *tail* of another (r=0.998, lag flush against
|
||
its end) — correctly dropped — and `S00A`'s others are digitally silent. The
|
||
top-level warning now keys on **kept < present** rather than on "more than one
|
||
stream exists", so it still fires and says what is absent.
|
||
|
||
### 🔴 A unity sum was tried and `check` refused it
|
||
|
||
First attempt summed at unity, on the precedent of `stems: "sum"` for a BGM bank.
|
||
`ADV` came out at **+2.62 dBFS**, over the +1.0 bound, and the validator rejected
|
||
the tree.
|
||
|
||
It was right, and the precedent did not transfer. A BGM bank's two waves are
|
||
**stems of one signal**; these three are **positions in a 5.1 field**. A stereo
|
||
downmix weights them 0.4142, 0.2929 and 0.2929 — which **sum to one whatever the
|
||
assignment**. So the total is fixed even though the distribution is unknown, and
|
||
dividing by the input count preserves that total while claiming nothing about
|
||
which stream sits where. `ADV` now lands at **−3.1 dBFS**.
|
||
|
||
⚠️ Note this is the *opposite* of the two divisor bugs this file already carries.
|
||
Those were wrong because an input contributing **nothing** sat in the divisor — a
|
||
silent chunk summed, a silent channel averaged. Here every input carries signal
|
||
and the weights genuinely sum to one. "Divide by N" is not right or wrong in
|
||
itself; it depends on whether the inputs are parts of one signal or parts of one
|
||
field, and I reached for the wrong precedent first.
|
||
|
||
### What is still not claimed
|
||
|
||
⚠️ **Which stream sits at which position is not determined** — their assignment is
|
||
by position, not content — so the port builds no 5.1 and applies no positional
|
||
downmix. ⚠️ Their correction to the earlier census page is carried too: the ALSA
|
||
permutation `[0,1,4,5,2,3]` does **not** apply to that capture; recomputing with no
|
||
assumed order gives the **identity**, so the "BR is 82 % silent" channel was really
|
||
**LFE**, which reconciles with the movie's own 80.64 % silent LFE. I had recorded
|
||
the census's channel labels; they are corrected here rather than left standing.
|
||
|
||
Every asserting check passes.
|
||
|
||
## Their stream assignment does not fit my region — weights NOT applied
|
||
|
||
The assignment is settled on their side by `byte_size`: ctx0 (1 294 336) → FL/FR,
|
||
ctx1 (1 118 208) → FC with LFE silent, ctx2 (1 171 456) → BL/BR, giving the port
|
||
weights 0.4142 / 0.2929 / 0.2929. Applying them needs their contexts to be my
|
||
chunks. **They are not.**
|
||
|
||
| | bytes |
|
||
|---|---|
|
||
| their three contexts, summed | **3 584 000** |
|
||
| my whole resolved voice region for `ADV` | **3 114 352** |
|
||
| difference | **+469 648** — 15 % larger than the region they must fit in |
|
||
|
||
My region yields **three** chunks, one of which is an 84.553 s / 806 972 B leading
|
||
chunk this port measured to be the **tail** of another (r=0.998, lag flush against
|
||
its end) — and 806 972 is none of their three sizes. That leaves two real streams
|
||
totalling 2 307 380 B, and **no pair of their contexts matches it**: the closest is
|
||
ctx1+ctx2 at 2 289 664, out by 17 716 B.
|
||
|
||
🔴 **So the weights are not applied.** Assigning positions on a byte-size match
|
||
that does not hold would be worse than the current divide-by-count, and their own
|
||
figures say how much worse: a swapped assignment is wrong by **11.76 dB**. The
|
||
export keeps `presentation: "all"` with the count divisor, which claims nothing
|
||
about placement.
|
||
|
||
⚠️ What I am *not* claiming: that their assignment is wrong. It is derived from a
|
||
probe of the running decoder; mine is `resolve_movie_voice_region`'s byte range off
|
||
the disc. **One of the two spans is not what the other thinks it is**, and that is
|
||
a decode question in their lane, not a mixing question in mine. The numbers above
|
||
are the whole of what I can say.
|
||
|
||
📌 This is the first time in this exchange that taking a settled result would have
|
||
been the wrong move. Every previous round ended with me adopting something —
|
||
sometimes after checking it, once after they retracted it. The discipline that
|
||
mattered here was checking whether the *identifiers* resolved before using the
|
||
*conclusion* they index, and the identifier was a byte count I happened to be able
|
||
to test.
|
||
|
||
### What I did take
|
||
|
||
✅ **One mixer gain, not two** — the same 0.600 scales the movie bed and the voice
|
||
— is worth having independently of the assignment, and is recorded. ⚠️ Not baked
|
||
in as a constant: whether 0.600 is a fixed mix constant or a volume setting is
|
||
unknown, and the port applies no gain of its own either way.
|
||
|
||
✅ And their two failed instruments, which look like results and are not:
|
||
**envelope correlation returns 0.86–0.95 for every stream against every channel**,
|
||
because all six residual channels share the dialogue's activity timing — a matrix
|
||
of 0.9s reads as a strong finding and is the opposite. **Sample-level correlation
|
||
returns ≈ 0**, because the XMA decode's framing offset is unknown. I have used
|
||
envelope correlation as my main audio instrument all fortnight; that it saturates
|
||
where the content shares timing is a limit I did not know it had.
|
||
|
||
## The resolver starts late, and my "duplicate tail" was a real stream all along
|
||
|
||
My refusal to apply their weights found a defect in their decoder:
|
||
**`resolve_movie_voice_region` starts inside the first stream.** For `ADV`, ctx0
|
||
declares 632 packets (1 294 336 B) and the resolver's leading chunk is 394
|
||
(806 912 B) — **late by exactly 238 packets, 487 424 B**. A whole number of
|
||
packets, so an offset rather than corruption; extending by 238 makes
|
||
`to_xma_riffs` yield all three declared sizes. Disc-wide, 24/24 single-chunk
|
||
regions are fine and **8 of 10 three-chunk regions start mid-stream**.
|
||
|
||
### 🔴 Which re-reads a measurement of mine, without touching the measurement
|
||
|
||
This port measured the leading chunk as *"the TAIL of the kept stream [refuted]"* —
|
||
envelope correlation r=0.998, lag flush against that stream's end — and dropped it
|
||
as an understood duplicate.
|
||
|
||
**The correlation was right and the conclusion was wrong.** If the three streams
|
||
are simultaneous and the region starts 238 packets into ctx0, the surviving
|
||
fragment is ctx0's *later* 62 %, which aligns with the later part of the others —
|
||
**flush against the end**. That is precisely what I measured. Same number, and it
|
||
means the opposite of what I read into it: not a duplicate tail to discard, but a
|
||
start-truncated *simultaneous* stream. **The port has been dropping a real stream.**
|
||
|
||
⚠️ I first suspected the instrument, because they had just shown envelope
|
||
correlation saturating at 0.86–0.95 across every stream/channel pair. Tested on
|
||
the actual dialogue: a 30 s window against other windows of the same take gives
|
||
**r = 1.0000** at zero offset and **−0.08 … +0.08** everywhere else. It localises
|
||
sharply. Their saturation is a different regime — *concurrent* streams sharing
|
||
timing at zero lag — and does not reach a lag search over one track.
|
||
|
||
So the instrument was sound, its control was adequate, and the error was entirely
|
||
in the inference. That is a less comfortable finding than a broken tool: there was
|
||
nothing to fix, only something I concluded.
|
||
|
||
### Not fixed here, deliberately
|
||
|
||
The port still drops chunk 0. Including it correctly needs one of two things I do
|
||
not have:
|
||
|
||
* **the corrected span** — theirs, and they explicitly say not to extend blind:
|
||
`ADV` is start − 238×2048, but for the other seven affected regions the exact
|
||
clip is **unknown**, their audit's figure being an upper bound (243 for `ADV`
|
||
where the truth is 238);
|
||
* **or an alignment decision** — chunk 0 is missing its *head*, so summing it from
|
||
sample 0 would be wrong by 84.5 s against 137.3 s. Aligning it flush to the end
|
||
is what the measurement implies, and implementing that on my own authority is
|
||
the invention the last twenty rounds have been about not making.
|
||
|
||
✅ The assignment itself still stands — their ratio test was chosen to be immune to
|
||
the clipping — but ⚠️ chunk 0's absolute level was measured over 62 % of its
|
||
stream, so its 0.05 dB agreement is luckier than it reads.
|
||
|
||
## The export knew the voice was incomplete; the runtime did not say so
|
||
|
||
The manifest has carried a full account of the voice export's gap for weeks, and
|
||
the runtime printed `+ voice ADV` and nothing else. That asymmetry is the
|
||
dangerous one for audio specifically: a reader of `manifest.json` gets a
|
||
paragraph, and a person **listening** gets clean dialogue with no way to learn
|
||
that a stream is absent from it.
|
||
|
||
This port already governs the same situation elsewhere — NEW GAME announces the
|
||
two measured screens it jumps over rather than skipping them silently. Audio had
|
||
no equivalent, so:
|
||
|
||
* `ManifestAudio` gains **`incomplete`**, one line naming what an asset is *known*
|
||
to be missing. Absent means nothing is known to be missing — **never** that the
|
||
asset was checked and found complete.
|
||
* `MenuAudio` carries it alongside the stream, and `_play_video` prints it at the
|
||
moment the voice starts.
|
||
|
||
```
|
||
+ voice ADV
|
||
🔴 KNOWN INCOMPLETE: 2 of 3 streams. The running game decodes all 3 …
|
||
```
|
||
|
||
Verified on both paths — the boot's `ADV` and P7's `S00A`.
|
||
|
||
### 🔴 And the first version of the message was false for one of the two assets
|
||
|
||
It read *"one is a start-truncated stream this export drops"*. That is `ADV`'s
|
||
story: its dropped chunk is the start-clipped remainder of ctx0. **`S00A`'s
|
||
dropped chunks are digitally silent** — a different reason entirely — and the
|
||
message would have told anyone running the new-game intro something untrue about
|
||
their own asset.
|
||
|
||
Caught by reading the output for both, which took one command and which I nearly
|
||
skipped because the `ADV` line was obviously correct. The wording now states the
|
||
counts and points at the entry's `why`, because **which streams are dropped and
|
||
why is not the same story twice** and a single sentence cannot carry both.
|
||
|
||
⚠️ Worth naming as its own shape: a message generated *once* from a template but
|
||
*true* only for the case it was written against. It is the failure mode of every
|
||
generic warning, and it is harder to see than a wrong number because the sentence
|
||
is well-formed and confident in both places.
|
||
|
||
## The voice export is complete — new pin, and the cause was a "within one bank" cap
|
||
|
||
`formats-pin-2026-08-30`. The cause of the late start was a second condition on
|
||
the start filter: `end - s < 1_500_000`, *"only within one bank"*. `ADV`'s
|
||
predecessor trailer sits **3 618 816 B** before `end`, so it was rejected and
|
||
`start` fell back to `anchor` — **a TOC offset, not a stream boundary**. That is
|
||
exactly why it hit regions over 1.5 MB (the multichannel three-stream ones) and
|
||
never the single-stream ones. 17 of 95 movies took the fallback.
|
||
|
||
| | before | after |
|
||
|---|---|---|
|
||
| `ADV` region | 3 114 352 B | **3 618 816 B** |
|
||
| `ADV` streams kept | 2 of 3 | **3 of 3 — complete** |
|
||
| `S00A` streams kept | 1 of 3 | **2 of 3**, the third digitally silent |
|
||
| `ADV` peak | −3.1 dBFS | −2.84 dBFS |
|
||
|
||
✅ **The voice export is now complete for both movies**, closing a defect that was
|
||
open for this entire session — and my re-reading of the "duplicate tail" as a
|
||
start-truncated simultaneous stream is what the fix confirms. `ADV` drops **zero**
|
||
chunks; the runtime no longer prints its incomplete line.
|
||
|
||
### 🔴 And the incompleteness warning was crying wolf on `S00A`
|
||
|
||
With `ADV` fixed, `S00A` still read **KNOWN INCOMPLETE** at 2 of 3 — because its
|
||
third chunk is **93.694 s of exact zeroes**. A dropped silent stream is not
|
||
missing content, and a warning that fires on it teaches a reader to ignore the one
|
||
case that means something.
|
||
|
||
`Exported` gains `content_waves` — how many sub-waves carry **signal** — and the
|
||
warning, the console line and the manifest's `incomplete` all key on
|
||
`kept < content` rather than `kept < present`. Both movies now report no gap,
|
||
correctly.
|
||
|
||
⚠️ Second time in two iterations that this warning was wrong in the *cautious*
|
||
direction: first a template message true only for `ADV`, now a gap claimed over
|
||
silence. Over-warning is not the safe failure it looks like — it is the failure
|
||
that makes the next real warning unreadable.
|
||
|
||
### A second movie, in the predicted direction
|
||
|
||
They note only `ADV` has external ground truth; the other 16 rest on their own
|
||
sweep. **`S00A` is a second data point from a different movie**: its kept count
|
||
went 1 → 2 because a chunk that was previously a different duration now matches
|
||
the others at 93.694 s — exactly what restoring a truncated first stream
|
||
predicts.
|
||
|
||
⚠️ It is **not** independent ground truth — it is my exporter reading their fixed
|
||
crate — but it is a different asset than the one the fix was derived on, and the
|
||
outcome was predicted before it was observed. Recorded as that and nothing more.
|
||
|
||
Oracle rows unmoved; MODDING rules pass.
|
||
|
||
## The positional weights are applied — keyed by byte size, so the key is a check
|
||
|
||
With the span fixed, `ADV`'s three chunks map onto the Decoder's contexts
|
||
**exactly** — each is a declared `byte_size` plus the 60-byte RIFF header
|
||
`to_xma_riffs` prepends:
|
||
|
||
| chunk | bytes | − 60 | context | position | weight |
|
||
|---|---|---|---|---|---|
|
||
| 0 | 1 294 396 | 1 294 336 | ctx0 | FL/FR | **0.4142** |
|
||
| 1 | 1 118 268 | 1 118 208 | ctx1 | FC, LFE silent | **0.2929** |
|
||
| 2 | 1 171 516 | 1 171 456 | ctx2 | BL/BR | **0.2929** |
|
||
|
||
`authored/audio.json` gains `voice.stream_weights`, **keyed by declared byte
|
||
size**, and the exporter applies positional weights only when *every* kept
|
||
stream's size is in the table — otherwise it falls back to the count divisor.
|
||
|
||
🔴 **The key is the check.** Two weeks ago these same sizes did **not** fit the
|
||
region the resolver returned, and that is how a 238-packet late start was found.
|
||
Applied by *position* instead, the weights would have gone onto the wrong streams
|
||
in silence. `S00A` matches nothing here and keeps the divisor: extending by
|
||
position would assume the ordering generalises from one movie, which is exactly
|
||
the inference the byte-size key exists to prevent.
|
||
|
||
`ADV` now mixes at 0.4142 / 0.2929 / 0.2929 and lands at **−2.87 dBFS**.
|
||
|
||
### ✅ An unlooked-for structural confirmation
|
||
|
||
The generated filter folds chunks 0 and 2 from **two** live channels
|
||
(`0.5*c0+0.5*c1`) and chunk 1 from **one** (`c0`). `live_channels` found that
|
||
independently, by measuring which channels carry signal — and it matches the
|
||
Decoder's structural claim that **ctx1 is the only stream with a digitally silent
|
||
channel, and LFE the only channel with an empty residual**. Their evidence is a
|
||
decomposition of the game's output; mine is a peak measurement on the disc's own
|
||
chunks. Different sides, same structure.
|
||
|
||
## 🔴 Unexplained: `verify-menu-audio`'s dead-press check has started failing
|
||
|
||
Its first assertion — five presses bound to nothing produce a Master bus
|
||
**bit-identical** to five waits — now reports DIFFER, reproducibly across three
|
||
runs. The two recordings diverge at **0.085 s**, differ on 92 % of samples, and
|
||
have different durations (1.300 s against 1.207 s) where they were previously
|
||
identical.
|
||
|
||
⚠️ **I have not identified the cause and am not guessing at one.** It is not the
|
||
voice change — that touches only the voice export, and neither control run plays
|
||
a voice. The candidates I can name and have not separated are the new pin, the
|
||
plate-pulse draw path, and the static-overlay clock.
|
||
|
||
📌 What the failure does expose is a weakness in the test I wrote: **it compares
|
||
two separate process runs and assumes bit-determinism across them.** That premise
|
||
held for weeks, which is why it looked like a strong assertion — no thresholds, no
|
||
tuning. It is strong only while startup is deterministic, and nothing in the test
|
||
checks that it still is. A comparison within one run, or an explicit determinism
|
||
control, would not have this failure mode.
|
||
|
||
Filed rather than patched: silencing it would remove the only check that a dead
|
||
press stays silent, and I would rather have a failing check than a passing one
|
||
whose premise I have stopped believing.
|
||
|
||
## External ground truth for every three-chunk region — the movies' own durations
|
||
|
||
They have no external check on the 15 regions beyond `ADV`, and no route to one
|
||
that does not go through the port or the emulator. There is one reference in
|
||
reach that owes nothing to their crate: **each movie's own duration, read from its
|
||
WMV**. A start-truncated stream must decode *shorter than its movie* — `ADV`'s was
|
||
84.55 s against 137.71, a 38 % deficit.
|
||
|
||
Dumping every region with their `adv_voice_dump` and decoding each chunk:
|
||
|
||
| | |
|
||
|---|---|
|
||
| three-chunk regions found | **25** |
|
||
| chunks decoded | **75** |
|
||
| chunks more than 5 % short of their movie | **0** |
|
||
| largest deviation of any chunk | **1.78 %** |
|
||
|
||
✅ **No region is still truncated.** The reference is external — the durations come
|
||
from `/disc/dat/movie/*.wmv`, which their resolver never touches — so this is the
|
||
independent confirmation `S00A` alone could not be. What it does *not* cover is
|
||
whether a region's start is byte-exact; it catches truncation, which is the defect
|
||
that existed.
|
||
|
||
🟡 **A population discrepancy worth their attention.** Their page says *"8 of 10
|
||
three-chunk regions start mid-stream"*. I find **25** three-chunk regions on this
|
||
disc, not 10. Both numbers cannot describe the same population, and I cannot tell
|
||
from here which is the different one — a filter of theirs, or a difference in what
|
||
`adv_voice_dump` returns after the fix. Reported, not resolved.
|
||
|
||
### 🔴 My first run silently covered half of them and said it was clean
|
||
|
||
It reported *"0 of 13 three-chunk regions have a short chunk"*. There were 25.
|
||
`cargo run` inside a `while read` loop **consumes stdin**, so every second line of
|
||
the movie list was eaten — the covered set was positions 1, 3, 5, 7… exactly.
|
||
|
||
The result was *correct for what it measured* and the population was half what the
|
||
sentence implied. I caught it only because 13 did not match the 25 I had counted
|
||
one command earlier, and I nearly did not compare them — the finding I wanted was
|
||
"0 short chunks", and it was there.
|
||
|
||
⚠️ This is the shape I have been cataloguing all fortnight arriving in my own
|
||
shell loop: **a silently reduced sample presenting as a complete one.** It is the
|
||
truncated-log trap, the `--screen` default at t=0, and the checker whose input was
|
||
smaller-but-valid. Redirecting the loop's input to fd 3 fixes it; noticing it at
|
||
all was luck, and the general defence is to state the population and the coverage
|
||
in the same breath, which the second run does.
|
||
|
||
## The menu bed loops at 61.93 s — and my 3.4 s "ugly seam" was mine, not the game's
|
||
|
||
The Decoder captured 240 s parked on the menu, reached in **26.8 s** via the
|
||
XMA-log oracle they wrote down rather than a screenshot. Two findings, and both
|
||
cut against what this port had authored:
|
||
|
||
* 🔴 **No seam.** Zero runs ≥0.3 s below median−18 dB in 232 s of the real menu.
|
||
* 🔴 **Not the wave's length.** r = **−0.009** at 87.750 s; top lag **61.909 s**.
|
||
A second instrument agrees — 30 s slices located inside the decoded waves show
|
||
playback advancing exactly +5.00 s per 5 s and wrapping at **61.93 s**, three
|
||
times, against a control that finds slices cut at 10/45/70 s at 10.00/45.00/70.00.
|
||
|
||
The loop is **[≈0, 61.93)** of an 87.744 s wave, so the final ~25.8 s — the
|
||
fade-out and trailing silence — is **never played**. The game loops before the
|
||
fade.
|
||
|
||
### What this port had recorded, and how confidently
|
||
|
||
`loop: "restart"` replayed from sample 0 at the wave's end, and I measured the
|
||
resulting seam off my own Master bus: **36 near-silent 50 ms windows spanning
|
||
84.40–87.80 s, about 3.4 seconds**. I wrote that up as *the price of a missing
|
||
loop point*, put it in `BLOCKED.md` to raise Q10's priority, and sent the Decoder
|
||
a message describing it as the cost of the field nobody had found.
|
||
|
||
**It was our seam.** The measurement was correct and the attribution was wrong —
|
||
a defect in the port's own loop, reported as a property of the disc.
|
||
|
||
⚠️ That is the second time this fortnight I have measured something real and
|
||
assigned it to the wrong side. The first was reading a start-truncated stream as
|
||
a duplicate tail. Both were cases where the number was solid and the *sentence
|
||
around it* named the wrong cause, which is a failure mode no amount of instrument
|
||
control catches.
|
||
|
||
### The fix, and why it trims the file
|
||
|
||
**Godot loops a whole file**, so a loop region has to *be* the file. `BgmSpec`
|
||
gains `loop_end_s` and the exporter trims: the bed is now **61.930 s**, and the
|
||
runtime's existing whole-file loop is then correct by construction rather than
|
||
carrying a loop point nothing could honour.
|
||
|
||
✅ Verified on the port's own bus over 131 s: **5** near-silent 50 ms windows, no
|
||
run ≥0.2 s — against 36 windows and 3.4 s before. The seam is gone.
|
||
|
||
⚠️ Recorded limits: the loop **start** is inferred, since [0.0, 61.93) and
|
||
[0.25, 62.18) are not separated at their resolution — the port takes 0 because a
|
||
bank's data begins there, and says the choice was not measured. And a modder
|
||
replacing `main_menu.ogg` is now replacing **the loop region**, not the whole
|
||
bank; `MODDING` rule 1 still holds (one logical asset, one file) because the
|
||
logical asset is what the game plays.
|
||
|
||
### 🔴 And their "8 of 10" was a truncated file, not a count
|
||
|
||
They have withdrawn it: the audit run was cut short, the committed file ends
|
||
mid-list at `S11A` **with no summary line**, and they read a partial file as a
|
||
complete one. So *"the defect is specific to multichannel regions"* is now
|
||
**unsupported — possibly true, not shown**, and my 25 stands unopposed.
|
||
|
||
📌 Their tell and mine were the same on the same day, from opposite directions:
|
||
their table had **no summary line** and mine had a **population that didn't match
|
||
a count from one command earlier**. The defence that covers both is to state the
|
||
population and the coverage together — and theirs adds a second: **refuse to read
|
||
a table whose summary line is missing.**
|
||
|
||
## The dead-press check was passing by luck, and the luck ran out
|
||
|
||
Two iterations ago `verify-menu-audio`'s first assertion — five presses bound to
|
||
nothing produce a Master bus **bit-identical** to five waits — began failing. I
|
||
filed it undiagnosed and named three candidates: the new pin, the plate-pulse draw
|
||
path, the static-overlay clock.
|
||
|
||
**It is none of them.** Three *identical* invocations of the same command give two
|
||
outcomes:
|
||
|
||
| run | duration |
|
||
|---|---|
|
||
| 1 | 1.207438 s |
|
||
| 2 | **1.300317 s** |
|
||
| 3 | 1.207438 s |
|
||
|
||
The difference is **0.092879 s = exactly 4096 samples**, one mixing buffer. The
|
||
recording quantises to whole buffers, and a one-buffer shift moves both the length
|
||
and the alignment of everything inside it. So a byte-for-byte comparison of two
|
||
separate runs cannot hold.
|
||
|
||
🔴 **The premise was never guaranteed — it was luck.** It held while the run's
|
||
timing sat away from a buffer boundary, and a larger export (three voice streams
|
||
where there had been one) moved it onto one. **A test that passes by luck reports
|
||
the luck running out as a regression in the code**, which is exactly what it did:
|
||
I spent two iterations listing suspects in the port, and the port was never
|
||
involved.
|
||
|
||
⚠️ It also passed for weeks *looking* like the strongest assertion in the harness —
|
||
exact equality, no threshold, nothing to tune. That was true and it was resting on
|
||
an assumption nothing checked. Strength of the assertion said nothing about
|
||
soundness of the premise.
|
||
|
||
### The fix keeps what mattered
|
||
|
||
Still **exact** equality and still no threshold; the comparison may now slide by
|
||
whole buffers, which is the one degree of freedom the recorder actually has. In
|
||
practice it finds `+0` or `+1`.
|
||
|
||
✅ And it can still fail, which is the part worth proving: `ctrl` against `walk` —
|
||
a run that really does contain cues — **differs at every alignment**.
|
||
|
||
📌 The general form, and it is not the same as the earlier entries: those were
|
||
checks nobody ran, or that ran and answered a different question. This one ran,
|
||
answered the right question, and rested on a property of the environment that was
|
||
never verified and had no reason to be stable. **The thing to state alongside an
|
||
assertion is not only what it checks, but what it assumes about the machine.**
|
||
|
||
## Independent confirmation of the 1.5 MB cap — the mechanism, not just the conclusion
|
||
|
||
Their census settles the population — 104 movies, 95 resolved, **25 three-chunk**,
|
||
confirming my count — and corrects their own claim twice: *"specific to
|
||
multichannel regions"* holds (17 of 17 changed regions are three-chunk, 0 are
|
||
one-chunk), while *"all three-chunk regions were broken"* is **false**, since 8
|
||
of 25 were already fine.
|
||
|
||
The 8 they name are the checkable part, because the cause predicts them. A region
|
||
trips the `end - s < 1_500_000` filter only if its span **exceeds** the cap.
|
||
Measuring every three-chunk region's span myself:
|
||
|
||
| set | count | span range |
|
||
|---|---|---|
|
||
| never affected — their 8 | 8 | **71 680 … 1 400 832 B** |
|
||
| affected — their 17 | 17 | **2 023 424 … 6 516 736 B** |
|
||
|
||
✅ **The cap separates the two sets exactly, with no violations**, and it is not
|
||
marginal: there is a **622 592 B gap** between the largest unaffected region and
|
||
the smallest affected one, with 1 500 000 sitting inside it. That confirms the
|
||
*mechanism* — a byte-size threshold — and not merely the list of names, which a
|
||
coincidence could reproduce.
|
||
|
||
### 🔴 My first run of this reported seventeen contradictions
|
||
|
||
It printed `🔴 CONTRADICTS` against all 17 affected regions and 0 for the
|
||
unaffected — a clean, consistent, entirely wrong pattern. Every span read **0 B**,
|
||
because `awk '{print $NF+0}'` took the trailing `B` of `= 3618816 B` rather than
|
||
the number.
|
||
|
||
Had I sent that, I would have told them their causal account was refuted 17 out
|
||
of 17 — and it would have looked *strong*, because the failure was uniform and
|
||
fell exactly along the line under test. **A broken extractor produces a pattern
|
||
shaped by the question, not by the data**, and the more structured the question,
|
||
the more convincing the artefact.
|
||
|
||
⚠️ What saved it was the 8 unaffected regions reading 0 B too. A span of zero is
|
||
impossible for a region that resolves, and the "confirming" half of the table was
|
||
as broken as the refuting half — which is only visible if you read the half that
|
||
agrees with you as carefully as the half that does not.
|
||
|
||
📌 Their note about my dead `8 of 10` sharing a digit with the 8 genuinely
|
||
unaffected regions is the same hazard from the other side: **a wrong number that
|
||
resembles the right answer is the one most likely to survive into a later
|
||
document.** They wrote the coincidence down rather than quietly replacing the
|
||
figure, which is what makes it safe.
|
||
|
||
## The loop is a runtime field, the two readings conflict, and the port keeps what it shipped
|
||
|
||
The loop point **is** decodable — `loop_start`/`loop_end` in the XMA decoder
|
||
context, set by `XMASetLoopData`, logged by Xenia without a patch. But the values
|
||
imply a cycle of roughly **[10 s, 72 s]** against the **[0.25, 57.18 s]** their
|
||
audio tracking gave, and neither reading is withdrawn.
|
||
|
||
✅ Two predictions of theirs were refuted by their own data, which is the part
|
||
that makes the conflict credible rather than a slip: `loop_start` is **not ~0**
|
||
(it is 11.6 % into the stream), and a linear bits→seconds conversion yields
|
||
**62.34 s and 63.29 s for two stems that must stay sample-synchronous** — 0.95 s
|
||
apart is impossible, so the data refutes the linear assumption on its own. XMA
|
||
frames are variable-length in bits.
|
||
|
||
**The port keeps `loop_end_s: 61.93`**, on their instruction and because the
|
||
*length* survives better than the *placement*: 61.93 rests on an autocorrelation
|
||
that used no wave at all.
|
||
|
||
### The one check the port could add, and what it is worth
|
||
|
||
Neither of their instruments asked whether the trim **joins smoothly**. Over
|
||
126.5 s of the port's own bus, the wrap at 61.93 s and again at 123.86 s shows a
|
||
maximum adjacent-sample step of **212** and **208**, against a whole-file median
|
||
of **132** and a 99.9th percentile of **3 737**. The join is not a click.
|
||
|
||
⚠️ **It does not discriminate the two readings**, and saying so is the point: a
|
||
smooth join means the waveform does not jump, not that the loop is musically
|
||
right, and a cut landing near a zero crossing is smooth wherever it falls. I
|
||
recorded it as evidence that nothing is *audibly broken* and explicitly not as
|
||
support for 61.93 over [10, 72].
|
||
|
||
🔴 What the conflict costs if the runtime fields win: **this export is about ten
|
||
seconds short**, since [61.93, 72] would be content the game plays and we omit.
|
||
Filed with that number rather than as "the loop point may move", because the
|
||
former is weighable and the latter is not.
|
||
|
||
### Their diagnosis of their own locator is the entry to keep
|
||
|
||
*"A control easier than the measurement does not bound the measurement's error."*
|
||
Their locator's control matched slices cut from the wave **itself** — exact
|
||
copies — where the real task was matching a capture differing by decoder, gain and
|
||
mix. The clean +5.00 s stepping showed it was **self-consistent**, not that it had
|
||
locked to the right phrase, and music with repeated sections is exactly where a
|
||
locator aliases.
|
||
|
||
📌 That is the same shape as my `awk '{print $NF+0}'` reading every span as 0 B:
|
||
in both cases the output was internally consistent and structured, and in both the
|
||
tell was in the rows that **agreed** — my confirming half read impossibly too, and
|
||
their control was passing a problem it never had to solve.
|
||
|
||
## The duration is confirmed and the window is wrong — and the start is now a visible field
|
||
|
||
They stopped *converting* the runtime fields and **timed** them instead: a probe
|
||
tailing the Apu debug log, stamping `read_offset` on arrival, watching **three**
|
||
wraps — each from its own `loop_end` to its own `loop_start`, with **both
|
||
contexts wrapping at the same instant every time**.
|
||
|
||
| | |
|
||
|---|---|
|
||
| observed cycle | 61.56 s, 62.06 s → **61.81 s** |
|
||
| authored here | **61.93 s** |
|
||
| difference | **0.2 %** |
|
||
|
||
✅ **The length is settled**, and by instruments sharing nothing: a wall clock
|
||
between decoder events against an autocorrelation that never touched the wave.
|
||
Both contexts wrapping together is the sample-synchrony the linear bit conversion
|
||
could not produce — the same conversion that gave 62.34 and 63.29 s for two stems
|
||
that must be synchronous, and so refuted itself.
|
||
|
||
🔴 **The window is wrong.** `loop_start` is at 3.6 M bits — **11.6 % of the
|
||
stream, about ten seconds** — not the 0.25 s their earlier tracking gave. So this
|
||
export has the right **duration** over the wrong **window**: it replays the bank's
|
||
intro every cycle and omits the tail the game plays.
|
||
|
||
📌 **My smooth-join check has a second use I could not have anticipated.** It said
|
||
the wrap is not a click, and explicitly not that the loop is musically right. That
|
||
distinction is now load-bearing: it explains **why a wrong ten-second window went
|
||
unheard**. A cut near a zero crossing is smooth wherever it falls — including on
|
||
the wrong ten seconds. A check whose limits are written down keeps working after
|
||
the thing it was checking turns out to be wrong.
|
||
|
||
### Not re-cut — and the assumption is now a field
|
||
|
||
Their instruction is to wait: the exact start is **not measured**. Linear
|
||
back-extrapolation says ~9–13 s, and linearity is refuted by the same run, where
|
||
the bit rate varies **4.4 %** within one stream.
|
||
|
||
But `loop_end_s` alone **silently asserted a start of zero**, and that start is now
|
||
known to be wrong. So the entry gains `loop_start_s`, authored as **0.0 and
|
||
flagged as wrong**, with `-ss` applied before `-t` so the pair is (start,
|
||
duration) and moving the start cannot silently change how much is kept.
|
||
|
||
⚠️ An assumption a reader has to infer from a **missing field** is not one they can
|
||
weigh. This is the same move as `layer_source` — the export must let a consumer
|
||
tell a measured value from an assumed one — applied to a value I had been carrying
|
||
implicitly for two days.
|
||
|
||
✅ The new path is **proved before it is needed**: with `loop_start_s = 10.0` the
|
||
command carries `-ss 10 -t 61.93` and the output stays 61.930 s — a window, not a
|
||
truncation. Restored to 0.0; the export is byte-unchanged. When the start is
|
||
measured this is a one-value edit, not a code change.
|
||
|
||
## The loop window is measured — `-ss 9.44 -t 61.87` — and the near-silence count tracked the error
|
||
|
||
The region is **[9.44 s, 71.31 s]** of an 87.744 s wave: the first 9.44 s is an
|
||
intro played **once**, the last 16.4 s a fade-out **never played**. Two
|
||
derivations on both stems, neither converting bits to seconds — the conversion
|
||
that had refuted itself by giving two sample-synchronous stems 62.34 and 63.29 s.
|
||
|
||
✅ **61.87 replaces 61.93**, 0.1 % apart. The measured value is taken because it
|
||
has the loop's own endpoints under it; the autocorrelation that produced 61.93
|
||
never touched the wave and agreed to a tenth of a percent, which is what makes
|
||
both worth having.
|
||
|
||
### The port's own near-silence count tracked the window's correctness
|
||
|
||
| window | near-silent 50 ms frames in ~127 s |
|
||
|---|---|
|
||
| no trim, `restart` at the wave's end | **36**, spanning 3.4 s |
|
||
| `[0, 61.93]` — right length, wrong window | **5**, no run ≥0.2 s |
|
||
| **`[9.44, 71.31]` — measured** | **0** |
|
||
|
||
That is a real corroboration from this side and it was not designed as one. The
|
||
old window kept part of the bank's quiet intro; the measured one excludes both the
|
||
intro and the fade, so no quiet stretch survives anywhere in the loop. The count
|
||
fell monotonically as the window got closer to right.
|
||
|
||
⚠️ Wrap continuity is unchanged and still not evidence: max adjacent-sample step
|
||
**287** and **354** at the two wraps against a 99.9th percentile of **3 812**. As
|
||
before, a cut near a zero crossing is smooth wherever it falls — the *silence*
|
||
count discriminated where the *step* count could not.
|
||
|
||
### 🔴 A stale `why` reached the manifest for two days
|
||
|
||
Correcting `loop_end_why` and `loop_start_why` left `loop_why` — **the field the
|
||
exporter concatenates into `manifest.json`** — still asserting that the loop would
|
||
be *"AUDIBLY WRONG AT THE SEAM [refuted]"*, that *"no loop-point field has been identified [refuted]
|
||
anywhere"*, and that trimming *"would INVENT a loop point"*. All three refuted;
|
||
all three shipped to any consumer reading the export.
|
||
|
||
**A correction that does not reach the artifact a consumer reads has not been
|
||
made.** The corrections existed, were accurate, and were in the wrong fields.
|
||
|
||
⚠️ And my first check of the fix reported the stale text still present — because
|
||
the replacement **quotes** the refuted sentences in order to name them, so a
|
||
substring search finds them inside the paragraph saying they are false. I had to
|
||
read the context to see it. That is the "check the rows that agree" lesson landing
|
||
on a grep: the match was real and its meaning was the opposite of what the search
|
||
implied.
|
||
|
||
### Why the wait for 9.44 was cheap
|
||
|
||
Their note is worth keeping: it was not that the field predicted the value, but
|
||
that `loop_end_s` alone was **asserting** a start of zero in a form no reader
|
||
could weigh or find — and that proving `10.0` produced a *window* rather than a
|
||
truncation **before the real value existed** meant arriving at 9.44 was a
|
||
one-value edit with a clean baseline behind it.
|
||
|
||
## Applying "grep the corpus for the claim" to my own corpus
|
||
|
||
The Decoder found that a claim they refuted in a *new page* was still standing in
|
||
`bgm-two-stems.md` and in `HANDOFF.md` — the page a reader is told to consult
|
||
instead of the rest. Their rule: **grep the corpus for the claim, not for the file
|
||
you were working in.** Run against mine, on four claims I refuted this fortnight:
|
||
|
||
| claim | where | state |
|
||
|---|---|---|
|
||
| "the leading chunk is the **TAIL** of the kept stream" | `audio.rs` → **`manifest.json`** | 🔴 **still shipping** |
|
||
| "six expected DIFFERS [refuted]" | `BLOCKED`, `DECISIONS` | marked |
|
||
| "the boot is **known too fast [refuted]** on both" | `DECISIONS` | 🔴 **standing, unmarked** |
|
||
| "the **only thing** making the plate reappear" | `BLOCKED` | marked |
|
||
|
||
### The one that shipped
|
||
|
||
The dropped-chunk explanation in the exported `why` still told readers the leading
|
||
chunk *"IS understood: the TAIL of the kept stream [refuted]"*. That interpretation was
|
||
refuted — the correlation was sound, but what matched end-flush was a
|
||
**start-truncated simultaneous stream**, because the resolver began 238 packets
|
||
inside it.
|
||
|
||
⚠️ And it was wrong twice over, in the shape I had already fixed once: `S00A`'s
|
||
dropped chunk is the **silent** one, not a leading chunk, so the sentence
|
||
described a case that was not present. **A template message true only for the case
|
||
it was written against** — the second instance of that exact defect in this file,
|
||
which suggests the first fix taught me nothing general.
|
||
|
||
Replaced with a per-case account that names the refutation rather than deleting it.
|
||
|
||
### The one that was standing
|
||
|
||
*"The port's boot is known too fast [refuted] on both, by an unmeasured amount"* — withdrawn
|
||
days later, when the splash dwells turned out to be **declared on the disc** and
|
||
the port already exact. I wrote the withdrawal as a **new section** and left the
|
||
original untouched, so a reader arriving at the older paragraph got the dead
|
||
answer. Annotated in place, pointing at the withdrawal.
|
||
|
||
### And a false positive that is its own lesson
|
||
|
||
`BLOCKED.md` matched *"the only thing making the plate [refuted] reappear"* — inside **my own
|
||
correction**, which quotes the refuted claim in order to name it. That is the grep
|
||
trap I documented two days ago, caught by the very audit that trap exists to
|
||
complicate. **Naming a refuted sentence keeps it greppable**, which is the price of
|
||
not deleting it, and the check therefore needs a human read of every hit rather
|
||
than a verdict from the match alone.
|
||
|
||
📌 Their delivery-check point pairs with this: *a control proves the instrument
|
||
reads correctly, a delivery check proves the experiment happened at all.* Their
|
||
second Ⓐ was never delivered — 2 pad lines is one press — and *"the press did
|
||
nothing"* and *"there was no press"* are identical from the screen. My equivalent
|
||
is that a correction can be written, be accurate, and never arrive.
|
||
|
||
## A refuted-claim register, because the audit found what the audit found
|
||
|
||
The Decoder ran my corpus audit against theirs and found **four** refuted claims
|
||
still standing — including one they had corrected in a message to me, agreed with,
|
||
and written a METHOD entry about, **without landing the correction for a full
|
||
iteration**. Their sharpening: *acknowledging a correction in conversation feels
|
||
like making it and isn't.*
|
||
|
||
A hand audit finds the instances present on the day it runs. It does not stop the
|
||
next one. So `tools/port/check-claims` is a **register**: each row is a claim this
|
||
corpus has refuted, and every occurrence must carry an explicit `[refuted]`
|
||
sentinel within 400 characters. `check-all` runs it.
|
||
|
||
### 🔴 It found four more than my hand audit did
|
||
|
||
My manual pass checked four claims and found two problems. The check, on the same
|
||
four, found **four further unmarked occurrences** I had read past — including one
|
||
in `authored/audio.json` and one in the very table where I had written *"standing,
|
||
unmarked"* about a different claim.
|
||
|
||
### The marker is a sentinel, not a keyword, and that mattered
|
||
|
||
The first version matched a per-claim keyword near the hit — "refuted",
|
||
"WITHDRAWN". **Every one of its failures was a quotation sitting inside a
|
||
correction whose wording happened not to contain the keyword**: a table cell
|
||
reading *"standing, unmarked"*, a sentence reading *"the real count was ten"*.
|
||
|
||
⚠️ The temptation was to widen the window or add synonyms until those passed.
|
||
**That is tuning a threshold until the answer comes out right** — the failure this
|
||
corpus has spent a fortnight cataloguing, arriving in the tool built to catch it.
|
||
So the marker became a token the author must place. It cannot be satisfied by
|
||
phrasing, and its absence means exactly one thing.
|
||
|
||
The cost is honest and is the point: 21 existing quotations had to be marked by
|
||
hand, and a new refuted claim means a new row plus marking what already quotes it.
|
||
|
||
✅ Proved it fails: removing one sentinel makes the run report that claim unmarked
|
||
and exit non-zero.
|
||
|
||
### What the register cannot do
|
||
|
||
⚠️ It only knows claims **someone has entered**. A refuted claim nobody registers
|
||
is invisible to it, so this is a ratchet on known corrections and not a search for
|
||
unknown ones — the audit still has to happen first. And it enforces *marking*, not
|
||
*correctness*: a sentinel next to a sentence that was never really refuted would
|
||
pass and be wrong in a new way.
|
||
|
||
📌 Their other finding is the one I acted on separately: **a "kept for the record"
|
||
block still asserts.** `BLOCKED.md`'s voice row had a struck heading and three
|
||
sentences below it asserting in the present tense — that the `1 of 3` warning
|
||
stays, that stream 1 is *"consistent with being stream 2's tail"*, that streams 2
|
||
and 3 are indistinguishable. All three resolved days earlier. Marking a heading
|
||
superseded does not mark the sentence a reader lands on, so the resolution now
|
||
sits at the top of the cell and names each superseded sentence.
|
||
|
||
## State of the port, and a claim I built on for a week without checking
|
||
|
||
Every asserting check passes: the format validator (16 screens against
|
||
`sylpheed.screen/3`), all five MODDING rules, the capture-control sweep, the
|
||
refuted-claim register, the decisions index. The oracle rows sit at the tone
|
||
floor — `title_plate` **0.00 %**, both splashes **0.01 %**, `main_menu` 0.06 %,
|
||
`main_menu_options` 0.15 %, `extras` 0.19 %, `title` 0.21 %, `title_band` 0.35 %
|
||
against its own oracle-to-oracle gap. The P5 walk runs and ends on the title.
|
||
|
||
### 🔴 The refutation attempt this iteration was of something I had already used
|
||
|
||
The claim that reframed the entire voice question — *`ADV.wmv` carries **one**
|
||
audio stream and it is **WMA Pro 5.1**, not XMA, so the movie's own track is the
|
||
bed and the three streams are additional* — is checkable in one command against
|
||
the disc:
|
||
|
||
```
|
||
index=0 codec_name=wmapro channels=6 channel_layout=5.1 sample_rate=48000
|
||
1 audio stream
|
||
```
|
||
|
||
✅ Exactly confirmed.
|
||
|
||
⚠️ **And I had built on it for a week without running it.** The positional
|
||
weights, the `presentation: "all"` change, the refusal to apply the assignment
|
||
when the byte sizes did not fit — all of it rests on that reframing, and the
|
||
verification cost one `ffprobe` against a file I have had all along. I checked the
|
||
*byte sizes* scrupulously because they were the identifier I could test, and never
|
||
checked the sentence the identifier was serving.
|
||
|
||
📌 That is a different failure from the ones this file catalogues. Not an
|
||
unexercised rule, not a correction that never landed, not a control easier than
|
||
the measurement: **a premise so foundational that everything downstream got
|
||
audited and the premise itself did not.** The scrutiny went to the parts that
|
||
moved.
|
||
|
||
### What is still authored rather than measured
|
||
|
||
Four values, and the file says so at each:
|
||
|
||
| value | state |
|
||
|---|---|
|
||
| `flow.screens.main_menu.on_cancel` | **authored — likely but UNPROVEN**; Ⓑ returning to the title is stated in HANDOFF with no capture behind it |
|
||
| `ptbtn01.after_video` | **authored**; the game goes into Mission 1, which MISSION §7 scopes out, so "return to the title" is a chosen end state |
|
||
| `flow.navigation.input_during_transition` | **authored, not measured** — nobody has watched a press mid-fade; ignoring invents least |
|
||
| `authored/rendering.json`'s withheld leaves | two leaf records deliberately not drawn, each with its reason |
|
||
|
||
Everything else in `authored/` now carries `kind: measured` — the BGM bank and its
|
||
loop window, the plate's pulse period, the keyframe unit, the black hold, the
|
||
navigation wrap, the cue bindings, the voice streams and their positional weights.
|
||
|
||
⚠️ The black hold is measured but sits at **the top of its range** (~6.5–9.2 units,
|
||
authored 9), and that is recorded at the value rather than in a footnote.
|
||
|
||
## Identifying their submenu capture: edges where intensity could not
|
||
|
||
They reached and captured a submenu but could not identify it. Their diagnosis is
|
||
the useful part: **correlation cannot discriminate when the candidate renders are
|
||
near-blank**, and near-blank is exactly what the `.tbm` hypothesis predicts — all
|
||
19 `GP_SAVE_LOAD` builds scored −0.004…−0.010, a ranking with no information in
|
||
it. *The instrument is disabled by the thing it was brought in to detect.*
|
||
|
||
That diagnosis implies its own fix. Their capture is **99.999 % non-black** — a
|
||
full-screen background our renderer omits — and an additive background swamps an
|
||
intensity correlation. **It does not survive an edge map**: a smooth ground has no
|
||
edges, and the UI does.
|
||
|
||
### The control first, because a ranking is worthless without one
|
||
|
||
Edge correlation against my own `title` capture, over seven `GP_TITLE` builds
|
||
whose answer I know:
|
||
|
||
| build | r |
|
||
|---|---|
|
||
| **4 — the right answer** | **+0.2792** |
|
||
| 6 (`extras`, its nearest sibling) | +0.1936 |
|
||
| everything else | ≤ +0.037 |
|
||
|
||
✅ Right answer on top, 1.4× over second and 7.6× over third. Modest absolute r,
|
||
and a clear ranking — so the method discriminates on this corpus.
|
||
|
||
### The result
|
||
|
||
Their capture against all 22 candidate builds:
|
||
|
||
| build | r |
|
||
|---|---|
|
||
| **`GP_TUTORIAL` build 0** | **+0.4962** |
|
||
| `GP_TUTORIAL` build 1 | +0.3137 |
|
||
| best `GP_SAVE_LOAD` (17) | +0.0713 |
|
||
| worst | −0.0331 |
|
||
|
||
🟢 **The submenu is a `GP_TUTORIAL` build.** The winning r is *higher* than the
|
||
control's, its margin over second is *better* (1.58× against 1.4×), and both
|
||
TUTORIAL builds sit **4–7× above every `GP_SAVE_LOAD` build** — the archive
|
||
separation is far stronger than the within-archive one.
|
||
|
||
✅ It is independently plausible: `authored/flow.json` has `ptbtn03` = **TUTORIAL**
|
||
→ `TUTORIAL_MENU`, noted as *"the lesson list is not a `GP_TITLE` build"*. An Ⓐ
|
||
on a menu whose focus was TUTORIAL lands exactly there, and HANDOFF Q5 measured
|
||
initial focus as unstable boot to boot.
|
||
|
||
⚠️ **What this does and does not settle.** The **archive** is identified with a
|
||
large margin. **Which build within it** is not: 1.58× is the same order as my
|
||
control's 1.4×, and the two TUTORIAL builds are variants of one screen — so I
|
||
would call build 0 the better fit and not a determination.
|
||
|
||
⚠️ And the method inherits a limit worth stating: an edge map is insensitive to
|
||
*what* the background is, which is the point, but it is also insensitive to a
|
||
missing element that has no edges. It answers "which screen", not "is our render
|
||
complete".
|
||
|
||
### Refutation attempt: "`screen render` omits every `.tbm` background, but none of your screens has one"
|
||
|
||
Their branch, HEAD `d92a962`. The first half is theirs to prove and they proved
|
||
it against a capture. **The half that decides whether my regression baseline is
|
||
sound is the second**, and it is a claim about *my* tree — so I tested it.
|
||
|
||
`screen info --all`, grepped for `.tbm`, across all **16** builds in my manifest:
|
||
zero references. ✅ Their claim holds, and holds wider than they stated — they
|
||
said "none of your five screens", it is none of sixteen.
|
||
|
||
**Both controls fired**, and this is the whole reason the result means anything.
|
||
A "none found" from an instrument never shown to find one is the failure this
|
||
corpus keeps repeating — my first attempt at this check printed nothing at all
|
||
from its control and I nearly read that as agreement:
|
||
|
||
| | `.tbm` mentions |
|
||
|---|---|
|
||
| positive — `GP_TUTORIAL` build 0 | **1** (`pubase.tbm`, the element they named) |
|
||
| negative — `GP_TITLE` build 5, `main_menu` | 0 |
|
||
|
||
### The guard, and why a passing check still needed one
|
||
|
||
So `tools/port/verify-screen` cannot be misled today. ⚠️ **That is a fact about
|
||
today's manifest, not a property of the script**, and the failure it would cause
|
||
is the expensive kind rather than a silent one: the port draws a background the
|
||
reference omits, the row reads `DIFFERS`, and this script's own header sends the
|
||
reader off to find out *which renderer moved*. Neither did. It would be a real
|
||
disagreement with a known cause on the reference side and nothing on screen
|
||
saying so.
|
||
|
||
The row now says so. It does **not** change the verdict or the bar — tuning until
|
||
things match is what that header warns against; it attaches provenance to the one
|
||
row that would otherwise mislead.
|
||
|
||
🔴 The guard cannot fire on any screen I ship, which is how a guard goes quietly
|
||
dead. Its expression is therefore controlled directly, both directions:
|
||
`GP_TUTORIAL` build 0 → 1, `GP_TITLE` build 5 → 0.
|
||
|
||
✅ Regression unchanged after the edit: `title` max 6 / over3 790, `main_menu`
|
||
max 4 / over3 0 — the committed baseline exactly.
|
||
|
||
### Their identification and mine agree, from unshared assumptions
|
||
|
||
They identified the screen by **reading the word `TUTORIAL` off the framebuffer**.
|
||
My edge correlation, run before that message arrived and without access to the
|
||
text, ranked `GP_TUTORIAL` build 0 first. Two methods with no assumption in
|
||
common, one answer.
|
||
|
||
📌 Worth keeping their methodological note over the result: their high-passed
|
||
matcher scored 1.28×, and they *declined to identify with it* — the number was
|
||
never used because it had been controlled. My 1.58× is barely better and I said
|
||
the same thing about the build-within-archive question. **The margin that
|
||
mattered was the archive one (4–7×), and the answer that settled it was reading
|
||
the label.** Build a matcher only after checking whether the artefact already
|
||
states the answer.
|
||
|
||
## `on_cancel`: one half measured, and a MEASURED stamp removed from the other
|
||
|
||
The Decoder measured **Ⓑ on the main menu → the title** (their `86a8ce7`,
|
||
`docs/re/data/b-on-main-menu.txt`): delivery-confirmed, 73.5 % of pixels changed,
|
||
both captures naming themselves, **≤ 0.4 s**, and **no loading screen** on the
|
||
path despite the disc carrying four.
|
||
|
||
✅ `authored/flow.json` `main_menu/on_cancel` moves from *"likely but UNPROVEN"*
|
||
to **MEASURED**. What makes it conclusive is the **latency, not the
|
||
destination** — my own `why` had named the confound: the title *also* returns on
|
||
its own after ~8–10 s idle, so an observer could not tell a response from a
|
||
timeout. ≤ 0.4 s is twenty times faster than the idle return, and that is what
|
||
separates them.
|
||
|
||
### 🔴 The other half: my tree stamped MEASURED on a claim with no evidence
|
||
|
||
`title/on_cancel_why` read **`"MEASURED, HANDOFF Q5: Ⓑ on the title does
|
||
nothing."`** The Decoder now says that is unevidenced — their 2026-08-30 run
|
||
cannot be counted, because the second Ⓑ landed *during* the title's build-in, so
|
||
the glyph 0 → 154 that followed is the build-in completing, not a response.
|
||
|
||
I did not invent the stamp, and that is the point worth recording. **HANDOFF Q5
|
||
(`9ca1eb5`) prefixes its entire row `**measured**` and then lists six clauses.**
|
||
In the source it links, that clause's evidence cell reads **`none`**, with a
|
||
yellow marker. The summary flattened six claims of differing strength into one
|
||
word, and my authored tree copied the word.
|
||
|
||
⚠️ **The value does not change — `null` either way.** Doing nothing is the safe
|
||
reading whether or not it is measured, so this correction moves no pixel. It
|
||
removes a false provenance, which is the thing that would have been believed
|
||
later.
|
||
|
||
### The same row has a second empty cell, which nobody flagged
|
||
|
||
Auditing the rest of Q5 rather than only the clause I was handed: the **`up /
|
||
down`** row — *"one item per press, no auto-repeat at the durations tried"* —
|
||
also has an **empty evidence cell**, and my `navigation` block cites that same
|
||
row.
|
||
|
||
✅ It splits cleanly, and only one half is exposed:
|
||
|
||
* **one item per press** is evidenced *indirectly and well* — the wrap montage's
|
||
count only comes out if each press moves one (4 presses from `EXTRAS` landing
|
||
on `OPTIONS`). Keep it.
|
||
* **no auto-repeat** has nothing behind it, and the source's own *"at the
|
||
durations tried"* hedges it.
|
||
|
||
🔴 Worse, the port already behaved this way **without stating it**: `boot.gd`'s
|
||
`_input` is edge-triggered, so holding a direction moves one item — an unexamined
|
||
consequence of how the handler was written, not a claim anyone could check.
|
||
`navigation.auto_repeat: false` is now explicit, marked a **choice**: a repeat we
|
||
did not implement cannot run a menu past the item the player wanted; inventing
|
||
one could.
|
||
|
||
### Audit of every MEASURED stamp in `authored/`
|
||
|
||
34 stamps. **Six cite a HANDOFF row and nothing else** — the laundering path
|
||
above. The other 26 that my crude grep flagged are fragments of multi-part `why`
|
||
arrays whose citation sits in a sibling field, so that heuristic over-reports and
|
||
I am not going to pretend otherwise. Of the six, one (Q5's Ⓑ) was actually wrong
|
||
and is fixed; the rest cite rows whose sources carry evidence.
|
||
|
||
📌 The generalisation, and it is the Decoder's own shape turned on a document:
|
||
**a summary that labels a row is not a citation for every clause in it.** A
|
||
bundled `**measured**` is exactly as strong as its weakest cell.
|
||
|
||
## BLOCKED.md's five "blocking" rows were all answered, some days ago
|
||
|
||
The standing instruction says this file rots, and it had. Rows 1 and 2 are
|
||
labelled **"(P3, blocking)"** while P3 through P7 have all shipped — a
|
||
contradiction on the file's own face, and one that misleads in the worse
|
||
direction: it under-reports progress and would send a reader to answer questions
|
||
already answered.
|
||
|
||
Audited every row against HANDOFF `9ca1eb5`, and — the part that makes this more
|
||
than bookkeeping — **checked whether the port actually acted on each answer**:
|
||
|
||
| row | answer | did the port act? |
|
||
|---|---|---|
|
||
| 1 splash predicate | ❔ no content rule exists; take the entry index | ✅ addressed by entry index; `publisher_logo` 10/13 now exported |
|
||
| 2 fade-out | **(a)**, play the group to its end | ✅ and see below — the prescribed constant was *deleted* |
|
||
| 3 focus over vs instead | ✅ my choice was fine; the miss was the ring | ✅ `ptbtneff01` exported and drawn |
|
||
| 4 rotation | human's call; pivot anchor **measured** | ✅ drawn about `pos + pivot` |
|
||
| 5 gamma | captures are not gamma-neutral, RMSE has a floor | ✅ in `verify-capture`'s header |
|
||
|
||
### 🔴 HANDOFF ask 2's prescribed action is stale, and following it would double-count
|
||
|
||
Ask 2 says: *"write one authored constant (~0.4 s / ~24 units) and play the group
|
||
to its end."* Under the corrected record layout (`formats-pin-2026-08-29c`) every
|
||
pose is timed, so the unknown that constant stood in for **does not exist** —
|
||
`exit_ramp_units` was already deleted for that reason.
|
||
|
||
Measuring what the file actually carries confirms the mechanism ask 2 describes
|
||
and contradicts its number. On `main_menu`, the final alpha ramps are:
|
||
|
||
* `pteff00` — the black quad — **0 → 255 over 10 units (0.17 s)**
|
||
* `ptmsg`, `pteff10`, `pteff12` — **255 → 0 over 6–8 units**
|
||
|
||
✅ *"the quad goes `a=255` while the buttons, `ptmsg` and the glows go `a=0`"* is
|
||
**in the file**, exactly as described. ⚠️ But the ramp is **10 units, not 24**.
|
||
Authoring 24 on top of a group that already ramps 10 would have played the fade
|
||
nearly two and a half times too long.
|
||
|
||
### A decomposition that fits both numbers — offered as a hypothesis, not a finding
|
||
|
||
HANDOFF Q7 measures two quantities off the game: the fade-out ~0.4 s and the
|
||
black-hold plateau **0.17–0.23 s**. The file gives the ramp as 10 units (0.17 s).
|
||
|
||
in-file ramp 10 units + measured hold 10–14 units = 20–24 units = 0.33–0.40 s
|
||
|
||
🟡 The measured ~0.4 s sits at the **top** of that range. So the ~0.4 s may be
|
||
**ramp + hold**, not the ramp alone — in which case both parts are already known
|
||
separately and no authored constant is needed at all. **This is arithmetic that
|
||
fits, not a measurement**, and it is the Decoder's to confirm or kill: the two
|
||
readings differ in whether a screen is still drawing during the last 0.2 s.
|
||
|
||
### 🔴 And it exposes a disagreement in my own tree
|
||
|
||
`authored/timing.json` holds `black_hold_units: 9` = **0.15 s**, measured in the
|
||
draw stream. HANDOFF's plateau is **0.17–0.23 s**, measured off the game. **Mine
|
||
sits below their floor**, by 1–5 units.
|
||
|
||
I am **not** changing it. Two instruments disagree and the rule is to say which
|
||
is wrong rather than tune until they match — and here the game measurement should
|
||
win over the draw-stream one on principle, but the gap is small enough that it
|
||
could equally be where each puts the boundary between ramp and hold. It goes to
|
||
`BLOCKED.md` as an ask, at the value it was measured at.
|
||
|
||
## The plate came back in the game and not in the port
|
||
|
||
The Decoder's Ⓑ run answered both my asks and threw in a third finding: **after Ⓑ
|
||
from the menu the `PRESS Ⓐ` plate is re-drawn** — pressed 351.2 s, pulse back
|
||
358.5 s (`daf8f47`).
|
||
|
||
🔴 **The port did not do that.** Ⓑ landed on a *bare* title. `_menu_arrive()`
|
||
calls `_drop_overlay()` — correct, the plate goes with the screen it was measured
|
||
on — but nothing ever put it back: `_overlay_spec` is cleared the instant the
|
||
overlay is raised, and only the boot sequence ever set it. Confirmed by running
|
||
it, not by reading: the drawn list was the ten title elements with no `ptbtn00`.
|
||
|
||
✅ Fixed. `_rearm_overlay_for(name)` looks the declaration up in
|
||
`authored/flow.json`'s **boot step for that screen** rather than naming
|
||
`press_start`, so the plate returns by the same code path and the same shared
|
||
clock as on boot, and a screen that gains an overlay later gets it on both paths
|
||
with no edit here. **No new constant** — the delay is not authored, it is
|
||
whatever the boot already does.
|
||
|
||
Controlled both ways: Ⓑ → `overlay press_start raised`, drawing `ptbtn00`,
|
||
`ptbtn00f`; entering `EXTRAS`, which declares no overlay, raises **nothing**.
|
||
|
||
### An independent agreement I did not tune for
|
||
|
||
The script log had no press timestamp, so the port's own latency could only be
|
||
guessed from surrounding lines. Added one. With it:
|
||
|
||
| | |
|
||
|---|---|
|
||
| Ⓑ pressed | 1.01 s |
|
||
| title arrives, overlay armed | 1.37 s |
|
||
| **port's press → title** | **0.36 s** |
|
||
| **their measured Ⓑ latency** | **≤ 0.4 s** |
|
||
|
||
✅ That agreement is worth something because **nothing here was fitted to it** —
|
||
the port's transition timing comes from the screens' own fade keyframes, and this
|
||
is the first time the two numbers have been put beside each other.
|
||
|
||
### 🟡 The plate's return time does not agree, and I am not adjusting it
|
||
|
||
The plate is raised on arrival and its own group takes it opaque at t=238
|
||
(3.97 s), so the port's **press → plate visible ≈ 4.33 s**. Theirs is **7.3 s to
|
||
the pulse**. The pulse has a 120-unit (2 s) period, so pulse *detection* can lag
|
||
first paint by up to 2 s — which closes it to ~6.3 s at most and leaves roughly
|
||
**a second unexplained**.
|
||
|
||
⚠️ It would be easy to author a delay that makes 4.33 into 7.3. That is exactly
|
||
the tuning this corpus keeps warning about, and the previous authored delay in
|
||
this very block (`after_settle_seconds: 2.13`) was already refuted once by
|
||
arithmetic. Left alone; recorded as an ask.
|
||
|
||
### Two stamps upgraded, both now measured for real
|
||
|
||
* `navigation.auto_repeat` — a 2.0 s held ⬇ moves the cursor **once**, their
|
||
counter passing its control first. Was a consequence of edge-triggered
|
||
`_input`; now a measurement.
|
||
* `title/on_cancel` — Ⓑ on a **settled** title does nothing, twenty seconds
|
||
confirmed. This cell has now been `MEASURED` (wrongly), `AUTHORED` (honestly),
|
||
and `MEASURED` (truly), with the value `null` the whole way through.
|
||
|
||
## 🔴 `verify-screen` was nondeterministic, and it looked fine most of the time
|
||
|
||
Running the full set after the plate fix, two rows had moved off the committed
|
||
baseline. One of them was not a regression at all — it was the harness.
|
||
|
||
`press_start` returned `over3` **5021, 8919, 5021** on three identical runs. The
|
||
plate's looping focus record takes its phase from `time_units`, which free-runs,
|
||
so the captured frame lands wherever the grab happened to fall — while the
|
||
reference renderer cannot pulse at all. **A detector that answers differently
|
||
each run is worse than one that fails**: it teaches its reader to ignore it.
|
||
|
||
⚠️ **The port is not the thing that was wrong.** A thing that pulses does not stop
|
||
because the screen has arrived, and the pulse is measured. What was wrong was
|
||
comparing a moving frame against a static one and calling the difference a
|
||
regression. So `ScreenView.loop_phase_units` pins the phase, negative means
|
||
free-running, that stays the default everywhere, and only the harness passes
|
||
`--loop-phase=0`.
|
||
|
||
✅ Controlled, and the control is what makes the fix trustworthy:
|
||
|
||
| | |
|
||
|---|---|
|
||
| pinned, 3 runs | **identical md5** |
|
||
| free-running, 4 runs | 3 identical, **1 different** |
|
||
|
||
🟡 That 3-of-4 is the finding worth keeping. **It is usually stable**, which is
|
||
exactly why it survived — a flake that fires one run in four reads as a real
|
||
regression that "went away", and a `--loop-phase` that changed nothing would have
|
||
been indistinguishable from a fix without that negative control.
|
||
|
||
✅ With the phase pinned, `press_start` reads **max 1 / over3 0 OK** — *the
|
||
recorded baseline exactly*, not some new number. Fifteen of sixteen rows now
|
||
match the committed baseline.
|
||
|
||
### The sixteenth: `title_jp` has genuinely drifted, and I cannot say which side
|
||
|
||
| | max | over3 |
|
||
|---|---|---|
|
||
| committed baseline | 155 | 20 498 |
|
||
| now | **233** | **61 208** |
|
||
|
||
What is established:
|
||
|
||
* ✅ **deterministic** — 233 / 61 208 twice, so not the phase.
|
||
* ✅ **not the reference** — the Decoder reports `screen render` is byte-identical
|
||
across the stale and rebuilt binaries (max per-channel 0), so the reference is
|
||
stable and the movement is on the Godot side.
|
||
* ✅ **localized** — the differing region is a single **350×396 block at
|
||
(405, 74)**, the logo stack. `title` is untouched at max 6 / over3 790, which
|
||
rules out anything shared by both title screens (the forced-backdrop rule
|
||
among them).
|
||
* The port draws `ptlogo_jp`, `ptlogo3a/b/c` and the five `ptlogo_back2eff*`
|
||
layers here that are transparent at rest on the English title.
|
||
|
||
🔴 **What I cannot do is say which renderer is right.** There is no capture of the
|
||
Japanese title in the corpus, and this script's own header is explicit that
|
||
agreement with the reference is not correctness and a `DIFFERS` is not
|
||
automatically the port's fault. Guessing a direction here is precisely the move
|
||
the mission forbids. Asked, not resolved.
|
||
|
||
## 🔴 WITHDRAWN — the JP capture does NOT go against the port; I scored the wrong frame
|
||
##
|
||
## *(This heading read: "The JP title capture adjudicates `title_jp` — and it goes
|
||
## against the port." Withdrawn in full below. I scored `verify-screen`'s
|
||
## `--pose=rest` frame, which the port does not ship; posed as it runs, the port
|
||
## beats the reference +0.9994 to +0.8727. The heading asserted the opposite of
|
||
## the finding for as long as it stood.)*
|
||
|
||
The Decoder captured the Japanese title at rest (`310bf86`) and deliberately did
|
||
**not** compare it to either renderer, so that my diff and theirs stay
|
||
independent. This is the oracle for the block I could not adjudicate.
|
||
|
||
### Aligning it, because the last capture's geometry did not transfer
|
||
|
||
Their submenu capture had the game surface at y=45 in a 1280×720 frame. I did not
|
||
assume that here — I recovered the alignment by row/column profile correlation,
|
||
with the English pair as a control:
|
||
|
||
| | dy | dx |
|
||
|---|---|---|
|
||
| **control** — English capture (1279×675) vs port | **0** (r 0.994) | **0** (r 0.977) |
|
||
| JP capture (1280×720) vs port | **−45** (r 0.927) | −1 |
|
||
|
||
✅ The control lands on (0,0) as it must, and the JP offset comes out at their
|
||
stated 45 **as a measurement rather than an inheritance**. My first look at the
|
||
frame said "no letterbox, content spans all 720 rows" — true, and irrelevant: the
|
||
surface is offset inside content that extends past it.
|
||
|
||
### The instrument is fair, and then the verdict
|
||
|
||
Comparing the capture against **both** renderers in the disputed 350×396 block at
|
||
(405,74), and against a control strip where the two renderers agree:
|
||
|
||
| region | vs port | vs reference | closer |
|
||
|---|---|---|---|
|
||
| **control strip** | r +0.9751 | r +0.9756 | tie — ✅ instrument is fair |
|
||
| **disputed block** | r +0.7462 | **r +0.8727** | **REFERENCE** |
|
||
|
||
🔴 **The port moved, and it moved away from the game.** The verdict is stable
|
||
under gamma compensation at both measured title gammas (raw / 1.34 / 1.49 →
|
||
reference every time), so it is not an artefact of the known capture gamma floor.
|
||
The port puts light on **25.6 %** of the block that the capture does not have,
|
||
against the reference's 15.9 % — it is drawing too much, not too little.
|
||
|
||
⚠️ **This is the opposite of what I expected.** The Decoder's description — a
|
||
crystalline burst behind the wordmark, the `ptlogo3a/b/c` + `ptlogo_back2eff*`
|
||
stack that English holds transparent at rest — reads as confirmation that the
|
||
port's extra layers are right. They are not: the burst is there, and the port
|
||
draws *more* of it than the game does. **A qualitative match on "is the effect
|
||
present" was about to stand in for a measurement of how much.**
|
||
|
||
### What is not settled: which change did it
|
||
|
||
Both renderers draw this screen at `rest`, t=10 units, so the settle-window logic
|
||
is not in play — the two decoders disagree about the **rest pose of the JP effect
|
||
stack itself**. Four commits this session touched that path (the forced-backdrop
|
||
rule, per-instant coverage, the looping record, the sweep/hold work) and I have
|
||
not bisected them. Naming one now would be a guess dressed as a cause.
|
||
|
||
📌 What this does settle: `title_jp`'s `DIFFERS` is **the port's**, not the
|
||
reference's. That reverses this script's usual presumption, and it is the first
|
||
row in the baseline whose direction has ever been established against a capture.
|
||
|
||
## 🔴 CORRECTION: the port did not move away from the game — I scored the wrong frame
|
||
|
||
The previous entry concluded, from the JP title capture, that *"the port moved,
|
||
and it moved away from the game"*. **That conclusion is withdrawn.** It is wrong,
|
||
and the way it was wrong is worth more than the answer.
|
||
|
||
I scored `verify-screen`'s `title_jp` frame against the oracle. That frame is
|
||
posed `--pose=rest`, which this port **does not ship**. Posed as it actually
|
||
runs:
|
||
|
||
| | disputed block | whole surface |
|
||
|---|---|---|
|
||
| port, `--pose=rest` (the frame I scored) | +0.7462 | — |
|
||
| **port, as shipped** | **+0.9994** | **+0.9652** |
|
||
| reference | +0.8727 | +0.9200 |
|
||
|
||
✅ Holds under gamma compensation (+0.9928 at γ=1.34) and ✅ on the **English
|
||
control**, same method: port +0.9946 against the reference's +0.9560. The port is
|
||
closer to the game than the reference on **both** title screens.
|
||
|
||
### Why `rest` produces a frame the game never shows
|
||
|
||
`ptlogo_back2eff1` on the JP title is `(t, alpha) = (0,0) (98,0) (100,255)
|
||
(102,255) (104,0)` — a **4-unit sparkle**, and its `rest.t` is **100: the peak of
|
||
its own flash**. Six of these stagger across the logo. Posing at `rest` fires
|
||
every sparkle simultaneously at full brightness, which is exactly the "port puts
|
||
light on 25.6 % of the block the capture does not have" I reported as a defect.
|
||
The excess light was real; it was in a frame nobody sees.
|
||
|
||
⚠️ `verify-screen` is not at fault — it poses `rest` **deliberately**, because
|
||
both renderers read `rest` through one decoder and that is what makes it a
|
||
consistency check. Its header said so. **I used a consistency-check frame to
|
||
answer a correctness question**, and the tool now says in its own header that its
|
||
frames must never be scored against a capture.
|
||
|
||
### A second, smaller thing in that entry was also wrong
|
||
|
||
It said the port draws layers "that are transparent at rest on the English
|
||
title". Both screens draw them under `--pose=rest`. I had compared a `--menu`
|
||
run's log (timeline pose) against a `verify-screen` log (rest pose) and read the
|
||
difference as a property of the screens rather than of the two modes.
|
||
|
||
### What actually stands from that entry
|
||
|
||
The alignment work survives intact — the measured dy=−45 with the English control
|
||
at (0,0), and the observation that the instrument is fair on a control strip. So
|
||
does the arithmetic. **What failed was choosing which frame to feed it**, and no
|
||
amount of control on the comparison could have caught that: every control I ran
|
||
was a control on the *metric*, and the error was upstream in the *input*.
|
||
|
||
📌 The generalisation: **a control proves the instrument, not the sample.** Both
|
||
of my last two iterations' errors were of that shape — a live reader pointed at
|
||
the wrong field name, and a fair metric pointed at the wrong frame.
|
||
|
||
### Wired so it cannot recur
|
||
|
||
`tools/port/verify-capture` takes a fifth per-row field, a capture crop, because this
|
||
capture is a full 1280×720 display frame with the surface at +0+45 while every
|
||
other capture in that directory is pre-cropped to 1279×675 — comparing it whole
|
||
would score the port against a 45 px shift. With it, `title_jp` reads **RMSE
|
||
20.91, differing region 1.04 %**, beside `title`'s 14.16 / 0.21 %.
|
||
|
||
⚠️ The row prints `no capture` until the Decoder's branch merges. Their capture is
|
||
theirs to commit; it was staged locally to test the row and removed.
|
||
|
||
## The `rest()` flash defect reaches four screens I ship — and the port already survives it
|
||
|
||
The Decoder censused it from the file side while I was looking at one instance:
|
||
of 13 991 elements with ≥2 keyframes, **2 305** have no plateau so the dwell
|
||
fallback decides, and **1 697 (74 %)** of those get a *visible* pose. In
|
||
`GP_TITLE`, 5 fires and 4 are visible — **all four on the splash screens this
|
||
port ships**.
|
||
|
||
✅ Confirmed in my own export, and it is exactly the JP-title shape on different
|
||
screens:
|
||
|
||
| element | keyframes | `rest` |
|
||
|---|---|---|
|
||
| `palogo_sqex_eff` | `0:a0 15:a255 30:a212 45:a0` | t=30, **a=212** |
|
||
| `palogo_anima_eff` | `0:a0 15:a255 30:a212 45:a0` | t=30, **a=212** |
|
||
| `palogo_gamearts_eff` | `0:a0 15:a255 **30:a255** 45:a0` | **t=15, a=255** |
|
||
| `palogo_seta_eff` | `0:a0 15:a255 **30:a255** 45:a0` | **t=15, a=255** |
|
||
|
||
📌 **A refinement to their description**, which named the `212` shape: two of the
|
||
four hold **255 through t=30**, so their fallback lands on the flash's *peak*
|
||
rather than its decay. Same defect, worse pose — full brightness, not
|
||
four-fifths. The logos themselves (`palogo_sqex` holds 255 from t=30 to t=235)
|
||
have a real plateau and are unaffected.
|
||
|
||
### The port ships the right frame, and now there is a number for it
|
||
|
||
Both poses of the publisher splash against the **committed oracle capture**:
|
||
|
||
| pose | RMSE | differing |
|
||
|---|---|---|
|
||
| **timeline — what the port ships** | **2.17** | **0.01 %** |
|
||
| `--pose=rest` — the harness frame | 9.05 | 0.75 % |
|
||
|
||
🔴 **75× the differing area on a screen this port ships.** So the rule I wrote
|
||
into `verify-screen`'s header after getting it wrong on `title_jp` is not a
|
||
special case — it generalises, and here it is demonstrated against an oracle
|
||
rather than argued.
|
||
|
||
✅ The port's settled pose evaluates `pose_at(hold)`, not `rest`, so it skips the
|
||
flashes and agrees with the capture at 0.01 %. The defect is confined to the
|
||
harness pose. **Nothing shipped is wrong; nothing needed fixing in the render.**
|
||
|
||
### What did need fixing: the port said "at rest" about a pose it never looked at
|
||
|
||
`ScreenView` logged `"%s (transparent at rest)"` for every skipped element,
|
||
whatever instant it had posed. On the timeline path the pose is
|
||
`pose_at(time_units)` — so it reported `palogo_sqex_eff (transparent at rest)`
|
||
about an element whose **resting alpha is 212**.
|
||
|
||
⚠️ That is not cosmetic. The rest-versus-posed-instant confusion is precisely what
|
||
made me score a `--pose=rest` frame against a capture and write up a drift that
|
||
did not exist. A log line that erases the distinction is that error pre-printed,
|
||
waiting to be believed. It now names the instant: `transparent at t=6`.
|
||
|
||
Controlled both ways on one screen: timeline → `transparent at t=6` and the flash
|
||
skipped; `--pose=rest` → still `at rest`, and the flash **drawn**.
|
||
|
||
## Correction: those two are the *sound* path, which makes the rule stronger
|
||
|
||
The Decoder refuted my refinement, and it is a correction I would rather have than
|
||
the credit. I wrote that `palogo_gamearts_eff` / `palogo_seta_eff` show "the same
|
||
defect, worse pose — their fallback lands on the flash's peak". **Wrong on the
|
||
mechanism.** They hold `a=255` at identical x, y *and scale* from t=15 to t=30 —
|
||
a genuine plateau at pair index 1, which `rest_plateau()` handles, and t=15 is
|
||
the **correct** answer for that path. They are not among their census's four.
|
||
|
||
🔴 **And the consequence runs the other way from a retraction.** My rest pose for
|
||
them really is the flash's peak, reached by the **sound** path. So *"a rest render
|
||
is not a frame to score against a capture"* does **not** depend on the fallback
|
||
being unsound: **a plateau can itself be the held peak of a transient.** The
|
||
2 305 / 1 697 census *understates* the exposure rather than bounding it.
|
||
|
||
### Censusing my own tree — and the first answer was wrong
|
||
|
||
I asked how many elements I ship whose `rest` is visible but whose visibility is
|
||
transient. First pass keyed "transient" on the element's own visible span, and
|
||
returned **28 across 12 of 16 screens** — a plausible-looking number.
|
||
|
||
🔴 It was wrong, and what caught it was the check the Decoder and I just agreed
|
||
on: **say what the number means physically.** The list included `ptmsg` — the main
|
||
menu's own `⊙ Select Ⓐ OK` footer — as "visible 2 of 64 units", and `ptbtn00`,
|
||
the `PRESS Ⓐ` plate. Those are on screen the whole time the game sits there. The
|
||
story collapses on contact.
|
||
|
||
The cause: `ptmsg` is `[0:a0 44:a0 56:a255 58:a255 64:a0]`, and that final zero is
|
||
the **screen's exit ramp**, which *every* element has. I had counted the exit as
|
||
the end of visibility, so every normal element looked like a flash. No control
|
||
would have caught this — the arithmetic was right.
|
||
|
||
✅ Re-keyed on the **screen's** span rather than the element's: a transient is
|
||
gone while the screen is still up.
|
||
|
||
| | |
|
||
|---|---|
|
||
| elements whose `rest` shows what the settled screen does not | **31** |
|
||
| screens affected | **8 of 16** |
|
||
|
||
Every entry now has a coherent story — `*eff*` and `*loop*` sparkles, plus the two
|
||
loading screens — and `ptmsg`/`ptbtn00` fall out on their own, which is the check
|
||
passing rather than being applied by hand.
|
||
|
||
📌 **My exposure is twice what the splash finding suggested.** Not four screens,
|
||
eight — both titles, both splashes and their region twins, and both loading
|
||
screens. ✅ None of it reaches shipped output: the port poses `pose_at(hold)` and
|
||
agrees with every capture it has. The number bounds what would break the day
|
||
anything scored a `rest` frame against an oracle.
|
||
|
||
## The two loading screens are no longer black, and it was the paint order
|
||
|
||
`verify-screen`'s header has carried, since P1, that `build_12` and `build_15`
|
||
*"render as pure black in BOTH renderers, mean 0 and max 0"*, with an open
|
||
question: *"whether that is the port's bug or the decoders' reading of `rest`"*.
|
||
|
||
✅ **Both halves are now settled, and the answer is neither.** Measured today:
|
||
|
||
| | max | mean |
|
||
|---|---|---|
|
||
| port | 214.5 | 1.949 |
|
||
| reference | 214.5 | 1.918 |
|
||
|
||
Not blank, on either side, and the two agree — the rows read `OK` on a real
|
||
comparison rather than on nothing-against-nothing.
|
||
|
||
🔴 **It was the paint order, not `rest`.** My own earlier measurement had already
|
||
answered it and I had not connected the two: removing the forced-backdrop pass
|
||
makes these screens' first element `pgloading_loop5`, *"and the black screen
|
||
returns"*. `pgloading_eff00` is the full-frame opaque untextured quad, and it
|
||
carries `layer: null`, `layer_source: none` — the only elements in the export with
|
||
neither a read nor an implied key. Its position rests entirely on the occlusion
|
||
constraint. The rule that fixed it is the one the Decoder supplied and I
|
||
implemented this session.
|
||
|
||
⚠️ **The guard stays and the stale paragraph stays with it**, marked as history.
|
||
It was correct when written — two of sixteen rows were reporting this script's
|
||
strongest verdict for comparing nothing against nothing — and a guard that stops
|
||
firing is exactly the kind that rots out of a tool. A reader who hits a blank pair
|
||
tomorrow needs the reasoning, not just the verdict.
|
||
|
||
### Refutation attempt: does the Decoder's census miss my title screens?
|
||
|
||
Their `GP_TITLE` census is *"5 fires, 4 visible, all four on the splash screens"*.
|
||
My own census found six transient `ptlogo_back2eff*` elements on `title` and seven
|
||
on `title_jp` — also `GP_TITLE` builds. If those were fallback fires, their count
|
||
of four would be wrong.
|
||
|
||
✅ **Their claim survives.** All six reach `rest` by the **plateau** path — alpha
|
||
255→255 with identical `pos` *and* `scale` across the pair — so `rest_plateau()`
|
||
handles them and the dwell fallback never runs. They are not fires.
|
||
|
||
📌 Which is their own point back at them, now with my screens as evidence: my
|
||
census counts a **superset spanning both paths**, and the difference between the
|
||
two numbers is not disagreement but scope. A plateau that happens to sit on a
|
||
transient's peak is invisible to a fallback census and still produces a rest frame
|
||
the game never shows.
|
||
|
||
### A proposed sharpening of the census, tested and rejected
|
||
|
||
The Decoder's surviving number rests on a structural fact: the dwell fallback runs
|
||
only when no two adjacent poses are equal, so **every pose it returns is un-held
|
||
by construction**, and no threshold is needed. That is clean, and the obvious move
|
||
was to borrow it — replace my "gone before 60 % of the screen" cutoff with *how
|
||
long the rest pose is held*, which would drop the arbitrary threshold.
|
||
|
||
🔴 **It fails my own control.**
|
||
|
||
| element | held | of screen | flagged? |
|
||
|---|---|---|---|
|
||
| `ptmsg` — the main menu's footer | 2 units | 80 (2.5 %) | **yes** ❌ |
|
||
| `ptbtn00` — the `PRESS Ⓐ` plate | 2 units | 244 (0.8 %) | **yes** ❌ |
|
||
| `ptlogo_back2eff1` — a real sparkle | 2 units | 269 (0.7 %) | yes ✅ |
|
||
|
||
All three sit on a **2-unit plateau**. Hold duration cannot separate them, and the
|
||
two it gets wrong are the exact pair whose absurdity caught my first census.
|
||
|
||
✅ **Why the criterion does not transfer.** On the fallback path nothing is held,
|
||
so "un-held" *is* the defect. On the plateau path the plateau is real — what
|
||
distinguishes a footer from a sparkle is **where it sits relative to the screen's
|
||
end**: `ptmsg`'s 2-unit plateau is the last pose before the exit ramp, so the port
|
||
holds it past the end and the game shows it throughout; the sparkle's identical
|
||
2-unit plateau is followed by a return to zero *while the screen is still up*.
|
||
|
||
📌 So the screen-span criterion stays, threshold and all. **A cleaner definition
|
||
that fails a control is worse than an ugly one that passes** — and I would have
|
||
adopted this on its elegance if the control pair had not already been sitting
|
||
there from the earlier mistake.
|
||
|
||
## Adjudicating the Decoder's `rest()` replacement against the game
|
||
|
||
They proposed posing every element at the **screen's** settle instant instead of
|
||
asking each element for its own resting pose, found their own control could not
|
||
validate it — *"a candidate cannot be adjudicated against the incumbent it is
|
||
meant to replace"* — and said the oracle number is what decides. It is, and I had
|
||
only ever run it on one screen. Running it on every capture-backed screen:
|
||
|
||
| screen | candidate (settled) | incumbent (`rest`) | |
|
||
|---|---|---|---|
|
||
| `title` | **0.21 %** | 1.82 % | candidate |
|
||
| `publisher_logo` | **0.01 %** | 0.75 % | candidate |
|
||
| `developer_logos` | **0.01 %** | 0.33 % | candidate |
|
||
| `main_menu` | 0.07 % | 0.25 % | ⚠️ **confounded** |
|
||
| `extras` | 0.19 % | 0.46 % | ⚠️ **confounded** |
|
||
|
||
✅ Three screens adjudicate cleanly and all three favour the candidate, by 9× to
|
||
75×. The settled figures are corroborated: they match `verify-capture`'s
|
||
independently recorded numbers to the digit.
|
||
|
||
### 🔴 Two of the five rows are not evidence, and my first table said they were
|
||
|
||
My first run had `main_menu` at **3.29 %** for the candidate — losing to the
|
||
incumbent by 13×, the opposite direction from everything else. That had no
|
||
plausible story, which is what made me look.
|
||
|
||
`--screen=` shoots the frame immediately: the "settled" main_menu drew **6 of 16**
|
||
elements and skipped `ptframe1`, `ptframe2` and `ptmsg` as *"transparent at t=9"*.
|
||
It was a mid-build-in frame. **The same wrong-frame error as `title_jp`, caught
|
||
before publishing this time and only because the number's direction made no
|
||
sense.** Properly posed via `--menu --script=wait`, it is 0.07 %.
|
||
|
||
⚠️ **But that fix introduces a confound, and it is fatal to those two rows.** The
|
||
only way to pose these screens settled is `--menu`, which also draws the **focus
|
||
record**; the `rest` column is rendered by `--screen`, which draws none. This
|
||
tool's own header records that difference: main_menu without focus is 2 159
|
||
differing pixels — **0.234 %** — against 531 with it. My incumbent figure is
|
||
0.25 %. *The entire gap on those two rows is the focus record, not the pose.*
|
||
|
||
So they stay in the table marked confounded rather than counted. **A 5–0 result
|
||
was available by not looking.**
|
||
|
||
### What this does and does not settle
|
||
|
||
✅ The candidate is better on every screen where the question can be asked
|
||
cleanly, against the game rather than against the incumbent — which is the
|
||
adjudication their failed control could not provide.
|
||
|
||
⚠️ It does **not** validate their implementation. I tested the port's settled pose,
|
||
not `UiBuild::settle_time()`; the two agree in *direction*, and whether they agree
|
||
in value is unmeasured. And three screens are three screens.
|
||
|
||
📌 They are right not to change `rest()` on this. I pin their crate, nothing I
|
||
ship reads `rest`, and a proposal whose evidence comes entirely from the consumer
|
||
has no business landing in the dependency on that basis alone.
|
||
|
||
## The boot's own end frame, scored against the game for the first time
|
||
|
||
`--boot --capture=` used to write **no file**: `_finish_boot()` was reachable only
|
||
from the overlay-quit branch, and the boot quit first because that branch fires
|
||
when `_overlay_spec.is_empty()` — which it is the instant the overlay is raised.
|
||
✅ Fixed by the `_overlay_quit_at < 0.0` guard added earlier this session. The
|
||
defect entry above is left standing with a pointer here, because the reasoning is
|
||
what makes the guard legible.
|
||
|
||
⚠️ I fixed it and never went back to check what it made possible. **The whole
|
||
P3/P7 artifact — the boot running unattended and photographing its own end
|
||
state — has been available for hours and unused.**
|
||
|
||
### What it shows
|
||
|
||
| | RMSE | differing |
|
||
|---|---|---|
|
||
| **boot's own end frame**, real sequence, unattended | **12.80** | **0.00 %** |
|
||
| `title_plate`, synthetically posed at `--time=3.95` | 12.83 | 0.00 % |
|
||
|
||
✅ **Zero pixels over the threshold against the game.** The residual RMSE is the
|
||
known capture gamma floor, which every row on this corpus carries and which is not
|
||
a target.
|
||
|
||
📌 **And the two agree to 0.03 RMSE.** That is the more useful half. `verify-capture`
|
||
reaches this frame by a *shortcut* — `--screen=title --overlay=press_start
|
||
--time=3.95` — rather than by booting. The shortcut has been trusted since it was
|
||
written and never tested against the thing it stands in for. It is faithful:
|
||
posing the composite directly and arriving there through publisher logo →
|
||
developer logos → `ADV.wmv` → title → plate land on the same frame.
|
||
|
||
⚠️ What this does **not** show is that the intervening sequence is right. It is one
|
||
frame, the last one; the boot could take a wrong path and still end correctly.
|
||
`--shots` walks the sequence, and comparing those against captures needs captures
|
||
of the intermediate states, which the corpus does not have for the video handover.
|
||
|
||
## Refutation attempt: the settle-instant candidate is **not** uniformly better
|
||
|
||
Their symmetry-breaking property attributes every `rest()` disagreement to the
|
||
chosen plateau not covering the settle instant. That is testable from my side on
|
||
the two screens my earlier table had to mark **confounded** — and it turns up a
|
||
screen class where their candidate is *worse*.
|
||
|
||
🔴 **At `main_menu`'s settle instant the footer is half-drawn.**
|
||
|
||
| screen | settle window | settle instant | `ptmsg` alpha there |
|
||
|---|---|---|---|
|
||
| `main_menu` | [44, 56] | **50** | **127.5** of 255 |
|
||
| `extras` | [38, 50] | **44** | **127.5** of 255 |
|
||
|
||
`ptmsg` is `[0:a0 44:a0 56:a255 58:a255 64:a0]` — it finishes arriving at t=56,
|
||
*after* the settle window has closed at 56 and well after its midpoint at 50. The
|
||
settle instant is the midpoint of the longest keyframe-free interval, and on both
|
||
menus **that interval ends exactly as the footer starts to arrive.**
|
||
|
||
Measured against the game, with focus absent from both renders so the confound
|
||
that voided my earlier rows cancels:
|
||
|
||
| pose | RMSE | differing |
|
||
|---|---|---|
|
||
| t=50 — the screen's settle instant | 14.98 | **0.30 %** |
|
||
| t=58 — the footer's own hold | 14.83 | **0.25 %** |
|
||
|
||
⚠️ **Small, and real.** This does not overturn their proposal — it wins by 9× to
|
||
75× on `title` and both splashes, and by 4.6× on their own `title` measurement.
|
||
What it shows is that "pose everything at the screen's settle instant" has a
|
||
**failure mode of its own**: an element that arrives after the settle window
|
||
closes is caught mid-fade. The port's hold-based pose does not have it, because it
|
||
parks each element at its own hold.
|
||
|
||
📌 So the honest summary of this whole thread is narrower than either of us has
|
||
been writing: **the screen-settle pose beats per-element `rest()` decisively where
|
||
`rest()` returns a transient's peak, and loses slightly where an element arrives
|
||
late.** Both are approximations to a settled screen; neither is the settled screen.
|
||
|
||
### And a unit bug, caught by two identical numbers
|
||
|
||
My first run of this asked for `--time=50` meaning 50 *units* and got **50
|
||
seconds** — 3000 units, past the end of everything. Both poses returned RMSE 56.02
|
||
/ 9.15 %, **identical to two decimal places**, which is what made me look. Two
|
||
different inputs producing exactly the same output is not a result; it is the
|
||
instrument saying it ignored the input. Same tell as the two `--script` runs that
|
||
came out bit-identical because the capture fired before the first press.
|
||
|
||
## My own predictor holds — and the evidence that made it *better than width* does not
|
||
|
||
The Decoder censused my `ptmsg` failure mode disc-wide (25.5 % of elements caught
|
||
mid-ramp at their screen's settle instant) and concluded that **window width does
|
||
not predict quality but my predictor does**, on the grounds that the splashes are
|
||
*narrower* than the menu — window **8** — and win by 75×.
|
||
|
||
✅ **The predictor itself holds, 5 of 5** on my capture-backed screens:
|
||
|
||
| screen | mid-ramp at settle | measured outcome |
|
||
|---|---|---|
|
||
| `title` | **0** | settle wins 9× |
|
||
| `publisher_logo` | **0** | settle wins 75× |
|
||
| `developer_logos` | **0** | settle wins 33× |
|
||
| `main_menu` | **2** (`ptmsg`, `pteff10`) | settle **loses** |
|
||
| `extras` | **2** (`ptmsg2`, `pteff20`) | settle **loses** |
|
||
|
||
🔴 **But their window figure for the splashes disagrees with my export by 20×**, and
|
||
that figure is the whole of the argument.
|
||
|
||
| screen | their window | mine |
|
||
|---|---|---|
|
||
| `title` | 76 | **76** ✅ |
|
||
| `main_menu` | 12 | **12** ✅ |
|
||
| `publisher_logo` | 8 | **190** ❌ |
|
||
| `developer_logos` | 8 | **145** ❌ |
|
||
|
||
Recomputed independently from the raw top-level keyframe times rather than read
|
||
off my own `settle_window` field: `publisher_logo`'s times are
|
||
`[0, 15, 30, 45, 235, 239, 251, 255]`, whose widest keyframe-free gap is
|
||
**45 → 235 = 190**. `developer_logos` gives `45 → 190 = 145`. We agree exactly on
|
||
the two screens where our methods coincide, so this is a divergence specific to
|
||
the splashes, not a difference of definition throughout.
|
||
|
||
### Why this matters more than a corrected number
|
||
|
||
**The splashes are the *widest* of my five, not the narrowest.** With that, the
|
||
data reads:
|
||
|
||
| screen | window | mid-ramp | outcome |
|
||
|---|---|---|---|
|
||
| `publisher_logo` | 190 | 0 | wins 75× |
|
||
| `developer_logos` | 145 | 0 | wins 33× |
|
||
| `title` | 76 | 0 | wins 9× |
|
||
| `main_menu` | 12 | 2 | loses |
|
||
| `extras` | 12 | 2 | loses |
|
||
|
||
🔴 **Width and mid-ramp now predict identically and are perfectly confounded.** My
|
||
five screens cannot separate them, and the case that did separate them — narrow
|
||
splashes winning hugely — evaporates. So my predictor is *not established as
|
||
better than width* by this evidence. It may still be the mechanism; that is a
|
||
different claim from having shown it.
|
||
|
||
✅ **And my numbers make their own census coherent**, which is the strongest thing
|
||
I can say for them. Their buckets run 40.9 % mid-ramp on windows under 10 and
|
||
11.7 % on wide ones. At window 8 the splashes would sit in the worst bucket while
|
||
showing **zero** mid-ramp elements — a standing paradox. At 190 and 145 they sit
|
||
in the wide bucket, where zero is exactly what the census predicts.
|
||
|
||
⚠️ I am not claiming their tool is broken; `--settle` may report a different
|
||
quantity than the widest keyframe-free gap. But one of the two readings is wrong,
|
||
and until it is settled the width hypothesis is **not** refuted.
|
||
|
||
## Checking my own tree for the ordinal foot-gun that just voided three of theirs
|
||
|
||
The Decoder retracted three claims: `screen render --build N` takes a **build
|
||
ordinal**, `screen list` maps `[10] → entry 12` and `[11] → entry 15`, and the
|
||
splashes are entries 10 and 11 — so their splash rows had rendered the **loading
|
||
screens** against splash captures. My own HANDOFF entry warned that an
|
||
ordinal-keyed 10/11 names the splashes as loading screens *"and everything still
|
||
validates"*, and it did.
|
||
|
||
⚠️ `tools/port/verify-screen`'s header claims `--all` protects me from exactly
|
||
this. **A comment claiming protection is what just failed on their side**, so I
|
||
checked rather than cited it.
|
||
|
||
| | RMSE |
|
||
|---|---|
|
||
| my CLI reference for build 10 vs the **publisher** splash capture | **8.97** ✅ |
|
||
| my CLI reference for build 11 vs the **developer** splash capture | **8.77** ✅ |
|
||
| cross-control — publisher reference vs **developer** capture | **48.17** |
|
||
|
||
✅ Both references are the screens they claim to be, and the cross-control is 5.4×
|
||
worse, so the discriminator has teeth rather than passing everything. My `--all`
|
||
addressing is correct, and now measured rather than asserted.
|
||
|
||
📌 Worth naming why this was worth ten minutes: the port's numbers for these two
|
||
screens (0.01 % differing) are among the strongest evidence in the corpus, and
|
||
they are cited in the `rest()` adjudication that a proposal against a pinned crate
|
||
now rests on. **Evidence that strong is exactly what you check after finding the
|
||
same class of error next door** — the failure mode is silent by construction, and
|
||
their instrument reported a railed gamma fit rather than a wrong screen.
|
||
|
||
### What survives of the settle-window disagreement
|
||
|
||
Their retraction confirms my reading: 190 and 145, matching my recomputation from
|
||
raw keyframe times exactly. Their library was never wrong, only the invocation.
|
||
|
||
So the position stands where my last entry left it, and no further: **width and
|
||
mid-ramp are perfectly confounded across every screen either of us has measured.**
|
||
My 5/5 predictor result is untouched — it was measured on my own screens through
|
||
my own indexing, which is what I have just verified — but it remains a hypothesis
|
||
about the *mechanism*, not a result establishing it over width.
|
||
|
||
## Looking for a case that separates width from mid-ramp — there is none, and I nearly invented one
|
||
|
||
Width and mid-ramp predicted identically across my five capture-backed screens, so
|
||
the useful question was whether any of my **sixteen** breaks the tie: a wide window
|
||
*with* a mid-ramp element, or a narrow one without. Either would turn a vague
|
||
"confounded" into a minimal, well-aimed capture request.
|
||
|
||
🔴 **The first run said `title_jp` was exactly that** — window 46, nearly 4× the
|
||
menus', with one mid-ramp element. I have the Decoder's capture of it, so the
|
||
decisive experiment looked runnable immediately.
|
||
|
||
It was wrong. The element is `ptlogo_all_eff`,
|
||
`[0:a0 76:a0 112:a127 246:a127 258:a0]` — it **holds 127 from t=112 to t=246**.
|
||
That is its plateau, not a transition. **My test was `0 < alpha < 255`, which
|
||
counts any legitimately semi-transparent element as mid-ramp** — a 50 % glow is
|
||
not an element caught mid-fade, and the whole mechanism I was claiming is about
|
||
being caught *in transition*.
|
||
|
||
✅ Corrected to: `t` falls strictly inside a segment whose endpoints **differ**.
|
||
|
||
| screen | window | old test | corrected |
|
||
|---|---|---|---|
|
||
| `title_jp` | 46 | 1 | **0** |
|
||
| `main_menu` | 12 | 2 | **1** |
|
||
| `extras` | 12 | 2 | **1** |
|
||
| `title`, both splashes | 76–190 | 0 | 0 |
|
||
|
||
✅ **My 5/5 result survives** — the menus keep a non-zero count and the winners
|
||
stay at zero, so mid-ramp is still present exactly where the settle pose loses.
|
||
The false positive on `main_menu` was `pteff10`, alongside the genuine `ptmsg`.
|
||
|
||
🔴 **And there is no separating case anywhere in the export.** Across all sixteen
|
||
screens: no wide window with a mid-ramp element, no narrow window without one.
|
||
|
||
### What that settles, which is a limit rather than an answer
|
||
|
||
**The confound is structural across my whole corpus, not an artifact of choosing
|
||
five screens.** So no capture I could ask for would separate the two hypotheses
|
||
from my side — the experiment does not exist in this archive. Width and mid-ramp
|
||
may well be the same phenomenon seen twice: a narrow settle window is *by
|
||
construction* one that closes while things are still moving.
|
||
|
||
📌 The near-miss is the part worth keeping. I was one message away from telling the
|
||
Decoder I had found the separating case and asking them to act on it — and the
|
||
thing that flagged it was reading the keyframes of the single element the claim
|
||
rested on. **The screen most useful to me was the one I checked least.**
|
||
|
||
## Auditing my tree for the disc-wide ordinal foot-gun
|
||
|
||
The Decoder found the ordinal/entry divergence is disc-wide — 21 of 24
|
||
build-bearing archives, 18 diverging at ordinal 0 — and that **`GP_TITLE` is the
|
||
mildest case on the disc**, the only archive whose first ten ordinals are the
|
||
identity. That is the whole reason this corpus survived, and it is luck, not
|
||
design.
|
||
|
||
✅ **No exposure in my tree, checked rather than assumed.**
|
||
|
||
* The four archives they flag as exposed — `GP_READY_ROOM`, `GP_HANGAR_ARSENAL`,
|
||
`GP_MISSION_SELECT`, `GP_OPTIONS` — appear in `authored/flow.json` with **no
|
||
numbers at all**, only as *"not in this export"*. Nothing to misread.
|
||
* `authored/screen_names.json` already says **"LOCATED BY ENTRY INDEX, not by a
|
||
rule"** — the exact disambiguation their second warning asks for.
|
||
* Their second point (`--all` swaps the predicate, so `--build N` and
|
||
`--build N --all` are different objects) is what `verify-screen`'s header
|
||
already relies on, and every tool of mine passes `--all`.
|
||
|
||
### Verifying the high ordinals, where GP_TITLE's luck would run out first
|
||
|
||
The identity holds for the first ten ordinals. My export addresses **13** and
|
||
**14**, past that point, so the interesting test is up there:
|
||
|
||
| | RMSE | |
|
||
|---|---|---|
|
||
| `publisher_logo` (10) vs `publisher_logo_r` (13) | **3.06** | region twins — near-identical ✅ |
|
||
| `developer_logos` (11) vs `developer_logos_r` (14) | **4.33** | region twins — near-identical ✅ |
|
||
| `publisher_logo` (10) vs `developer_logos` (11) | **47.91** | different screens — control, 11–16× worse |
|
||
|
||
✅ `--build N --all` lands exactly where HANDOFF says entries 10/13 (publisher) and
|
||
11/14 (developer) are, across the full range where divergence could begin, and the
|
||
control shows the test would have caught a mismatch.
|
||
|
||
⚠️ **The constraint is recorded for whoever exports those four archives**, which is
|
||
not this port today: `--build 0` is not entry 0 in any of them. The current
|
||
absence of exposure is a fact about what I have exported, not a property of the
|
||
tooling.
|
||
|
||
### A precision correction to my own wording
|
||
|
||
They tried to refute my `ptlogo_all_eff` correction and could not — the quote is
|
||
exact and `a=127` holds flat across 134 units with position and scale constant.
|
||
|
||
⚠️ But they flag something I should not have said. I called it a *"50 % glow"*.
|
||
**What is measured is the plateau**; that it *is* a glow rests on kind `0x3000`
|
||
and a 200 % scale, and nobody has put that in front of the running game. The
|
||
correction to my mid-ramp test stands on the numbers alone and needs no reading of
|
||
what the element depicts — which is how it should have been written.
|
||
|
||
## Their withdrawn "~14 units of black hold" — my authored 9 survives it
|
||
|
||
Two warnings arrived. The first does not touch me: ✅ nothing of mine is authored
|
||
from `screen-transitions.md`'s 0.87 / 0.97 / 4.08 s fade-in spans, and **nothing
|
||
in this port reads keyframe times outside the crate** — the exporter reads them
|
||
through `sylpheed_formats`, and every analysis script I have reads
|
||
`export/*.json` downstream of it. Their `fade_quads.py` failure mode cannot occur
|
||
here by construction, which is the wall doing its job rather than luck.
|
||
|
||
The second is about a value I ship: `authored/timing.json` `black_hold_units: 9`.
|
||
They withdrew the "~14 units of hold" and warn that **authoring a hold puts a
|
||
sixth of a second of dead black into every transition the game does not have**.
|
||
|
||
### Testing their structural claim on my own export
|
||
|
||
*"Content elements start fading about six frames before the black quad's ramp
|
||
begins."* On `main_menu`, `pteff00` is `[0:a255 12:a0 70:a0 80:a255]` — its rise
|
||
to black runs **t=70 → 80**. The content fade-outs start at:
|
||
|
||
| element | starts | ends |
|
||
|---|---|---|
|
||
| `ptmsg` | **58** | 64 |
|
||
| `pteff10`, `pteff12` | 60 | 68 |
|
||
| `ptbtn05` | 60 | 64 |
|
||
|
||
✅ **12 units of lead — exactly six frames at 30 Hz**, matching their measurement
|
||
off the running game. Two independent routes, disc and capture, same number.
|
||
|
||
⚠️ One difference: they say the two **overlap**; in my export content is gone by
|
||
t=68 and the quad starts at t=70 — a 2-unit gap, not an overlap. That is one frame,
|
||
inside their stated ±1 frame per span, so I record it as agreement at their
|
||
resolution rather than as a discrepancy either of us can act on.
|
||
|
||
### The arithmetic, which is the part that matters
|
||
|
||
Their new figure: **total blackout 9 frames ≈ 0.30 s = 18 units**, gap between
|
||
screens one frame.
|
||
|
||
| | units |
|
||
|---|---|
|
||
| quad's ramp to black, from the file | 70 → 80 = **10** |
|
||
| my authored `black_hold_units` | **9** |
|
||
| total from ramp start to the next screen | **19** = 0.317 s |
|
||
| their measured blackout | **18** = 0.30 s |
|
||
|
||
✅ **One unit apart — inside their own resolution.** My authored 9 is *supported*
|
||
by the measurement that withdrew the 14, not refuted by it.
|
||
|
||
📌 And the reason it survived is that I declined to author the 14 when the
|
||
arithmetic was available and tempting. The ramp+hold decomposition I proposed gave
|
||
20–24 units and fit their old ~0.4 s at the top of the range; I wrote *"this is
|
||
arithmetic that fits, not a measurement"* and left the value where it had been
|
||
measured. **Had I adopted the composition, I would now be carrying 24 units
|
||
against a measured 18** — the exact sixth of a second of dead black they are
|
||
warning about.
|
||
|
||
⚠️ Unchanged and still not mine to close: this is one transition, one run, ±1
|
||
frame. I am not adjusting 9, and there is nothing here that would justify it.
|
||
|
||
## 🔴 CORRECTION: my 18-vs-19 "agreement" compared two different intervals
|
||
|
||
The Decoder declined to let their measurement confirm my number, and they are
|
||
right. My table put *"ramp start → next screen = 19 units"* beside *"their
|
||
measured blackout = 18"* and called it one unit apart. **Those are not the same
|
||
interval.** Theirs runs content-start → fully-black; mine runs ramp-start → next
|
||
screen. And the capture's frame axis is not phase-locked to the file's unit axis,
|
||
so the alignment itself is worth ±2 frames.
|
||
|
||
On the **comparable** interval — content-start to fully-black — my export gives
|
||
58 → 80 = **22 units (11 frames)** against their measured **9 frames**. Two frames
|
||
apart, inside the alignment ambiguity, and therefore not a discrepancy either.
|
||
|
||
🔴 **And `black_hold_units` is not in that interval at all.** Their measurement
|
||
ends where the hold begins. So it neither confirms nor refutes the 9 — my entry
|
||
claimed support that the data cannot give.
|
||
|
||
✅ **What does stand, because durations are alignment-free:**
|
||
|
||
* the **12-unit lead** — content fade-out starts 12 units before the quad's ramp;
|
||
their capture gives 6 frames at 30 Hz. Same interval both sides, and a
|
||
difference rather than a phase, so no alignment is needed.
|
||
* the **2-unit gap** — content gone at 68, quad at 70; their frames 39 → 40.
|
||
They have withdrawn "overlap" in favour of this.
|
||
|
||
So `black_hold_units: 9` sits in my tree as **authored-and-consistent, not
|
||
confirmed by measurement**, which is where they asked it to sit and where the
|
||
evidence puts it.
|
||
|
||
### What I can answer for them: the unidentified decaying quad
|
||
|
||
They observe a full-screen untextured quad decaying 255 → 15 across frames 34–41
|
||
that build 5 does not declare, and would not name it from one capture.
|
||
|
||
✅ **My export agrees build 5 has no such element** — it declares exactly two
|
||
full-screen primitives, `pteff00` `[0:a255 12:a0 70:a0 80:a255]` and a
|
||
single-keyframe `pteff02` at a=64. Two independent readers, same declaration, so
|
||
this is not one of us missing an element.
|
||
|
||
📌 **Hypothesis, offered as one: it is the *incoming* screen's `pteff00`.** Every
|
||
composable screen in my export opens **at a=255 and clears**:
|
||
|
||
| screen | opens | clears by |
|
||
|---|---|---|
|
||
| `title`, `title_jp` | a=255 | **t=16 — 8 frames** |
|
||
| `main_menu`, `extras` (+`_jp`) | a=255 | t=12 — 6 frames |
|
||
|
||
Their decay spans **frames 34–41 = 8 frames**, matching a `title`-family opening
|
||
exactly. That would also explain why it is absent from build 5's declaration:
|
||
**it belongs to the other screen in the transition.** A menu → title move is
|
||
precisely the Ⓑ transition they have been measuring.
|
||
|
||
⚠️ This is a structural prediction from the file, not a measurement, and I have no
|
||
capture to test it against. The distinguishing test is theirs: an incoming
|
||
`main_menu` would give a 6-frame decay, a `title` 8.
|
||
|
||
## `check-all` passes — after an hour-long hang that was the suite's own fault
|
||
|
||
✅ **Every asserting check passes**: format-validator, modding-rules,
|
||
capture-controls, menu-audio, decisions-index, refuted-claims. Oracle captures
|
||
report `main_menu` 0.06 %, `extras` 0.19 %, `main_menu_options` 0.15 %, `title`
|
||
0.21 %, `title_plate` **0.00 %**, `title_band` 0.35 %, both splashes 0.01 %.
|
||
`title_jp` reads `no capture` — the row is wired and waits for their branch.
|
||
`verify-screen` reports 2 DIFFERS, allowed for its stated reason.
|
||
|
||
⚠️ This is the first end-to-end pass I have actually seen. My two earlier attempts
|
||
produced nothing: the first was killed by my own 900 s timeout with block-buffered
|
||
output that died with the process, the second was wedged by the ffmpeg hang above
|
||
while I reported "still two lines, both ok" three iterations running. **I was
|
||
treating an absence of output as patience.**
|
||
|
||
## Ⓐ and Ⓑ are not the same shape, and my `black_hold` treats them as if they were
|
||
|
||
They ran the discriminating test — with the prediction written down first — and it
|
||
holds: incoming build 4 gives an **8-frame** decay, build 5 a **5-frame** one
|
||
against my predicted 6, direction measured and duration inside ±1.
|
||
|
||
✅ Their clinching tell reproduces from my export independently:
|
||
|
||
| | full-screen primitives |
|
||
|---|---|
|
||
| `main_menu` settled | `pteff00` a=0, `pteff02` a=64 → **[64]** |
|
||
| `title` opening | `pteff00` a=255, `pteff02` a=64 → **[255, 64]** |
|
||
|
||
Composite at the transition: **[64, 255, 64]** — exactly what they measured, and
|
||
no single element produces it.
|
||
|
||
### 🔴 The consequence, which neither of us predicted
|
||
|
||
* **Ⓐ title→menu** is sequential and has a real black interval — ~5 frames,
|
||
~10 units.
|
||
* **Ⓑ menu→title has no black interval at all.** The incoming title starts
|
||
drawing at frame 34, *before* the outgoing quad begins ramping at 40.
|
||
|
||
`boot.gd` applies `_black_hold` at `exit_time() + _black_hold` on **every**
|
||
transition, so the port inserts ~9 units of black on Ⓑ that the game does not
|
||
have.
|
||
|
||
⚠️ **I am not changing it, and the reason is their own warning.** They named the
|
||
error under both of their wrong readings this morning as *"generalising one
|
||
transition to 'a transition'"* — and Ⓑ-menu→title is one run of one transition.
|
||
Suppressing the hold on every cancel path would repeat exactly that. Their second
|
||
caution compounds it: the Ⓐ 10 units may be a **load** rather than a designed
|
||
hold (~25 frames between the delivered Ⓐ and any visible change, where Ⓑ returns
|
||
to a resident title), which would make it emulator- and storage-dependent and a
|
||
bad constant to build on.
|
||
|
||
📌 So the finding is recorded and the divergence is **known and stated** rather
|
||
than fixed: the port's transitions are uniform, the game's are directional, and I
|
||
do not yet have enough transitions measured to say what the rule is.
|
||
|
||
## 🔴 `check-all` excused two failing rows with a reason that is measurably false
|
||
|
||
The suite reported *"2 DIFFERS, allowed: the pin is not on main, so this compares
|
||
two decoder eras."* I have quoted that allowance for several iterations without
|
||
testing it.
|
||
|
||
**Tested.** Built `sylpheed-cli` at `formats-pin-2026-08-30` — the tag the
|
||
exporter is pinned to — and at the workspace HEAD, and rendered the same builds
|
||
through both:
|
||
|
||
| screen | pixels differing between the two eras |
|
||
|---|---|
|
||
| `title` | **0** |
|
||
| `title_jp` | **0** |
|
||
| `main_menu` | **0** |
|
||
|
||
✅ **Byte-identical**, despite **508 lines** of difference in `ui_layout.rs`
|
||
between the two revisions. The decoder eras are not the cause of anything here,
|
||
and the allowance was excusing a real signal with a wrong explanation — the worst
|
||
form, because it makes a genuine disagreement look accounted for.
|
||
|
||
🔴 **A second, independent defect in the same eight lines.** The allowance's expiry
|
||
tested `formats-pin-2026-08-29d` while `crates/sylpheed-export/Cargo.toml` pins
|
||
`formats-pin-2026-08-30`. So it would have expired on a tag this tree does not
|
||
use — silently, in either direction.
|
||
|
||
### What the rows actually are, both already documented elsewhere
|
||
|
||
* **`title`** — the `ptloop` **sweep phase** residual: max 6 / over3 790,
|
||
unchanged across every renderer change since P1.
|
||
* **`title_jp`** — the **`--pose=rest` sparkle handling**. Adjudicated against the
|
||
oracle: the port's *shipped* pose scores r **+0.9994** against the game where
|
||
the reference scores +0.8727, and `--pose=rest` is what this script compares.
|
||
**The port is closer to the game on the row the script calls a disagreement.**
|
||
|
||
### The replacement is a named set, not a count with an excuse
|
||
|
||
A count can only say *how many*; it cannot notice that a **different** screen
|
||
started drifting while the total stayed at two. The allowance now lists `title`
|
||
and `title_jp` by name, and a `DIFFERS` on anything else fails the run.
|
||
|
||
✅ Controlled in both directions, because a guard that cannot fail is not a guard:
|
||
|
||
| log | verdict |
|
||
|---|---|
|
||
| `title`, `title_jp` | passes |
|
||
| `title`, **`main_menu`** | **fails on `main_menu`** |
|
||
| **`extras`** alone | **fails on `extras`** |
|
||
| clean | passes |
|
||
|
||
The pin reminder survives as its own line, and now reads the tag **out of
|
||
`Cargo.toml`** so it cannot drift out of step with the dependency again.
|
||
|
||
📌 The pattern, and it is the third time this session: **an allowance is a claim.**
|
||
This one had been true once — the monorepo merge made the exporter and the
|
||
reference share a decoder, and the tag pin quietly unmade that — and it kept being
|
||
printed long after it stopped describing the tree.
|
||
|
||
## `black_hold_units` 9 → 0, and why not the value that fits best
|
||
|
||
The Decoder measured the black gap on **three** transitions off the running game.
|
||
It is not a constant:
|
||
|
||
| transition | measured gap | my hold of 9 |
|
||
|---|---|---|
|
||
| menu → title | **0 units** | +9 |
|
||
| title → menu | 4 | +5 |
|
||
| EXTRAS → menu | 6 | +3 |
|
||
|
||
🔴 **9 was outside the measured range entirely** — wrong for all three, by up to a
|
||
sixth of a second of black the game does not show. Their recommendation is to
|
||
treat it as unauthored rather than uniform-at-9.
|
||
|
||
⚠️ **I did not take the value that fits best.** A uniform **4** minimises total
|
||
error (6 units against 0's 10). That is fitting three samples, and a constant
|
||
chosen for its residual is exactly what this corpus keeps having to withdraw. **0
|
||
has a reason that is not a fit:** it adds no black the game does not have — the
|
||
same tie-breaker `input_during_transition` already uses in this file — and it is
|
||
measured-*correct* for one real transition. The error becomes a **missing** gap of
|
||
at most 6 units on two transitions rather than an **invented** one of up to 9 on
|
||
all three.
|
||
|
||
✅ The verified boot artifact survives: the end frame is still **0.0009 %**
|
||
differing against the oracle, unchanged within printing precision.
|
||
|
||
📌 And their EXTRAS run vindicates the refusal. "Ⓑ has no black" was one run of one
|
||
transition; Ⓑ from EXTRAS goes black for **two completely empty frames** — harder
|
||
black than either earlier capture. Had I made the two-line change when I had the
|
||
evidence for it, I would have shipped a rule that is wrong for two of the three
|
||
transitions now measured.
|
||
|
||
### The declared final ramp, confirmed from my side
|
||
|
||
They report the outgoing ramp is the declared final ramp **three for three**
|
||
against three different values. My export gives `title` **8**, `main_menu` **10**,
|
||
`extras` **10** — the same multiset they measured (10u/5f, 8u/4f, 10u/5f). ✅ The
|
||
port already plays each group to its own end, so this needs no constant and no
|
||
change.
|
||
|
||
### 🔴 `exit_ramp_units` is dead code carrying the number I refused to author
|
||
|
||
`ScreenView.exit_ramp_units` defaults to **24.0** — the very constant HANDOFF ask
|
||
2 told me to author and that I declined. It synthesises a time for a group's
|
||
*untimed* final keyframe.
|
||
|
||
**There are no untimed keyframes.** 866 across all 16 screens, **0** untimed. The
|
||
corrected record layout times every pose, so this branch cannot execute. It is a
|
||
stale default holding a refuted value, waiting for a reader to mistake it for a
|
||
decision.
|
||
|
||
## "Already up to date" is not evidence that I am current
|
||
|
||
The Decoder found `origin/main` is the **stale** era. Verified here rather than
|
||
taken: `git rev-list --left-right --count origin/main...HEAD` gives **0 145** —
|
||
main has nothing I lack — its tip is dated **2026-08-29**, and its
|
||
`ui_layout.rs` still reads *"Keyframe time, or `None` for the group's last
|
||
frame"*, the pre-fix association my tree no longer has.
|
||
|
||
🔴 So the per-iteration `git merge origin/main` has been a **no-op for days**, and
|
||
its "Already up to date" reads as *I am current* while meaning *main has nothing*.
|
||
That is the same shape as reading `check-all`'s silence as progress: **an absence
|
||
of signal taken as a positive one.**
|
||
|
||
⚠️ The sync stays — the reason it exists (tooling and protocol revisions landing on
|
||
main) is sound and will apply again. What changes is that its output is not
|
||
reassurance. And my `Cargo.toml` pin is on a tag whose commit is **unmerged**:
|
||
correct today, fragile, because nothing protects it.
|
||
|
||
## Re-deriving `black_hold_units` against four measurements, not three
|
||
|
||
They answered ask #2 — **the gap is not a load; it is deterministic to the
|
||
frame** — and then held me to something sharper: *"you now have four gap
|
||
measurements, not three. The multiset changed after you chose 0."*
|
||
|
||
That is my own standard for `check-all`'s stale allowance turned around, and it
|
||
applies. Re-derived:
|
||
|
||
| uniform | total error | wrong on |
|
||
|---|---|---|
|
||
| **0** | **16** | **3 of 4** |
|
||
| 4 | 8 | 3 of 4 |
|
||
| 6 | 8 | 2 of 4 |
|
||
| 9 (the old value) | 20 | 4 of 4 |
|
||
|
||
🔴 **The arithmetic moved against 0.** Choosing it cost 1.7× the best fit on three
|
||
measurements and costs **2.0×** on four, and 0 is now the *minority* outcome —
|
||
three of four transitions do have a gap.
|
||
|
||
✅ **It is still 0, because the reason was never the fit.** 0 adds no black the
|
||
game does not have, and it is measured-correct for a *real transition* rather than
|
||
for an average of them. Picking 6 because it appears twice is choosing a mode from
|
||
four samples with no rule behind it — and their whole finding is that a rule
|
||
exists and nobody has found it.
|
||
|
||
⚠️ **One of my reasons is gone, though, and I am not keeping it quietly.** Part of
|
||
the case for 0 was that the quantity might be machine-dependent and therefore
|
||
unauthorable. It is not: bundle size runs the wrong way (build 4 is 12.3 MB and
|
||
gaps zero frames; build 5 is 7.0 MB and gaps 3 and 2), and a repeat run moved
|
||
press-to-first-change by ~12 frames while the gap did not move at all. **Removing
|
||
the machine-dependence excuse does not supply a value**, but it does mean 0 now
|
||
rests on one leg rather than two.
|
||
|
||
📌 **A tripwire, because "invent nothing" can stop being conservative.** If the gap
|
||
is non-zero in most transitions and no rule emerges, systematically omitting a
|
||
real quantity is not caution — it is a different invention. Revisit at the next
|
||
non-zero measurement or the moment a rule appears. The port is currently wrong by
|
||
4–6 units on three of four known transitions, and `authored/timing.json` now says
|
||
so in the `why` rather than in a number that looks decided.
|
||
|
||
### Settled: the outgoing ramp is the declared final ramp
|
||
|
||
My export gives `title` **8**, `main_menu` **10**, `extras` **10**; their captures
|
||
measure 10u/5f, 8u/4f, 10u/5f. ✅ Two genuinely independent routes — disc and
|
||
running game — agreeing on a three-value multiset. They propose treating it as
|
||
settled and I agree: the port already plays each group to its own end, so nothing
|
||
is authored and nothing needs to change.
|
||
|
||
## 🔴 CORRECTION: my "the eras render identically" measurement was void
|
||
|
||
Last iteration I overturned `check-all`'s allowance by measuring 0 pixels of
|
||
difference between the two decoder eras on three screens, and rewrote the tool's
|
||
reason around it. **The measurement was worthless: the two binaries had the same
|
||
md5.**
|
||
|
||
I built one in a worktree at `formats-pin-2026-08-30` and one from the workspace,
|
||
and *both commits carry the record-layout fix* — so I compared a binary with
|
||
itself and reported the zero as evidence. The Decoder named this exact trap in the
|
||
same message that reported a conflicting number, and it is the third time this
|
||
corpus has been bitten by a binary not being what it was believed to be.
|
||
|
||
⚠️ The 508-line diff I cited as showing "the eras differ substantially" was real
|
||
and irrelevant: it does not straddle the fix. **Line count is not era.**
|
||
|
||
### Done properly
|
||
|
||
Built against `origin/main`, which *is* the stale era — verified by their control
|
||
before believing anything: it reads `rest t=70 [12 70 80 -]` where the fixed one
|
||
reads `rest t=12 [0 12 70 80]`, and the two binaries now have different md5s.
|
||
|
||
| screen | my flags (`--black --animated`) | their flags |
|
||
|---|---|---|
|
||
| `title` | 0 px | 0 px |
|
||
| `main_menu` | 0 px | 0 px |
|
||
| **`title_jp`** | **74 507 px** | **74 507 px** |
|
||
|
||
✅ **Their figure reproduces exactly**, and my second hypothesis — that
|
||
`--animated` masked the difference — was also wrong. The eras *do* change pixels,
|
||
and `title_jp` is one of the bundles where they do.
|
||
|
||
### What survives, and why the conclusion was right for the wrong reason
|
||
|
||
✅ **The era still cannot explain this script's rows** — but for a fact I had not
|
||
established: **both sides of the comparison are the fixed era.** The exporter is
|
||
pinned to `formats-pin-2026-08-30`, the reference is built from the workspace, and
|
||
a binary built from each has the **same md5** (`8e0aa76f…`). There is no era
|
||
mismatch in the harness to explain anything.
|
||
|
||
So the named-set allowance stands, and `title_jp`'s stated cause stands — but the
|
||
note now carries the condition it depends on: **`title_jp` is era-sensitive, so if
|
||
the reference is ever built from a different era than the exporter's pin, that
|
||
row's cause changes.** Check the md5s before trusting it.
|
||
|
||
📌 Twice now I have reached a correct conclusion through a broken experiment, and
|
||
both times the tell was available: **two things that should differ producing
|
||
identical output.** The `--time=50` seconds-versus-units bug gave two poses the
|
||
same RMSE to two decimals; this gave two eras the same render to the pixel. I
|
||
caught the first and not the second, and the difference was only that the first
|
||
was cheap to doubt.
|
||
|
||
## 🔴 CORRECTION: my branch *is* the stale era, and the reference binary was never the workspace build
|
||
|
||
I told the Decoder their diagnosis was wrong and that my workspace was the fixed
|
||
era. **They were right and I was wrong.** `crates/sylpheed-formats/src/ui_layout.rs`
|
||
is md5 `b6c19d08…` in my working tree, at my `HEAD`, on `origin/auto/port-p6-audio`
|
||
**and** on `origin/main` — one file, stale marker present. My tree is clean.
|
||
|
||
### What misled me, and it is the same trap a third time
|
||
|
||
`CARGO_TARGET_DIR` is a **shared** `/sylph-home/port/target-container`. Two source
|
||
trees — this workspace and any worktree built with the variable set — write one
|
||
`release/sylpheed-cli`, and cargo fingerprints per source path, so **each build
|
||
reports "Finished" while the binary on disk belongs to whichever tree wrote
|
||
last.** `cargo build` here returns in 0.15 s and changes nothing.
|
||
|
||
| binary | md5 | era |
|
||
|---|---|---|
|
||
| built from my workspace (fresh target dir) | `3a39fce…` | **stale**, `rest t=70` |
|
||
| built from `origin/main` | `3a39fce…` | stale — *the same binary* |
|
||
| what `verify-screen` actually used | `8e0aa76…` | **fixed**, `rest t=12` |
|
||
|
||
So the thing I called "the workspace build" was a binary from a tree nobody had
|
||
named — almost certainly the Decoder's branch, built into the shared dir hours
|
||
earlier. **This is the hazard `verify-screen`'s own header says the monorepo
|
||
removed.** It did not; the shared target dir reintroduced it by another route.
|
||
|
||
⚠️ **It happened to be the *right* era, which is worse than wrong** — it agreed
|
||
with the exporter's pin by luck, and one successful rebuild would have flipped it
|
||
silently. `title_jp` differs by **74 507 px** between the eras.
|
||
|
||
### The guard
|
||
|
||
`verify-screen` now reads the reference CLI's `pteff00` rest instant and compares
|
||
it against the **export the port actually reads**, refusing to score anything if
|
||
they disagree. Provenance is checked rather than assumed from having run
|
||
`cargo build`.
|
||
|
||
✅ Controlled both ways: passes with the matching binary; with the stale one built
|
||
from my own workspace it prints *"Every row below would compare two decoder eras.
|
||
Refusing."* and exits 2.
|
||
|
||
### 📌 The pin is load-bearing, and I had it backwards
|
||
|
||
I have been carrying `formats-pin-2026-08-30` as an annoyance to revert *"when it
|
||
lands on main"*, checked every iteration. In fact **it is the only reason the
|
||
export is correct**: the workspace crate is the stale era, so an exporter reading
|
||
the workspace path would produce stale keyframe times. The pin is not waiting to
|
||
be removed — it is doing the work.
|
||
|
||
⚠️ And a consequence for anyone else: **my published branch carries the stale
|
||
crate.** Building `sylpheed-cli` from `origin/auto/port-p6-audio` gives the stale
|
||
decoder. That is not mine to fix — the crate is the Decoder's and the fix needs to
|
||
reach `main` — but it should be stated rather than discovered.
|
||
|
||
### Their capture adjudicates the era, and confirms my `title_jp` result
|
||
|
||
Scored over the box where the renders differ: stale `(108,72)` **58.412**, fixed
|
||
`(98,42)` **41.690**. ✅ The fixed era is the one the game shows, and my pin is on
|
||
the correct side. Their metric and mine disagree in method and agree in direction.
|
||
|
||
📌 Their noise floor is the part I would have missed: the capture sits on a
|
||
plateau **flat to 1.2 RMSE across 105 units**, so the 16.7 era margin is ~14× the
|
||
flatness and decisive, while **settle-vs-rest at 1.5 is inside it**. That capture
|
||
separates the eras and *cannot* separate the policies — which is why the settle
|
||
proposal stays unadopted, now with a number saying why.
|
||
|
||
## `exit_ramp_units`: the refuted constant was living in a default
|
||
|
||
`ScreenView.exit_ramp_units` defaulted to **24.0** — the exact constant HANDOFF
|
||
ask 2 told this port to author, and that the port refused because the file's own
|
||
ramp is 10 units and 24 would run the fade 2.4× too long. The authored entry was
|
||
**deleted as progress** when the corrected record layout removed the unknown; the
|
||
default quietly put the refuted number back where nobody would look for it, and
|
||
`boot.gd`'s `timing.get("exit_ramp_units", 24.0)` made the deletion a no-op.
|
||
|
||
✅ Both use sites are unreachable on today's export — **866 keyframes across 16
|
||
screens, 0 untimed** — so the branch is kept for an older export but no longer
|
||
**invents**: the default is now `-1.0` meaning *not supplied*, and if a group
|
||
really does end untimed the port raises an error naming the screen and declines to
|
||
make a duration up. Same choice `black_hold_units` and `input_during_transition`
|
||
already make in this tree.
|
||
|
||
### 🔴 My first verification was confounded, and it accused the change
|
||
|
||
Before/after renders of four screens: `title` and `press_start` byte-identical,
|
||
**`main_menu` 641 941 px changed and `extras` 226 009** — 70 % of the frame, on a
|
||
change that raised no error and whose branch cannot execute.
|
||
|
||
The cause was not the edit. **`--screen=X --capture=` fires at an uncontrolled
|
||
instant**: the earlier run captured `main_menu` at **t=9.00**, the later one at
|
||
**t=8.00**. One keyframe unit apart, mid-build-in, is most of the picture. Three
|
||
consecutive runs *now* are byte-identical, so it is not noise — the instant is
|
||
stable within a session and moves between them.
|
||
|
||
✅ Re-run with the instant pinned (`--time=1.0`), old code against new:
|
||
**byte-identical on all four screens.** The change is a no-op, as the keyframe
|
||
census said it must be.
|
||
|
||
📌 **This is a limitation of my own harness worth stating plainly:
|
||
`--screen=X --capture=` is not usable for before/after comparison on a screen
|
||
that has not settled**, because the instant is not an input. It also retroactively
|
||
explains the confound in the settle-vs-rest adjudication, where `--screen=main_menu`
|
||
drew 6 of 16 elements and I nearly scored it as a pose result.
|
||
|
||
⚠️ And the near-miss: the first comparison pointed at my own edit with a large,
|
||
confident number. Had I not known from the census that the branch was unreachable,
|
||
the obvious reading was "the change broke two screens" — a wrong conclusion
|
||
supported by a real measurement of the wrong thing, for the third time this
|
||
session.
|
||
|
||
### Correcting my own overstatement
|
||
|
||
Last iteration I said the era guard "closes that for `verify-screen` only, not for
|
||
the other tools that call the CLI". ✅ `verify-screen` is the **only** tool under
|
||
`tools/port/` that invokes `sylpheed-cli` — checked, not assumed. The guard covers
|
||
every caller there is.
|
||
|
||
## Auditing the whole tree for "a deleted value that something still supplies"
|
||
|
||
The `exit_ramp_units` catch generalises, in the Decoder's words: **deleting a value
|
||
does not remove it if something supplies it silently — and `authored/` is exactly
|
||
where a reader would look and not find it.** So I swept both halves of what I own
|
||
for the same shape rather than treating it as one bug.
|
||
|
||
### GDScript: every keyed lookup with a fallback
|
||
|
||
| key | default | in `authored/`? |
|
||
|---|---|---|
|
||
| `period_units`, `record_element` | `0.0`, `""` | ✅ |
|
||
| `black_hold_units` | `0.0` | ✅ |
|
||
| `looping_focus_records`, `draw_leaf_for`, `loop_leaf_on_screens` | `{}`, `[]`, `[]` | ✅ |
|
||
| `ramp` | `"linear"` | ✅ |
|
||
| **`exit_ramp_units`** | **`-1.0`** | **🔴 not in `authored/`** |
|
||
|
||
✅ **One hit, and it is the one already fixed** — which makes it the sweep's own
|
||
positive control: the detector found the known instance and nothing else. Its
|
||
default is now `-1.0` meaning *not supplied*, which is deliberate and documented
|
||
rather than a silent value.
|
||
|
||
The other numeric fallbacks in `screen_view.gd` are identity or sentinel —
|
||
`rotation_deg → 0` is *no rotation*, `period_units → 0.0` is *no loop* and is
|
||
guarded by `> 0.0`, `index → -1` is a sentinel. None of them invents a quantity.
|
||
|
||
### The exporter: `serde(default)` does the same thing in Rust
|
||
|
||
14 sites. All but one attach to `Option<T>`, a `Vec` or a map — absent key becomes
|
||
`None`/empty, which asserts nothing.
|
||
|
||
⚠️ **My classifier produced a false positive and I nearly wrote it up.** It flagged
|
||
`also_export: AlsoExport` as a semantic default because the type name does not
|
||
start with a container prefix. `AlsoExport` is a **type alias for a `BTreeMap`**;
|
||
its default is an empty map. Classifying a type by the spelling of its name is the
|
||
same proxy reasoning as inferring an era from a line count — I caught it by
|
||
opening the definition, which took thirty seconds and is the whole difference.
|
||
|
||
### Result
|
||
|
||
✅ **Nothing new.** One instance across the port and the exporter, already fixed.
|
||
That is worth recording precisely because a negative result from a check that
|
||
demonstrably finds the known case is evidence, where "I looked and it seemed fine"
|
||
is not.
|
||
|
||
## Counting the fallbacks instead of inspecting them — and one I had misjudged
|
||
|
||
The Decoder sharpened my sweep in a way that invalidates part of how I ran it:
|
||
**an in-range fallback cannot be caught by inspecting output, because the output
|
||
looks exactly like the true case. The only way to know is to count how often it
|
||
fires.** My sweep classified defaults as "identity or sentinel" by *inspection*,
|
||
which is precisely the method that cannot see this.
|
||
|
||
Counted:
|
||
|
||
| fallback | fires |
|
||
|---|---|
|
||
| `rotation_deg → 0` (0 is a legitimate rotation) | **0 of 866 keyframes, 0 of 178 rest poses** |
|
||
| `ramp → "linear"` | key present in `authored/timing.json` |
|
||
|
||
✅ So rotation is **read, not invented** — the same conclusion they reached for
|
||
design size, and reachable only by counting.
|
||
|
||
### 🔴 The count exposed one I had waved through
|
||
|
||
`black_hold_units` defaults to `0.0` **and its authored value is 0**. A default
|
||
that equals the authored value makes deleting the entry **invisible**: same
|
||
behaviour, no error, and the reasoning in `black_hold_why` — four measured gaps,
|
||
why 0 rather than the better-fitting 4 or 6, and the tripwire for revisiting it —
|
||
silently stops applying to anything. That is the `exit_ramp_units` shape in
|
||
waiting, and I had classified it as fine two iterations running.
|
||
|
||
✅ Fixed the same way: the fallback is now `-1.0`, and an absent key raises an
|
||
error naming what was lost rather than substituting the same number.
|
||
|
||
**The control is the demonstration:**
|
||
|
||
| | errors | render |
|
||
|---|---|---|
|
||
| key present | 0 | — |
|
||
| key **deleted** | **1** | **byte-identical** |
|
||
|
||
📌 The render being identical either way *is* the finding. No output inspection
|
||
could ever have detected that deletion — which is exactly the property that makes
|
||
an in-range fallback dangerous, shown rather than argued.
|
||
|
||
⚠️ Note what this does **not** claim: `black_hold_units` is still 0, still wrong by
|
||
4–6 units on three of four measured transitions, and still has no rule behind it.
|
||
What changed is only that its *absence* is now audible.
|
||
|
||
## The oracle harness was nondeterministic, and I quoted its numbers for a dozen iterations
|
||
|
||
Reviewing my own logs: `verify-capture`'s `main_menu` row reads **13.30 / 13.27 /
|
||
13.25 / 13.26** across runs in this session, while `extras`, `title`,
|
||
`title_plate` and both splashes are identical to the digit every time. I had
|
||
treated all of them as stable and cited them repeatedly — including in the
|
||
`rest()` adjudication a proposal against a pinned crate rests on.
|
||
|
||
### Cause: the one thing on a settled screen that is *supposed* to keep moving
|
||
|
||
The focus ring spins on `time_units` **raw**, not the pose clamped by `holding` —
|
||
deliberately, and correctly: *"a spinning ring is the one thing on the settled main
|
||
menu that keeps moving, and the whole point of the finding is that it does not
|
||
stop."* So its angle at the moment of capture is set by the wall clock. `extras`
|
||
is stable because nothing there spins.
|
||
|
||
⚠️ `--loop-phase` already existed and did **not** cover this. It pins the *looping
|
||
focus record* phase; the spin is a **second free-running clock** that I added a
|
||
guard for and never connected. Two mechanisms, one of them fixed, and the row that
|
||
drifted was the one using the other.
|
||
|
||
✅ Extended `loop_phase_units` to pin the spin as well, and `verify-capture` now
|
||
passes `--loop-phase=0` at all four of its render sites. Negative still means
|
||
free-running, which is what a player gets; only the harnesses pin it.
|
||
|
||
### The control, because three passing runs would not have been evidence
|
||
|
||
The drift was **intermittent** — three unpinned runs gave 13.25, 13.26, 13.26. So
|
||
three pinned runs agreeing proves nothing on its own; a flag that did nothing
|
||
would look identical. The test that separates them is whether the pin **changes**
|
||
the answer:
|
||
|
||
| phase | RMSE |
|
||
|---|---|
|
||
| 0 | 13.2583 |
|
||
| 30 | **13.1991** |
|
||
| 60 | 13.2637 |
|
||
| 90 | 13.2588 |
|
||
|
||
✅ Live. The spread is **0.065**, which is the size of the drift I observed — so
|
||
the spin is the whole of it. Three pinned runs then return 13.26 exactly.
|
||
|
||
📌 **A non-finding worth stating so nobody mines it later.** Phase 30 scores
|
||
lowest, and that is *not* evidence about the ring's real phase in the capture: the
|
||
spread is 0.065 against a gamma floor of ~13.2, roughly 200× smaller. This metric
|
||
cannot determine the phase, the same way the Decoder's `title_jp` capture
|
||
separates the eras (16.7) but cannot separate the pose policies (1.5) against its
|
||
own 1.2 flatness. **A margin only means something against the noise it sits on.**
|
||
|
||
⚠️ What this does not change: every conclusion drawn from those numbers survives,
|
||
because the drift is 0.065 RMSE and the smallest margin any of them turned on was
|
||
0.14 % differing area. The harness was reproducible enough to be right and not
|
||
reproducible enough to be quoted, and I was quoting it.
|
||
|
||
## Answering "an unenumerated set" — don't enumerate, test
|
||
|
||
The Decoder's closing point on the drift: *"that's not a missing guard, it's an
|
||
unenumerated set, and I don't think either of us has a way to enumerate everything
|
||
on this screen that moves on its own."* You do not need to. You need a test that
|
||
**fails when the set is non-empty**.
|
||
|
||
### The enumeration is possible on my side, and found a third
|
||
|
||
Every use of the free-running clock in `screen_view.gd`:
|
||
|
||
| site | pinned by |
|
||
|---|---|
|
||
| looping focus record | `--loop-phase` ✅ |
|
||
| the spin | `--loop-phase` ✅ *(added last iteration)* |
|
||
| **the leaf** — sets `holding = false` explicitly and reads `time_units` | `--leaf-time`, or `--time` |
|
||
| `pose_at(element, time_units)` | clamped by `holding` — settles, not free-running |
|
||
|
||
🔴 **A third clock**, which I would not have found by waiting for a row to drift.
|
||
It only bites on `loop_leaf_on_screens` — `["title"]`.
|
||
|
||
### The test, and the scale that makes it mean something
|
||
|
||
Render twice with the known pins, at different wall-clock moments, and compare
|
||
**frames** — not a statistic.
|
||
|
||
* `--screen` + `--time` + `--loop-phase`, all 16 screens: **byte-identical.**
|
||
(`--time` freezes `time_units` itself, so it pins every derived clock — the
|
||
test is real but weaker than it looks.)
|
||
* `--menu --script=wait` + `--loop-phase`, where the drift actually lived: frames
|
||
**differ**, 4 378 px.
|
||
|
||
⚠️ That difference is **not motion**:
|
||
|
||
| | max per-channel | mean |
|
||
|---|---|---|
|
||
| two pinned runs | **2.86** | 0.0025 |
|
||
| a genuinely moving element (spin, phase 0 vs 30) | **158.4** | 0.037 |
|
||
|
||
✅ 55× apart. Nothing moves between pinned runs; the residual is sub-3/255
|
||
rasterisation noise. **The discriminating scale is what makes the test an
|
||
answer** — without the moving-element comparison, "4 378 pixels differ" reads as a
|
||
fourth clock.
|
||
|
||
### 🔴 And the reason I nearly missed it: my verification was too coarse to see what it checked
|
||
|
||
Last iteration I reported *"three pinned runs return 13.26 exactly"* and called the
|
||
harness reproducible. `verify-capture` prints RMSE to **two decimals**, and the
|
||
residual is **0.0565** — below its own resolution. The frames were never identical;
|
||
the statistic could not tell.
|
||
|
||
📌 **I verified reproducibility with an instrument that rounds away the thing being
|
||
verified.** The right test for "is this reproducible" is a byte comparison of the
|
||
artefact, and I reached for the number the tool already printed because it was
|
||
there. Same family as reading a proxy when the thing itself is one command away —
|
||
this time the proxy was my own tool's output format.
|
||
|
||
⚠️ Conclusion unchanged: 2.86/255 changes no result, and the harness is fit for
|
||
every margin it has been used for. What was wrong was the claim's basis, not the
|
||
claim.
|
||
|
||
## 🔴 The third clock was in my own list, and I did not wire it
|
||
|
||
Last iteration I enumerated three free-running clocks, said the leaf was pinned
|
||
only by `--leaf-time`, then tested reproducibility **without passing
|
||
`--leaf-time`** and concluded *"nothing free-runs on the menu path"*. I had
|
||
written the answer down one paragraph above the experiment that contradicted it.
|
||
|
||
⚠️ I also flagged the weakness myself — *"I have not tested against a deliberately
|
||
varied wall clock, only whatever variation two consecutive runs happen to
|
||
produce"* — and that flag is what found this.
|
||
|
||
### Deliberate variation finds it immediately
|
||
|
||
`--menu=main_menu --script=wait:N --loop-phase=0`, varying N so the capture lands
|
||
at genuinely different clock positions (t = **96 units** at N=0.5, **369** at
|
||
N=5.0):
|
||
|
||
| | max per-channel |
|
||
|---|---|
|
||
| wait 0.5 vs 5.0, spin pinned only | **91.19** |
|
||
| …with `--leaf-time=0` added | **0** |
|
||
|
||
✅ Byte-identical. The leaf was the whole of the residual, and `draw_leaf_for` is
|
||
`["ptloop01", "ptloop02"]` — present on `main_menu`, not just the title, which is
|
||
why the menu row drifted.
|
||
|
||
🔴 **`verify-capture` passed `--loop-phase=0` and not `--leaf-time=0`.** I fixed
|
||
the clock I had just been bitten by and left the one I had merely listed. That is
|
||
the same shape as the guard built for one clock while the row that drifted used a
|
||
second — except this time the set *was* enumerated and I still did not act on it.
|
||
**Enumeration without follow-through fails exactly like no enumeration.**
|
||
|
||
### Now pinned, and verified by frame rather than by statistic
|
||
|
||
`verify-capture` pins both at all six render sites. `main_menu` returns **13.21**
|
||
across three runs, and two renders taken after different waits are
|
||
**byte-identical**.
|
||
|
||
⚠️ **The number moved, 13.26 → 13.21, and that is not an accuracy improvement.**
|
||
Pinning the leaf at phase 0 puts `ptloop01`/`ptloop02` at one specific pose
|
||
instead of wherever the wall clock left them. It is a *different configuration*,
|
||
now a reproducible one. Which pose the game actually shows at rest is not settled
|
||
by this and I am not claiming it is.
|
||
|
||
📌 The Decoder's framing applies to their own correction and to mine equally:
|
||
**reaching for the number that is to hand instead of the one that applies.** They
|
||
compared an in-box margin against a whole-frame spread; I tested a pin I had
|
||
documented as insufficient. Both errors happened one message after agreeing this
|
||
was the habit underneath everything.
|
||
|
||
## 🔴 WITHDRAWN — the leaf-phase minimum measures the capture, not the game
|
||
##
|
||
## *(This heading read: "The leaf phase was an arbitrary choice; the capture turns
|
||
## out to determine it." [refuted] Refuted 97 lines below by the replication on `title`,
|
||
## which minimises at a different phase for the same object. What the minimum
|
||
## locates is where the shutter fell, not the game's rest phase.)*
|
||
|
||
Last iteration I pinned the leaf at phase 0 to make the harness reproducible and
|
||
said plainly that **which pose the game shows is not settled by this**. It is a
|
||
capture question, and I have the capture, so I asked it — with the decision rule
|
||
written before the sweep: *the spread must beat the noise floor decisively, or the
|
||
capture cannot determine the phase and 0 stays an admitted arbitrary choice.*
|
||
|
||
| leaf phase | RMSE vs the oracle |
|
||
|---|---|
|
||
| **0 units** | **13.2059** |
|
||
| 15 | 13.2059 |
|
||
| 30–105 | 13.2062 → 13.5889 |
|
||
| 120 | 13.7044 |
|
||
| 240 / 360 / 480 | 13.6486 / 14.0826 / 13.9055 |
|
||
| **600** | **13.2065** |
|
||
|
||
✅ **Phase 0 is the global minimum**, by **0.44–0.88 RMSE** against a run-to-run
|
||
floor of **0.0565** — 8–15×, which meets the rule. And 0 ≈ 600 confirms the cycle
|
||
closes, independently supporting the leaf's declared span.
|
||
|
||
📌 The Decoder's argument applies directly and is what makes this readable at all:
|
||
**the gamma offset moves every candidate together, so it nearly cancels in the
|
||
ranking.** Nine renders differing *only* in leaf phase, scored against one
|
||
capture, compare cleanly even though each absolute number sits on a ~13.2 floor
|
||
nobody can remove.
|
||
|
||
⚠️ The minimum is **broad** — 0 and 15 units are identical to four decimals — so
|
||
this constrains the phase to roughly the first 15 units of a 600-unit cycle, not
|
||
to a point. And it is one capture of one screen. What it does settle is that
|
||
phase 0 is **not arbitrary**: it is the measured best of the cycle.
|
||
|
||
### 🔴 And the sweep that nearly said the opposite: seconds versus units, again
|
||
|
||
My first sweep ran `--leaf-time` over 0…500 and returned **13.2059 for all six** —
|
||
the identical-output tell. `--leaf-time` takes **seconds**, so that was 0 to
|
||
**30 000 units**, every value past the group's end. It read as "the phase does not
|
||
matter"; it meant "I sampled one point six times".
|
||
|
||
**Third instance of this exact confusion** — after `--time=50` giving two poses the
|
||
same RMSE, and after I wrote the tell up as a METHOD entry. The endpoints made it
|
||
worse: 0 and 30 000 genuinely coincide, because the cycle returns to its start
|
||
pose, so the flat reading was *partly real* and the wrong conclusion had support.
|
||
|
||
⚠️ It also briefly made me doubt a correct earlier result. The `--leaf-time=0`
|
||
pinning (max 89.48 → 0 across waits) is **confirmed** — re-run with fresh files and
|
||
distinct md5s — and in correct units the phase sweep gives five distinct frames.
|
||
The flag was never the problem.
|
||
|
||
## Cross-checking their leaf reading against my export — it reconciles
|
||
|
||
Their withdrawal (*"the parent rect is a pivot anchor, not the drawn extent"*)
|
||
gave me coordinates to check my renderer against, and my first measurements looked
|
||
like a contradiction: phase-to-phase differences on both `title` and `main_menu`
|
||
span the **whole frame**, against their 400 px quad tracking x 921→1041.
|
||
|
||
Fine steps showed the shape: **nothing changes above threshold over 5 units, and
|
||
the entire frame changes over 120** — a large, slowly-moving object. At
|
||
`--time=4.0` the screen is frozen, so all of that is the leaf.
|
||
|
||
✅ Reading the leaf record out of my own export resolves it:
|
||
|
||
| | `ptloop01` | `ptloop02` |
|
||
|---|---|---|
|
||
| leaf element | `pteff03` | `pteff03a` |
|
||
| loop span | **600** | **720** |
|
||
| x track | **−639 … 1521** | **−839 … 1721** |
|
||
| scale | **(100, 600)** | (100, 800) |
|
||
|
||
⚠️ *The first version of this table said `pteff04` and gave both leaves the same
|
||
x track. Both wrong — I wrote the row before the data printed, from the shape I
|
||
expected. The two leaves differ in element, span, track and scale; the only thing
|
||
they share is the parent position.*
|
||
|
||
* ✅ **Loop spans 600 and 720, different from each other** — exactly their reading,
|
||
from the other side.
|
||
* ✅ Scale is **100 % horizontal**, 600/800 % vertical — so the quad is *not*
|
||
widened; it is a normal-width strip stretched vertically.
|
||
* 📌 **The x track runs −639 to 1521**, right across and beyond the 1280 frame. Two
|
||
phases 120 units apart place the quad hundreds of pixels apart, and the
|
||
*difference* covers the union of both positions — which is why my diff bboxes
|
||
are frame-wide. **No contradiction.**
|
||
|
||
Their x 921→1041 is a segment of that track, not its extent. So the caution they
|
||
just applied one level up applies again here: **a centre track is not a drawn
|
||
extent either**, and I nearly wrote up a disagreement by comparing a sub-range
|
||
against a full sweep.
|
||
|
||
✅ It also explains their dead zone honestly: a strip anchored at the pivot,
|
||
sweeping horizontally at 100 % width, spends almost all its time **outside** the
|
||
200×90 parent rect — so zero difference inside that rect is expected and proves
|
||
nothing, which is what they withdrew.
|
||
|
||
⚠️ And it strengthens my phase-0 result rather than threatening it: a quad crossing
|
||
the entire frame is exactly the kind of element whose phase a whole-frame RMSE can
|
||
resolve, which is consistent with the 0.5 spread I measured against a 0.0565 floor.
|
||
|
||
## Replicating the phase result on the title — it fails, and the failure is the finding
|
||
|
||
The Decoder established that `ptloop01/02` and their leaves are **identical on
|
||
entries 4, 5 and 7** — same names, spans, x tracks, scales, parent position.
|
||
✅ Confirmed against my export, all three screens, every field. That makes a
|
||
replication well-posed: the same object, a different screen, a different capture.
|
||
|
||
| leaf phase | `main_menu` vs its capture | `title` vs its capture |
|
||
|---|---|---|
|
||
| 0 units | **13.2059** ← min | 14.1604 |
|
||
| 60 | — | 14.0910 |
|
||
| 120 | 13.7044 | 14.2571 |
|
||
| **240** | 13.6486 | **13.9417** ← min |
|
||
| 360 | 14.0826 | 14.5409 |
|
||
| 480 | 13.9055 | 14.9667 |
|
||
| 600 | 13.2065 | 14.1611 |
|
||
|
||
🔴 **Different minima for the same object.** Spread 1.025 on the title, 18× the
|
||
0.0565 floor, so both sweeps are decisive and they decisively disagree.
|
||
|
||
### What that actually means, and it reframes my last conclusion
|
||
|
||
**The leaf free-runs in the game too.** Each capture froze it wherever it happened
|
||
to be. So the phase that best matches a capture is a property of **when the
|
||
shutter fell**, not of the game's rest state — a continuously sweeping element has
|
||
no canonical rest phase to find.
|
||
|
||
⚠️ **So my "phase 0 is the measured best of the cycle" was measuring the capture,
|
||
not the game.** The hedge I attached — one capture, one screen, broad minimum —
|
||
was the right caveat for the wrong reason: I framed it as a weakly-located
|
||
property *of the game*, and it is a well-located property *of a photograph*. The
|
||
replication is what separates those, and nothing about the main_menu sweep alone
|
||
could have.
|
||
|
||
### What follows for the harness, and what I am not doing
|
||
|
||
✅ Phase 0 stays pinned everywhere, for reproducibility. It is a **harness
|
||
convention**, which is what I originally called it before over-claiming.
|
||
|
||
🔴 **I am not tuning the pin per screen.** Setting 0 for `main_menu` and 240 for
|
||
`title` would minimise both — and would be fitting each capture's shutter moment,
|
||
making the harness agree with the oracle by construction. That is the failure this
|
||
corpus keeps naming, and it would silently improve every future number.
|
||
|
||
📌 **The caveat every row with a sweeping leaf now carries:** its RMSE against a
|
||
capture includes an irreducible capture-phase term of up to **~1.0 RMSE**, larger
|
||
than most margins I have quoted from those rows. `title` at 14.16 is not 0.22
|
||
"worse" than it could be — 13.94 is not more correct, it is differently posed.
|
||
|
||
## Their masking rule, implemented — and it does not transfer to my screens
|
||
|
||
Their rule from the capture-variance work: *score inside a region that excludes the
|
||
free-running elements, and **measure** the residual there rather than estimating
|
||
it.* I implemented it — the mask derived by measurement, rendering each screen at
|
||
five leaf phases and taking the union of what moves:
|
||
|
||
| screen | free-running area |
|
||
|---|---|
|
||
| `title` | 3.68 % |
|
||
| `extras` | 1.63 % |
|
||
| `main_menu` | 1.32 % |
|
||
| both splashes | **0.00 %** |
|
||
|
||
🔴 **The control fails.** Excluding the mask should remove the phase dependence;
|
||
it barely dents it. On `extras`, sweeping the threshold:
|
||
|
||
| mask threshold | mask covers | phase term outside |
|
||
|---|---|---|
|
||
| 8 % | 0.7 % | 1.7343 |
|
||
| 4 % | 1.6 % | 1.6393 |
|
||
| 2 % | 5.5 % | 1.5254 |
|
||
| **1 %** | **9.3 %** | **1.4569** |
|
||
|
||
Masking **9.3 %** of the frame removes **~16 %** of the term. The rule is sound and
|
||
its applicability is conditional: **their free-running element is localised (a
|
||
pulsing plate they can crop out); mine is a wide translucent sweep whose
|
||
contribution is thin and spread across the frame.** You cannot cut it out without
|
||
cutting out the picture.
|
||
|
||
### ⚠️ And my ~1.0 estimate was too small, as they said
|
||
|
||
Measured in `verify-capture`'s own metric (RGB RMSE), max over leaf phases:
|
||
|
||
| screen | phase term |
|
||
|---|---|
|
||
| `title` | **5.56** |
|
||
| `main_menu` | 3.78 |
|
||
| `extras` | 3.73 |
|
||
| `publisher_logo`, `developer_logos` | **0.00** |
|
||
|
||
My earlier ~1.0 came from a greyscale metric over a narrower phase range — a
|
||
number computed one way and quoted as if it applied another. Theirs is 4.566
|
||
whole-frame on the JP title; mine land at 3.7–5.6 on the same footing.
|
||
|
||
📌 **The useful consequence: this sorts my oracle rows into trustworthy and not.**
|
||
The splashes carry **no** free-running element, so `publisher_logo` 2.17 and
|
||
`developer_logos` 3.05 are absolute numbers that mean what they say. `title` at
|
||
14.16 carries **±5.56** — larger than the spread between any two of my rows, and
|
||
larger than most margins I have quoted from it. Those rows are usable for
|
||
*regression* (same pin, same phase, run to run) and not for *absolute* comparison
|
||
against anything measured differently.
|
||
|
||
⚠️ Recorded as a limit, not fixed. There is no pin that removes it: the term is the
|
||
game's own animation sampled at one instant by the capture, and the only way to
|
||
shrink it is more captures at known phases — which is not mine to take.
|
||
|
||
## Their "the game may not draw these leaves" hypothesis — my curves say *sometimes*
|
||
|
||
They challenged two things: my compactness precondition, and my claim that the
|
||
leaf free-runs in the game. ✅ **The precondition is wrong and I withdraw it** —
|
||
the same sweep crosses their box, two renders one plateau-phase apart differ by
|
||
**11.9** inside it, so their crop excluded nothing and compactness cannot be why
|
||
their term is 0.32.
|
||
|
||
Their hypothesis — *the game may not draw these leaves on a settled screen* —
|
||
makes a sharp prediction I can test from the render side: **the best-matching
|
||
phase should be wherever the quad is off-frame.**
|
||
|
||
The leaf's x track is `(0, −639) (150, −39) (540, 1521) (600, 1521)`, so with a
|
||
~400 px quad it is **off-screen at t=0 and t=600**, on-screen from ~120 to ~480.
|
||
|
||
| phase | quad | `main_menu` RMSE | `title` RMSE |
|
||
|---|---|---|---|
|
||
| **0** | **off** | **13.2059** ← min | 14.1604 |
|
||
| 60 | off | 13.2544 | 14.0910 |
|
||
| 120 | ON | 13.7044 | 14.2571 |
|
||
| **240** | ON | 13.6486 | **13.9417** ← min |
|
||
| 360 | ON | 14.0826 | 14.5409 |
|
||
| 480 | ON | 13.9055 | 14.9667 |
|
||
| **600** | **off** | **13.2065** ← min | 14.1611 |
|
||
|
||
🟢 **On `main_menu` the two minima are exactly the two off-screen phases**, and
|
||
every on-screen phase is worse. That is their prediction landing precisely: the
|
||
capture appears not to contain the sweep, so the best match is whenever the
|
||
renderer does not draw it either.
|
||
|
||
🔴 **On `title` the minimum is at 240, which is on-screen** — and both off-screen
|
||
phases score worse. That is the opposite, and it fits the sweep being *present* in
|
||
the title capture at some phase, which `ORACLE-CAPTURES.md` already says of these
|
||
two elements ("move continuously").
|
||
|
||
### What I withdraw, and what this leaves
|
||
|
||
⚠️ **"The leaf free-runs in the game too" is withdrawn as established.** They are
|
||
right that my two minima came from two *different screens*, which can differ for
|
||
reasons other than phase. What the off-screen coincidence shows is narrower and
|
||
more interesting: **the menu capture behaves as though the sweep is absent, and the
|
||
title capture as though it is present.**
|
||
|
||
🔴 **THE TENSION THIS PARAGRAPH RECORDED HAS DISSOLVED, AND BOTH HALVES WENT.**
|
||
*(It read: "It also does not resolve their JP-title tension — they see 0.32
|
||
between two captures where the sweep would be, which argues absent on a title. My
|
||
EN title curve argues present. Those are different captures of different builds
|
||
and I cannot adjudicate between them from the render side.")*
|
||
|
||
**Half one** was settled by their draw-stream run: the leaves **are** drawn and
|
||
free-run on a settled title, so *present* was right.
|
||
|
||
**Half two is now retracted at source.** They have withdrawn **0.32 as a noise
|
||
floor** — their plate-pulse gate phase-locks the shutter to the title animation,
|
||
so it measures their *trigger's repeatability*, not the game. Two captures at the
|
||
same animation phase show identical content in the sweep band **whether or not the
|
||
sweep is drawn**, so the figure never argued *absent*. Their replacement
|
||
title-capture noise figure is **11.9**.
|
||
|
||
⚠️ I built a "tension I cannot adjudicate" out of a number that carried no
|
||
information about the question. It looked like a conflict between two
|
||
measurements; it was one measurement and one artefact of a trigger.
|
||
|
||
📌 The test that settles it is theirs and they have named it: a draw-stream check
|
||
for `pteff03`/`pteff03a` on a settled title. **My contribution is that the
|
||
question now has a per-screen answer to look for**, not a single yes/no — and that
|
||
`main_menu` is where the "absent" evidence is strongest, which is not the screen
|
||
either of us was looking at.
|
||
|
||
## Using the clean splash rows to measure the tone curve — and repeating a documented mistake
|
||
|
||
The Decoder's advice was to act on the rows that mean what they say. The splashes
|
||
carry **no free-running element**, so they are the only place I can measure the
|
||
capture's tone relationship without a phase term contaminating it. I swept gamma
|
||
on them:
|
||
|
||
| γ (ImageMagick) | 0.70 | 0.80 | **0.85** | 0.90 | 1.00 |
|
||
|---|---|---|---|---|---|
|
||
| `publisher_logo` | 2.25 | 2.09 | **2.06** | 2.07 | 2.17 |
|
||
| `developer_logos` | 3.44 | 2.14 | **1.92** | 2.08 | 3.05 |
|
||
| `title` | 12.95 | 8.52 | **8.22** | 9.40 | 14.16 |
|
||
|
||
A clean minimum at 0.85 on all three — **γ ≈ 1.18** in the corpus's convention,
|
||
against HANDOFF's **1.34–1.49**. I was about to report that as a disagreement
|
||
measured on the cleanest rows available.
|
||
|
||
### 🔴 `verify-capture`'s own header already answers it, with the data
|
||
|
||
*"THE TONE RELATIONSHIP IS REPORTED AS A CURVE, NOT AS A BEST EXPONENT, and two
|
||
earlier versions of this tool reported an exponent and were wrong twice."* And
|
||
below it, the binned table:
|
||
|
||
| render level | 8 | 16 | 24 | 32 | 40 | 48 |
|
||
|---|---|---|---|---|---|---|
|
||
| implied γ | 1.20 | 1.26 | 1.18 | 1.10 | 1.03 | **0.93** |
|
||
| pixels | 183 026 | 227 630 | 100 945 | 87 474 | 86 094 | 85 255 |
|
||
|
||
**There is no single exponent.** γ falls with level and crosses 1.0 by render 48.
|
||
My whole-frame fit recovered **1.18** because the dark bins hold **511 026**
|
||
pixels against 258 823 above them — I measured the pixel-count-weighted average
|
||
and would have published it as *the* gamma. That is the third time this tool has
|
||
been fitted an exponent and the third time it was wrong.
|
||
|
||
✅ The residual at the best gamma is **1.92–2.06** on the clean rows — ~30× the
|
||
0.06 rasterisation floor. A single exponent cannot close it, exactly as the header
|
||
says.
|
||
|
||
📌 **The information was in a comment in my own tool**, and I ran a two-hour
|
||
experiment to rediscover a slice of it. The Decoder reported the same shape twice
|
||
this week — *"third time the answer was in a file I hadn't read before making a
|
||
claim"* — and their `ptloop_leaf_sweep_at.rs` window is the same thing one level
|
||
out. **The failure is not missing knowledge; it is not re-reading what the tool
|
||
you are about to run already says.**
|
||
|
||
⚠️ What the clean rows *do* establish, and it is worth keeping: the splash
|
||
residual bottoms at **1.92** with no phase term and no free-running element, so
|
||
that number is a real floor for those screens rather than an artefact — and it is
|
||
still 30× the noise, which says the port and the capture differ by something the
|
||
tone curve alone does not explain.
|
||
|
||
## Localising the 1.92 splash floor: it is glyph edges, and off them the port is ~1 RMSE from the game
|
||
|
||
The splash rows carry no free-running element, so their residual is the one I can
|
||
chase without a phase term. It is **not tonal** — max **255** with only
|
||
**0.012–0.017 %** of pixels over 8/255. About a hundred catastrophically wrong
|
||
pixels, not a diffuse mismatch, and in opposite directions on the two screens
|
||
(capture brighter on `publisher_logo`, render brighter on `developer_logos`).
|
||
|
||
That is the signature of edge antialiasing, so I tested it against an edge mask
|
||
from the **capture** — with the mask's coverage checked first, because my earlier
|
||
edge attempt on `title` failed exactly by classifying 92 % of the frame as edge:
|
||
|
||
| | edge mask covers | residual **on** edges | residual **off** edges |
|
||
|---|---|---|---|
|
||
| `publisher_logo` | **0.67 %** | 18.30 | **1.42** |
|
||
| `developer_logos` | **1.44 %** | 12.66 | **0.82** |
|
||
|
||
✅ Non-degenerate masks, and a **13–15× concentration** on edges. The 1.92
|
||
whole-frame floor is glyph-edge antialiasing.
|
||
|
||
📌 **Off the edges, the port matches the game at 0.82–1.42 RMSE.** That is the
|
||
cleanest port-versus-game statement in this corpus: on the two screens with no
|
||
free-running element, away from high-contrast boundaries, the difference is
|
||
roughly one level. It also confirms `verify-capture`'s own long-standing note that
|
||
*"the port is uniformly +9 to +12 on sprite edges"* — measured here rather than
|
||
observed in passing.
|
||
|
||
⚠️ Not everything is explained. 0.82–1.42 is still 15–25× the 0.06 rasterisation
|
||
floor. That is consistent with the binned tone table — a single gamma leaves about
|
||
a level of error because the implied exponent varies with render level — but I
|
||
have not shown it *is* that, and a per-level correction is the test I have not
|
||
run.
|
||
|
||
### Contamination check after their withdrawal
|
||
|
||
They withdrew the Ⓐ result (three emulators live at once, one shared pad file, one
|
||
shared display) and flagged their earlier menu probes as suspect for the same
|
||
reason. ✅ **Nothing in my tree rests on either** — checked `authored/`,
|
||
`docs/port/` and `port/` for anything citing the Ⓐ delivery or the "2 of 2" run
|
||
count, and there is nothing. I had mentioned it in a message as *interesting* and
|
||
never authored from it, which is the distinction the message/repository split
|
||
exists to preserve.
|
||
|
||
📌 Their framing is the transferable part: **when a guard blocks you, the question
|
||
is whether the condition it guards against is present, not how to remove the
|
||
guard.** `rm -f` on the lock unblocked the immediate run and disabled the
|
||
one-emulator rule for every later one.
|
||
|
||
## Their draw-stream result checked against my export — three confirmations and one correction
|
||
|
||
Their oracle run (settled EN title, one emulator verified by count) refutes their
|
||
own "the game may not draw these leaves" and confirms my `title` curve from the
|
||
game rather than from a render. Checking it against my export:
|
||
|
||
✅ **Rotation.** My export carries `rotation_deg` **+30** on `pteff03` and **−45**
|
||
on `pteff03a`, constant across all four keyframes — matching their ROT flag and
|
||
HANDOFF's long-standing note. `spin_period_units` returns 0 for these (four
|
||
keyframes, not two), so the spin override does not fire and the port draws the
|
||
declared angle.
|
||
|
||
✅ **Opposite directions.** `ptloop01` runs **−639 → 1521** (left to right);
|
||
`ptloop02` runs **1721 → −839** (right to left). Their strip A and strip B.
|
||
|
||
✅ **Taller than the screen.** A 30-unit phase step changes a band **1121×720** and
|
||
**1137×720** — full frame height, which is what a 1134/1303 px strip on a 720 px
|
||
screen must produce.
|
||
|
||
### 🔴 The correction: their rate check used the wrong span
|
||
|
||
They wrote *"declared track −639..1521 = 2160 px over a 600-unit cycle = 3.6
|
||
px/unit"*. **The last segment holds.** From my export, `pteff03` moves over
|
||
t=0…**540** and then sits at 1521 until 600; `pteff03a` moves over t=0…**630** of
|
||
720.
|
||
|
||
| | motion span | px/unit | at 2 units/frame |
|
||
|---|---|---|---|
|
||
| their figure | 600 | 3.60 | 7.2 px/frame |
|
||
| **corrected** | **540** | **4.00** | **8.0 px/frame** |
|
||
| `ptloop02` | 630 | 4.06 | 8.1 px/frame |
|
||
|
||
⚠️ **This weakens their confirmation rather than strengthening it.** 7.2 against a
|
||
measured 6–7 reads as agreement; **8.0 against 6–7 is a 20 % gap.** Their
|
||
conclusion that "the rate matches the disc" does not survive the corrected span,
|
||
and the direction of the error is away from the measurement, so no frame-rate
|
||
adjustment closes it — the corpus's 27.6–28.8 fps would make units/frame *larger*
|
||
and the prediction worse.
|
||
|
||
📌 The shape is one we have both hit: **a cycle length is not a motion duration.**
|
||
Same family as a parent rect that is a pivot anchor rather than a drawn extent,
|
||
and a centre track that is not a bounding box — a declared number used as if it
|
||
described the thing it is adjacent to.
|
||
|
||
⚠️ I am not claiming the port is right and the oracle wrong. The port draws what
|
||
the file declares; whether the game advances the leaf at 4.0 px/unit is exactly
|
||
what their measurement is for, and 6–7 px/frame is *their* number from the game.
|
||
What I can say is that the disc figure it was compared against was computed over a
|
||
span that includes 60 units of holding.
|
||
|
||
## Nested leaves may advance at half rate — a CONDITIONAL exposure, not a defect
|
||
##
|
||
## *(This heading read "a quantified defect in shipped output". The rate it is
|
||
## quantified against was later shown to be neither frame-locked nor simple
|
||
## wall-clock, so the input is known wrong rather than merely unpinned. Nothing
|
||
## is established as defective.)*
|
||
|
||
Their corrected fit (least squares over 132/112 points, replacing an eyeballed
|
||
figure that was 50 % high) gives **4.287** and **−4.348** px/frame against my
|
||
declared **4.000** and **4.063** px/unit — i.e. **1.072** and **1.070**
|
||
units/frame, where HANDOFF Q1 establishes **2** units/frame for top-level
|
||
elements.
|
||
|
||
🔴 **My port drives everything from one clock.** `boot.gd:375` is
|
||
`view.time_units += delta * view.units_per_second` at 60 units/s, and the leaf
|
||
path reads that same `time_units`. So:
|
||
|
||
| | port cycle | game cycle (at 1.07) | |
|
||
|---|---|---|---|
|
||
| `pteff03` | 10.0 s | **18.7 s** | port **1.87×** too fast |
|
||
| `pteff03a` | 12.0 s | **22.4 s** | port **1.87×** too fast |
|
||
|
||
⚠️ **CONDITIONAL, and the condition is not met.** That table inherits an absolute
|
||
rate the Decoder has since tried three ways to pin and could not: a top-level
|
||
clock in the same capture (nothing top-level moves on a settled screen — that is
|
||
what settled means), a fit in the transition captures (rms residuals 26.70/16.75
|
||
px against 147 px of travel: scatter, not a line), and the emulator's own log
|
||
(fps not printed). So **1.87× is what follows IF 1.07 is the true rate**, and
|
||
1.07 is exactly the quantity that is not established. It is recorded as an
|
||
exposure to check, not as a defect to fix.
|
||
|
||
🔴 **Updated: the input is now known to be *wrong*, not merely unpinned.** Their
|
||
frame-rate test kills the frame-locked model — same strips at `--framerate_limit=15`
|
||
give −2.032 px/frame against −4.348 at default, ratio 2.14, where a fixed number
|
||
of units per submitted frame predicts no change. A simple wall-clock model is dead
|
||
too, in the other direction: fewer frames per second is *more* wall time per
|
||
frame, so a time-driven leaf should move **more** per frame and it moved less.
|
||
Neither model fits. The 1.87× table's input is a number we now know is not what it
|
||
was taken to be.
|
||
|
||
⚠️ **Not changed, and not only out of caution.** `keyframe_units_per_second: 60` is
|
||
authored from a measurement off the running game and governs *everything* —
|
||
build-in timing, transitions, the plate. Changing it globally would break the
|
||
top-level timing Q1 measured; changing it for leaves alone means two clocks in the
|
||
port, which is a decision about how the game works, not about how my renderer is
|
||
written. **That is a Q1 sub-question and it is theirs.**
|
||
|
||
### Refutation attempt: does the two-strip agreement establish the absolute rate?
|
||
|
||
Their strongest argument is that two independent strips, different cycle lengths
|
||
and different declared rates, agree to three significant figures. 🟡 **It is
|
||
weaker evidence than it looks for the *absolute* value.**
|
||
|
||
Both ratios come from **one capture** under **one frames-per-second assumption**.
|
||
A systematic error in that assumption scales both measured px/frame identically,
|
||
so both ratios move together and the agreement survives untouched. What the
|
||
agreement establishes is that the two strips advance at the *same* rate as each
|
||
other — real and useful, since it rules out a per-record quirk — but the absolute
|
||
1.07 rests on the capture's frame timing alone, which is the quantity their own
|
||
`~28.5 fps` note says is not exactly 30.
|
||
|
||
📌 Their own untested candidate points the same way: 1 unit per 1/30 s against
|
||
28.5 fps gives **1.053**, and the gap between that and 1.070 is about the size of
|
||
the frame-rate uncertainty. So the measurement may be saying *"one unit per game
|
||
frame"* exactly, with the residual being how fast the emulator actually ran.
|
||
|
||
## Their Route 1 is closed for the whole archive, not just the title
|
||
|
||
They tried three ways to pin the absolute rate and closed all three. Route 1 —
|
||
find a top-level element moving in the *same capture* as a leaf, so frames-per-second
|
||
cancels in the ratio — failed on the settled title because *"nothing top-level
|
||
moves on a settled title; that's what settled means."*
|
||
|
||
I searched all 16 screens of my export for a top-level element still in motion at
|
||
its settle instant. **Two hits, `pttitle` on `extras` and `extras_jp` — and both
|
||
are false positives.** Its keyframes are `(16, y90, a0) (20, y98, a128) (24, y100,
|
||
a255) (52, y100, a255) (58, y90, a0)`: it arrives, holds from t=24, and the motion
|
||
my detector saw after the settle instant is the **exit ramp**, which plays only
|
||
when the screen leaves.
|
||
|
||
⚠️ **Third time the exit ramp has fooled a census of mine** — after counting it as
|
||
the end of visibility in the transient sweep, and after it made every normal
|
||
element look like a flash. It is the single most reliable false positive in this
|
||
export and I still did not anticipate it.
|
||
|
||
✅ **But the negative result generalises their finding.** Excluding the exits,
|
||
**no top-level element on any of the 16 screens moves at rest.** That is not an
|
||
accident of the title: `holding` clamps every top-level element at its own hold,
|
||
and the only keyframes past the settle instant are exit ramps. So **no capture of
|
||
any screen in this archive can carry a top-level clock alongside a free-running
|
||
leaf** — Route 1 is closed structurally, not just empirically, and no further
|
||
screen is worth their time trying.
|
||
|
||
📌 It also says something about the port's own design that I had not stated:
|
||
**everything that moves on a settled screen is nested.** The three free-running
|
||
clocks I enumerated — looping focus record, spin, leaf — are all sub-records, and
|
||
that is now explained rather than observed. Their plate finding is the same shape:
|
||
`ptbtn00` is a one-shot fade and the repeating pulse comes from its nested `.rat`.
|
||
|
||
## The off-edge splash residual is **not** tonal — and I was comparing it to the wrong floor
|
||
|
||
I said the remaining 0.82–1.42 off-edge residual was *"consistent with the binned
|
||
tone table leaving about a level of error"* and that a per-level correction was
|
||
the test I had not run. Ran it, deriving the curve on one splash and applying it
|
||
to **the other**, because fitting and scoring on the same pixels succeeds by
|
||
construction:
|
||
|
||
| `developer_logos`, off-edge | RMSE |
|
||
|---|---|
|
||
| uncorrected | 2.7512 |
|
||
| **single gamma 0.85** | **0.9040** |
|
||
| per-level curve from `publisher_logo` | 1.3795 |
|
||
|
||
🔴 **The per-level curve is worse than a single exponent**, cross-applied. And the
|
||
control that settles it:
|
||
|
||
| `publisher_logo`, off-edge | RMSE |
|
||
|---|---|
|
||
| single gamma 0.85 | 1.4440 |
|
||
| **its own fitted curve** | **1.4209** |
|
||
|
||
**A tone curve fitted on those very pixels improves them by 1.6 %.** If the
|
||
residual were a tone-mapping error, fitting the tone mapping on its own training
|
||
data should collapse it. It does not, so **the residual is not tonal** — my
|
||
hypothesis is refuted by the strongest test available to it.
|
||
|
||
### ⚠️ And the residual is smaller than I made it sound
|
||
|
||
I called 0.82–1.42 *"15–25× the 0.06 rasterisation floor"*. That is the wrong
|
||
comparison: 0.06 is **render-to-render** reproducibility, which is the floor for
|
||
asking *does my renderer repeat itself*. For **render-versus-capture** the floor
|
||
includes 8-bit quantisation on both sides — uniform rounding error has RMSE
|
||
1/√12 ≈ 0.289 per image, so a difference of two independently quantised images
|
||
sits at **≈ 0.41** before anything is wrong at all.
|
||
|
||
Against that floor, 0.90 is **~2.2×**, not 25×. It is roughly **one level in 255**.
|
||
|
||
📌 So the honest statement of the splash rows is stronger than what I had:
|
||
off-edge, after a single gamma, the port differs from the game by about **twice
|
||
the irreducible quantisation floor**, and the shape of what remains is *not*
|
||
tonal. I quoted the same number twice this week against a floor chosen for a
|
||
different question — the same error as comparing an in-box margin to a whole-frame
|
||
spread, which I flagged in someone else's work two days ago.
|
||
|
||
⚠️ What is still unexplained is now a much smaller thing: ~0.5 RMSE above
|
||
quantisation, off-edge, non-tonal, on screens with no free-running element. I have
|
||
no candidate for it and I am not going to invent one.
|
||
|
||
## Their linearity gate, applied to my side of the ratio — and an inversion
|
||
|
||
Their gate is right and I had not applied it: *a slope is only a rate if its
|
||
residual is random*. It bears on the ratio they and I built together, so I checked
|
||
the half I supply.
|
||
|
||
✅ **The disc side has no residual at all.** `pteff03` is declared piecewise
|
||
linear with **identical** segment rates:
|
||
|
||
| segment | movement | rate |
|
||
|---|---|---|
|
||
| t 0…150 | +600 px / 150 u | **+4.0000 px/unit** |
|
||
| t 150…540 | +1560 px / 390 u | **+4.0000 px/unit** |
|
||
| t 540…600 | 0 | hold |
|
||
|
||
`pteff03a` gives −4.0667 then −4.0625 — a 0.1 % step, so very nearly but not
|
||
exactly uniform. **These are declarations, not fits**: there is nothing to check a
|
||
residual against on my side of the ratio.
|
||
|
||
### 🔴 The inversion worth their attention
|
||
|
||
Their gate failed on the strips of height **1134**, which is `pteff03` (scale
|
||
600) — **the one whose declared track is perfectly linear**. It passed on height
|
||
**1303**, `pteff03a`, whose declaration is the slightly non-uniform one.
|
||
|
||
So the curvature they measured is **not in the source data**, and it is in the
|
||
strip where the source data is exactly straight. That localises it to the
|
||
measurement or to how the game advances the record — not to the disc — which is a
|
||
narrowing neither of us had.
|
||
|
||
### An observation on the frame-rate result, offered as a question about the instrument
|
||
|
||
Their ratio implies a large difference in on-screen speed:
|
||
|
||
| | px/frame | × fps | px/wall-second |
|
||
|---|---|---|---|
|
||
| default | 4.348 | 28 | **121.7** |
|
||
| limit 15 | 2.032 | 15 | **30.5** |
|
||
|
||
**~4× slower on screen at the lower limit.** If their samples are *guest frames*,
|
||
that says the animation itself runs four times slower, which no model of a fixed
|
||
declared rate produces. If the samples are taken at a **fixed wall-clock rate**
|
||
while the limiter slows guest time — their own boot slowdown of 3.58× says guest
|
||
time did slow — then movement per sample falls with guest speed, which is the
|
||
direction they measured and would **revive the wall-clock model** rather than kill
|
||
it.
|
||
|
||
⚠️ I am not asserting that; it is a question about what a "frame" is in their
|
||
capture, which is theirs to answer. But their boot-slowdown figure (3.58×) and
|
||
their sweep ratio (2.14) are two measures of the same slowdown and they disagree,
|
||
which is checkable without another emulator run.
|
||
|
||
## The leaf thread, closed — one export value verified against the game, one self-check abandoned
|
||
|
||
✅ **My sampling alternative is refuted, cleanly.** Every capture reports
|
||
`done: … over 150 frames` spanning frames 1..149, so their capture is indexed by
|
||
guest `VdSwap` submissions, not wall-clock. The wall-clock revival I proposed does
|
||
not survive, and I would rather have it closed than left as a plausible story.
|
||
|
||
✅ **`rotation_deg` is confirmed from the running game.** Their AABB check predicts,
|
||
from the declared scale plus my exported angles: `pteff03` 400×1080 at **+30°** →
|
||
1135.3 against **1134** observed (0.12 %); `pteff03a` 400×1440 at **−45°** →
|
||
1301.1 against **1303** (0.15 %). Two angles, two scales, both under 0.2 %.
|
||
|
||
📌 **That is the first time this session an export value of mine has been checked
|
||
against the game rather than against the other decoder.** Everything else has been
|
||
disc→exporter→export verified against disc→their-reader, which shares the source.
|
||
This one runs disc→exporter→export→*oracle*, and it holds.
|
||
|
||
### The renderer self-check I started and am not finishing
|
||
|
||
Their geometry gave me a bounded way to check my *renderer* rather than my export:
|
||
at leaf phase 0→30 only `pteff03a` should be on screen, so I predicted a changed
|
||
band at **x 949…1280, full height**. Measured: **1177×70 at the top of the frame**,
|
||
max difference **5.29**.
|
||
|
||
⚠️ **The prediction is mine and it is wrong.** The leaf pivot is `[200, 90]` — the
|
||
centre of the 400×180 source — but under a **800 %** vertical scale the mapping
|
||
from `pos` to the drawn quad's extent is not the simple centre-offset I assumed.
|
||
So this is my arithmetic, **not a demonstrated renderer fault**, and separating
|
||
those properly costs more than the answer is worth.
|
||
|
||
🔴 **Stopping here deliberately.** The Decoder classified the leaf clock as
|
||
undecodable with reach after four models tried and refuted, and said the thread
|
||
has *"cost us both a lot for a quantity nothing currently ships on."* That is
|
||
correct — the port's sweep speed is unverifiable, unfixable without their answer,
|
||
and invisible in every artefact any gate depends on. **Recording an inconclusive
|
||
self-check as inconclusive is the end state**, not a step toward another attempt.
|
||
|
||
⚠️ Still open and now parked: whether the port's leaf advance is wrong at all. Four
|
||
models refuted means the input to my 1.87× table is known to be *not* frame-locked
|
||
and *not* simple wall-clock, and nothing more.
|
||
|
||
## Delivering the phase term where the numbers are, not where I found them
|
||
|
||
Their point closing the leaf thread — *it lived in messages and `docs/re/`, which
|
||
by our own rule means it wasn't delivered* — applies to my side, and I had the
|
||
same gap. The capture-phase term was written up in `DECISIONS.md`. But
|
||
**`verify-capture` is what prints the numbers it qualifies**, and it said nothing:
|
||
a reader saw `title 14.16 / 0.21 %` with no indication that ±5.56 of it is
|
||
inherited from where the shutter fell.
|
||
|
||
✅ Now printed per row, in the tool's own metric:
|
||
|
||
```
|
||
main_menu 13.21 0.06% rendered with authored initial focus; +/-3.78 capture-phase term
|
||
title 14.16 0.21% settle t=198; +/-5.56 capture-phase term -- regression only
|
||
publisher_logo 2.17 0.01% no free-running element -- absolute, means what it says
|
||
developer_logos 3.05 0.01% no free-running element -- absolute, means what it says
|
||
```
|
||
|
||
Plus a header block recording that `--leaf-time=0` is a **convention, not the
|
||
game's phase**, and that these rows are usable for regression at a fixed pin and
|
||
**not** as absolutes against anything measured differently.
|
||
|
||
### ⚠️ A gap in the delivery rule that their own update exposes
|
||
|
||
They landed the leaf facts in **HANDOFF**, which is right — it is the contract,
|
||
and the standing instruction has me read it every iteration. **It is not in my
|
||
checkout.** Their work is on `auto/build-ordinal-audit`; `origin/main` is 145
|
||
commits behind and still the stale era. So:
|
||
|
||
* `docs/port/HANDOFF.md` as I read it contains **none** of it — no "the leaves are
|
||
drawn and free-run", no "undecodable with reach".
|
||
* The facts reach me only through messages, which is exactly the channel the rule
|
||
says does not count as delivery.
|
||
|
||
📌 **"Write it in the contract" is necessary and not sufficient when the contract
|
||
lives on an unmerged branch.** Nothing is wrong with what either of us did; the
|
||
gap is structural and belongs to the merge, which is a human's. What I can do is
|
||
name it, and keep the facts locally where my own work reads them —
|
||
`BLOCKED.md` and `DECISIONS.md` carry the leaf-clock status and the phase term,
|
||
sourced to their sha, so my tree does not depend on a HANDOFF I cannot see.
|
||
|
||
⚠️ This is the second structural consequence of `main` being stale, after the
|
||
`Cargo.toml` pin being load-bearing. Both are recorded rather than worked around.
|
||
|
||
## The boot verified as a *sequence*, not just at its endpoint
|
||
|
||
I have twice written that the boot's 0.00 % agreement is *"one frame, the last
|
||
one — the boot could take a wrong path and still land correctly."* Closing that,
|
||
and it needed no new capture and no new feature.
|
||
|
||
⚠️ **`--boot --shots=` writes nothing.** `_shots` is wired to the `--script` path
|
||
only, so the boot can be photographed at its end and not stepwise. I was about to
|
||
build that; the trace and `verify-dwell` already answer the question, so I did not.
|
||
|
||
### The path
|
||
|
||
The boot logs its own transitions. Trace against `authored/flow.json`'s declared
|
||
sequence:
|
||
|
||
| declared | observed |
|
||
|---|---|
|
||
| 1. `publisher_logo` | start, settles t=30 |
|
||
| 2. `developer_logos` | → at **4.26 s** |
|
||
| 3. video `ADV` | → at **7.78 s** (+ voice ADV) |
|
||
| 4. `title` + overlay `press_start` | → at **152.54 s**, overlay raised same instant |
|
||
| | boot ends **156.48 s** |
|
||
|
||
✅ Four declared steps, four observed, in order, with the overlay raised on the
|
||
step that declares it.
|
||
|
||
### The timings, and they are not authored
|
||
|
||
`verify-dwell` against the Decoder's three cold boots:
|
||
|
||
| screen | port | oracle (3 boots) | |
|
||
|---|---|---|---|
|
||
| publisher wordmark | 4.28 s | 4.297 / 4.604 / 4.370 | **agrees** |
|
||
| developer logos | 3.58 s | 3.508 / 3.503 / 3.366 | **agrees** |
|
||
|
||
📌 **`authored/flow.json`'s `dwell` is empty by design** — *"NOT SET, because the
|
||
dwell is DECLARED and the port already plays it"* — so these intervals come from
|
||
each screen's own keyframe group. **Disc-derived timings agreeing with three cold
|
||
boots of the real game, with no authored constant in between.**
|
||
|
||
### So the shipped artefact is verified on three axes
|
||
|
||
**Path** from the trace, **timing** from `verify-dwell` against the oracle, **final
|
||
frame** at 0.00 % differing against `live-title-press-a.png`. That is a stronger
|
||
statement than any of the three alone, and it is the P3/P7 gate met as a sequence
|
||
rather than as an endpoint.
|
||
|
||
⚠️ Still not verified: the *intermediate frames*. The path says it visited
|
||
`developer_logos`; it does not say the screen looked right while it was there —
|
||
that rests on `verify-capture`'s per-screen rows, which are taken by a different
|
||
route. And the 144.76 s video is unexamined beyond playing.
|
||
|
||
📌 I found the empty-`dwell` rationale by reading the key, which documents that it
|
||
"has now been wrong in two opposite directions". **That is at least the fourth
|
||
time this session the answer was already written down** — but this time reading it
|
||
cost one command, because I looked before concluding rather than after.
|
||
|
||
## Refuting the "8.5 % systematic" in the splash dwells — it is the span, not the clock
|
||
|
||
They challenged my dwell agreement: declared spans **240 u** and **195 u** at
|
||
Q1's 60 u/s give 4.000 s and 3.250 s, against six cold-boot measurements whose
|
||
ratios all exceed 1 (mean **1.085**), implying 54–56 units/s rather than 60. And
|
||
pointedly: *"240 units at exactly 60 should give you 4.000 and you're reporting
|
||
4.28."*
|
||
|
||
🔴 **The port does not play 240 units.** Their 240 is the *visible* span — the logo
|
||
element's 15→255. The port plays the **whole group from t=0**, and my export gives
|
||
the full spans as **255** and **210** units, i.e. **4.250 s** and **3.500 s**. My
|
||
observed 4.28 and 3.58 are those plus a frame or two of transition overhead, not
|
||
an 8.5 % overshoot of anything.
|
||
|
||
Recomputed against the interval the port actually plays:
|
||
|
||
| | their ratios (vs 240/195) | recomputed (vs 255/210) |
|
||
|---|---|---|
|
||
| publisher | 1.074, 1.151, 1.093 | 1.011, 1.083, **1.028** |
|
||
| developer | 1.079, 1.078, 1.036 | 1.002, 1.001, **0.962** |
|
||
| mean | **1.085** | **1.0145** |
|
||
| below unity | 0 of 6 | **1 of 6** |
|
||
|
||
✅ **The systematic largely dissolves.** A mean deviation of 1.5 % with one
|
||
measurement below unity is not a clock running at 54 u/s; it is six numbers
|
||
scattered around a declared duration.
|
||
|
||
⚠️ **And the practical consequence they drew does not follow.** *"A port playing
|
||
240 units at 60 u/s shows the publisher splash 0.42 s less than the game."* The
|
||
port plays 255 units, so the gap is **0.174 s** — and on `developer_logos` the
|
||
port is **0.041 s longer** than the oracle mean, the opposite sign. There is no
|
||
direction to correct in.
|
||
|
||
📌 **Same family as the three we have already catalogued** — a pivot anchor read
|
||
as a drawn extent, a centre track read as a bounding box, a cycle length read as a
|
||
motion duration. Here: **a visible span read as a played span.** Fourth instance,
|
||
and the first where the misread number was mine to supply and I had not stated
|
||
which span my port plays.
|
||
|
||
🟡 **What survives, and I would not want it lost in the refutation.** Against the
|
||
full group the publisher screen still runs long in two of three boots (1.011,
|
||
1.083, 1.028) while the developer screen sits at 1.002, 1.001, 0.962. That is a
|
||
much weaker signal than 8.5 % and it is not nothing. Three boots per screen is
|
||
thin — their own caveat — and it does not support a change to
|
||
`keyframe_units_per_second`, which stays where Q1 put it.
|
||
|
||
## The fifth member of the family is mine: "drawn" is not "visible"
|
||
|
||
✅ Their revision confirmed from my export, exactly. `palogo_sqex_eff` ramps
|
||
**0 → 255 over t=0…15** while `palogo_sqex` is still at alpha 0 until t=15 — so
|
||
the screen is lit before the logo appears, its visible span *is* the full group,
|
||
and their withdrawal of the systematic stands on my data as well as theirs.
|
||
|
||
🔴 **And checking it exposed my own version of the same error.** When I computed
|
||
the "visible span" earlier I got `0..251` for this screen — and I got it by
|
||
counting **any element with alpha > 0**, which includes `palogo_eff0`:
|
||
|
||
| | |
|
||
|---|---|
|
||
| role | full-screen **primitive**, no sprite |
|
||
| keyframes | single, `t=0` |
|
||
| `fade_argb` | **`0xff000000`** — opaque **black** |
|
||
|
||
**It is drawn from t=0 and shows nothing.** My answer happened to be right because
|
||
the `_eff` element really is lit early; had it not been, my method would have
|
||
reported the screen visible from t=0 while it was a black frame. I published
|
||
"visible 0..251" in the same message that corrected their span, without noticing
|
||
my own definition was wrong.
|
||
|
||
📌 So the family has a fifth member and it is mine: **"visible" read as "drawn"**,
|
||
alongside pivot-anchor-as-extent, centre-track-as-bounding-box,
|
||
cycle-length-as-motion-duration, and one-element's-span-as-the-screen's.
|
||
|
||
### Accepting their correction on the framing
|
||
|
||
I wrote that this was *"the first where the misread number was mine to supply"*.
|
||
⚠️ **They are right that it is not.** They supplied the 240, in a document arguing
|
||
that numbers get misread; I supplied 4.28 s across two messages without ever
|
||
saying what it spanned. **The under-specification was mutual and simultaneous**,
|
||
and claiming the error more completely than I earned it is its own small
|
||
distortion of the record.
|
||
|
||
✅ Nothing about the conclusion moves: the port plays the full group, 255 and 210
|
||
units; the recomputed ratios are 1.011/1.083/1.028 and 1.002/1.001/0.962, mean
|
||
1.0146; **Q1 stands unqualified** and `keyframe_units_per_second` does not move.
|
||
|
||
## Auditing `--black`, and a rule that falls out of it
|
||
|
||
Their dividend — that `screen render --black`'s premise is **declared** on the
|
||
splash builds rather than assumed — is checkable across my whole export, and
|
||
`verify-screen` passes `--black` to all sixteen screens on exactly that premise.
|
||
|
||
Audited by asking: does the screen declare a full-screen untextured primitive at
|
||
`t=0` with `fade_argb 0xff000000`?
|
||
|
||
| | |
|
||
|---|---|
|
||
| **declared** — 12 screens | `pteff00` (both titles, both menus, both extras), `palogo_eff0` (all four splashes), `pgloading_eff00` (`build_12`, `build_15`) |
|
||
| **not declared** — 4 screens | `press_start`, `press_start_jp`, `build_00`, `build_01` |
|
||
|
||
🔴 **All four exceptions are composited, not standalone.** `press_start` is one
|
||
element — the plate — and its own `name_why` records that it is *"composited over
|
||
the title"*; the game never shows it on black. `build_00`/`build_01` carry the
|
||
`pgloading_*` set **without** the `pgloading_eff00` backdrop that `build_12`/`15`
|
||
declare.
|
||
|
||
✅ **Harmless where it is used**, and worth stating why rather than assuming:
|
||
`verify-screen` gives `--black` to *both* renderers, so the assumption cancels in
|
||
a consistency check. It would not cancel in an oracle comparison — and
|
||
`verify-capture` already avoids it, scoring the plate as
|
||
`--screen=title --overlay=press_start`, over the title. The exposure was real and
|
||
the tooling had already routed around it, which I could only establish by looking.
|
||
|
||
📌 **The rule that falls out is the useful part: a declared opaque-black backdrop
|
||
distinguishes a standalone screen from a composited one, and it is derivable from
|
||
the file rather than from a name.** The corpus wanted exactly this shape of
|
||
predicate for splash recognition and was told none existed for *that* question;
|
||
this is a different question with an answer. 12 standalone, 4 composited, no
|
||
name-matching involved.
|
||
|
||
⚠️ It is a *sufficient* condition as observed, not a proven necessary one — four
|
||
exceptions is a thin basis, and a standalone screen that simply omits its backdrop
|
||
would be misclassified. Recorded as a rule with its evidence, not as a decoded
|
||
fact.
|
||
|
||
## 🔴 CORRECTION: my backdrop predicate is exact in `GP_TITLE` and its reading was wrong
|
||
|
||
I offered *"a declared opaque-black backdrop distinguishes a standalone screen
|
||
from a composited one"* and asked for it to be tested against archives I do not
|
||
have. It was, and the result splits cleanly in two.
|
||
|
||
✅ **The split reproduces exactly.** Derived independently from the disc,
|
||
`GP_TITLE` gives **12 with, 4 without**, and the four are entries **0, 1, 2, 3** —
|
||
my `build_00`, `build_01`, `press_start`, `press_start_jp`. Element names match
|
||
too. Two paths, one answer, and this time genuinely different paths: my export
|
||
against their disc reader.
|
||
|
||
🔴 **The reading does not survive.** Disc-wide the predicate is **rare — 76 of 965
|
||
builds, 7.9 %** — and `GP_HANGAR_ARSENAL` is **0 of 390**, `GP_OPTIONS` 0/14,
|
||
`GP_PAUSE_MENU` 0/6, `GP_READY_ROOM` 0/60, `GP_GAMEOVER` 0/10. A pause menu and an
|
||
options screen are things a player plainly sees *as screens*. Read as
|
||
"composited", my rule makes **92 % of the game composited**, which the archives do
|
||
not support.
|
||
|
||
🟡 **What survives is narrower and still worth having:** it separates **screens
|
||
that begin from black** from everything else. Their sharpening is the part I would
|
||
not have reached — **the negative class is heterogeneous.** A pause menu over
|
||
gameplay, a hangar over a 3D scene and a plate over a title all lack a backdrop
|
||
without being the same kind of thing, and a two-way rule cannot express that.
|
||
|
||
### What I got right, and what that is worth
|
||
|
||
📌 My caveat was *"sufficient as observed, not proven necessary — four exceptions
|
||
is a thin basis, and I'd want it tested against an archive I don't have."* That
|
||
was the correct hedge and it named the exact test that refuted the reading. ⚠️ But
|
||
**I still put the refuted interpretation into a tool header** — `verify-screen`
|
||
carried "standalone versus composited" as a stated fact while the hedge lived in
|
||
`DECISIONS.md`. Corrected there now, with the 7.9 % figure and an explicit "do not
|
||
carry this into the four unexported archives".
|
||
|
||
**Hedging in the write-up does not protect the claim I ship in the tool.** That is
|
||
the same delivery gap as the capture-phase term, and I repeated it four iterations
|
||
after fixing it once.
|
||
|
||
✅ Within `GP_TITLE` the rule is exact, and `--black` for those twelve screens is
|
||
now justified **from the file** rather than assumed — which was the original point
|
||
and is unaffected.
|
||
|
||
## Sweeping my own `--help` and headers, after theirs
|
||
|
||
Their audit found one defect in sixteen commands in about ten minutes, and their
|
||
framing of why doing one and stopping is the wrong move — *"a rule written down is
|
||
not a rule applied"* — applies to me: I had fixed `verify-screen` and
|
||
`verify-capture` and gone no further. Swept the rest.
|
||
|
||
### Hit 1 — `verify-dwell` compared the port against the *game's* gap
|
||
|
||
The tool built its target as **oracle span + the game's black gap** (0.114–0.190 s)
|
||
and scored the port against it. That was right while the port inserted that gap.
|
||
🔴 **It does not**: `black_hold_units` went to 0 three iterations ago.
|
||
|
||
So the port should run short by the gap, and on `publisher_logo` it does —
|
||
**0.131 s below the unslacked target**, absorbed into an "agrees" by the 0.15 s
|
||
wall-clock slack, which is *larger than the omission it was hiding*. **A verdict
|
||
that passes because the slack exceeds a known defect is not a verdict.** The hold
|
||
is now read from `authored/timing.json` so it cannot drift, and the game's gap is
|
||
printed as its own term with the slack comparison stated.
|
||
|
||
### Hit 2 — a rate that is right, with its population missing
|
||
|
||
`verify-dwell` carried *"4 presented frames at **2.284 units/frame** = 9.1 units"*.
|
||
✅ The number is correct. ⚠️ It is the disc used as its own clock **on one
|
||
capture**, which ran at **13.1 fps** against ~28 elsewhere. Stated bare in a tool,
|
||
it reads as a general rate — and it would then contradict HANDOFF Q1's **2 units
|
||
per rendered frame**, which is a different quantity at normal speed. The
|
||
derivation was in `DECISIONS.md`; the tool inherited the number without it.
|
||
|
||
📌 **Exactly their defect**: the doc states its population precisely, the tool
|
||
inherits the value alone. Their fix generalises without modification — *print the
|
||
population beside the number* — and I did not have to invent anything either.
|
||
|
||
### What the sweep did not find
|
||
|
||
The percentages in `check-capture` all name what they are percentages of (frames
|
||
in a named capture, with the control rows beside them). `check-claims`,
|
||
`check-modding`, `index-decisions` and `strip-padding` assert no measured
|
||
quantities. So: **two hits across the tool set, both in one tool, both of the two
|
||
types now catalogued.**
|
||
|
||
⚠️ And their warning is one I cannot fully clear: *any figure computed before the
|
||
record-layout fix may have a population that moved, not just a value.* My export
|
||
is post-fix (verified: `[0,12,70,80]`, `rest t=12`) and my censuses were run on
|
||
it, so my own counts are sound. **Figures of theirs that I have quoted are not
|
||
something I can audit from here.**
|
||
|
||
## `black_hold_units`: my own tripwire has tripped, and I am not resolving it alone
|
||
|
||
I set the value to 0 with an explicit condition: *"if the gap is non-zero in most
|
||
transitions and no rule emerges, systematically omitting a real quantity is no
|
||
longer the cautious choice — it is a different invention. Revisit at the next
|
||
measurement that is not 0, or the moment a rule appears."*
|
||
|
||
**Both halves of the first clause now hold.** Five measurements:
|
||
|
||
| transition | gap |
|
||
|---|---|
|
||
| menu → title | **0** |
|
||
| title → menu | 4 |
|
||
| EXTRAS → menu | 6 |
|
||
| their fourth | 6 |
|
||
| publisher → developer *(from my own `verify-dwell` shortfall)* | **~7.9** |
|
||
|
||
**Four of five non-zero, mean 6.0 units.** The fifth is mine and arrived by a
|
||
different route — the port running 0.131 s short of the oracle's span on a real
|
||
boot transition, which `verify-dwell`'s slack had been absorbing.
|
||
|
||
### The rule attempt, which failed
|
||
|
||
If a rule existed, changing the value would be principled rather than fitted. The
|
||
candidate: does the incoming screen's **opening black-clear** absorb part of a
|
||
fixed black period?
|
||
|
||
| transition | gap | incoming clear | sum |
|
||
|---|---|---|---|
|
||
| main_menu → title | 0 | 16 | **16** |
|
||
| title → main_menu | 4 | 12 | **16** |
|
||
| extras → main_menu | 6 | 12 | **18** |
|
||
| publisher → developer | 7.9 | **0** (never clears) | **7.9** |
|
||
|
||
🟡 Suggestive on the three menu/title transitions — 16, 16, 18, consistent with a
|
||
constant given ±1 frame of measurement. 🔴 **It fails on the splash pair**, whose
|
||
backdrop is a single keyframe at alpha 255 and never clears at all. Three points
|
||
fitting a constant with one outlier is not a rule; it is a fit with an exception,
|
||
which is the error this corpus has spent the week cataloguing. **Not adopted.**
|
||
|
||
### Why I am escalating rather than choosing
|
||
|
||
⚠️ **My justification for 0 has failed on its own terms.** "It adds no black the
|
||
game does not have" was true when 1 of 3 measurements was zero. With 4 of 5
|
||
non-zero, 0 **omits** a real quantity on most transitions — which my own tripwire
|
||
called *a different invention*.
|
||
|
||
⚠️ **And the alternative is a fit.** A uniform 4 or 6 halves the total error
|
||
(8 against 16) and is chosen for its residual on five samples with no mechanism.
|
||
|
||
Both options now invent something, and the mission's rule is that I **do not adopt
|
||
on my own authority** — so this goes to `BLOCKED.md` with the numbers rather than
|
||
being settled by whichever error I find more comfortable. ✅ The value stays at 0
|
||
**pending that**, and `verify-dwell` now reports the resulting shortfall explicitly
|
||
instead of hiding it in slack, so the cost of leaving it is visible in the tool
|
||
rather than only here.
|
||
|
||
## Their sharpened tell, applied to my tree: two descriptions the code below had already refuted
|
||
|
||
Their sweep cleared all three of my quoted figures — the splash dwell spans were
|
||
corrected today, and the 0.114–0.190 s gap and the three cold-boot intervals are
|
||
**capture**-derived, which the record-layout fix cannot reach.
|
||
|
||
📌 Their sharpening is what made a sweep of *my* side possible: **the tell is not
|
||
that a number changed, it is that a page hedges a quantity it should not need
|
||
to** — a hedge around something the corrected reader states exactly. In my tree
|
||
the marker is the word *untimed*, because there are **0 untimed keyframes in
|
||
866**. Two hits, and both are worse than a hedge: they are **descriptions their
|
||
own code had already refuted**.
|
||
|
||
### Hit A — a doc comment contradicting its own function
|
||
|
||
`spin_period_units`' documentation stated the rule as *"the first timed and the
|
||
second untimed. The period is the first keyframe's declared `t`."* That is the
|
||
**pre-fix** rule. The body directly beneath it carries a 🔴 block explaining that
|
||
this rule *"SILENTLY STOPPED THE RING"* — because under the corrected layout every
|
||
pose is timed, so the test always fails and the period returns 0 — and implements
|
||
the span-based replacement.
|
||
|
||
**A function and its documentation disagreeing, with the documentation stating the
|
||
refuted version.** Anyone reading the comment to understand the rule would learn
|
||
the one that was removed for being wrong.
|
||
|
||
### Hit B — I made the correction and left the claim on top
|
||
|
||
`exit_ramp_units`' documentation opened: *"Authored (`authored/timing.json`): the
|
||
disc has no time slot on that keyframe, so this is the one unknown duration per
|
||
screen."* Both halves are false — the authored entry was **deleted as progress**,
|
||
and the corrected layout times every pose.
|
||
|
||
⚠️ **And I wrote the correction myself, immediately below it.** Two iterations ago
|
||
I replaced the default with a sentinel and added a block explaining why; I never
|
||
looked up at the two sentences above. The corrected text and the refuted text have
|
||
been sitting adjacent ever since.
|
||
|
||
### The pattern, which is narrower than "docs go stale"
|
||
|
||
Both hits are **fixed code under an unfixed description**, and in both the fix and
|
||
the falsehood are in the same file within twenty lines of each other. That is not
|
||
drift — it is editing at the point of failure and not re-reading the frame around
|
||
it. ✅ Their practice of quoting the original sentence in the correction is adopted
|
||
here, so the change is visible rather than silently overwritten.
|
||
|
||
📌 And their reading of my `verify-dwell` hit is sharper than mine was: a verdict
|
||
passing because 0.15 s of slack exceeded the 0.131 s defect it hid is **not a weak
|
||
check but an inverted one — the tolerance was doing the work the measurement was
|
||
supposed to.**
|
||
|
||
## The grep found two more — and the reason is my correction *habit*, not my attention
|
||
|
||
Their generalisation of my `untimed` marker is the cheap version and it works:
|
||
**search for the vocabulary the dead rule needed.** A description that survived a
|
||
fix still speaks the dead rule's language, which makes it findable without reading
|
||
anything. Swept my tree for the nouns of every rule refuted this session —
|
||
*shifted*, *no time slot*, *first-declared*, *two decoder eras*,
|
||
*standalone/composited*.
|
||
|
||
Most hits were already-quoted corrections (`check.rs`, `check-all`) or the live
|
||
guard message in `verify-screen`. **Two were real:**
|
||
|
||
| | |
|
||
|---|---|
|
||
| `verify-screen:57` | still asserted *"all four are **COMPOSITED** rather than standalone"* — the reading I withdrew a few iterations ago after they tested it disc-wide at 7.9 % |
|
||
| `boot.gd:197` | opened *"the one unknown duration per screen … the disc has no time slot there"*, then retracted it in the next sentence |
|
||
|
||
### 🔴 The diagnosis is a habit, not a lapse
|
||
|
||
This is the **third and fourth** instance, after `spin_period_units` and
|
||
`exit_ramp_units`. In every one of the four, **the correction sits below the false
|
||
claim in the same comment block, and I wrote both.**
|
||
|
||
**My corrections are additive.** I append a `🔴 CORRECTION` block and leave the
|
||
original sentence standing above it, which is right for a *record* — the change
|
||
should be visible, and their practice of quoting the original is exactly that —
|
||
but it is wrong for a *statement*. A reader takes the first assertion; the
|
||
retraction three lines later has already lost. That is why "having been bitten
|
||
four times" produced four instances rather than fewer: **the habit that creates
|
||
them is the same habit I adopted to make corrections honest.**
|
||
|
||
✅ The fix is to keep quoting the original but **demote it grammatically** — lead
|
||
with "what this used to say", so the false sentence can never be read as the live
|
||
one. Both hits rewritten that way.
|
||
|
||
✅ Verified as a comment-only edit **by artifact rather than by reading**, which is
|
||
their practice: the `main_menu` render is byte-identical before and after, and
|
||
`verify-screen` parses.
|
||
|
||
📌 So the toolkit now has **two mechanical searches**: their *hedge around a
|
||
quantity the current reader states exactly* for prose, and *the vocabulary the old
|
||
rule needed* for code. Neither requires understanding what you are looking at,
|
||
which is the point — five instances of this class survived people who understood
|
||
the material perfectly well.
|
||
|
||
### On their caution about my failed rule
|
||
|
||
⚠️ They flag that *gap + incoming clear = 16/16/18* failing on the splash pair
|
||
*"may be the honest signal rather than an exception to carve out"* — three
|
||
transitions supporting a constant and one structurally different case breaking it
|
||
is the shape of a rule fitted to its own sample. ✅ Agreed, and worth stating
|
||
plainly: **I did not carve out the exception.** The rule was rejected, not
|
||
narrowed to menu transitions, and `black_hold_units` remains escalated rather than
|
||
set from it.
|
||
|
||
## Auditing headings — and my own index was amplifying the withdrawn ones
|
||
|
||
Their third mechanical search — **audit headings first**, because they assert with
|
||
maximum reach and minimum context and a reader scanning them never reaches the
|
||
retraction — lands harder on my tree than theirs, for a reason neither of us
|
||
anticipated: **`tools/port/index-decisions` builds a table of contents *from the
|
||
headings*.** A withdrawn claim was not merely sitting above its correction; it was
|
||
being *republished* at the top of the file as a live finding.
|
||
|
||
Three hits:
|
||
|
||
| heading | status |
|
||
|---|---|
|
||
| *"The JP title capture adjudicates `title_jp` — and it goes against the port"* [refuted] | **asserted the opposite of the finding.** I scored `--pose=rest`, which the port does not ship; posed as it runs the port beats the reference +0.9994 to +0.8727 |
|
||
| *"The leaf phase was an arbitrary choice; the capture turns out to determine it"* [refuted] | **refuted 97 lines below** by the replication on `title` |
|
||
| *"Nested leaves may advance at half rate — a quantified defect in shipped output"* | **not a defect** — the rate it is quantified against is known wrong |
|
||
|
||
✅ All three now lead with the correction, with the original quoted and demoted
|
||
beneath — my own fix from last iteration, applied to the class where it matters
|
||
most.
|
||
|
||
### ⚠️ Scope, stated because the number is unflattering
|
||
|
||
I audited **the ~30 headings from this session, plus one older one I happened to
|
||
remember**. There are **211**. So roughly 180 are unaudited — and *older headings
|
||
are likelier to be stale*, not less, because they have had more chances to be
|
||
overturned. **This is a sample, not a sweep**, and calling it an audit without the
|
||
denominator would be the exact failure this whole thread is about.
|
||
|
||
📌 The generalisation their refinement earns: **an index is an amplifier.** Any
|
||
mechanism that republishes headings — a table of contents, a summary, a `--help`
|
||
listing — multiplies the reach of whatever the heading asserts, including the
|
||
things it asserts wrongly. My index was built to make decisions findable and it
|
||
was making three withdrawn claims findable first.
|
||
|
||
⚠️ Their point about *why* the additive habit fails is the one I would keep over my
|
||
own framing: **"record" and "statement" want opposite orders, and a single block
|
||
cannot be both without deciding which one leads.** That is more precise than
|
||
calling the habit wrong — it isn't wrong, it is under-specified about ordering.
|
||
|
||
## Ranking instructions above descriptions — swept, and the worst class is clean
|
||
|
||
Their sharpening: **a stale instruction manufactures a false confirmation**, which
|
||
is strictly worse than a stale description that merely misleads. Their example is
|
||
a doc naming an environment variable removed with the record-layout fix — a reader
|
||
sets something inert, gets default behaviour, and concludes the two readings
|
||
agree. So: rank instructions above descriptions when sweeping.
|
||
|
||
Applied to my tree, the instruction surface is the documented invocations in the
|
||
tool and script headers. Fifteen distinct flags appear across them.
|
||
|
||
✅ **All fifteen are parsed** — no silently ignored flag, so nothing in my headers
|
||
can produce their failure mode by being inert.
|
||
|
||
⚠️ **But "parsed" is a proxy and I know its gap**: `--shots` parses and does
|
||
**nothing** on the `--boot` path, which I found two iterations ago. Parsing is not
|
||
working. So I ran two documented examples end to end rather than trusting the
|
||
grep — `--screen=main_menu --pose=rest --capture` and
|
||
`--screen=title --overlay=press_start --time=4` — and both produce a 1280×720
|
||
frame. (`--boot --shots` is not a documented combination, which is why the gap has
|
||
not bitten a reader.)
|
||
|
||
### Two hits, both of the *loud* kind
|
||
|
||
| | |
|
||
|---|---|
|
||
| **11 references** to `tools/verify-capture` / `tools/verify-screen` | those paths do not exist; the tools are under `tools/port/`. Fixed in 4 files. |
|
||
| `check-all`: *"There are **eleven** tools under `tools/port/`"* | there are **fourteen**. Now states both, so the sentence dates itself. |
|
||
|
||
📌 **The distinction worth recording: mine fail loudly, theirs failed silently.** A
|
||
wrong path errors out and announces itself; an inert environment variable returns
|
||
a clean, wrong result. **Both are stale instructions and only one manufactures
|
||
evidence.** That is the ranking their sharpening earns, and it means my two hits —
|
||
while real — are the cheap kind.
|
||
|
||
⚠️ And the honest limit on this sweep: I tested the **flag surface**, plus two
|
||
examples end to end. I did not run all thirteen documented invocations. The `--boot`
|
||
ones take 156 s each and I judged the flag-parse check plus two spot runs
|
||
sufficient; that is a judgement about cost, not a claim of coverage.
|
||
|
||
## Live-but-undocumented flags — and I wrote a dead instruction while fixing dead instructions
|
||
|
||
Their newest class is one step past a stale instruction: **the instruction is dead
|
||
*and* the working one is undocumented.** That inverts the sweep I ran last
|
||
iteration — I checked documented → parsed; the reverse is **parsed → documented**,
|
||
and like their env vars it enumerates, so it completes rather than samples.
|
||
|
||
Eighteen flags parsed, fifteen documented, **three live and undocumented**:
|
||
|
||
| flag | |
|
||
|---|---|
|
||
| `--film-interval` | used by `verify-dwell`, in no usage example |
|
||
| `--skip-at` | same |
|
||
| **`--no-hold`** | plays a screen **past its rest** instead of clamping each element at its hold — documented in `DECISIONS.md` and **absent from the header a reader consults** |
|
||
|
||
📌 `--no-hold` is the one that matters: **a capability that exists only in an
|
||
11 000-line record is, to anyone reading the interface, a capability that does not
|
||
exist.**
|
||
|
||
### 🔴 And then I documented it wrong, in the same command
|
||
|
||
I wrote the example as `--screen=title --no-hold --time=6` and tested it. **The
|
||
two renders are byte-identical — the flag no-ops.** `--time` sets `frozen`, and
|
||
`pose_at` tests `holding and not frozen`, so an explicit instant makes `--no-hold`
|
||
inert. Without `--time` the same pair differs by **max 253**.
|
||
|
||
**I wrote a dead instruction inside the commit that fixes dead instructions**, and
|
||
the only reason it did not ship is that I ran the example instead of trusting that
|
||
a parsed flag works — the exact gap I had named one iteration earlier and then
|
||
walked into. The corrected line now carries the interaction and the measurement
|
||
that establishes it.
|
||
|
||
⚠️ This is the strongest evidence yet for their ranking. A description I get wrong
|
||
costs a reader's belief; **an instruction I get wrong hands them a null result
|
||
that looks like a finding** — here, "`--no-hold` changes nothing", which is false
|
||
and would have been reproducible.
|
||
|
||
## Their `XPR_*` lead traced and closed — and their class found in my own lane
|
||
|
||
They flagged five `XPR_*` texture-decode toggles as relevant *"since you consume
|
||
textures"*, and my off-edge splash residual — non-tonal, ~0.5 RMSE above
|
||
quantisation, **no candidate** — has exactly the shape a subtle decode difference
|
||
would produce. So it was worth tracing rather than filing.
|
||
|
||
🔴 **Closed: they cannot reach my sprites.** The toggles live in
|
||
`texture.rs::decode_surface`, documented as *"shared by `from_xpr2` and
|
||
`cube_faces_from_xpr2`"*. My exporter calls **neither** — sprites come from
|
||
`t8ad::parse`, a different module. And `t8ad.rs` reads **no environment variables
|
||
at all** in its 202 lines, so the sprite path has no hidden degrees of freedom
|
||
either. **The candidate is eliminated and no replacement takes its place**; the
|
||
residual keeps its status as named-without-explanation.
|
||
|
||
### And a live undocumented remedy in my own lane, which I had called clean
|
||
|
||
Enumerating what my exporter can reach turned up `SYLPHEED_KF_TIME_SHIFT` — the
|
||
variable they reported as *"removed with the record-layout fix, appears nowhere in
|
||
`crates/`"*. ⚠️ **True on their branch, false on mine**: my `ui_layout.rs` is the
|
||
stale era, and the knob is live at line 497. Not a contradiction — a branch
|
||
difference, and my `Cargo.toml:66` already records it.
|
||
|
||
✅ **The pinned tag has 0 occurrences of it** (and 2 of `SYLPHEED_KF_TIME_LEGACY`),
|
||
so it cannot perturb `export/`. But `verify-screen` builds its reference **from the
|
||
workspace**, which can be perturbed.
|
||
|
||
Tested both directions rather than reasoned:
|
||
|
||
| | reference reports | era guard |
|
||
|---|---|---|
|
||
| `SYLPHEED_KF_TIME_SHIFT=1` | `rest t=12` — the **corrected** reading | **passes**, eras agree |
|
||
| unset | `rest t=70` — stale | **refuses** |
|
||
|
||
📌 So the knob is not a debug curiosity: **it is the working remedy that makes a
|
||
workspace-built reference usable**, and it appeared in no tool, no help text and
|
||
no instruction anywhere in my tree. My guard said *a mismatch exists* and never
|
||
said *here is how to clear it* — **their exact class, in the lane I had just told
|
||
them was clean.** The refusal message now carries the remedy and the measurement
|
||
that establishes it.
|
||
|
||
✅ Incidental: the era guard covers an env-var route it was not designed for,
|
||
verified in both directions.
|
||
|
||
## Branches that announce themselves — their lesson, applied where it already bit me
|
||
|
||
Their salvaged iteration produced the rule I most needed: **have each branch
|
||
announce itself in the log, so a run that took the wrong path says so before its
|
||
numbers are read.** Their patch silently failed a branch condition and produced a
|
||
well-formed capture of the *wrong transition*; what caught it was **the log
|
||
lacking lines the intended branch prints**, not anything wrong with the data.
|
||
|
||
*"Assertions catch the edit; log lines catch the execution."*
|
||
|
||
I have been bitten by this twice, both times in ways an announcing branch would
|
||
have caught immediately:
|
||
|
||
* **`--no-hold` under `--time`** — I wrote it as a documented example, and the
|
||
renders were byte-identical because `--time` sets `frozen` and `pose_at` tests
|
||
`holding and not frozen`. A request silently overridden reads exactly like one
|
||
that worked.
|
||
* **the leaf clock** — I enumerated three free-running clocks, wired two, and a
|
||
run that pinned two of three looked identical to one that pinned all three.
|
||
|
||
✅ Both now announce:
|
||
|
||
```
|
||
--no-hold: INERT -- --time sets `frozen`, which overrides holding
|
||
t = 360.00 units (6.000 s), pose = timeline [frozen, loop-phase=free, leaf=free]
|
||
|
||
--no-hold: playing past the rest, not clamping at each hold
|
||
t = 9.15 units (0.153 s), pose = timeline [running, loop-phase=0.0, leaf=free]
|
||
```
|
||
|
||
📌 The second line is the more useful of the two: **every run now states the
|
||
effective configuration of all three clocks**, not the requested one. The failure
|
||
it prevents is precisely the one I shipped — pinning a subset and reading the
|
||
result as pinned.
|
||
|
||
✅ Verified the harnesses are unaffected: nothing under `tools/port/` parses that
|
||
line, and `verify-screen` and `verify-capture` return their usual rows.
|
||
|
||
### Their scope correction, accepted
|
||
|
||
⚠️ *"'Appears nowhere in `crates/`' is a claim about a tree, and I stated it
|
||
without one."* Exactly right, and it generalises the noun lesson: **a claim about
|
||
code needs its ref attached**, the same way a number needs what it is a number of.
|
||
With `main` 145 commits behind and both of us on topic branches, "the code
|
||
contains X" is underspecified by default here — which is how both of us were
|
||
correct about `SYLPHEED_KF_TIME_SHIFT` simultaneously.
|
||
|
||
## Every documented invocation verified — and one runs forever without saying so
|
||
|
||
I flagged the `--boot` family as unverified three iterations running, each time
|
||
deferring on cost (156 s per run). Done, and the deferral hid something.
|
||
|
||
| invocation | result |
|
||
|---|---|
|
||
| `--boot` | ✅ terminates at 156 s on title + plate |
|
||
| `--boot --skip-at=1` | ✅ **title at 7.80 s** against 152.54 s — the skip is real and quantified |
|
||
| `--boot --film=… --film-interval=0.5` | ✅ 375 frames written |
|
||
| `--boot --play` | ✅ hands over — *"menu on title"* at 7.77 s, then stays live by design |
|
||
|
||
### 🔴 `--boot --film=` never terminates, and the doc did not say
|
||
|
||
The boot-quit branch is gated on `_film == ""` (line ~499), and a second quit path
|
||
on the same condition. **A filming run keeps capturing past the title forever.**
|
||
Measured: title at 7.8 s, still filming at **300 s**, 375 frames.
|
||
|
||
⚠️ `verify-dwell` wraps it in `timeout`, so the behaviour was known to whoever
|
||
wrote that tool — me. But the **documented example is bare**, and a reader
|
||
following it gets a process that looks hung.
|
||
|
||
📌 That is the failure `boot.gd`'s own header warns about, committed in its own
|
||
usage block: *"it does not fail, it waits, and a job that waits forever reads as a
|
||
job still working."* The warning and the violation are in the same file, twelve
|
||
lines apart. Fixed — the example now states it runs until killed, with the
|
||
measurement.
|
||
|
||
**The deferral was the mechanism.** Three times I judged the cost too high and
|
||
recorded the judgement honestly, which felt like the careful call each time. What
|
||
it actually did was keep a non-terminating documented instruction alive for three
|
||
iterations. **"Too expensive to verify" and "unverified" are the same state, and
|
||
only one of them sounds like a decision.**
|
||
|
||
### Their correction, which strengthens my position rather than weakening it
|
||
|
||
They withdrew *"the outgoing screen determines the gap"* in favour of an ordering:
|
||
the menu has **two** values, 0 leaving for the title and 1 for EXTRAS. And a third
|
||
— menu → a pak outside `GP_TITLE` — also gives **1**, so *leaving the archive
|
||
costs no extra black*, a confound they named in advance and measured absent.
|
||
|
||
✅ For `black_hold_units` this makes **"not modelled" more robust, not less**: even
|
||
a per-outgoing-screen key would not be single-valued, since the menu alone spans
|
||
{0, 1}. The data forbade a constant; it now also forbids the obvious keyed
|
||
replacement.
|
||
|
||
🔴 **WITHDRAWN — and the refutation was in my own tree.** *(This paragraph read:
|
||
"they note EXTRAS is stuck at n=1 with no second destination in this archive — a
|
||
structural limit, not an unrun experiment. Worth recording as such: that row
|
||
cannot be strengthened by anyone.")* [refuted]
|
||
|
||
`export/` lists **three** buttons for `extras` — `ptbtn11`, `ptbtn12`, `ptbtn13` —
|
||
and **`authored/flow.json`, which I wrote**, records `ptbtn11` → **GP_MISSION_SELECT**:
|
||
a destination outside `GP_TITLE`, which is precisely the exit they have now
|
||
measured at gap 3. See the correction below.
|
||
|
||
## 🔴 I promoted an unverified claim of theirs to a fact, against data I had authored
|
||
|
||
They withdrew *"EXTRAS's sole exit is Ⓑ to the menu, so n=1 is structural"* after
|
||
one `screen info` showed build 6 declares three buttons. ⚠️ **I had recorded that
|
||
claim in this file as a limit "that row cannot be strengthened by anyone"** — and
|
||
the refutation was sitting in two files of mine:
|
||
|
||
| | |
|
||
|---|---|
|
||
| `export/screens/title/extras.json` | `buttons: ["ptbtn11", "ptbtn12", "ptbtn13"]` |
|
||
| `authored/flow.json` — **written by me** | `ptbtn11` → *"The stage list is GP_MISSION_SELECT, not in this export"* |
|
||
|
||
**`ptbtn11` leaves `GP_TITLE`.** That is exactly the second destination they said
|
||
did not exist, and exactly the one they have now measured. I did not need their
|
||
emulator or a new run — I needed to read a file I authored.
|
||
|
||
📌 This is a failure mode distinct from the rest of the session's. Not *"my claim
|
||
outran its evidence"* but **"I promoted someone else's unverified claim to an
|
||
established fact in my own record, while holding the data that refuted it."** A
|
||
message carries no evidence; the protocol says so explicitly, and I copied a
|
||
sentence out of one into `DECISIONS.md` as a finding.
|
||
|
||
✅ Their METHOD entry — *"structural" and "impossible" are the two words most worth
|
||
distrusting in your own notes, because they retire a question rather than
|
||
answering it* — is right, and I would add the corollary this instance shows:
|
||
**they are worth distrusting hardest when someone else writes them**, because
|
||
then they arrive without the doubt the author would have had.
|
||
|
||
### What the second measurement does to the result
|
||
|
||
| outgoing | gaps | n |
|
||
|---|---|---|
|
||
| menu | 0, 1, 1 | 3 |
|
||
| **EXTRAS** | **2, 3** | 2 |
|
||
| title | 3, 3, 3 | 3 |
|
||
|
||
🔴 **The ordering is weaker than what I recorded last iteration.** EXTRAS {2,3} and
|
||
title {3,3,3} **overlap at 3**, so "menu < EXTRAS < title" no longer separates
|
||
them. What survives: the outgoing screen constrains the gap to a ~2-wide band,
|
||
bands not disjoint.
|
||
|
||
✅ **One thing got stronger** — a pairwise control holding the destination class
|
||
constant: menu → another archive gives **1**, EXTRAS → another archive gives **3**.
|
||
Same kind of destination, different gap, so the destination is not the variable.
|
||
|
||
✅ `black_hold_units` stays **0 = not modelled**, and is now *better* supported: a
|
||
uniform value was already excluded, and the obvious keyed replacement is excluded
|
||
too, since neither the menu {0,1} nor EXTRAS {2,3} is single-valued.
|
||
|
||
## The half-guard they named, tested — and it found a real gap on first use
|
||
|
||
They flagged that my pose line *"reports `[frozen, loop-phase, leaf]` from the
|
||
variables in force, never checked against a pin that's set but doesn't reach the
|
||
view"* — the case `--no-hold`-under-`--time` turned out to be. I had recorded the
|
||
same doubt and not acted on it.
|
||
|
||
**The case exists and I could name it exactly: the overlay is a second
|
||
`ScreenView` with its own pins**, and the announcement read `view.*` only. The
|
||
plate carries a looping focus record — the very clock in question — and draws from
|
||
`overlay.*`.
|
||
|
||
✅ Extended the line to report the overlay's pins. 🔴 **Its first use found a real
|
||
gap:**
|
||
|
||
```
|
||
[frozen, loop-phase=0.0, leaf=0.0, overlay(loop-phase=0.0, leaf=free)]
|
||
```
|
||
|
||
**`overlay.loop_phase_units` was wired; `overlay.leaf_time_units` was not.** A run
|
||
requesting both pins had one reach the overlay and one not — and the *pre-fix*
|
||
announcement would have printed `leaf=0.0` from the main view while the overlay
|
||
drew free-running. That is their half-guard precisely: **an announcement reporting
|
||
a value it cannot resolve for the case in question.**
|
||
|
||
⚠️ **Currently inert, and worth saying so rather than claiming a save.**
|
||
`press_start` carries no leaf (`draw_leaf_for` is `ptloop01`/`ptloop02`), so the
|
||
render is byte-identical before and after. The gap was real, live for any overlay
|
||
that carries a leaf, and cost nothing today.
|
||
|
||
📌 **This is the fourth instance of their one remedy** — *put the qualifier in the
|
||
text rather than in the reader's memory*: state what the number is a number of →
|
||
write the index space into the token (`e10`) → write the source into the claim →
|
||
**state each view's effective pins rather than inferring them from the request.**
|
||
Each has now caught something the corresponding discipline did not, and this one
|
||
caught it within a minute of existing.
|
||
|
||
✅ And their asymmetry is the argument for the exchange itself: *"I'd never have
|
||
caught your `--no-hold` no-op, and you'd never have caught my 'sole exit'."*
|
||
Neither of us is the right auditor of our own retiring words. I had written the
|
||
doubt about this guard into my own file and left it there; it took someone else
|
||
repeating it back for me to run the test.
|
||
|
||
## The ordered pair determines the gap — and nothing declared predicts it
|
||
|
||
Their latest run gives **five replicates with no variation** — `title→menu` 3,3,3
|
||
and `EXTRAS→menu` 2,2 — while every *differing* value comes from a different
|
||
ordered pair. The same origin gives different values to different destinations
|
||
(menu 0 vs 1, EXTRAS 2 vs 3). **The origin constrains; the ordered pair
|
||
determines, reproducibly.**
|
||
|
||
🔴 **That excludes a second model for `black_hold_units`.** A constant was already
|
||
out; **keying on the outgoing screen is now out too.** Only an ordered-pair key
|
||
survives, needing a measured value per pair — six known, two replicated.
|
||
|
||
### My independent check: no declared quantity predicts it
|
||
|
||
They said nothing declared predicts the values. Checked from my export rather than
|
||
taken:
|
||
|
||
| pair | gap | out.close | in.clear | out.span | in.span |
|
||
|---|---|---|---|---|---|
|
||
| title → menu | 3 | 15 | 12 | 269 | 80 |
|
||
| EXTRAS → menu | 2 | 10 | 12 | 74 | 80 |
|
||
| menu → title | 0 | 10 | 16 | 80 | 269 |
|
||
| menu → EXTRAS | 1 | 10 | 12 | 80 | 74 |
|
||
|
||
✅ **Each column has two rows sharing a value with different gaps** — `out.close`
|
||
10 gives 2, 0 and 1; `in.clear` 12 gives 3, 2 and 1; `out.span` 80 gives 0 and 1;
|
||
`in.span` 80 gives 3 and 2. **No single declared quantity determines the gap**,
|
||
independently from my side.
|
||
|
||
⚠️ **And I did not search combinations of them, deliberately.** Four intra-archive
|
||
pairs against many candidate two-screen functions fits **by construction** — the
|
||
error this corpus has catalogued five times, most recently my own 16/16/18. A
|
||
formula found here would be indistinguishable from one found in noise, and I would
|
||
have no way to tell the difference with the data that exists.
|
||
|
||
📌 So the position is now: **`black_hold_units` = 0, meaning not modelled**, with
|
||
*two* candidate models positively excluded rather than one, and the surviving
|
||
shape harder than when I escalated the decision. Their advice stands and I agree:
|
||
don't key it.
|
||
|
||
## The overlay leaf-pin fix, verified live with a negative control
|
||
|
||
I recorded that fix as *"currently inert — `press_start` carries no leaf, so
|
||
nothing verifies it in a live case."* That was honest and it left the fix
|
||
unverified, which is a state I have learned this session not to leave alone.
|
||
|
||
**`title` carries the leaves**, so raising it *as* an overlay exercises the path:
|
||
|
||
| | overlay pins reported | renders at leaf-time 0 vs 4 |
|
||
|---|---|---|
|
||
| **pre-fix** (line reverted) | — | **identical** — the pin does not reach |
|
||
| **post-fix** | `overlay(leaf=0.0)` → `overlay(leaf=240.0)` | **differ, max 105.86** |
|
||
|
||
✅ **A proper before/after**: the negative control shows the failure the fix
|
||
removes, rather than only showing the fixed state working. Pre-fix the overlay's
|
||
leaf read the frozen `time_units` in both runs, so the two were identical — which
|
||
is exactly why the gap was invisible until the announcement exposed it.
|
||
|
||
⚠️ **The configuration is synthetic.** `--overlay=title` over `main_menu` is
|
||
something the game never does. What it tests is the *wiring* — whether a requested
|
||
pin reaches a second `ScreenView` — which is screen-independent, so the result
|
||
transfers even though the picture does not.
|
||
|
||
### A process failure worth keeping
|
||
|
||
I reverted the fix with a text patch to run the control, and **the restore script
|
||
half-failed**: it removed the line and then threw on the way to putting it back.
|
||
The tree was left without the fix.
|
||
|
||
🔴 **What caught it was two independent checks disagreeing.** `grep -c` reported
|
||
the fix **absent** while the render comparison reported the output **matching the
|
||
fixed run**. Both cannot be true. Had I printed only the render check — the one I
|
||
actually cared about — I would have concluded the restore worked and carried on
|
||
with a silently reverted file.
|
||
|
||
✅ Restored with `git checkout` rather than re-patching, and confirmed clean:
|
||
tree clean, fix present, and the verification re-run gives the same 105.86.
|
||
|
||
📌 **Reverting a committed change by editing text is choosing the fragile path
|
||
when the robust one is one command away.** Same shape as reading a proxy when the
|
||
thing is one command away — and the same remedy: use the mechanism that cannot be
|
||
half-right.
|
||
|
||
## Their incoming-primitive observation, checked — and a sharpening they can use
|
||
|
||
They offered, *with its counter-example attached rather than fitted*, that the
|
||
incoming screen's full-screen primitive is `[255]` where the gap is 0 and `[127]`
|
||
where it is 1 — a screen beginning from opaque black needing no blank frame. And
|
||
that it **fails on `menu → EXTRAS`**, which declares a black backdrop and still
|
||
gives 1.
|
||
|
||
⚠️ **My first check got it wrong and would have dissolved their counter-example.**
|
||
I took the *first* full-screen primitive in element order and reported `extras`
|
||
arriving at alpha **64**, which would have made it not a `[255]` incoming at all.
|
||
`extras` has **two** such primitives; I read one and called it the screen's.
|
||
|
||
✅ Corrected — and all three `GP_TITLE` screens are identical in this respect:
|
||
|
||
| screen | primitives at t=0 |
|
||
|---|---|
|
||
| `title` | `pteff00`=**255** (paints 24th, last), `pteff02`=64 (paints 5th) |
|
||
| `main_menu` | `pteff00`=**255** (paints 16th, last), `pteff02`=64 |
|
||
| `extras` | `pteff00`=**255** (paints 18th, last), `pteff02`=64 |
|
||
|
||
**Every one opens with an opaque black quad painted on top**, clearing over 12–16
|
||
units. So `extras` does arrive at 255 and their counter-example stands.
|
||
|
||
📌 **The sharpening: within `GP_TITLE` this quantity is constant, so it cannot
|
||
explain any variation among the four intra-archive pairs.** It could only ever
|
||
separate `GP_TITLE` screens from the outside ones (255 against 127) — which is a
|
||
much narrower claim than "begins from black ⇒ no blank frame", and it is already
|
||
contradicted by `menu → EXTRAS` = 1 against `menu → other-2` = 0, both arriving at
|
||
an opaque incoming.
|
||
|
||
✅ And their new pair kills the origin story outright from my side too: the menu
|
||
now gives **{0, 1, 1, 0}** across four destinations — both extremes from one
|
||
origin — while the two repeated pairs stay internally identical. `black_hold_units`
|
||
unaffected: constant excluded, origin-keyed excluded, pair-keyed surviving with
|
||
seven pairs known and two replicated.
|
||
|
||
⚠️ **I am not pursuing the incoming-primitive idea either**, and for their reason
|
||
rather than a new one: nine transitions against many candidate two-screen
|
||
functions is the construction we have both now declined once each. The difference
|
||
between declining and not-having-looked is only visible if someone says so, which
|
||
is why they said so and why I am repeating it.
|
||
|
||
## `PORT-MISSION.md` had two stale blockers — the file I am told to read every iteration
|
||
|
||
Their finding was that `MISSION.md` carried three stale headings while they had
|
||
audited headings, instructions, env vars and fallbacks *everywhere else*. Their
|
||
diagnosis is the transferable part: **a document read only for instructions is
|
||
never read for review, and the more central it is the more often it is consulted
|
||
and the less often checked.**
|
||
|
||
The brief names **five** documents to read every iteration. I have audited
|
||
`BLOCKED.md` (struck five rows) and **never** `PORT-MISSION.md` or `MODDING.md`.
|
||
|
||
🔴 Two stale blockers, in the table I am instructed to consult *to find the lowest
|
||
unfinished milestone*:
|
||
|
||
| | said | actually |
|
||
|---|---|---|
|
||
| **P2** | *"Blocked on HANDOFF Q1 (the time unit). Do not invent it"* | Q1 is **✅ answered** — ramp linear, 2 units/frame, 1 unit = 1/60 s settled. P2 shipped long ago |
|
||
| **P6** | *"Looping is blocked on HANDOFF Q10"* | Q10 is **✅ answered** — two stems played together. The P6 gate is recorded as verified |
|
||
|
||
**A reader following the instruction would look at P2, read "do not invent it",
|
||
and treat an answered question as open.** That is worse than a stale heading in a
|
||
record: it is a stale *instruction* in a document whose purpose is instruction —
|
||
the class we ranked highest.
|
||
|
||
### Scope, because this file is not mine
|
||
|
||
⚠️ `PROTOCOL.md`: *"The mission files are the only authority, and only the human
|
||
changes a mission."* So I corrected **the factual status clause and nothing else**
|
||
— every gate, every requirement and every ask is untouched, and the original text
|
||
is struck through rather than deleted so the change is visible and trivially
|
||
reversible. If a human reads this as a mission change rather than maintenance,
|
||
revert the two table cells; nothing else moved.
|
||
|
||
📌 Their own line for it is the right one: **keeping it true is maintenance;
|
||
changing what it asks would be overstepping.**
|
||
|
||
⚠️ `MODDING.md` is still unaudited. I am naming that rather than quietly finishing
|
||
one of two.
|
||
|
||
### `MODDING.md` — audited, clean, and mechanically so
|
||
|
||
Finished the second half rather than leaving it named. `MODDING.md` carries **no
|
||
status markers at all** — no 🔴, no "not yet", no "planned" — so there is nothing
|
||
of the stale-blocker shape in it. But their finding was about *claims*, not
|
||
markers, so the real question is whether its assertions still hold.
|
||
|
||
✅ It states **five rules**, and `tools/port/check-modding` asserts **all five by
|
||
name** — one section per rule — and passes in `check-all`. So those claims are not
|
||
merely *unstaled*, they are **mechanically verified on every suite run**, which is
|
||
a stronger result than reading them and finding nothing wrong.
|
||
|
||
📌 So of the five documents the brief names, three are mine to audit:
|
||
`BLOCKED.md` (five rows struck, earlier), `PORT-MISSION.md` (**two stale blockers,
|
||
corrected above**), `MODDING.md` (clean, checker-backed). `PROTOCOL.md` and
|
||
`HANDOFF.md` are not mine to correct — and `HANDOFF.md` as I read it is the stale
|
||
`main` copy, which is its own recorded problem.
|
||
|
||
### Their "ranked list always has a winner" — checked against my own matching
|
||
|
||
They tried to identify the ninth pair's destination and **rejected their own
|
||
result**: best fits 43.30 (margin 5.88) and 45.74 (margin 2.28) against a
|
||
calibration putting a true match at ~18–20 with margin ~10. The general form is
|
||
worth keeping: **a ranked list always has a winner, and nothing in the ranking
|
||
says whether the winner is good enough — any nearest-match report needs a
|
||
known-good score beside it or it will name something every time it is asked.**
|
||
|
||
✅ Checked my own instance. When I identified their submenu capture as
|
||
`GP_TUTORIAL` by edge correlation, I ran the control **first**: my own `title`
|
||
capture over seven builds with a known answer, picking build 4 at **+0.2792**,
|
||
1.4× over second. The submenu then scored **+0.4962** with a 1.58× margin —
|
||
*above* the calibrated true-match score, not merely top of a list. That
|
||
identification carries its known-good; I have no other nearest-match report in the
|
||
tree.
|
||
|
||
## Their `REFUTED.md` gap, in my tree — where I already had the mechanism and fed it nothing
|
||
|
||
Their finding: eight claims died this session and **none reached `REFUTED.md`**,
|
||
the file their brief says to grep before proposing anything. Their split is the
|
||
transferable part — **the pages are where a refutation is *argued*; the index is
|
||
where it is *found*** — and only the second saves anyone.
|
||
|
||
🔴 **Mine is the same gap and worse in one respect.** I have
|
||
`tools/port/check-claims`: a register that **fails the run** if a refuted claim is
|
||
quoted without its `[refuted]` token, and it is in `check-all`. So an entry there
|
||
does not merely publish a death — it *enforces* it. **It held 7 rows, all from
|
||
earlier work, and I added none while withdrawing ~8 claims this session.** Their
|
||
index would have been consulted; mine would have been enforced, and I fed it
|
||
nothing.
|
||
|
||
### What happened when I fed it
|
||
|
||
Registered four of this session's withdrawals. The checker immediately flagged
|
||
**three still asserted unmarked** — and every one was inside a *correction*:
|
||
|
||
| | where |
|
||
|---|---|
|
||
| *"and it goes against the port"* [refuted] | my own headings-audit table, in the row explaining it asserted the opposite of the finding |
|
||
| *"the capture turns out to determine it"* | the next row of the same table |
|
||
| *"a structural limit, not an unrun experiment"* | inside the withdrawal block that retracts it |
|
||
|
||
📌 **That is the token doing what phrasing cannot.** All three read as corrections
|
||
to a human — they *are* corrections, written by me, saying so explicitly — and the
|
||
marker fired anyway, because it tests for a token an author must place rather than
|
||
for language that sounds retracted. The header's claim that *"its absence means
|
||
exactly one thing"* is now demonstrated rather than asserted.
|
||
|
||
✅ Marked; the register passes: *"every refuted claim appears only inside its
|
||
correction."*
|
||
|
||
⚠️ **Scope: I registered four of roughly eight.** Not registered — the compactness
|
||
precondition, the half-rate "defect", "the eras render identically", and my
|
||
16/16/18 gap-plus-clear rule. Each is argued in its own correction here and
|
||
**findable by nobody**. I stopped at four because each row costs marking every
|
||
existing quotation by hand, which is the honest price the header names; the
|
||
remaining four are named so the shortfall is visible rather than implied.
|
||
|
||
📌 And their observation about my `check-modding` standard applies right back:
|
||
**nothing mechanically checks that a future withdrawal reaches the register.** The
|
||
register enforces claims it knows about; knowing about them is still manual. That
|
||
is the same gap they named in themselves, one level up, and I have not closed it
|
||
either.
|
||
|
||
## Building the withdrawal-time hook — the thing we agreed neither of us was about to close
|
||
|
||
They ended with: *"it needs a hook at withdrawal time, not a sweep."* True, and it
|
||
turned out to be expressible, because a correction here has a **shape**: a heading
|
||
carrying `WITHDRAWN` / `CORRECTION` / *refuted*. **A correction section containing
|
||
no registered phrase is a death that was argued and never indexed.**
|
||
|
||
`check-claims` now reports them. Its first run names **more than my "four of
|
||
eight"** — the shortfall runs back through earlier work, not just this session.
|
||
|
||
🟡 **Reported, not asserted, and deliberately.** Not every correction retires a
|
||
*claim* — some fix a number, a scope, a wrong floor. Forcing a register row for
|
||
those would push rows in to silence the check, which is the failure this file
|
||
exists to prevent. It names candidates; a person decides.
|
||
|
||
### Two failures while building it, both worth more than the tool
|
||
|
||
🔴 **The first version pasted the register rows into its own heredoc** — so every
|
||
registered phrase became an unmarked quotation, and `check-claims` flagged **its
|
||
own source**. A tool that violates the rule it enforces *by being written*. Fixed
|
||
by passing the register through the environment instead of duplicating it. The
|
||
irony is the useful part: the check was right, and the thing it caught was me.
|
||
|
||
🔴 **And writing up the previous catch re-introduced three unmarked quotations.**
|
||
Describing a refuted claim quotes it, so every correction is itself a new
|
||
occurrence needing the token. **The cost is recursive**, which the header's *"every
|
||
quotation must be marked by hand"* implies but does not say out loud. Marked; the
|
||
register passes.
|
||
|
||
📌 What the hook does *not* do: it fires when a correction section is written,
|
||
which is still after the fact — it cannot fire when a claim is withdrawn in a
|
||
message and never written down at all. **It closes the gap between arguing and
|
||
indexing, not the gap between believing and arguing.** That second one is the one
|
||
that let me copy their "structural" claim into my record; nothing here would have
|
||
caught that.
|
||
|
||
## Applying "a correction is a new claim" to my own most recent correction
|
||
|
||
Their rule, from replacing a stale status with an unchecked one *in the edit that
|
||
criticised the document for unchecked status*: **a correction is a new claim and
|
||
needs the same check as the claim it replaces.** The urge to correct supplies
|
||
confidence the correction has not earned, and the risk is highest when the edit is
|
||
*about* checking.
|
||
|
||
I made that shape of edit last iteration — correcting `PORT-MISSION.md`'s P2 and
|
||
P6 blockers. Checking my own work against their rule:
|
||
|
||
✅ **The blocker halves were checked.** I grepped HANDOFF and confirmed Q1 and Q10
|
||
both read *"✅ answered"* before writing that they were.
|
||
|
||
🔴 **The gate half was not.** My correction also asserts **"Gate met"** for P2, and
|
||
**there is no P2 gate record in `DECISIONS.md` at all.** I wrote it from
|
||
confidence. Their failure exactly, in my most recent edit, discovered only because
|
||
they named the shape.
|
||
|
||
### Resolved by measuring rather than withdrawing
|
||
|
||
`ptbtn01` declares y **142 → 162** across t=28…34. Rendering `main_menu` at both
|
||
instants:
|
||
|
||
| | |
|
||
|---|---|
|
||
| changed region | **307×215 at (542, 162)** — x and final y matching the declared button exactly |
|
||
| max difference | **159** |
|
||
|
||
✅ **The port does slide the buttons in. P2's gate is met — now on evidence rather
|
||
than on my say-so.**
|
||
|
||
⚠️ **And being right is the dangerous part.** My unchecked assertion happened to be
|
||
true, which is precisely the case that does not announce itself: had it been
|
||
false, the next reader would have inherited it from a document I had just
|
||
corrected *for carrying unchecked status*. Correct-by-luck and correct-by-checking
|
||
are indistinguishable in the text.
|
||
|
||
### What the check turned up on its own
|
||
|
||
🔴 **P0, P2 and P5 have no gate record** in `DECISIONS.md`, while P1, P3, P4, P6
|
||
and P7 do. The mission states every milestone is gated by an artifact, *"never by
|
||
'it compiles'"* — three of the eight have no artifact written down.
|
||
|
||
⚠️ I have verified **P2** here. **P0 and P5 remain unrecorded and unverified**, and
|
||
I am naming that rather than fixing one and implying three. P5's gate is *"a human
|
||
clicks through it"*, which I cannot self-certify at all.
|
||
|
||
📌 Their boundary is the honest limit and it holds for this instance: my correction
|
||
was written, indexed, and would have passed my own withdrawal-time hook cleanly.
|
||
**Neither mechanism tests whether a correction is true — only whether it is
|
||
recorded.** They enforce bookkeeping and cannot enforce accuracy.
|
||
|
||
## P0 gate — recorded at last, and the gap it belongs to
|
||
|
||
They were right that the P0/P2/P5 finding matters more than the P2 fix. **P0 is
|
||
the one of the three I can close alone** — its gate names no human and no
|
||
emulator: *"`export/screens/title/main_menu.json` validates against FORMAT.md and
|
||
the PNGs open."*
|
||
|
||
| | |
|
||
|---|---|
|
||
| the named file | exists, **51 011 bytes** |
|
||
| validation | **16 screens validate against `sylpheed.screen/3`**, that file among them |
|
||
| sprites it references | **20** |
|
||
| open as PNG | **20** — 0 missing, 0 unreadable |
|
||
|
||
✅ **P0's gate is met, on an artifact, and now written down.** It had been met for
|
||
a very long time; what was missing was the record.
|
||
|
||
### The shape this belongs to
|
||
|
||
📌 That is the **argued-versus-indexed split one level up**. The refutation
|
||
register taught it about deaths: the page is where a refutation is argued, the
|
||
index is where it is found, and I had eight arguments and no index entries. **Here
|
||
the *milestone* was completed and never indexed** — the work existed, the artifact
|
||
existed, the gate record did not. Same failure, different object.
|
||
|
||
⚠️ **Remaining, and stated rather than quietly finished:**
|
||
|
||
* **P2** — verified last iteration (buttons slide: changed region 307×215 at
|
||
(542, 162), matching the declared button), recorded there.
|
||
* **P5** — *"a human clicks through it."* ❌ **I cannot self-certify this and will
|
||
not try.** A gate written to require a person is not satisfied by me deciding it
|
||
looks fine, and converting it into something I *can* check would be rewriting
|
||
the gate to fit the checker — which is the mission's own warning about gating on
|
||
"it compiles", in a more flattering costume.
|
||
|
||
📌 So: two of the three closed with artifacts, one left open **by its own terms**.
|
||
Their line about P5 is the right one — *the right kind of thing to leave standing
|
||
rather than quietly satisfy.*
|
||
|
||
## Their sufficiency gap, run on `authored/` — clean, after I nearly reported 35 false positives
|
||
|
||
Their audit found 48 citations resolving and 0 missing, with the caveat that
|
||
matters: *"it cannot see data a page should have cited and did not — a page citing
|
||
nothing would have passed as 0 missing. Existence and substance, never
|
||
sufficiency."*
|
||
|
||
The port-side analogue is exact. My earlier audit checked **what a MEASURED stamp
|
||
cited**; it could not see an authored value carrying **no `why` at all**, which
|
||
passes every such check by being absent. The mission requires *every authored
|
||
entry carries a `why`*, so that absence is the thing to look for.
|
||
|
||
🔴 **First pass: 35 of 131 values flagged as bare.** Inspecting before reporting —
|
||
every sample was a false positive:
|
||
|
||
* `ptbtn01`'s `label` and `goto` have no `why` key, and the object carries
|
||
**five** `*_why` siblings (`skipped_chain_why`, `then_video_why`,
|
||
`unobserved_why`, `skippable_why`) plus a `blocked` explanation.
|
||
* `/voice/stream_weights/…/position` is covered by a `_` key **one level above
|
||
it**, which my check only looked for in the same object.
|
||
|
||
✅ **Ancestor-aware, the real number: 126 values, 0 uncovered.** Every authored
|
||
scalar has a `why`, `_` or `*_why` in its own object or an ancestor.
|
||
|
||
📌 **35 was the instrument's resolution, not a finding** — and this is the third
|
||
instance in one exchange: their 9 raw hits → 2 real, my 33 hook candidates against
|
||
a real shortfall of a few, and now 35 → 0. **A first count from a new detector is
|
||
a measurement of the detector.** All three of us stopped and inspected rather than
|
||
publishing the raw number, which is the only reason none of them became a claim.
|
||
|
||
⚠️ **And their caveat transfers unchanged, so I will state it rather than enjoy the
|
||
clean result:** this tests that a `why` exists **in scope**, not that it *explains
|
||
that value*. A parent `_` covering twenty values may say nothing about any one of
|
||
them. Existence and scope, never sufficiency — the same limit they named, and I
|
||
have no better instrument for it either.
|
||
|
||
## Their absence shape on my own citations — and the wording gap in my P0 closure
|
||
|
||
Their finding was about their own audit rather than their corpus: evidence exists
|
||
in **three forms** — data files, inline tables, committed tests — and their check
|
||
looked for one, so *"48 citations, 0 missing"* was a statement about the data-file
|
||
form. **They reported it in the wording rather than the scope.**
|
||
|
||
The analogue I could run: **do my own citations resolve?** 32 distinct file paths
|
||
cited in `DECISIONS.md`; **12 do not resolve.** Inspected before publishing — the
|
||
fifth instance of that habit in this exchange — and most are not findings:
|
||
|
||
| kind | example |
|
||
|---|---|
|
||
| relative fragments quoted mid-sentence | `title-builds/live-title-press-a.png` (full path exists) |
|
||
| the Decoder's files, on their branch | `docs/re/data/b-on-main-menu.txt` |
|
||
| a historical absolute path | `/reborn/docs/re/captures/…` |
|
||
| a hypothetical modding example | `data/mods/sprites/…` |
|
||
|
||
✅ **Genuine: five stale citations** from the `docs/` → `docs/port/`
|
||
reorganisation — `docs/port/BLOCKED.md` ×4 and `docs/port/FORMAT.md` ×1. A reader following
|
||
them fails. Rewritten.
|
||
|
||
### 🔴 And the one that reached a claim I made last iteration
|
||
|
||
**P0's gate says the export *"validates against FORMAT.md"*. My closure reported
|
||
the validator saying *"16 screens validate against `sylpheed.screen/3`"*.** Those
|
||
are different words, and I certified the gate on one while quoting the other —
|
||
**their exact failure, in the gate closure I published as verified.**
|
||
|
||
✅ Checked rather than assumed: `docs/port/FORMAT.md` is **405 lines** and the
|
||
string `"format": "sylpheed.screen/3"` appears in it as the specification. So the
|
||
schema the validator enforces **is** the one FORMAT.md defines, and the closure
|
||
stands.
|
||
|
||
⚠️ But it stood on an unstated identity. Had FORMAT.md described a different or
|
||
superseded format, my P0 closure would have been a confident artifact-backed
|
||
certification of the wrong thing — and nothing in the check I ran would have said
|
||
so, because the validator's output never mentions FORMAT.md at all.
|
||
|
||
📌 **The general form, now with both instances: verifying in the tool's vocabulary
|
||
and certifying in the gate's vocabulary is a substitution nobody performs
|
||
explicitly.** It is the noun problem again — the number was right, the thing it
|
||
was a number *of* went unstated — and this time the two nouns happened to denote
|
||
the same object.
|
||
|
||
## The off-edge splash residual, localised — three mechanisms ruled out, one honest description
|
||
|
||
The last open technical question I own: after excluding glyph edges, the splashes
|
||
differ from the game by 0.82–1.42 RMSE — ~2× the double-quantisation floor,
|
||
**non-tonal**, and with no candidate since the `XPR_*` texture toggles turned out
|
||
not to reach `t8ad::parse`.
|
||
|
||
**Tested the one signature left: is it positional?** A sub-pixel or resampling
|
||
difference makes the residual track the local gradient. Rule stated first: r > 0.5
|
||
to call it gradient-linked.
|
||
|
||
| | gradient | brightness *(control)* |
|
||
|---|---|---|
|
||
| `publisher_logo` | +0.109 | +0.047 |
|
||
| `developer_logos` | +0.307 | **+0.471** |
|
||
|
||
🔴 **Rejected.** Neither meets the bar, they disagree by 3×, and the control
|
||
settles it: on `developer_logos` **brightness correlates more strongly than
|
||
gradient**, so the gradient signal is not distinguishable from *"content is where
|
||
things happen"*. **Not positional.**
|
||
|
||
### Where the residual actually lives
|
||
|
||
The brightness correlation pointed somewhere better. Signed residual
|
||
(render − capture) by capture brightness, off-edge:
|
||
|
||
| band | 0–15 | 16–47 | 48–95 | 96–159 | 160–255 |
|
||
|---|---|---|---|---|---|
|
||
| `publisher_logo` | **−0.00** | +1.68 | −1.09 | −2.48 | +0.45 |
|
||
| n | **843 025** | 542 | 951 | 312 | 12 743 |
|
||
| `developer_logos` | **−0.02** | +2.19 | +0.74 | −1.57 | −0.96 |
|
||
| n | **812 111** | 26 196 | 4 498 | 5 232 | 2 832 |
|
||
|
||
📌 **98 % of the off-edge area has a residual of essentially zero.** The entire
|
||
0.82–1.42 RMSE comes from the ~2 % of pixels that are lit — the logo interiors.
|
||
|
||
⚠️ And within those, the sign is **inconsistent across bands and across screens**:
|
||
`publisher` runs +1.68, −1.09, −2.48, +0.45 while `developer` runs +2.19, +0.74,
|
||
−1.57, −0.96. **Not a global gain, not a global curve** — which is why fitting one
|
||
never helped, and is consistent with the earlier finding that a per-level LUT
|
||
fitted on its own pixels improved them by 1.6 %.
|
||
|
||
### What this is worth
|
||
|
||
✅ Three mechanisms are now ruled out with evidence: **global tone** (a curve
|
||
fitted on its own data barely moves it), **texture decode** (the toggles do not
|
||
reach the sprite path), **positional** (gradient loses to brightness).
|
||
|
||
✅ And the description is far better than "0.5 RMSE, no candidate": **the port
|
||
matches the game exactly across 98 % of the off-edge area, and differs by 1–2.5
|
||
levels inside the lit logo, with no consistent direction.**
|
||
|
||
⚠️ Still no mechanism. That is now a much smaller and better-posed question than
|
||
it was — but naming what it is *not* four times over is not the same as finding
|
||
what it is, and I am not going to invent a fifth candidate to close it.
|
||
|
||
## Full regression after a session of edits — and the phase term moving two published rows
|
||
|
||
I had changed `boot.gd`, `screen_view.gd`, four tools and two authored files
|
||
without a full suite run. Ran it.
|
||
|
||
✅ **Every asserting check passes** — format-validator, modding-rules,
|
||
capture-controls, menu-audio, decisions-index, refuted-claims. `verify-screen`'s
|
||
two DIFFERS are the named pair with their per-screen reasons.
|
||
|
||
### 🟡 Two oracle rows moved, and not as a regression
|
||
|
||
| row | before | now |
|
||
|---|---|---|
|
||
| `title_plate` | 12.83 / **0.00 %** | 13.04 / **0.09 %** |
|
||
| `title_band` | 15.31 / **0.35 %** | 12.86 / **0.00 %** |
|
||
|
||
**They moved in opposite directions**, which is the signature of a *phase change*
|
||
rather than a regression — and the cause is mine: adding `--leaf-time=0` to
|
||
`verify-capture`'s render sites pinned the sweeps to one pose, and the captures
|
||
froze them at whatever pose the shutter caught.
|
||
|
||
📌 **This makes the capture-phase term concrete rather than theoretical.** I
|
||
documented ±5.56 RMSE for `title` from a sweep; here it moved two *published* rows
|
||
by 0.09 and 0.35 percentage points of differing area, in opposite directions, from
|
||
a one-line harness change. The annotation was not decoration.
|
||
|
||
⚠️ **And it touches a number I published.** My boot-end-frame verification quoted
|
||
**0.00 % differing**, measured before the pin, with the boot's leaf free-running.
|
||
The equivalent row now reads 0.09 %. Both are inside the stated term, and **the
|
||
right reading is that neither is "the" number** — a row containing a sweeping leaf
|
||
has a phase-dependent value, and quoting either without the term attached is the
|
||
error the annotation exists to prevent.
|
||
|
||
## Narrowing my own hook — 33 was a measurement of the regex
|
||
|
||
The withdrawal-time hook reported **33** correction sections registering nothing.
|
||
I called that a detector measurement at the time and then left it standing, which
|
||
is the same shape as everything else this session.
|
||
|
||
🔴 Its regex matched headings **about** corrections, not headings **making** them:
|
||
*"Resolved by measuring rather than **withdraw**ing"*, *"Their `REFUTED`.md gap"*,
|
||
*"Building the **withdrawal**-time hook"*. Narrowed to a leading
|
||
`WITHDRAWN`/`CORRECTION`/`Refuted` or an explicit *"is withdrawn"*.
|
||
|
||
✅ **33 → 10, and every one of the ten is a genuine retraction.** The list is now
|
||
actionable where it was noise.
|
||
|
||
⚠️ **A residual limit worth naming:** several of the ten are flagged because the
|
||
registered phrase does not appear *in that section* — the corrected JP heading
|
||
reads *"does **NOT go** against the port"*, which does not contain the registered
|
||
*"goes against the port"* [refuted]. **The register wants the claim quoted; a good correction
|
||
paraphrases it away.** Those two pull against each other, and I do not think the
|
||
tension resolves — it is the cost of a substring register, like the 0.32 collision
|
||
that made that claim unregistrable.
|
||
|
||
## Their Q10 correction checked, and the register's cost is per-*mention*, not per-correction
|
||
|
||
✅ **Their stale Q10 row does not reach me.** My `stems_why` reads *"a bank is
|
||
exactly **TWO** waves of identical duration"* — the corrected understanding, not
|
||
the three-sub-waves row they withdrew — and the discrepancy is already recorded in
|
||
this file as refuted. `stems: "sum"` is unchanged, which is what they said it
|
||
should be.
|
||
|
||
✅ **Nor do I cite their coherence discriminator anywhere.** They flagged it
|
||
because its own control showed L-vs-R within a single wave reading only 0.22–0.50,
|
||
so the test's premise fails in this material. Nothing of mine depends on which
|
||
*kind* of second stem it is — only that both play, aligned at sample 0.
|
||
|
||
### Their paraphrase resolution, adopted
|
||
|
||
The register-versus-paraphrase tension: *"keep the dead phrase quoted verbatim in
|
||
`REFUTED.md` and paraphrase freely everywhere else — they are different documents,
|
||
so it costs the correction nothing."* ✅ Right, and it resolves the *prose* half
|
||
cleanly: the phrase always has one exact home without any correction having to
|
||
carry it.
|
||
|
||
⚠️ It does **not** resolve my hook, and I have written that limit into the tool
|
||
rather than chasing it: the hook detects *"does this section contain a registered
|
||
phrase"*, so it **will always over-report on well-written corrections**. Its
|
||
candidate list mixes *never registered* with *registered and paraphrased* and
|
||
cannot separate them — **a prompt to check, never a defect count.**
|
||
|
||
### 🔴 Fourth instance of the recursive cost, and it happened while I documented it
|
||
|
||
Writing that comment **quoted a registered phrase**, and `check-claims` failed. So
|
||
did my previous entry, which quoted the phrase while explaining that the corrected
|
||
heading no longer contains it. Both marked.
|
||
|
||
📌 So the honest statement of the cost is sharper than the header's: it is not
|
||
per-*correction*, it is **per-mention** — and mentions multiply once the register
|
||
becomes a subject of discussion. Every time I write about a dead claim I create a
|
||
new occurrence needing the token, including in the sentence explaining that this
|
||
happens. **Four instances, each inside text about the mechanism.** That is not a
|
||
reason to drop the token — its absence still means exactly one thing — but the
|
||
cost curve is steeper than "mark it once when you retire it".
|
||
|
||
## The contract I read every iteration is 3 185 lines shorter than the contract
|
||
|
||
📌 **`docs/port/HANDOFF.md` on `main`: 926 lines, last touched `9ca1eb5`, 2026-08-29.
|
||
The live one: 4 111 lines, `27938aa`, today. 96 commits I have never read,
|
||
+3 930/−745.** The mission tells me to read HANDOFF every iteration and I have.
|
||
I have been reading `main`'s copy. The Decoder writes it on
|
||
`origin/auto/no-disc-and-menu-captures`, which `main` is a hundred-odd commits
|
||
behind, so the contract and the copy of the contract I open have been diverging
|
||
for two days.
|
||
|
||
Several of those commits are addressed to me by name — *"handoff: deliver the
|
||
concurrent-streams refutation **to the page the port reads**"*, *"handoff: tell
|
||
the port its refusal found a decoder defect"*. They were delivered to the page I
|
||
read. The page I read is not the page they were delivered to.
|
||
|
||
### 🔴 The instruction that was supposed to prevent this cannot detect it
|
||
|
||
`BLOCKED.md`'s own header says rows rot because they carry no derivation sha, and
|
||
the standing rule is to record the HANDOFF commit each row derives from. I built
|
||
`tools/port/blocked-provenance` to supply them from history rather than memory —
|
||
`git log -S` on each row's key phrase gives the commit that introduced it — and
|
||
the answer is that **every one of the 27 open rows derives from `9ca1eb5`**,
|
||
because HANDOFF-on-`main` has not moved since. A constant cannot discriminate.
|
||
|
||
So the sha the rule asks for is the one field guaranteed to be identical on a
|
||
fresh row and a rotten one. **The rot is not that rows are old. It is that the
|
||
document they derive from is frozen while the thing it is a copy of moves.**
|
||
|
||
⚠️ And my own `BLOCKED.md` asserts *"HANDOFF has not moved in four milestones" [refuted]*.
|
||
**That is withdrawn.** HANDOFF has moved 96 times. It has not moved *on `main`*,
|
||
and I wrote the observation up without the qualifier that carried all of its
|
||
meaning.
|
||
|
||
### What the tool measures instead, and the control that caught it lying
|
||
|
||
Counted against every ref rather than my own ancestry, each row has **196 unread
|
||
`docs/re/` commits** behind it — again identical for every row, because none of
|
||
that branch is my ancestor. A number that is the same everywhere is a property of
|
||
the *document*, not of a row.
|
||
|
||
To make it per-row, the tool ranks the unread commits by word overlap with each
|
||
row. **The first version silently missed its own known positive.** `P6 looping`
|
||
asks where the menu loop restarts; `712cac8` measures it at 9.44 s and this port
|
||
has shipped that value since. The pair scored zero: `looping` did not stem to
|
||
`loop`, `menu` was stoplisted, and the `≥2 shared words` threshold dropped what
|
||
was left.
|
||
|
||
The threshold was the defect, not the constant. **Two common words scored the
|
||
same as two rare ones**, in a corpus where nearly every subject says *menu*.
|
||
Weighting each shared stem by `log(N / subjects containing it)` lets one rare word
|
||
outrank two common ones and **removes the cutoff altogether** — the list is
|
||
ranked and fixed-length, so nothing is decided by a number I could have tuned.
|
||
The control then passes at **rank 1 of 7**, and it passed without touching the
|
||
stoplist, which is the difference between fixing an instrument and fitting it.
|
||
|
||
📌 **Every discard is now counted**: struck rows not scanned, scoring pairs below
|
||
the cut, stoplisted words that can never match. The Decoder reached the same rule
|
||
from the opposite failure the same day — their checker's suppression path was
|
||
silent and its clean runs were therefore unfalsifiable, while mine over-reports
|
||
loudly. **A detector that can drop a candidate without saying how many must not
|
||
be believed when it reports zero.**
|
||
|
||
### It immediately found two open rows whose answers were already written
|
||
|
||
| row | unread commit | |
|
||
|---|---|---|
|
||
| `P3 — the plate's PULSE` | `07e93ce` (score 14.0) | the period is **120, not 105** |
|
||
| `P5 — Ⓑ on the main menu` | `9a10258` (score 10.6) | the menu has **no idle self-return** — the row's own reasoning is refuted |
|
||
|
||
Both had sat unread for a day. Neither needed an experiment; they needed the
|
||
document to be looked at.
|
||
|
||
## A refutation attempt on `+0x08 is the loop length` — it survives, and the port adopts it
|
||
|
||
The claim the port was about to build on, so the one to attack (PROTOCOL:
|
||
*refutation is cheapest where the other agent is most confident*). HANDOFF
|
||
`27938aa`, delivered at `07e93ce`: a nested record is itself a RATC bundle, its
|
||
header's `+0x08` is the **loop length**, and the plate's glow therefore cycles
|
||
over 120 units while its keyframes end at 105 — *"🔴 So stop shipping 105."*
|
||
|
||
I re-ran **their own two controls** on my own reading of the disc rather than
|
||
taking the census —
|
||
`cargo run -p sylpheed-export --example record_loop_control`:
|
||
|
||
| | disc-wide | |
|
||
|---|---|---|
|
||
| timed nested records | 1 781 | |
|
||
| `+0x08 == max t` | 1 643 | 92.3 % |
|
||
| `+0x08 > max t` (a hold) | 138 | 7.7 % |
|
||
| **`+0x08 < max t`** | **0** | **0.00 % — the falsifier never fires** |
|
||
|
||
Identical to their figures. The falsifier is the load-bearing one: an animation
|
||
cannot restart before its own last pose, so a wrong reading of the field should
|
||
produce violations, and none exist in 1 781 records. The non-triviality control
|
||
holds too — a field that always equalled `max t` would carry nothing.
|
||
|
||
⚠️ **And I added the control they could not run: the same two restricted to the
|
||
eight records this port actually animates.** A disc-wide 0.00 % says nothing
|
||
about my six screens if all six sit in the exceptional tail.
|
||
|
||
```
|
||
record +0x08 max t slack
|
||
ptbtn00f 120 105 15
|
||
ptbtn01f…05f 120 120 0
|
||
ptloop01/02 600/720 600/720 0
|
||
shipped: 7 exact, 1 hold, 0 falsified
|
||
```
|
||
|
||
Their table, cell for cell. ⚠️ Note how narrowly non-trivial it is **here**:
|
||
across the disc 7.7 % of records hold, but on my shipped set exactly **one of
|
||
eight** does. The claim survives on my data; it is not richly confirmed by it.
|
||
|
||
### 🔴 The instruction cannot be complied with using anything they published
|
||
|
||
*"Stop shipping 105"* presumes the port can read the field. `loop_length_units`
|
||
is decoded in an **example** and a **test** and documented in `docs/re/` — and
|
||
exposed in `sylpheed_formats`' public API **on no ref at all**. I checked every
|
||
ref that touches the crate's `src/`.
|
||
|
||
It is still reachable: `parse_build` publishes each record's `(offset, size)`, so
|
||
`screen.rs` reads four big-endian bytes at a documented offset inside a span
|
||
whose magic it checks. That is consuming a delivered finding, not writing a
|
||
second decoder — but it is **the port holding a format detail it should not own**,
|
||
and the doc comment says to delete it the day the crate exposes it. Filed as an
|
||
ask, not a complaint: one `pub` field would take it back where it belongs.
|
||
|
||
### The value does not change. Its provenance does.
|
||
|
||
`authored/timing.json` already had `period_units: 120` for the plate — from a
|
||
**wall-clock measurement of the running game**, ≈2.37 s. The disc declares
|
||
**120**. So this port never shipped 105 for the plate, and the instruction was
|
||
aimed at a state I had already left by a different route.
|
||
|
||
📌 **That is the result worth keeping.** An emulator stopwatch and a field on the
|
||
disc, sharing no instrument, no code and no assumption, land on the same number.
|
||
`ScreenView._loop_period` now prefers the derived value and keeps the authored one
|
||
as the fallback **and as that second witness** — and a disagreement between them
|
||
is announced with `push_warning`, never silently resolved, because preferring one
|
||
number quietly is exactly how a measurement and a declaration drift apart for
|
||
milestones without anybody finding out.
|
||
|
||
❔ **Not settled, and not mine:** the export shows `ptbtn11f/12f/13f` on `extras`
|
||
and `ptbtn01f…05f` on the main menu all declaring the same 120-unit cycle, while
|
||
`looping_focus_records` names only the plate. Whether those records *animate*
|
||
while focused is behavioural — an ask, not an inference from the header.
|
||
|
||
## The contract is checked now, not read — `tools/port/contract-check`
|
||
|
||
Last iteration established that `docs/port/HANDOFF.md` on `main` is **926 lines
|
||
frozen at `9ca1eb5`** while the live document on the Decoder's branch is **4 111
|
||
lines at `27938aa`** — now **99** unread commits. The obvious response is "read
|
||
the other one", and it is not good enough: there are **70 sections in it this
|
||
port has never opened**, more arrive daily, and the failure mode is not laziness
|
||
but that nothing tells me *which* of them contradict what I ship.
|
||
|
||
So the contract's numbers are **checked against the port's own tree** instead.
|
||
Each check pulls its expected value **out of the live HANDOFF text by pattern** —
|
||
never restating it here, which would make this file a third copy to go stale —
|
||
and compares it against `export/` or `authored/`.
|
||
|
||
| | contract | port | |
|
||
|---|---|---|---|
|
||
| fade quad, title / menu / extras | `[0,16,261,269]` `[0,12,70,80]` `[0,12,64,74]`, α 255/0/0/255 | identical | ✅ |
|
||
| fade-out ramps | 10, 10, **8** on the title | 10, 10, 8 | ✅ |
|
||
| plate glow cycle | 120 | 120 derived, 120 authored | ✅ |
|
||
| menu BGM loop window | `-ss 9.44 -t 61.87` | 9.44 / 61.87 | ✅ |
|
||
| black hold between screens | 0 | 0 | ✅ |
|
||
| menu BGM bank | `BGM_103` | `BGM_103.slb` | ✅ |
|
||
| boot splash dwells | 190 and 145 | 190 and 145 | ✅ |
|
||
|
||
### Three outcomes, and the third is the point
|
||
|
||
`ok`, `MISMATCH`, and **`ANCHOR LOST`** — the pattern no longer matches the
|
||
contract. That is reported as loudly as a mismatch, because **a check whose
|
||
anchor has drifted passes forever while measuring nothing**, which is the exact
|
||
shape of failure this tool exists to catch one level up.
|
||
|
||
### The known negative, because a clean first run is not evidence
|
||
|
||
`--control` perturbs the contract by one token per check — `120, not 105` becomes
|
||
`121, not 105`, `-ss 9.44` becomes `-ss 9.45` — and **requires every check to
|
||
fail**. All seven do. Without it I would be reporting seven passes from an
|
||
instrument nobody had ever seen react to anything, which is the same
|
||
unfalsifiable clean run the suppression counting fixed in `check-claims`
|
||
yesterday. Both are in `check-all` now, the control as its own asserting step.
|
||
|
||
### 🔴 What a pass does not mean
|
||
|
||
Seven values out of a 4 111-line contract. **The other 70 sections are still read
|
||
by eye or not read at all**, and the tool prints that line on every run so a green
|
||
result cannot be quoted as "the port agrees with the contract".
|
||
|
||
## A refutation attempt on the fade numbers — it survives, from a third reader
|
||
|
||
The claim to attack, per PROTOCOL's *prefer what the port is about to build on*:
|
||
HANDOFF's *"🔴 the transition is OVERLAP, not ramp-then-hold. **And your menu
|
||
fade-in is 5× too slow**"*, which corrected `screen-transitions.md` from a 0.97 s
|
||
menu fade-in to **12 units, 0.20 s**. Their cause: `fade_quads.py` read each
|
||
pose's time from `blk+36`, the *next* record's time word — the association the
|
||
record-layout fix retired in the crate, never swept into the Python helper.
|
||
|
||
**The port never held that number.** There is no authored fade duration anywhere
|
||
in `authored/` or `port/scripts/` — `ScreenView` animates `pteff00` from its own
|
||
exported keyframes, so the 5× error could not reach it. The instruction was aimed
|
||
at a state I was not in, for the second consecutive iteration.
|
||
|
||
✅ **But it makes my export an independent check on their correction**, and it
|
||
holds exactly: `[0,16,261,269]`, `[0,12,70,80]`, `[0,12,64,74]` with α
|
||
255/0/0/255, and fade-outs 10/10/8. **Their rebuilt tools and my pinned crate are
|
||
different readers of the same bytes**, so agreement means both got the
|
||
record-layout fix — which is precisely what their helper had *not* had.
|
||
|
||
📌 And the same for the splash dwells: HANDOFF's `190 and 145` retraction was
|
||
caused by my recomputation, and the export now re-derives 190 and 145 from the
|
||
keyframe times a third time. A retraction confirmed by the party that provoked it
|
||
is worth less than one confirmed by a third reading; this is the third reading.
|
||
|
||
## The walk is checked too, and "only the ring moves" tested against my own renderer
|
||
|
||
`docs/game/navigation.md` — the screen-by-screen walk written from the committed
|
||
oracle frames — is a **second document unreachable from `main`**, and
|
||
`authored/flow.json` is its executable form. Nothing in the port fails when a
|
||
label drifts from it, so three more checks join `contract-check`, anchored on the
|
||
walk's own text: **the five main-menu labels in order**, **EXTRAS' three items**,
|
||
and **the cursor wrapping**. All three agree; all three fail their known
|
||
negative. Ten checks now, ten controls.
|
||
|
||
The manual audit that produced them found nothing else to fix: initial focus is
|
||
already `kind: "authored"` citing Q5's instability, `left_right` is an explicit
|
||
no-op, `auto_repeat` is measured, and every unexported destination is marked
|
||
`blocked` with the reason rather than invented.
|
||
|
||
### The refutation target: *"it is the only thing moving on this screen"*
|
||
|
||
The walk says the focus ring turns continuously and is **the only** thing moving
|
||
on the settled main menu — labels, bracket and footer at temporal std **exactly
|
||
0.000 over 20 s**. I cannot test that against the game, but I can test whether my
|
||
port obeys it, which is the direction that matters. Five renders across a full
|
||
ring cycle (`--loop-phase` 0…96, `--leaf-time` pinned):
|
||
|
||
| | |
|
||
|---|---|
|
||
| pixels varying by > 2 | **1 428 of 921 600 — 0.155 %** |
|
||
| bounding box | 46 × 44 at x 498–543, y 158–201 |
|
||
| distinct clusters | **1** |
|
||
|
||
One region, beside the focused item. ✅ **The port animates one ring, not five** —
|
||
worth checking, because the export shows all five `ptbtn01f…05f` declaring the
|
||
same 120-unit cycle and a renderer that ran them all would look identical to one
|
||
that ran the right one until you diffed frames.
|
||
|
||
### 🔴 And I nearly filed a defect against myself off a debug pin
|
||
|
||
Sweeping the **other** free clock — `--leaf-time` 0…8 s with the ring pinned —
|
||
moved **10.4 % of the frame, full-screen bounding box**. On a screen the contract
|
||
says has exactly one moving thing, that reads as a serious P5 defect.
|
||
|
||
It is not one. `--leaf-time` is a **debugging pin**, and 0…8 s is 0…480 units,
|
||
which lands *inside the build-in*: `ptloop01`'s sweep runs t=0→600 and `ptloop02`
|
||
t=0→720. At settle they are parked at **x = 1521** and **x = −839**, both
|
||
off-screen on a 1280-wide frame, and `loop_leaf_on_screens` scopes the replay to
|
||
`title` alone. So the settled menu moves exactly the ring, and the 10.4 % was me
|
||
driving an animation the settled screen has already finished.
|
||
|
||
📌 **The general form is worth more than the incident: a pin that can address
|
||
states the screen never occupies will manufacture defects on demand.** The three
|
||
pins exist precisely so a render is reproducible, and reading their output as if
|
||
it were the shipped behaviour inverts what they are for. Same shape as the
|
||
`--leaf-time` seconds-versus-units error, from the opposite side.
|
||
|
||
## The `+0x08` ask came back answered — and is not consumable yet
|
||
|
||
The Decoder exposed `ui_layout::loop_length_units` at **`b5df02a`**, and it is
|
||
byte-for-byte the logic `screen.rs` holds: same `RATC` guard, same `0x08`, same
|
||
big-endian read. So the deletion my doc comment promises is a one-line switch.
|
||
|
||
⚠️ **Not taken this iteration, and not for a reason about the code.**
|
||
`crates/sylpheed-export/Cargo.toml` pins `tag = "formats-pin-2026-08-30"` and
|
||
**there is no tag carrying `b5df02a`**. Moving to a bare `rev` on an unmerged
|
||
branch would replace a deliberate pin with an incidental one — and `BLOCKED.md`
|
||
already records this pin as load-bearing. **Asked for a tag; keeping the local
|
||
read, which is guarded and controlled, until one exists.**
|
||
|
||
## The pin moves to `formats-pin-2026-08-30b`, and the port stops owning `+0x08`
|
||
|
||
The tag was cut within the iteration, so the deletion the doc comment promised is
|
||
done: `screen.rs` calls `ui_layout::loop_length_units` and its local `RATC` guard
|
||
and byte read are **gone**. One line, exactly as predicted — the promise in the
|
||
comment is the only reason a temporary reading did not quietly become permanent.
|
||
|
||
**What the pin actually brings, checked before taking it.** A pin bump moves the
|
||
whole crate, not one function, and this one is recorded load-bearing, so the two
|
||
commits between the tags were read rather than assumed:
|
||
|
||
| | |
|
||
|---|---|
|
||
| `b5df02a` | adds the public `loop_length_units` |
|
||
| `d020845` | **comment-only** — two "fixed code under an unfixed description" corrections |
|
||
|
||
No behavioural change in either. `d020845` is worth noting for what it is: the
|
||
`rest` override's stated purpose was retired by the record-layout fix and the
|
||
comment still claimed it tested the shifted reading, and a `continue` branch was
|
||
documented with the pre-fix rule. **Both are the same failure this port hit in
|
||
`spin_period_units` — a doc comment describing the rule the body no longer
|
||
implements.** Three instances now, across two agents and two languages.
|
||
|
||
### 🔴 The control did NOT follow the API, on purpose
|
||
|
||
`examples/record_loop_control.rs` still reads the raw four bytes. **The moment a
|
||
control calls the API it exists to check, it stops being a control and becomes
|
||
the API tested against itself.** The falsifier — 0 of 1 781 records declaring
|
||
less than their own last pose — means something only because the reading is
|
||
independent of the crate's. Re-run at the new pin: unchanged, 7 exact and
|
||
`ptbtn00f` the one hold.
|
||
|
||
📌 So the port now holds **one** copy of this reading instead of two, and it is
|
||
the copy whose job is to disagree.
|
||
|
||
## The menu remembers its cursor — a measured P5 defect, fixed and scoped
|
||
|
||
The Decoder measured it today: Ⓑ from the main menu to the title and Ⓐ back
|
||
returns to **the item you left**, not to a default. Their control passed first —
|
||
two delivery-confirmed DOWNs moved the cursor exactly two items before the round
|
||
trip, so it demonstrably was not where it started.
|
||
|
||
🔴 **The port reset to `initial_focus` on every entry**, so this was a defect and
|
||
not a refinement: a player who moved to EXTRAS, pressed Ⓑ and then Ⓐ landed back
|
||
on NEW GAME. `MenuFlow.enter()` now consults `opening_focus()`, and a new
|
||
`set_focus()` writes the memory.
|
||
|
||
**`set_focus()` exists because two call sites set focus** — a cursor move and Ⓑ's
|
||
restore — and a memory updated at only one of them is right until the player uses
|
||
the other. That is a bug I have written before in this file; here it is a
|
||
four-line function instead.
|
||
|
||
### 🔴 The scope is the authored part, and widening it would contradict a measurement
|
||
|
||
`focus_persists` is true on `main_menu` and **nowhere else**. The measurement is
|
||
of one screen. `wrap` became a menu-wide rule because it was measured on two —
|
||
this was measured on one, and my own note on the pulse rule says a rule justified
|
||
by n=1 is a special case wearing a rule's clothes.
|
||
|
||
📌 Here it is stronger than a style preference: **generalising would overwrite
|
||
another measurement.** `extras` opens on `MISSION SELECT` as a *measured* initial
|
||
focus, and a remembered cursor would override it on re-entry. A menu-wide memory
|
||
would have silently replaced a measured value with a derived one.
|
||
|
||
Both halves are checked in one artifact, because a one-sided test passes a port
|
||
that quietly generalised:
|
||
|
||
```
|
||
--script=down,down,down,down,cancel,accept
|
||
menu on main_menu, focus ptbtn01 → ptbtn05
|
||
menu on title …
|
||
menu on main_menu, focus ptbtn05 ← remembered
|
||
|
||
--script=down,down,down,down,accept,down,cancel,accept
|
||
menu on extras, focus ptbtn11 → ptbtn12
|
||
menu on main_menu, focus restored to ptbtn05
|
||
menu on extras, focus ptbtn11 ← NOT remembered, measured value wins
|
||
```
|
||
|
||
`contract-check` asserts the pair — on where it was measured, off everywhere
|
||
else — and fails its known negative. **Eleven checks now.**
|
||
|
||
⚠️ **Not known, and not assumed:** whether the memory survives a *reboot* (the
|
||
reading that would matter for authoring a default), and whether any other screen
|
||
has it. The Decoder marks the reach as one boot, one round trip, one direction.
|
||
|
||
📌 **And it reframes the initial-focus warning I was sent this morning without
|
||
settling it.** If focus persists, an "initial focus" reading not taken on a fresh
|
||
boot's first menu entry is measuring history — so the records that disagreed need
|
||
not disagree about the game. My `NEW GAME` stays **authored**, on its own
|
||
reasoning. Nothing here confirms it.
|
||
|
||
## 🔴 Correction, same day: I encoded an absence of measurement as a finding
|
||
|
||
The check I shipped this iteration asserted a **pair** — `focus_persists` on for
|
||
`main_menu`, off everywhere else — and called both halves agreement with the
|
||
contract. The Decoder caught it: **nothing measured that `extras` does not
|
||
persist.** The corpus has EXTRAS' opening item from *one entry* and Ⓑ restoring
|
||
the *parent's* focus 4/4. Neither says what a submenu's own cursor does on
|
||
re-entry.
|
||
|
||
📌 **It is the exact mirror of the trap I had just congratulated myself on
|
||
avoiding.** I refused to let a derived menu-wide rule overwrite a measured value
|
||
— and then let *"not measured here"* become a positive assertion of the negative.
|
||
Both errors treat a gap in the corpus as if it carried information; they only
|
||
differ in which direction they fill it.
|
||
|
||
And the failure mode was the bad one: **if the game does persist EXTRAS, the
|
||
check holds the port to the wrong behaviour and passes while doing it.** A wrong
|
||
assertion that fails is a nuisance; a wrong assertion that passes is a
|
||
manufactured fact.
|
||
|
||
### What changed
|
||
|
||
* `check_focus_persists` now asserts **only the measured half** against the
|
||
contract.
|
||
* The scope is a separate **`guard`** with its own outcome word, printing
|
||
`only main_menu -- AUTHORED DEFAULT, unmeasured elsewhere`. It still fails if
|
||
someone widens it, because that should be a deliberate edit with a `why` — but
|
||
a passing run can no longer be read as the game being known to reset.
|
||
* `focus_persists_why` records the correction rather than being rewritten, and
|
||
says the non-persistence half is **the port's default, not the game's
|
||
behaviour**.
|
||
|
||
### And it weakens a `kind` label I had been leaning on
|
||
|
||
EXTRAS' `initial_focus: ptbtn11` is marked `measured`, and the same objection
|
||
applies to it: **it was taken on a single entry.** Now that the main menu is known
|
||
to remember its cursor, a one-entry reading of any screen may be measuring
|
||
*history* rather than what the screen opens on — the same argument that reframed
|
||
the main menu's TUTORIAL-versus-NEW-GAME disagreement this morning.
|
||
|
||
The observation stands; its *reading* as an initial focus does not. Left as
|
||
`measured` with the caveat attached, because the frame really does show MISSION
|
||
SELECT focused and it is the only reading there is — flagged so that if EXTRAS
|
||
turns out to persist, the label changes with it.
|
||
|
||
⚠️ **Not building further on the non-persistence half** until their EXTRAS
|
||
re-entry run comes back.
|
||
|
||
📌 Their sharpening of the pins point is the general form of all of this: **the
|
||
disciplines that fail this way are the ones that never visibly failed.**
|
||
Delivery-confirmation went to Ⓐ and Ⓑ because those broke once; the d-pad had
|
||
always quietly worked, so nothing directed attention at it. `kind: "measured"` is
|
||
the same shape — it has never visibly failed, so nothing has been checking what
|
||
each instance of it actually rests on.
|
||
|
||
## The `kind` sweep I said I owed: 15 labels, and 7 rested on a neighbour's argument
|
||
|
||
Every authored entry carries a `kind` — `measured`, `authored`, `name match, not
|
||
measured` — and the label is the load-bearing part: `measured` tells a reader
|
||
downstream that the port is repeating something observed off the running game.
|
||
**Nothing had ever checked them**, which is the point — the Decoder's sharpening
|
||
is that *the disciplines that fail this way are the ones that never visibly
|
||
failed*. `tools/port/audit-kinds` now reports what each label rests on.
|
||
|
||
### 🔴 It found the same error I was corrected for, one level down
|
||
|
||
Seven of fifteen labels — **every `goto_name_kind`** — had no `why` of their own.
|
||
Four of them scored `ok` on the first run because the audit fell back to the
|
||
parent's `why`, which argues **the destination**. `goto_name_kind` is about
|
||
**where the NAME came from**. Different claims, and the audit was crediting one
|
||
with the other's evidence.
|
||
|
||
That is precisely what I had been corrected for the previous iteration: treating
|
||
evidence as bearing on a claim it does not bear on. Borrowed evidence is now its
|
||
own outcome, `BORROW`, because *a label resting on a neighbour's argument reads
|
||
as evidenced and is not*. All seven now carry a `why` citing HANDOFF Q4's own
|
||
words — *"the screens are measured; the ids are a name match onto the
|
||
executable's class names"* — and state that the port never branches on the field.
|
||
|
||
### And the audit refuted itself twice before it was worth trusting
|
||
|
||
* **Four false positives.** The first version counted only paths, shas and
|
||
filenames as citations, so `HANDOFF Q1` and `PORT-MISSION section 7` read as
|
||
*cites nothing*. **An audit that invents defects is worse than no audit**: its
|
||
false positives are indistinguishable from its true ones until each is opened
|
||
by hand.
|
||
* **Seven false dangling.** It resolved paths against committed refs only, so a
|
||
`why` citing the tool being written this iteration failed. Working-tree paths
|
||
count now.
|
||
|
||
⚠️ **What it cannot do** is read the cited page and confirm it says what the `why`
|
||
claims, and it prints that on every run. Fifteen labels with resolving citations
|
||
is not fifteen verified labels.
|
||
|
||
📌 One cosmetic find with teeth: `MEASURED` and `measured` both existed. **A
|
||
consumer comparing `== "measured"` misses the other, and a label that fails to
|
||
match reads as ABSENT rather than as wrong.** Normalised.
|
||
|
||
## A refutation attempt on Q2's map of `GP_TITLE` — the count is right, the list is short
|
||
|
||
Q2 is titled *"which build is which screen state"* and reads: **`GP_TITLE` is 8
|
||
screens shipped twice, EN/JP** — `2/3` plate, `4/7` title, `5/8` main menu, `6/9`
|
||
EXTRAS, and `0/1` and `12/15` the loading screen. The port ships **16** screen
|
||
files, so I checked the enumeration against my own export's entry map:
|
||
|
||
| entries | state |
|
||
|---|---|
|
||
| 0/1, 12/15 | loading, two variants — ✅ in Q2 |
|
||
| 2/3, 4/7, 5/8, 6/9 | plate, title, main menu, EXTRAS — ✅ in Q2 |
|
||
| **10/13, 11/14** | **`publisher_logo`, `developer_logos` — absent from Q2** |
|
||
|
||
✅ **The headline survives and is exactly right.** Four UI states + two loading
|
||
variants + **two boot splashes** = 8 states, each shipped twice = **16 entries**,
|
||
which is what the archive holds. The count is confirmed by a second reading.
|
||
|
||
🔴 **The row's own enumeration lists 6 of those 8.** A reader who counts Q2's
|
||
entries gets twelve and has no slot for the splashes — and **this is the row
|
||
already corrected once for an ordinal-versus-entry error**, which is exactly the
|
||
mistake an incomplete map feeds: four unlisted entries are four places for an
|
||
off-by-three to hide. The splashes are not obscure to the contract either; the
|
||
190/145 dwell retraction is about these very entries.
|
||
|
||
Not a defect in the port — `publisher_logo` and `developer_logos` are exported,
|
||
named and verified against captures at RMSE 2.17 and 3.05. Reported because the
|
||
map is what the next reader will trust.
|
||
|
||
## An authored value became a measured one, and a difference-only check got an origin
|
||
|
||
The Decoder corrected their own focus delivery today: the item names in the
|
||
persistence run were **two positions out** — reported `TUTORIAL → EXTRAS →
|
||
EXTRAS`, actually `NEW GAME → TUTORIAL → TUTORIAL` — from a reader using
|
||
design-space rows against captures carrying Xenia's chrome and a 1.060 scale.
|
||
|
||
### ✅ `initial_focus` is no longer authored
|
||
|
||
**`NEW GAME` on a fresh boot, 2/2 fresh boots, both the first menu entry.**
|
||
`initial_focus_kind` moves from `authored` to `measured`.
|
||
|
||
📌 **The value did not change; its standing did** — and the upgrade is *not*
|
||
because the measurement agrees with me. They had said explicitly that my agreeing
|
||
with their records was no evidence, which was correct; this is a direct reading of
|
||
a fresh boot's first entry, independent of the reasoning that chose `NEW GAME`
|
||
here. **"First entry" is the load-bearing phrase**: since the menu remembers its
|
||
cursor, any reading taken later is measuring history, which is exactly the
|
||
objection that voided the earlier TUTORIAL-versus-NEW-GAME disagreement.
|
||
|
||
The superseded reasoning is kept under `(was)` lines rather than deleted. It is
|
||
what made the wait cheap: the field existed and was labelled honestly, so
|
||
arriving at a measurement was a **label change and not an archaeology problem** —
|
||
the third time that pattern has paid off here, after `loop_start_s` and the
|
||
`+0x08` read.
|
||
|
||
### 🔴 My anchor survived a correction it should not have been able to detect
|
||
|
||
`check_focus_persists` anchors on the **heading** — the conclusion — not on the
|
||
item names, so the correction did not break it. That is lucky rather than
|
||
designed: the conclusion is genuinely geometry-free (the ring sits at y 384.0
|
||
before the round trip and 385.5 after, an *equality* immune to a constant
|
||
offset), while the names were not. **My check would not have caught the label
|
||
error**, and nothing in it distinguishes "anchored on a robust claim" from
|
||
"anchored above the part that was wrong".
|
||
|
||
### The generalisation, and where it bit me
|
||
|
||
Their statement of it: **a control that only checks differences is blind to the
|
||
origin.** Theirs asserted "two DOWNs move exactly two items", which a constant
|
||
offset preserves perfectly — so it passed for a whole session on a reader two
|
||
items wrong. Ground truth caught it; the control could not.
|
||
|
||
🔴 **`check_splash_dwell` is that shape.** It compares the *widest gap* between
|
||
keyframe times — 190 and 145 — and a reader with every time shifted by a constant
|
||
produces the same gaps and passes. Added `check_splash_times`, which asserts the
|
||
**absolute** list the contract prints, `[0,15,30,45,235,239,251,255]`. Origin and
|
||
difference are now both checked, and they fail independently.
|
||
|
||
⚠️ And writing that control reproduced the same error one level down: its
|
||
perturbation literal was written from memory of the prose, with a space where the
|
||
document has a newline, so it reported `the control's own anchor is gone`. The
|
||
check's `\s*` had spanned the line break; the control's literal did not. **A
|
||
control written from a memory of the source rather than from the source is the
|
||
class of error these checks exist to catch.** Thirteen controls now, all firing.
|
||
|
||
❔ **EXTRAS remains unmeasured** — the run meant to settle it navigated to OPTIONS
|
||
believing it was EXTRAS, so `initial_focus: ptbtn11` stays undecided and nothing
|
||
here is built on it.
|
||
|
||
## EXTRAS resets — measured. The assertion was right and that does not make it evidence.
|
||
|
||
Ring at **347.5** on entry (`MISSION SELECT`), **427.5** after one
|
||
delivery-confirmed DOWN, **347.5** on re-entry, with the frame **0.0 %**
|
||
different from the first entry — and the screen confirmed by eye as EXTRAS,
|
||
because an earlier run was fooled about which screen it was on.
|
||
|
||
Two things settle in my tree:
|
||
|
||
* ✅ **The caveat on `extras/initial_focus` comes off.** `MISSION SELECT` is a
|
||
genuine initial focus: because this screen *resets*, a single-entry reading of
|
||
it is not measuring history. That objection was live only while persistence
|
||
here was unknown.
|
||
* ✅ **`focus_persists: false` for `extras` is now written explicitly, with
|
||
`kind: measured`.** The port already defaulted to false, so nothing changes at
|
||
runtime. 📌 **The absent key and a measured `false` behave identically and mean
|
||
opposite things** — "nobody looked" versus "the game was watched doing it" —
|
||
and only the second is visible to `audit-kinds`. That is the whole reason to
|
||
spend a key on it.
|
||
|
||
### 🔴 It does not vindicate how I got there, and I am not recording it as if it did
|
||
|
||
For one iteration `contract-check` **asserted** EXTRAS non-persistence with
|
||
nothing behind it. The Decoder flagged it; the measurement then agreed with it.
|
||
|
||
Their separation of the two moves is the one to keep, and it is sharper than my
|
||
own account was:
|
||
|
||
* **Declining to generalise the memory past `main_menu` was correct** — on the
|
||
evidence then, and now on measurement, since the two screens genuinely disagree.
|
||
* **Encoding "not measured here" as a positive assertion of the negative was a
|
||
different move**, and it happened to land. **Being right by luck does not
|
||
retroactively make it evidence.** The measurement is what makes it true; the
|
||
assertion never did — and the fact that it *could* have been wrong in a way
|
||
that passed is exactly why it was worth measuring rather than leaving to stand.
|
||
|
||
The check is rewritten to rest on the measurement rather than left in place
|
||
looking vindicated, and the `why` says all of this where a later reader will hit
|
||
it instead of inferring a clean run.
|
||
|
||
### The guard now guards the right thing
|
||
|
||
There is no menu-wide rule to state — two screens are measured and disagree — so
|
||
`guard_focus_scope` no longer polices "only `main_menu`". It states both measured
|
||
values and **counts the screens that say nothing**, printing
|
||
`UNMEASURED, not 'resets'`. The silent ones are the port defaulting, not a
|
||
finding.
|
||
|
||
⚠️ **Untested and not built on:** `OPTIONS`, `LOAD GAME` and `TUTORIAL` — three
|
||
more submenus, none touched. And nobody can separate **"resets to `MISSION
|
||
SELECT`"** from **"resets to the top item"**; they coincide here, since `ptbtn11`
|
||
is both. The port's value is right under either reading and **the reason is not
|
||
established** — which matters the day a screen is authored whose opening item is
|
||
not its first.
|
||
|
||
📌 Their symmetric caution, worth more than the result: their ring reader now
|
||
**refuses to name a row outside its calibration rather than guessing**, and that
|
||
refusal is doing more work than any threshold they could have picked. The same
|
||
shape as `ANCHOR LOST` here — the useful behaviour is not a better guess, it is
|
||
declining to produce one.
|
||
|
||
## Running the port as a player finds two things reading it did not
|
||
|
||
### 🔴 `--boot --script=…` parsed, was stored, and did nothing
|
||
|
||
The script only ever starts at `_menu_enter`, and a `--boot` run without `--play`
|
||
never enters a menu — it holds on the title and quits. So my scripted boot
|
||
**completed, exit 0, with no menu line and no press**: a clean-looking result to a
|
||
question that was never asked.
|
||
|
||
📌 **This file already warns about exactly this shape, 600 lines above the bug**,
|
||
where `--capture` used to photograph the first frame of a scripted run: *"a flag
|
||
combination that silently photographs the wrong instant is worse than one that
|
||
errors."* The warning was written, kept, and did not prevent the same class
|
||
recurring in the neighbouring flag — and it was found by **running the port the
|
||
way a human would**, not by reading it.
|
||
|
||
It now `push_error`s and exits 2, naming both working forms. **Refusing rather
|
||
than implying `--play`**: the two runs differ by 157 seconds of intro, and quietly
|
||
choosing that for someone is its own surprise.
|
||
|
||
✅ The working combination is verified end to end: `--boot --play --script=…`
|
||
walks power-on → splashes → `ADV` → title → Ⓐ → main menu → ⬇ → Ⓐ, which is the
|
||
whole P5 path from a cold start.
|
||
|
||
### 🔴 A comment in `boot.gd` describing a world refuted a week ago
|
||
|
||
Above `audio.play_bed("main_menu")`: *"AUTHORED, and the weakest thing in P6:
|
||
HANDOFF Q10 says nothing on the disc [refuted] names which track a menu plays, so
|
||
`authored/audio.json` picks one."* `BGM_103` is **measured** on three independent
|
||
legs, and `audio.json` says so; only this comment still described the port as
|
||
choosing.
|
||
|
||
**Third instance of the drifted-comment trap**, after `spin_period_units` here
|
||
and two in the crate. The correction lands in the code or the data and the
|
||
sentence above it keeps describing the old world. The dead phrase is now a
|
||
`check-claims` register row, **controlled**: a planted revival fails the check and
|
||
removing it passes.
|
||
|
||
## The boot's wall-clock seconds are a property of this container, not of the port
|
||
|
||
`ADV` runs 7.78 → 154.38 s in the boot: **146.6 s of wall clock for 137.44 s of
|
||
media, +6.7 %**. My first hypothesis was a fixed post-roll; the second was slow
|
||
software playback in general. A second video of a different size separates them:
|
||
|
||
| | media | wall | |
|
||
|---|---|---|---|
|
||
| `ADV` 1280×720 | 137.44 s | 146.60 s | **+6.7 %** |
|
||
| `S00A` 768×432 | 93.78 s | 93.37 s | −0.4 %, real time |
|
||
|
||
So it is neither a post-roll nor a general deficit: **this box has no GPU, and
|
||
720p Theora decodes below real time here while 432p keeps up.** The transcode is
|
||
faithful (137.44 s against a 137.71 s source) and the exporter does not rescale —
|
||
`S00A.wmv` is natively 768×432 on the disc.
|
||
|
||
🔴 **The consequence is about my own artifacts.** P3/P7 runs quote wall-clock
|
||
seconds — *"boot ends at 158.13 s"* — and those seconds contain this deficit.
|
||
They are reproducible **here** and are not a statement about the port, still less
|
||
about the game. The Decoder has been careful to carry an explicit emulator pacing
|
||
factor for exactly this reason; I have been quoting my seconds as though mine
|
||
were exact. **Any comparison between a boot timing of mine and a measurement of
|
||
theirs has to go through the media length, not the wall clock.**
|
||
|
||
⚠️ Not fixed, because there is nothing to fix in the port: it plays the file at
|
||
the speed the machine can decode it. Recorded so the numbers are read correctly.
|
||
|
||
## Their negative result, and the trap in choosing the more general instrument
|
||
|
||
`LOAD GAME`, `TUTORIAL` and `OPTIONS` remain unmeasured, so `guard_focus_scope`
|
||
counting them as **UNMEASURED rather than "resets"** stays right, and no value of
|
||
mine moves.
|
||
|
||
📌 The transferable part is their instrument story: a narrow calibrated reader
|
||
failed on those screens (it scanned the main menu's gutter column, where these
|
||
three put nothing), so they replaced it with a whole-frame comparison — which then
|
||
died the moment Xenia's crash dialog overlaid the frame, while **the narrow reader
|
||
kept working**. *"After a specific instrument fails, the general one feels safer,
|
||
and its failure mode is only one you have not met yet."*
|
||
|
||
That is worth holding against my own habits: `contract-check` is deliberately a
|
||
list of **narrow, individually anchored** checks rather than one general document
|
||
comparison, and the reason is the same. The temptation after an `ANCHOR LOST` will
|
||
be to make the matching looser and more general. **That would trade a failure I
|
||
can see for one I cannot.**
|
||
|
||
✅ And a refutation attempt on my Ⓑ-restores-parent-focus claim failed in my
|
||
favour — a fifth instance, recovered by the narrow reader from the run they had
|
||
written off.
|
||
|
||
## 🔴 Correction: my media-versus-wall-clock method cannot audit container pacing
|
||
|
||
The Decoder marked *"the game presents at 27.6 fps"* as confounded — a guest
|
||
running at ~92 % of real time produces that number, and so does a game genuinely
|
||
presenting at 27.6 — and proposed borrowing my method to settle it: **an asset
|
||
whose duration is fixed by its own data, wall clock compared against media
|
||
length, in this container.**
|
||
|
||
**It does not work, and the reason is worth more than the result.** I ran it three
|
||
times on `S00A`, whose 93.78 s is fixed by its own sample rate:
|
||
|
||
| run | video span | vs media |
|
||
|---|---|---|
|
||
| 1 | 93.37 s | −0.44 % |
|
||
| 2 | 93.30 s | −0.51 % |
|
||
| 3 | 93.31 s | −0.50 % |
|
||
|
||
Tight, reproducible, and **it cannot answer the question it was asked**. The video
|
||
player is *driven by the container clock*: it decides which frame to present from
|
||
elapsed time as that clock reports it. If the clock ran uniformly slow, the player
|
||
would present fewer frames per real second and still finish in exactly 93.78 s of
|
||
container time — **a perfect match, produced by the failure it was meant to
|
||
detect.** Every timer available to me shares that clock, including the shell's
|
||
`date`, so no measurement from inside this container can separate a slow clock
|
||
from real time.
|
||
|
||
📌 **What my earlier entry got right and wrong.** "Compare through media length,
|
||
not wall clock" is sound for **cross-agent comparison** — media length is
|
||
container-independent, so it is the right common unit between their numbers and
|
||
mine. It is **not** an audit of pacing, and my write-up did not distinguish those
|
||
two uses. Corrected here rather than in place.
|
||
|
||
### What the ADV/S00A contrast *does* establish, and it favours their doubt
|
||
|
||
Same container, same clock, same player, two assets:
|
||
|
||
| | media | container time | |
|
||
|---|---|---|---|
|
||
| `ADV` 1280×720 | 137.44 s | 146.60 s | **+6.7 %** |
|
||
| `S00A` 768×432 | 93.78 s | 93.31 s | −0.5 % |
|
||
|
||
✅ **Load-dependent starvation is demonstrated here, positively** — not inferred.
|
||
A light decode keeps pace with the container clock; a heavy one falls 6.7 %
|
||
behind it. Xenia is a far heavier workload than 720p Theora, and their frame
|
||
counts are taken **per container-second**, which is exactly the axis this
|
||
starvation acts on. So their confound is not hypothetical in this environment: I
|
||
have a direct demonstration of the mechanism in the same box.
|
||
|
||
❔ **What would settle it is a clock the guest does not control.** Audio hardware
|
||
consumes samples at a fixed rate, so frames presented per *sample consumed* is a
|
||
frame rate measured against a quartz reference rather than against a timer that
|
||
may itself be starved. Whether Xenia's audio path exposes that is theirs to say —
|
||
offered as a route, not a finding.
|
||
|
||
📌 And their addendum to the global-versus-narrow lesson is the sharpest form of
|
||
it: **they did not loosen the instrument gradually, they swapped it wholesale the
|
||
moment it failed, and the swap felt like rigour.** So when an `ANCHOR LOST` comes,
|
||
the cheaper move is **a second narrow anchor, not one looser one** — written into
|
||
`contract-check`'s header so the next reader hits it before reaching for a
|
||
general matcher.
|
||
|
||
## The leak was not mine — a negative result, and the "fix" is reverted
|
||
|
||
Every run ends with `N ObjectDB instances were leaked at exit`, and the leaked
|
||
objects are `AudioStreamOggVorbis` / `OggPacketSequence` / their playbacks —
|
||
exactly the cues that had actually sounded. The obvious diagnosis is that
|
||
`MenuAudio` holds references past teardown.
|
||
|
||
**It does not.** I added `_exit_tree()` releasing every reference the port owns —
|
||
stopping each player, nulling every `stream`, clearing `_players`, then clearing
|
||
the `cues`, `beds` and `voices` dictionaries as well — and **the count did not
|
||
move: 8 before, 8 after.** A debug print confirms `_exit_tree` runs. Removing the
|
||
cleanup again: still 8.
|
||
|
||
📌 **Reverted rather than kept.** Cleanup that changes nothing measurable, sitting
|
||
under a comment claiming to fix a leak, is worse than no cleanup: the next reader
|
||
sees the leak handled and does not look. This project's own recurring finding is
|
||
*a rule stated, believed, and unexercised* — shipping a fix that fixes nothing is
|
||
the same shape.
|
||
|
||
✅ **What is worth keeping is the negative:** the warning is engine-side, not the
|
||
port's to fix, and it is the same eight objects every run. Recorded so nobody —
|
||
including me next iteration — spends another hour on it. **It stays as log noise,
|
||
and that has a cost:** the previous iteration found two real defects by reading
|
||
the port's own log, and doing so meant filtering a line that had been there long
|
||
enough to read as scenery.
|
||
|
||
## A second narrow anchor, where I had already found the weakness and not acted
|
||
|
||
Last iteration I recorded that `check_focus_persists` survived the Decoder's
|
||
correction **by luck**: it anchors on the heading — the conclusion — while the
|
||
item names that were wrong sat below it. I wrote that down and left the check as
|
||
it was.
|
||
|
||
Their advice made the repair concrete: **after an anchor fails, add a second
|
||
narrow anchor, never one looser one.** So the check now rests on the *evidence*
|
||
as well — *"ring sits at y 384.0 before the round trip and 385.5 after"*, the
|
||
geometry-free equality the conclusion actually stands on, and the thing a future
|
||
correction to the measurement would have to touch.
|
||
|
||
📌 **And the two anchors are checked against each other**, not merely both
|
||
required. If one matches and the other does not, the check reports `ANCHOR
|
||
SPLIT` — *one moved without the other* — which is the state that means the
|
||
document has been edited in a way neither anchor alone can see.
|
||
|
||
⚠️ **The second anchor gets its own known negative**, perturbing only the evidence
|
||
line. Without that it would be decorative and the check would still be resting on
|
||
the conclusion alone — which is precisely the failure it was added to fix. Both
|
||
controls fire.
|
||
|
||
## Reported: a live-reading HANDOFF section that two later ones have overtaken
|
||
|
||
`## 🔴 2026-08-30 — do not hardcode the menu's initial focus; the sources
|
||
disagree` still reads as current, and carries no forward marker. Two of its
|
||
claims are now false:
|
||
|
||
* *"the sources disagree … expect it to change"* — settled since, by direct
|
||
measurement of a fresh boot's **first** menu entry, `NEW GAME`, 2/2.
|
||
* *"Also unanswered, and **never once run**: whether focus persists across
|
||
menu → Ⓑ → title → Ⓐ → menu"* — run, and answered: it persists.
|
||
|
||
⚠️ **Mitigated by their newest-first convention**, so a reader coming top-down
|
||
meets both corrections before this section. Reported rather than filed as
|
||
blocking, because nothing of mine depends on it — my anchors are on the newer
|
||
text — but a grep lands mid-document, and this is the second time a superseded
|
||
HANDOFF section has read as live.
|
||
|
||
## Their rule applied backwards: my video result is stronger than my withdrawal said
|
||
|
||
The Decoder's rule, taken from my correction and sharper than it: **ask whether
|
||
the quantity you are timing can be skipped.** Frames, video and animation
|
||
timelines can. Bytes consumed cannot — their `input_buffer_read_offset` only
|
||
advances if the bits are actually decoded, so a starved guest makes the wall time
|
||
between two loop wraps *longer*, never equal. That is why my withdrawal reaches
|
||
my test and not theirs, and the distinction is not obvious from outside: both
|
||
look like "wall clock against a quantity fixed by data".
|
||
|
||
📌 **Applying it back here changes what my own numbers are worth.** I withdrew the
|
||
media-versus-wall method as a pacing audit, correctly — a uniformly slow clock is
|
||
undetectable from inside. But the load result I filed alongside it is on firmer
|
||
ground than I gave it credit for:
|
||
|
||
**The overrun IS the evidence that nothing was skipped.** If Godot's video player
|
||
dropped frames to stay on schedule, `ADV` would have finished in ~137 s of
|
||
container time with frames missing, and I would have measured nothing. It took
|
||
**146.6 s**. A player that runs long is a player that decoded everything — so
|
||
`ADV` +6.7 % and `S00A` −0.5 % *are* "time to consume a fixed quantity", the class
|
||
of measurement they endorse, and not the skippable-frame kind I feared.
|
||
|
||
⚠️ **What it still cannot do** is detect a uniform clock skew, because the
|
||
scheduler and the timer share a clock. The withdrawal stands for the *audit* use;
|
||
what is recovered is the *load-starvation* result, which was the half that
|
||
mattered to them.
|
||
|
||
🔴 **And the sweep their rule implies, on my own tools:** every timing this port
|
||
publishes is frame-derived — boot spans, screen dwells in seconds, the film
|
||
cadence. Frames are skippable in principle, and the only reason those numbers
|
||
mean anything is that this player demonstrably does **not** skip. That is an
|
||
empirical property of Godot's `VideoStreamPlayer` under load here, **not a
|
||
guarantee**, and nothing in the port checks it. Recorded as the standing caveat:
|
||
if a future Godot drops frames under load, every second this port prints becomes
|
||
silently wrong in the direction that looks correct.
|
||
|
||
📌 Their four-fault void run is worth noting for what caught it: a period
|
||
estimator returning **its own search floor** instead of the plate's known 2.53 s.
|
||
That is the same family as `--leaf-time` sweeping a state the screen never
|
||
occupies — **an instrument answering with a property of itself.** Third time this
|
||
project has hit it; the control caught it each time, and nothing else would have.
|
||
|
||
## 🔴 I measured my own claim and it is wrong: the player skips, heavily
|
||
|
||
I told the Decoder that *"a player that runs long decoded everything"*, and that
|
||
therefore my video spans were time-to-consume measurements. They granted the
|
||
argument and added the refinement I had not claimed: **running long proves the
|
||
player did not skip enough to stay on schedule, not that it skipped nothing.**
|
||
|
||
That refinement is testable, because **a video player cannot present more video
|
||
frames than the engine draws.** Instrumenting `Engine.get_frames_drawn()` across
|
||
each playback:
|
||
|
||
| video | engine frames | span | engine fps | frames in the media | presented |
|
||
|---|---|---|---|---|---|
|
||
| `S00A` 768×432 | 775 | 93.33 s | **8.3** | 2 813 | **28 %** |
|
||
| `ADV` 1280×720 | 1 941 | 140.77 s | **13.8** | 4 123 | **47 %** |
|
||
|
||
**Both skip most of their frames.** `S00A` "kept real time" *because* it dropped
|
||
roughly three frames in four to stay on schedule — the exact mechanism I claimed
|
||
was absent. My sentence was not merely unproven, it was **false**, and the probe
|
||
that refutes it is four lines long and could have been written the day I wrote the
|
||
claim.
|
||
|
||
⚠️ **The honest limit of the probe, stated because it cuts the other way:** it
|
||
counts *presented* frames, not *decoded* ones. Theora is inter-frame predicted, so
|
||
a decoder generally must decode frames it never displays. So this refutes
|
||
**"presented every frame"** and leaves **"decoded every frame"** unmeasured — I do
|
||
not have an instrument for the second, and I should not have asserted it from the
|
||
first. Their `read_offset` counter is a consumption counter precisely because it
|
||
cannot have that gap.
|
||
|
||
### And the number I sent them twice is a spread, not a constant
|
||
|
||
Three `ADV` runs: **146.42 s, 146.60 s, 140.77 s** → **+6.5 %, +6.7 %, +2.4 %**.
|
||
I have been quoting **+6.7 %** as though it were the measurement. It is the top of
|
||
a range whose spread is nearly as large as the effect on the third run, and the
|
||
runs differed in what else the port was doing (`--boot --play --script` versus
|
||
`--boot` alone). 📌 **Reported as +2.4 % … +6.7 %, n=3, load-dependent** from here.
|
||
|
||
✅ **What survives.** The qualitative result still holds and is what mattered to
|
||
them: heavy decode falls behind the container clock and light decode does not,
|
||
demonstrated in one box. But it is now a *lower bound on a deficit measured under
|
||
skipping*, exactly as they said — and with the presented-frame counts in hand, a
|
||
much weaker claim than the one I made.
|
||
|
||
📌 **The general form, which is the third time this project has produced it:** I
|
||
argued from an absence — no overrun would have been visible if frames were
|
||
dropped — instead of measuring the thing directly. The direct measurement cost
|
||
four lines. **An argument that a mechanism is absent is not a measurement that it
|
||
is absent**, and I had just finished telling the Decoder that being right by luck
|
||
is not evidence.
|
||
|
||
⚠️ Their own sweep for stale HANDOFF sections is recorded as a negative: 7
|
||
candidates, 0 real, because in that corpus 🔴 marks a correction being delivered
|
||
far more often than a section overtaken. **Neither of us should build that.** It is
|
||
my own *"an audit that invents defects is worse than no audit"*, arrived at from
|
||
their side.
|
||
|
||
## 🔴 Correcting the correction: the frame probe is an UPPER BOUND, and my contrast was contention
|
||
|
||
I refuted my own claim yesterday with a frame counter and reported *"the player
|
||
skips, heavily — 28 % of `S00A`'s frames and 47 % of `ADV`'s"* [refuted]. **Both numbers
|
||
were taken while other work was running on this box, and the instrument does not
|
||
mean what I said it means.**
|
||
|
||
Measured again with nothing else running:
|
||
|
||
| | engine frames | media frames | span vs media |
|
||
|---|---|---|---|
|
||
| `S00A` ×3 | 2 531 / 2 532 / 2 477 | 2 813 | **+6.8 %, +6.8 %, +6.7 %** |
|
||
| `ADV` ×1 | **6 480** | 4 123 | **+6.9 %** |
|
||
|
||
**`ADV` drew 6 480 frames across a 4 123-frame video — 157 %.** The engine renders
|
||
the UI at its own rate, not the movie's, so engine frames bound *shown* frames
|
||
from above **only while the engine is slower than the stream**. Above that
|
||
crossover the counter constrains nothing, and "157 % presented" is not a
|
||
measurement — it is the instrument used outside its range. The runtime report now
|
||
says exactly that instead of printing a percentage.
|
||
|
||
### Two of my own claims fall, and one of them was the headline
|
||
|
||
* 🔴 **"The player skips, heavily" is not supported.** What the contended run
|
||
showed is that at 8.3 engine fps `S00A` *could not* have shown more than 28 % —
|
||
a valid upper bound under contention, and nothing more. On a quiet box the
|
||
bound is 88–90 %, which permits anything from no drops to a tenth.
|
||
* 🔴 **The 720p-versus-432p contrast is refuted, and it was the finding I sent
|
||
them twice.** I reported `ADV` +6.7 % against `S00A` −0.5 % and built
|
||
"heavy decode falls behind, light decode keeps up" on it. **Quiet, both run
|
||
+6.7 … +6.9 %.** The −0.5 % was a *contended* run in which the player dropped
|
||
frames to hold its schedule. **I was measuring which run happened to share the
|
||
box, and reading it as a property of the resolution.**
|
||
|
||
✅ **What survives, and it is now the sturdiest number here:** playback runs
|
||
**+6.7 % … +6.9 %** long on this container, five runs, both videos, quiet —
|
||
consistent and resolution-independent. That is a real deficit and it is *not* the
|
||
mechanism I described.
|
||
|
||
📌 **Three corrections in three iterations, all mine, all the same shape.** I
|
||
argued from an absence; measured it and over-read the measurement; then found the
|
||
measurement was taken under a confound I had introduced myself by running the
|
||
suite alongside it. **The instrument was right to build and I published each
|
||
reading before asking what else was true of the run that produced it.** The
|
||
Decoder's rule needs a companion: ask what the quantity can be skipped by — *and
|
||
ask what else was running.*
|
||
|
||
⚠️ The probe stays **permanent and printed by default**, with its crossover
|
||
stated in the report itself. Its value was never the first number it gave; it is
|
||
that the next claim about frames has to be made in front of it.
|
||
|
||
## The P4 fidelity question, attempted: four traps reproduced, no verdict yet
|
||
|
||
`AUDIO-VERIFICATION.md` §1 calls transcode fidelity *"the question P4 actually
|
||
raised"*, needing neither an engine nor a device, and gives the method in four
|
||
lines of shell. **Nothing implemented it.** `verify-video-audio` deliberately
|
||
does not — it proves Godot emits non-silence and says outright that a difference
|
||
RMS without alignment is meaningless — so the P4/P7 gate has rested on level and
|
||
non-silence, and the fidelity claim has never been made.
|
||
|
||
`tools/port/verify-transcode-fidelity` now exists. **It does not yet produce a
|
||
verdict, and it is committed saying so.**
|
||
|
||
### What it found on the way — each reproduced, none reasoned about
|
||
|
||
| | |
|
||
|---|---|
|
||
| **Sign of the lag** | Indexing `b[i+off]` with a negative `off` wraps to the end of the array in Python, so the "difference" was the transcode subtracted from an unrelated part of the source. Reported the difference **7 dB louder** than the source — §1's catastrophic-looking misalignment number, arrived at by a different route. |
|
||
| **Channel layout** | My regex for the recorded `-af` truncated the fold to its **FL half**, folding the source to a left-only signal while the transcode carried both. §1 names this trap; I reached it through a *parsing bug*, and the matrix contains runs of spaces so it cannot be tokenised on whitespace. |
|
||
| **Imprecise seek — NOT in §1** | `-ss` before `-i` is a container-level jump. On this WMA Pro source a 4.0 s request returned **4.6 s** while the Ogg side returned 4.0 s, so the two windows covered **different stretches of the movie**: best normalised correlation **0.172**, no shift could align them. Decoder-side `-ss` after `-i` is exact. **This failure is indistinguishable from the alignment trap §1 does name**, which is why it cost a diagnostic rather than a guess. |
|
||
| **A search pinned at its own edge** | The single-resolution correlation returned **+2413 against a window of ±2400** — the boundary, not the peak. Same family as the Decoder's period estimator returning its own search floor: **an instrument answering with a property of itself.** Replaced with a coarse-to-fine search that **refuses** when the best lag sits on the boundary. |
|
||
|
||
### 🔴 Why it is committed without a verdict
|
||
|
||
Best alignment so far is **corr 0.763** on `S00A` and **0.075** on `ADV`, and both
|
||
still report the difference **louder** than the source — which cannot be true of
|
||
two aligned signals at equal level. **The remaining fault is on my side of the
|
||
instrument, not necessarily in the transcodes.**
|
||
|
||
A tool that printed *"not faithful"* in that state would put a **false defect on
|
||
the exporter**, and this project has already established what a confident wrong
|
||
number costs. So it reports and refuses to conclude, and it distinguishes
|
||
**"could not align"** from **"not faithful"** — two failures I conflated twice
|
||
before separating them.
|
||
|
||
📌 The transferable finding is about the doc, not the transcodes: **§1's four
|
||
lines of shell have at least four ways to lie, and three of the four produce the
|
||
same catastrophic-looking symptom.** The doc names three traps; the seek one is
|
||
new and I have not added it to §1 yet, because §1 is the human's document and the
|
||
right move is to propose the addition rather than edit it silently.
|
||
|
||
⚠️ **Not settled and explicitly not claimed:** whether `ADV.ogv` and `S00A.ogv`
|
||
are faithful to their sources. After this iteration that is *less* settled than
|
||
it looked yesterday, because the question now has an instrument that says it
|
||
cannot answer yet, instead of no instrument at all.
|
||
|
||
## Changing the KIND of quantity answered it on the first attempt
|
||
|
||
The Decoder's rule, from two failed attempts of their own: **two failed attempts
|
||
at the same measurement are evidence the quantity is wrong, not the parsing.** I
|
||
was four attempts into sample-exact difference-signal alignment with no verdict —
|
||
well past the point where that rule applies.
|
||
|
||
So the quantity changed. **Band energies need no alignment at all**: a statistic
|
||
over the window cannot be corrupted by a lag of any size.
|
||
|
||
| band | `ADV` | `S00A` |
|
||
|---|---|---|
|
||
| 0–500 Hz | −0.15 dB | −0.02 dB |
|
||
| 500–2 000 Hz | −0.28 dB | −0.15 dB |
|
||
| 2 000–6 000 Hz | −0.66 dB | −0.22 dB |
|
||
| 6 000–16 000 Hz | −0.63 dB | −0.29 dB |
|
||
|
||
**Worst deviation 0.66 dB**, transcode consistently a fraction of a dB quieter,
|
||
which is what lossy encoding should look like. The **known negative runs on every
|
||
invocation, not behind a flag**: comparing each source against the *other* movie's
|
||
transcode gives **20.02 dB** and **19.10 dB** — two populations an order of
|
||
magnitude apart, so the 1.5 dB tolerance sits between measured values rather than
|
||
being picked. Now an asserting step in `check-all`.
|
||
|
||
### It also diagnoses the failure it replaced, by elimination
|
||
|
||
Matching spectra within 0.66 dB mean the two decodes **are the same content at
|
||
the same level**. So the difference-signal result — difference louder than source
|
||
— cannot be a level mismatch or a content mismatch. **It is my alignment, and now
|
||
that is evidence rather than my assumption.** The difference path stays in the
|
||
tool, report-only, asserting nothing.
|
||
|
||
⚠️ **The honest limit, stated in the tool's own output:** band agreement cannot
|
||
distinguish a faithful transcode from one that preserved the spectrum and mangled
|
||
the waveform. That is exactly what the difference signal was for, and it is still
|
||
open. **This is a weaker claim than P4 wanted, and it is the one I can support.**
|
||
|
||
## 🔴 My seek trap was over-general — the Decoder narrowed it
|
||
|
||
I wrote *"`-ss` before `-i` is a container-level jump"* as though inexactness
|
||
followed from the placement. They checked it on the same movies: the **video**
|
||
container-seek here is **exact** — a frame taken at 20 s via container seek is
|
||
**byte-identical** to one from a full decode, on both films.
|
||
|
||
📌 So on this disc it is a property of the **audio stream**, not of `-ss`
|
||
placement as such. The correction matters in the direction that bites: **a check
|
||
that only looked at video would clear a path that is still unsafe for audio.**
|
||
Narrowed in the tool's own trap list rather than in prose only.
|
||
|
||
✅ Their reproduction is independent and closer than I expected — 4.597 s on
|
||
`ADV` and 4.256 s on `S00A` for a 4.0 s request, against my 4.6 on a different
|
||
file in a different container, with correlations −0.03 and −0.34 at zero shift.
|
||
**Different content, not a shift**, which is the distinction that makes this trap
|
||
imitate the one §1 names.
|
||
|
||
📌 And their cheap defence for the boundary family is now in the tool: **print
|
||
the search range beside the answer, so an edge reads as an edge.** The refusal
|
||
path was already there; the range was not, and a refusal that does not say what
|
||
it was searching is one an impatient reader widens by guessing. **Four instances
|
||
between us now, and the control caught all four.**
|
||
|
||
### 🔴 And the control caught its own harness
|
||
|
||
`check_extras_resets` reported **`PASSES A WRONG CONTRACT — it checks nothing`**.
|
||
Not a broken check: the Decoder's delivery heading now appears **twice** in
|
||
HANDOFF, and `--control` perturbed only the **first** occurrence
|
||
(`h.replace(old, new, 1)`), so the check found the untouched duplicate and passed.
|
||
|
||
📌 **A perturbation that does not reach every copy of the anchor makes the check
|
||
untestable, and does it silently — because the check keeps passing.** The only
|
||
reason this surfaced is that the control asserts *the check must fail*, so a
|
||
check that could no longer be broken became a loud failure instead of a quiet
|
||
pass. Fixed to replace every occurrence, in both the HANDOFF and walk paths.
|
||
|
||
⚠️ Worth naming: **this is the first time a control has failed because of a
|
||
change in someone else's document rather than in my code.** The anchors couple me
|
||
to their prose, which we both knew; what is new is that *duplicating* a sentence
|
||
is enough to disarm a check without either of us touching a checked value.
|
||
|
||
## A capital letter hid a refuted claim in the file whose job is to say what is open
|
||
|
||
The Decoder read my `BLOCKED.md` and found the `P6 looping` row still asserting
|
||
**"No loop-point field has been identified in any bank"** — days after
|
||
`authored/audio.json` shipped `loop_start_s: 9.44` / `loop_end_s: 61.87` and
|
||
marked that very sentence `[refuted]` in its own `why`.
|
||
|
||
📌 **The correction reached the manifest and not the blocked list**, which is the
|
||
exact failure `audio.json`'s `why` warns about in its own text: *a correction that
|
||
does not reach the artifact a consumer reads has not been made.* I wrote that
|
||
sentence and then did it.
|
||
|
||
### 🔴 And my checker held the phrase and could not see it
|
||
|
||
`check-claims` has carried `no loop-point field has been identified` in its
|
||
register the whole time. It matched **case-sensitively**, and the copy in
|
||
`BLOCKED.md` begins a sentence — so **a capital `N` hid a registered dead claim**,
|
||
and the check reported clean on every run.
|
||
|
||
This is the Decoder's finding of the same day in its cheapest possible form.
|
||
Theirs was a register missing a revival that kept the claim and changed the second
|
||
clause; mine was one letter. **A register matching exact wording does not protect
|
||
the documents that rewrite most, and capitalising a sentence is the smallest
|
||
rewrite there is.**
|
||
|
||
✅ Matching is case-insensitive now, and it **immediately surfaced five more
|
||
unmarked sites** the old check had never been able to see:
|
||
|
||
| | |
|
||
|---|---|
|
||
| `authored/flow.json`, `authored/timing.json` | *"the boot is KNOWN TOO FAST [refuted] on both splashes"* — inside its own withdrawal, untokened |
|
||
| `tools/port/verify-screen` | *"composited rather than standalone"* [refuted] — likewise |
|
||
| `tools/port/check-claims` | my new comment quoting the phrase while explaining it — the recursion, again |
|
||
| `docs/port/DECISIONS.md` | **a whole section, *"The loop seam is ugly on purpose"*, still describing the refuted state** |
|
||
|
||
All six fixed: five tokened, and the two that were **stale rather than
|
||
un-tokened** — the `BLOCKED` row and the `DECISIONS` section — rewritten with the
|
||
shipped values and the supersession stated. Controlled: a planted **capitalised**
|
||
revival fails the check, and removing it passes.
|
||
|
||
### What I am not accepting from the same message
|
||
|
||
⚠️ They also flagged **"P4/P7 video — whether Ⓐ skips a movie"** as stale in my
|
||
file. **It is not.** That row reads *"🟡 (a) ANSWERED, (b) still open"*, cites
|
||
HANDOFF Q9, and points at `authored/flow.json`'s `skippable: true` with its `why`
|
||
carrying the 57 s against 193 s baseline. The open half **(b)** is a different
|
||
question. Reported back rather than quietly "fixed", because accepting a
|
||
correction to a row that is already right would put a false stale-marker on a
|
||
live one — and their own message is about an index amplifying exactly that kind
|
||
of error.
|
||
|
||
📌 Their root-cause note is worth keeping and applies to my pages too: **a
|
||
negative about the METHOD written as a negative about the SUBJECT.** *"`Static.slb`
|
||
resists static scanning"* became *"SE audio is not extractable"* — and the wrong
|
||
one was the heading. Every ❔ row I write asserting something *cannot be known*
|
||
should be checked for whether it means *my instrument cannot see it*.
|
||
|
||
## The difference path cannot verify a lossless encode — so nothing it says counts
|
||
|
||
Three measurements, each cheap, and together they locate the fault exactly:
|
||
|
||
| test | result | what it proves |
|
||
|---|---|---|
|
||
| **identity** — source vs a second decode of itself, lag 0 | **−inf dB difference** | decode-and-subtract is **exact**; the pipeline is not the problem |
|
||
| **lossless** — flac of the *identical* fold, exhaustive stride-1 search over 300 lags | **14.2 dB down** at lag −2596 | it **cannot verify an encode known to preserve every sample** |
|
||
| the shipped `S00A.ogv` | 8.73 dB down | meaningless, given the row above |
|
||
|
||
📌 **A lossless encode must return ≈90 dB down. It returns 14.2.** So the whole
|
||
difference path is disqualified — not "inconclusive", *disqualified* — and every
|
||
number it has produced in this thread, including the ones I reported as
|
||
"difference louder than source", was an artefact of the lag search rather than
|
||
evidence about a transcode.
|
||
|
||
✅ **The identity test is what made this diagnosable**, and it costs one decode and
|
||
no encoder. It should have been the first thing I ran, three iterations ago:
|
||
**before asking whether an instrument can measure a difference, ask whether it
|
||
returns zero for no difference.**
|
||
|
||
### Hypotheses ruled out along the way, so nobody re-runs them
|
||
|
||
* **Drift** — the offset is stable at ≈−2465 samples across t=2, 10 and 20 s.
|
||
Not a clock mismatch.
|
||
* **The container start time** — `start_time` is exactly 0 on `S00A.wmv`, so the
|
||
~51 ms offset is not a container timeline shift.
|
||
* **The codec being perceptual** — I suspected Vorbis q5 simply cannot reach §1's
|
||
40 dB. Plausible, and **not the explanation here**: the lossless control fails
|
||
the same way, so the ceiling is my search, not the encoder.
|
||
* **Level or content mismatch** — already excluded by the band check (0.66 dB
|
||
across four bands).
|
||
|
||
⚠️ **The tool now refuses on this path**, and carries the acceptance test in the
|
||
code rather than leaving it to be rediscovered: **make lossless-vs-source return
|
||
≥ 60 dB down before believing anything the difference path says.** The band check
|
||
is unaffected and still asserts.
|
||
|
||
📌 What this changes about the milestone: **P4's waveform question is not merely
|
||
open, it is open with a disqualified instrument.** That is worse than it looked
|
||
yesterday and better than believing 8.73 dB meant something.
|
||
|
||
## The identity rule, turned back on my own newest tool — and it was biased
|
||
|
||
The Decoder ran my identity rule against their coherence estimator, it passed, and
|
||
they returned a sharper form of it: **a positive control that is merely "high"
|
||
hides the difference between an exact instrument and a lossy one.** Theirs read
|
||
0.94 for two reasons at once — a correct estimator *plus* a windowed delay — and
|
||
only the identity case could separate them.
|
||
|
||
📌 **That lands on the band check I shipped yesterday and asserted in
|
||
`check-all`.** Its positive control was **0.29 and 0.66 dB** — small, and *small
|
||
is not zero*. A systematic bias would sit inside 0.66 dB looking like a pass.
|
||
|
||
Adding source-against-itself: **7.656 dB.** Larger than the number the check calls
|
||
faithful.
|
||
|
||
### The bias was in the control's construction, not the measurement
|
||
|
||
`bands()` applies the fold to the **left side only**, which is correct for the
|
||
real comparison — a 5.1 source needs folding, an already-stereo transcode does
|
||
not. Applied to source-against-itself, that same asymmetry compares a folded
|
||
signal against a raw six-channel average. The fold is now per-side, and identity
|
||
reads **0.000 dB, exact**.
|
||
|
||
✅ **The published 0.66 dB is unchanged** — that comparison was always
|
||
asymmetric-by-design and remains correct. What changed is that the instrument is
|
||
now **known unbiased** rather than assumed to be, and the check has three
|
||
separated populations instead of two:
|
||
|
||
| | |
|
||
|---|---|
|
||
| identity | **0.000 dB — exact** |
|
||
| source vs its own transcode | 0.29 / 0.66 dB |
|
||
| source vs an unrelated movie | 19.10 / 20.02 dB |
|
||
|
||
**The bottom of that scale is now anchored rather than inferred**, which is the
|
||
whole difference between "0.66 is small" and "0.66 is small *compared with zero,
|
||
measured*".
|
||
|
||
📌 The general rule, now stated in the form that catches both our cases: **a
|
||
control that establishes only an upper bound on error cannot distinguish an exact
|
||
instrument from a slightly wrong one — and "slightly wrong" is the interesting
|
||
failure, because it passes.** Mine was one function argument. Theirs was one line.
|
||
Both were available from the day the tool was written.
|
||
|
||
## Their refutation attempt on my band check found a coverage hole and two defects
|
||
|
||
They tried to refute *"band energies need no alignment"*. **It survives** — 1 s of
|
||
misalignment costs 0.16 dB, well inside the pass band — but they narrowed it
|
||
correctly: at **10 s the cost reaches 1.00 dB**, because a fixed analysis window
|
||
covers different material once the shift is large. *"Needs no alignment"* was my
|
||
wording and it was too strong; the tool now says **robust to misalignment, not
|
||
free of it**.
|
||
|
||
Their second point is the one that mattered: **the separation margin is
|
||
material-dependent.** Two unrelated music banks separate by only 5.28 dB where an
|
||
unrelated movie gave me 19–20. **A movie is a very easy negative.** So I built the
|
||
*hard* one — the failure this check exists to catch — and it failed.
|
||
|
||
### 🔴 A 6 kHz-lowpassed source: `ADV` caught it, `S00A` does not
|
||
|
||
| | worst band deviation |
|
||
|---|---|
|
||
| real transcodes | 0.29 / 0.67 dB |
|
||
| **6 kHz lowpass, `ADV`** | **4.27 dB — covered, 2.8×** |
|
||
| **6 kHz lowpass, `S00A`** | **1.28 dB — NOT COVERED, under the 1.5 dB threshold** |
|
||
| unrelated movie | 21.78 / 22.55 dB |
|
||
|
||
**A transcode that lost everything above 6 kHz would pass this check on `S00A`**,
|
||
because `S00A`'s own 6–16 kHz content sits at −67 dB — removing it changes almost
|
||
nothing. The check's sensitivity is a property of the *material*, which is the
|
||
Decoder's negative-separation finding arriving on the positive side.
|
||
|
||
📌 On the way, splitting the top band raised `ADV`'s detection from 2.58 to
|
||
4.27 dB. **That is changing the instrument's resolution so it can see a failure it
|
||
must see, driven by a control it failed — not loosening the pass threshold**,
|
||
which is unchanged. The distinction is the whole difference between fixing an
|
||
instrument and fitting one.
|
||
|
||
⚠️ Reported per asset as **COVERED / NOT COVERED** rather than asserted: making
|
||
the suite permanently red on a gap I cannot close today helps nobody, and hiding
|
||
it turns a coverage hole into scenery. Tracked in `BLOCKED.md`.
|
||
|
||
### 🔴 And repairing it exposed two defects that had been hiding each other
|
||
|
||
* **`return 0` was unconditional.** Making the difference path report-only
|
||
swallowed the band verdict with it, so `check-all`'s `transcode-bands
|
||
must-pass` step **could not fail** — an asserting step that asserts nothing,
|
||
shipped by me, one day after I wrote up the same shape in someone else's work.
|
||
Band failures were being printed and discarded.
|
||
* **The disqualified difference path was still voting on the exit code.** Fixing
|
||
the return turned the run red for that reason rather than the real one.
|
||
|
||
📌 **Two defects hiding each other**: with the return broken, the voting bug was
|
||
invisible; with the voting bug present, fixing the return produced a red run for
|
||
the wrong cause. Neither would have surfaced without building a control the tool
|
||
could fail — which is the argument for hard negatives in one line.
|
||
|
||
## 🔴 RETRACTED: the `S00A` coverage hole was my control's filter, not the check
|
||
|
||
Yesterday I reported that a 6 kHz-lowpassed `S00A` deviated only 1.28 dB and
|
||
therefore **"a transcode that lost its top end would pass this check"**, filed it
|
||
as a coverage hole, and sent it to the Decoder — who wrote back that it was the
|
||
part of my message they would keep.
|
||
|
||
**It is wrong, and the fault was in the control.** `lowpass=f=6000` is
|
||
**single-pole**, 6 dB/octave: a mild tilt that leaves most of the octave above
|
||
6 kHz in place. I named it *"a transcode that lost its top end"* and it did not
|
||
build that failure. A real brick wall — four poles — is caught:
|
||
|
||
| | 1-pole (what I tested) | **4-pole (a real top-end loss)** |
|
||
|---|---|---|
|
||
| `ADV` | 4.27 dB, 2.8× | **6.52 dB, 4.3×** |
|
||
| `S00A` | 1.28 dB — *"NOT COVERED"* | **1.83 dB, 1.2× — covered** |
|
||
|
||
📌 **The instrument took the blame for the control's weakness.** I had just
|
||
finished telling the Decoder that a control must be a *hard* negative; the harder
|
||
lesson is that **a control has to CONSTRUCT the failure it is named after** — mine
|
||
carried the right name over the wrong filter, and I read the resulting miss as a
|
||
property of the check.
|
||
|
||
⚠️ **What survives is weaker and more precise than either version:** `S00A` is
|
||
covered by **1.2×**, which is thin, and the tool now prints *"⚠️ THIN — little HF
|
||
in this material"* whenever the margin is under 2×. That is a real sensitivity
|
||
statement — the margin depends on how much HF the material has — and it is the
|
||
defensible remainder of what I called a hole.
|
||
|
||
🔴 And the retraction had to travel fast: the Decoder had already decided to keep
|
||
the finding. **A wrong result that the other agent has adopted is more expensive
|
||
than one they ignored**, which is an argument for sending corrections at the same
|
||
priority as findings, not lower.
|
||
|
||
## Their two tools had the shape I shipped, and the general form is sharper now
|
||
|
||
They tested *"an asserting step that asserts nothing"* against their own tools and
|
||
**both had it**: `check_refuted.py` found a planted revival, printed it, and
|
||
exited 0; `impossibility_scope.py` printed `CONTROL FAILED` and exited 0 — written
|
||
the same day they read my report of the shape.
|
||
|
||
📌 Their statement of it is better than mine: **a check has two failure modes and
|
||
the loud one hides the quiet one.** A wrong answer gets noticed; a check that can
|
||
only ever say "fine" is reported as passing forever, *and its output looks like
|
||
evidence*. **Printing a verdict is not asserting it.**
|
||
|
||
✅ And they controlled the exit code **in both directions** — clean 0, planted
|
||
revival 1, control passing 0, control deliberately broken 2. Verifying only that a
|
||
check passes when it should is exactly what leaves this invisible. My
|
||
`--control` flags assert failure-on-perturbation but **not that a broken control
|
||
reports broken**, which is the same gap one level up; noted as the next thing to
|
||
close here.
|
||
|
||
📌 Second instance of the backtick loss, theirs landing **in the commit message
|
||
describing the defect class**. Two agents, same shell trap, same dropped-noun
|
||
property — the sentence stays grammatical, so nothing looks wrong.
|
||
|
||
## Closing the two-directional gap: the control harness now asserts itself
|
||
|
||
The gap I named and the Decoder prioritised: **every `--control` run asserts that
|
||
each check fails on a perturbed contract; none asserted that a broken control
|
||
reports broken.** That is *printing a verdict without asserting it*, one level
|
||
up — and a harness that silently approves a dead check is exactly as useless as a
|
||
check that silently approves a dead value.
|
||
|
||
`contract-check --selftest` feeds the machinery a **stub that cannot fail** — a
|
||
function that prints "everything is fine" and asserts nothing, which is precisely
|
||
the defect I shipped in `verify-transcode-fidelity`'s unconditional `return 0` —
|
||
and requires the machinery to flag it. Exit codes follow the Decoder's
|
||
convention, which separates the two failures that matter: **0** all good, **1** a
|
||
real check failed, **2** the **harness** is broken and nothing it has reported can
|
||
be trusted. Now an asserting step in `check-all`.
|
||
|
||
### 🔴 It caught me twice while being written
|
||
|
||
* **The first version argued instead of measuring.** It checked that the stub
|
||
left the failure counter at zero and then *reasoned* that `control()` would
|
||
therefore flag it. That is the error this entire thread has been about,
|
||
committed inside the tool built to prevent it. Rewritten to push the stub
|
||
through the real `control()` loop and read its actual verdict.
|
||
* **Then it returned 2 immediately** — the stub was flagged, but as *"the
|
||
control's own anchor is gone"* rather than as a dead check. My `src` selection
|
||
read `h if … in CONTROLS else nav()[0]`, so anything not in that one list was
|
||
anchored at the **walk** document. **A real failure for a fabricated reason**,
|
||
which is the same confusion the `ANCHOR SPLIT` outcome exists to separate.
|
||
Inverted to test membership in `NAV_CONTROLS` instead.
|
||
|
||
📌 Both were found by the self-test *doing its job on itself* — which is the
|
||
argument for the exit code being the assertion. The Decoder's version of this
|
||
caught a broken decision rule mid-flight and **refused to run**; without it their
|
||
sweep would have reported a uniform, confident, fabricated answer for three
|
||
screens.
|
||
|
||
⚠️ **What this still does not cover:** `check-claims`, `audit-kinds` and
|
||
`verify-transcode-fidelity` have controls but no harness self-test. The shape is
|
||
now known and the fix is cheap; it is not done, and saying so is the point of the
|
||
row rather than leaving it to look finished.
|
||
|
||
## The register check had no executable control, and an empty register passed forever
|
||
|
||
`check-claims` guards the refuted register — the thing both agents lean on when
|
||
they say a dead claim is not being re-asserted. It had **no control machinery at
|
||
all**. Every *"planted a revival, it failed, removed it, it passed"* in this file
|
||
was done **by hand, once, and never again** — in a repository where two of my own
|
||
tools carry the line *"a control that does not execute is not a control"*. I wrote
|
||
that about somebody else's tool.
|
||
|
||
### 🔴 And the hole the Decoder found in theirs was here too
|
||
|
||
The scan loop runs once per register row. **With no rows it runs zero times**,
|
||
`fail` stays 0, and the script printed *"every refuted claim appears only inside
|
||
its correction"* and exited **0**. A register that parses nothing reported clean,
|
||
forever — the stub defect, in the checker whose clean runs both of us cite. It now
|
||
exits **2** with *"the harness is broken, not the corpus"*.
|
||
|
||
### Four cases, executed, driving the real script as a subprocess
|
||
|
||
| case | exit |
|
||
|---|---|
|
||
| clean tree | **0** |
|
||
| unmarked revival planted | **1** |
|
||
| revival planted **marked** | **0** — and no false positive |
|
||
| register emptied | **2** |
|
||
|
||
📌 Two things taken from the Decoder's build of the same thing rather than
|
||
invented: **the self-test drives the real machinery and reads its actual exit
|
||
code** — my first `--selftest` reasoned about what the harness *would* do, which
|
||
is the cheaper mistake and the one I made — and **the three-way exit convention**,
|
||
which is what lets "the corpus is dirty" and "the checker is broken" be different
|
||
answers instead of both being "nonzero".
|
||
|
||
⚠️ **The plant lands in a real scanned directory**, because a control that runs
|
||
somewhere the tool does not look proves nothing about the tool. Verified by
|
||
breaking it deliberately: pointing the plant at an unscanned path makes the
|
||
control report **🔴 the control machinery itself is broken**, which is the
|
||
two-directional assertion — it can fail, and it fails for the right reason.
|
||
|
||
⚠️ Still without harness self-tests, and filed rather than left looking finished:
|
||
`audit-kinds` and `verify-transcode-fidelity`. Same shape, cheap, not done.
|
||
|
||
## Two harness gaps closed, and one of them was mine done by hand
|
||
|
||
### The boundary case I had verified once, by hand
|
||
|
||
`check-claims --control` plants a revival in `docs/port/` and requires exit 1.
|
||
That the plant lands **inside a scanned directory** was a property I checked
|
||
manually, one time, and wrote up — **the exact pattern I had criticised in this
|
||
same tool one iteration earlier.**
|
||
|
||
A **fifth case** now plants the *identical text* outside the scanned root and
|
||
requires **0**. The pair is what asserts the boundary is real: same text, exit 1
|
||
inside and 0 outside. **Either half alone is consistent with the tool scanning
|
||
everything, or nothing.** Five cases: clean 0, unmarked 1, marked 0, outside-root
|
||
0, empty register 2.
|
||
|
||
📌 The Decoder added the same case to theirs after I raised the boundary, and
|
||
their reason is the sharper statement: **the property held because they had
|
||
reasoned it, not because anything asserted it.** Mine was in precisely that state
|
||
while I was writing that criticism about hand-run controls.
|
||
|
||
### `audit-kinds` now asks whether it can find anything
|
||
|
||
It has always reported what it found and never been asked whether it *can* find
|
||
anything — and its clean runs are cited in this file as evidence that fifteen
|
||
labels are grounded. A walk matching no labels, an extractor accepting
|
||
everything, or a `main` returning 0 regardless would all have produced the same
|
||
clean run.
|
||
|
||
`--selftest` pushes three synthetic rows through the **real** classifier and reads
|
||
its verdict: a `why` citing nothing must come back **BARE**, one citing a real
|
||
path **ok**, one citing a missing path **DANGLING**. Verified two-directionally —
|
||
an extractor stubbed to accept everything returns **exit 2**, *"nothing this tool
|
||
has reported clean is trustworthy"*. Asserting in `check-all`.
|
||
|
||
## All four submenus reset, and I am not promoting it to a rule
|
||
|
||
Measured: **LOAD GAME, TUTORIAL and OPTIONS reset**, joining EXTRAS. **Four of
|
||
four submenus reset; the main menu is the only screen that remembers.** Three of
|
||
those four are not in this export, so **no authored value changes** — the guard's
|
||
statement gets stronger, the data does not move.
|
||
|
||
🔴 **Not promoted to a rule, deliberately.** *"Submenus reset"* at 4/4 is better
|
||
evidence than the 2/2 that made `wrap` a menu-wide rule. Adopting it would
|
||
**change nothing today** — the only submenu this port ships is already measured —
|
||
and what it *would* do is pre-decide the next screen from a generalisation
|
||
instead of a measurement. That is the trap that nearly let a derived rule
|
||
overwrite EXTRAS' measured opening item. The guard prints the 4/4 finding beside
|
||
its per-screen values so the evidence is visible without being load-bearing.
|
||
|
||
⚠️ **My MISSION-SELECT-versus-top-item question stays open**, and they looked for
|
||
the case I named: none of the three separates it — each opens on its own first
|
||
item. `LOAD GAME` looked like the counter-example, opening on slot 01 with slots
|
||
19 and 20 drawn *above* it, but that is a wrapping list around a centred
|
||
selection and 01 is still first. **NEW GAME is untested.**
|
||
|
||
📌 Both agents now hold the same outstanding item — controls without harness
|
||
self-tests — and neither list is empty. Mine is down to
|
||
`verify-transcode-fidelity`.
|
||
|
||
## 🔴 The counter-example I kept asking for was in a file I wrote
|
||
|
||
For several iterations I have said the MISSION-SELECT-versus-top-item ambiguity
|
||
would be decided by *"a screen whose opening item is not its first"*, and that no
|
||
such screen was known. The Decoder found one and reported that it had been sitting
|
||
in their corpus, unconnected, the whole time.
|
||
|
||
**It is in mine too, and I authored it.** `authored/flow.json`, under
|
||
`main_menu/buttons/ptbtn01`, has read since **2026-08-29** (`eef45ec`):
|
||
|
||
> *"MEASURED destination (EASY/NORMAL/HARD/BACK, **opening on NORMAL**, then
|
||
> SELECT DATA)"*
|
||
|
||
**DIFFICULTY opens on the second of four items.** So *"a screen opens on its first
|
||
item"* is **refuted as a general description of this game** — and on `EXTRAS`,
|
||
`TUTORIAL` and `OPTIONS` the named item and the top item coincide **by accident**.
|
||
|
||
📌 Worse than an index failing to amplify: **my `extras/initial_focus_why` framed
|
||
the ambiguity as conditional — *"it matters IF another screen is ever authored"* —
|
||
in the same file that already recorded such a screen.** Future tense over a fact
|
||
twelve keys away. Corrected to name DIFFICULTY concretely.
|
||
|
||
### What it changes in the port, and what it does not
|
||
|
||
* ✅ `MenuFlow.initial_focus`'s `buttons[0]` fallback is now documented as **a
|
||
repair for broken data, not a default** — and that is measured rather than
|
||
fastidious. If a screen ever reaches that line silently, the port shows a
|
||
top-item default *for a game that does not always have one*.
|
||
* ⚠️ **No authored value moves.** DIFFICULTY is not a `GP_TITLE` build and is not
|
||
in this export; `EXTRAS` keeps `ptbtn11`, which is correct under either
|
||
reading. Walk re-run to confirm: unchanged.
|
||
* ❔ **It still does not settle my question**, which is about *reset*, not
|
||
*opening*. That needs the cursor moved inside DIFFICULTY, left, and re-entered
|
||
— and DIFFICULTY's forward path crashes the guest at `SELECT DATA`, so the run
|
||
has to go back rather than on. Theirs to run.
|
||
|
||
📌 **And no checker either of us has built would have caught this.** Every
|
||
instrument in this project verifies that a *claim* matches a *value*. Nothing
|
||
detects that an answer already written down is not being connected to the
|
||
question it answers — mine included, and mine had both halves in one file.
|
||
|
||
⚠️ It also makes the previous iteration's restraint look better than it did:
|
||
declining to promote *"4/4 submenus reset"* to a rule was argued from the
|
||
principle that a generalisation should not pre-decide the next screen. **The next
|
||
screen turns out to be one the generalisation would have got wrong.**
|
||
|
||
## The last control harness, and a clean sweep for the top-item assumption
|
||
|
||
### `verify-transcode-fidelity --selftest`
|
||
|
||
The last tool on my list with controls and no harness self-test. It has **three**
|
||
controls that run every time — identity, a 4-pole top-end loss, an unrelated
|
||
movie — and none of them asked whether the **measurement itself is live**.
|
||
|
||
🔴 **With an empty band list every comparison returns a worst deviation of
|
||
0.0 dB.** Identity passes. The real pair passes. Only the unrelated-movie control
|
||
fails — reporting **exit 1, a corpus problem**, for what is actually a broken
|
||
instrument. Exactly the empty-register shape from `check-claims`, and it gets the
|
||
same fix: **exit 2, the harness is broken, not the transcodes.**
|
||
|
||
`--selftest` drives the script as a subprocess over a short window and reads its
|
||
real exit code: **normal → 0, band list emptied → 2.** Both pass. Asserting in
|
||
`check-all`.
|
||
|
||
📌 That closes my list. Both agents started this thread with tools whose controls
|
||
had never been controlled; **`FID_BANDS` and `FID_WINDOW` exist for no reason
|
||
except to let the self-test break the tool on purpose**, which is the same
|
||
admission the `CLAIMS_REGISTER` override makes.
|
||
|
||
### The top-item sweep, from yesterday's DIFFICULTY finding
|
||
|
||
`DIFFICULTY` opening on **NORMAL, the second of four**, refutes *"a screen opens
|
||
on its first item"* — so anything in the port that quietly assumes the top item is
|
||
now known wrong for a real screen. Swept `port/scripts/`, `tools/port/` and
|
||
`crates/sylpheed-export/src/`:
|
||
|
||
✅ **One site**, `MenuFlow.initial_focus`'s `buttons[0]`, already documented as a
|
||
repair for broken data rather than a default. Every other `[0]` in the tree is
|
||
unrelated indexing — a first git sha, a WAV chunk field, the first timed
|
||
keyframe. **Nothing to fix**, recorded as a negative so the sweep is known to have
|
||
run rather than assumed.
|
||
|
||
## Settled: a submenu resets to its OWN OPENING ITEM, not to its top item
|
||
|
||
Measured on a fresh boot: `DIFFICULTY` opens on `NORMAL` (second of four); after a
|
||
confirmed DOWN to `HARD`, Ⓑ out and Ⓐ back returns to **`NORMAL`** — in-cursor
|
||
**1.0** from where it opened against **93.9** from where it was left.
|
||
|
||
✅ **So `ptbtn11` is right for a reason rather than by coincidence**, and
|
||
`extras/initial_focus_why`'s ambiguity block is replaced by the resolution. The
|
||
reset target is the **authored opening item**, and that item is a per-screen
|
||
default which **need not be the first**.
|
||
|
||
📌 **`MenuFlow.initial_focus`'s `buttons[0]` is a repair, not a default — and that
|
||
is now measured rather than principled.** I documented it that way yesterday from
|
||
the DIFFICULTY *opening* state; the *reset* measurement is what makes it a fact
|
||
about the game instead of a defensible reading.
|
||
|
||
`contract-check` gains a fourth anchor in this area, `check_reset_target`,
|
||
asserting that the port's reset target is the **authored** value rather than an
|
||
index. ⚠️ Its teeth are limited and the code says so: on `EXTRAS` the named item
|
||
*happens* to be first, so agreement here is not evidence — what it guards is that
|
||
a future refactor does not quietly replace the authored lookup with `buttons[0]`,
|
||
which is now known wrong for a real screen.
|
||
|
||
❔ **Not leaned on:** whether the reset target moves once a difficulty has actually
|
||
been **confirmed**. A game that remembered your last choice would behave
|
||
differently, and the probe never confirms one — the same `SELECT DATA` crash that
|
||
constrained the run prevents testing it.
|
||
|
||
📌 On the connection failure we both had, I agree with their reading and want it
|
||
recorded rather than quietly dropped: **neither of us is going to build a regex
|
||
over "questions I have asked"** — that is the amplifier problem with more steps.
|
||
Two agents independently held an answer each had written down. That is **evidence
|
||
the corpus is now larger than either of us can hold**, which is a different
|
||
problem, and one more checker does not solve it.
|
||
|
||
## Their refutation attempt on `extras/initial_focus` — checked against the bytes, twice
|
||
|
||
They attempted to refute `ptbtn11` **against the disc rather than against their
|
||
agreement**, which is what they owed me after the initial-focus corroboration they
|
||
got wrong. It survives: `ptbtn11` y **282**, `ptbtn12` **362**, `ptbtn13` **442**
|
||
— so it is the top button, and the value is right whichever reading of the reset
|
||
target applies.
|
||
|
||
✅ **Re-checked from this port's own export**, a different reader of the same
|
||
disc, and the numbers are identical — extras **282/362/442**, main menu
|
||
**162/242/322/401/482** as the control. Two readers, same bytes, same answer.
|
||
|
||
🔴 **And it confirms why EXTRAS could never have settled the question**: the named
|
||
item and the top item coincide here. It took `DIFFICULTY`, opening on its second
|
||
of four, to separate them.
|
||
|
||
## Menu focus does not survive a reboot — and the reach matters more than the result
|
||
|
||
Six fresh boots all opened on `NEW GAME`, and **three followed a session that
|
||
ended with the cursor on `EXTRAS` or `OPTIONS`** — which is what makes it a test
|
||
of persistence rather than six repetitions of the same start. So my authored
|
||
`NEW GAME` is a **fresh-start value**, not an artefact of session history.
|
||
|
||
⚠️ **The reach is theirs and I am carrying it verbatim into the `why`:** every one
|
||
of those sessions ended with the emulator **killed, not shut down cleanly**. A
|
||
game that writes menu state on a clean exit never gets the chance — so this
|
||
measures *"does not survive a killed session"*. **If a real console remembers a
|
||
cursor across a power cycle, that does not contradict this.**
|
||
|
||
📌 **No boot was spent on it.** The captures already existed from earlier runs;
|
||
they had been listing this as untested while the evidence sat in six directories.
|
||
**That is the connection failure we both hit yesterday, occurring a third time** —
|
||
and this instance was found *because* we had just named it, which is the only
|
||
encouraging thing about the pattern.
|
||
|
||
⚠️ Noted, touching nothing of mine: their `ring_row.py` calibration was fitted
|
||
against another tool's row centres rather than the disc's button rows and was
|
||
wrong (`49.5 + 1.060·y` re-fitted to `64.82 + 0.9919·y`, residuals under 0.7 px —
|
||
an offset, essentially no scaling). **No item assignment changed**, because the
|
||
reader's constants were measured off captures and never used the bad fit. The
|
||
disc rows they re-fitted against are the same 162/242/322/401/482 my export
|
||
prints.
|
||
|
||
## Liveness: every one of my tools passed on an empty input
|
||
|
||
The Decoder generalised my empty-band case into the rule I now keep: **a control
|
||
that only compares two things cannot tell you the comparison is happening.** An
|
||
empty band list, a blank frame, an empty register — each makes a checker
|
||
**agreeable rather than wrong**, and agreeable is indistinguishable from correct
|
||
in a log.
|
||
|
||
Swept my own tools against inputs that contain nothing:
|
||
|
||
| tool | before | now |
|
||
|---|---|---|
|
||
| `audit-kinds` | **exit 0** — printed *"0 kind label(s)"* and reported clean | **exit 2** |
|
||
| `check-claims` | **exit 1** from a `FileNotFoundError` inside the withdrawal hook | **exit 2**, via a preflight |
|
||
| `verify-transcode-fidelity` | manifest with no videos → loop never runs, *"every transcode faithful"* having compared none | **exit 2** |
|
||
|
||
🔴 **The `check-claims` case is the one worth naming.** Run from the wrong
|
||
directory it died in the hook and exited **1** — which in that script's own
|
||
vocabulary means *"a refuted claim is still being asserted"*. **A real failure
|
||
with a fabricated diagnosis**, the same shape as my control anchoring at the
|
||
wrong document two iterations ago, and the third instance of that family. A
|
||
preflight now names the roots it needs and calls their absence a **harness**
|
||
fault.
|
||
|
||
✅ Both self-tests gained the liveness case, driven as subprocesses so the real
|
||
exit code is read: `audit-kinds --selftest` runs itself in an empty directory and
|
||
requires 2; `check-claims --control` is now **six** cases — clean 0, unmarked 1,
|
||
marked 0, outside-root 0, empty register 2, **nothing to scan 2**.
|
||
|
||
📌 What makes this worth an iteration rather than tidying: **none of these tools
|
||
was wrong.** Each produced correct output on real input, every time it ran. What
|
||
they could not do is tell the difference between *"I checked and it was fine"* and
|
||
*"I checked nothing"* — and every green line I have quoted in this file was the
|
||
first of those only because the directory happened to be right.
|
||
|
||
## Their `ring_row.py` defect, and why it did not reach me
|
||
|
||
Their liveness self-test found that `main_menu_item(ring_row(f)) is not None` was
|
||
being used as a main-menu test, and **a TITLE frame passes it** — the gutter
|
||
carries a bright cluster at y=243, inside tolerance of row 0, so the title reads
|
||
as `NEW GAME`. Glyph count separates them cleanly (714 against the menu's 327);
|
||
the ring row alone does not.
|
||
|
||
✅ **No result they sent me is affected**, and the reason is structural rather
|
||
than lucky: Ⓑ from a submenu goes to the menu, never the title, so the weak test
|
||
was never presented with the frame that breaks it. **The test was weaker than
|
||
they were trusting it to be, not wrong in what it produced** — which is precisely
|
||
the state a self-test exists to expose *before* a screen sequence changes and it
|
||
starts mattering.
|
||
|
||
⚠️ I have not re-derived their focus results, and I am not treating this as a
|
||
reason to. Several of my authored values rest on them; what I have instead is
|
||
their statement of the exposure and the structural argument for why it did not
|
||
fire. Recorded as that, not as verification.
|
||
|
||
## The liveness lesson, applied to the product: a mistyped override was silent
|
||
|
||
Every checker fix this week has been about a tool that could not tell *"I checked
|
||
and it was fine"* from *"I checked nothing"*. **The port itself had the same
|
||
defect, facing the person the asset tree exists for.**
|
||
|
||
`ExportTree.resolve` announces every shadow as it happens, and its comment already
|
||
records why a startup summary was wrong. **Nothing reported the opposite.**
|
||
Measured with two planted overrides — one correct, one in a mistyped directory:
|
||
|
||
```
|
||
mod: sprites/title/main_menu/ptbase.png <- data/mods/... ← announced
|
||
sprites/title/TYPO_menu/pteff05.png ← NO OUTPUT AT ALL
|
||
```
|
||
|
||
The modder sees the port load, run, and say nothing about the file that did
|
||
nothing. **That is MODDING rule 4's own failure mode**: base-and-overrides is only
|
||
usable if an override that misses says so.
|
||
|
||
`ExportTree.unused_mods()` + a report at run end now lists them. Controlled both
|
||
directions: **one inert file with the typo present, silent with it removed.**
|
||
|
||
### 🔴 Getting the report's *category* right took three tries, and that is the point
|
||
|
||
* **v1 — "never used".** Flagged `data/mods/README.md` on every run. **A report
|
||
with a standing false positive is one nobody reads**, which is precisely the
|
||
failure it exists to fix.
|
||
* **v2 — "no such path in the export".** Correct, and still flagged the README:
|
||
it genuinely cannot shadow anything.
|
||
* **v3 — excluded by extension, with the rule checked rather than assumed.** The
|
||
export tree contains only `png`, `json`, `ogg`, `ogv`, `cmd` — **verified, zero
|
||
`.md` anywhere** — so a `.md` in `data/mods` could never be an override *by
|
||
construction*. Flagging a class that can never be one is noise.
|
||
|
||
📌 And the report distinguishes two things v1 conflated: a file whose path exists
|
||
in the export but **was not read this run** (a `--menu` run touches one screen) is
|
||
**not listed**. Every line printed is an override that can never apply, whatever
|
||
the run does.
|
||
|
||
⚠️ One incident worth keeping: `boot.gd` **already had an `_exit_tree`**, and
|
||
adding a second was a **parse error** — the run failed loudly instead of one hook
|
||
silently replacing the other. The cheapest possible failure mode, and only
|
||
because GDScript happens to reject it.
|
||
|
||
## Their P3 delivery, taken at the strength they gave it
|
||
|
||
Q6's count-match now has **disc support for its structure**: every button record
|
||
across all 16 `GP_TITLE` entries is `ptbtn00`, `ptbtn01–05`, `ptbtn11–13` — three
|
||
button screens and no fourth, with the other four destinations in their own paks.
|
||
|
||
⚠️ **Not authored from, and they said not to.** *"It shows the shape the
|
||
count-match asserts is real on the disc; it does not show that event 3 is a
|
||
particular row."* My `flow.json` already binds buttons to destinations by
|
||
measured screen rather than by event index, so nothing here changes — and if a
|
||
button-to-event map were ever needed, **there is not one**.
|
||
|
||
📌 Their negative carries its own reach, which is the part I would have got wrong:
|
||
they searched every pak for an 8-button-record build and found none, but the
|
||
search assumed DIFFICULTY's four items pair with `f` variants as `GP_TITLE`'s
|
||
screens do. So what is established is *"not an 8-record `btn`-named build
|
||
anywhere"* — **narrower than "not found"**, and the narrowing is theirs.
|
||
|
||
## `docs/port/RUNNING.md` — the P5 gate needed a human and had no runbook
|
||
|
||
P5's gate is *"a human clicks through it"*, and **no document told a human how**.
|
||
The commands existed in `boot.gd`'s header and scattered through a
|
||
twelve-thousand-line `DECISIONS.md` — which is this project's own finding about
|
||
capabilities that live only in the record, applied to the one milestone that
|
||
cannot be self-certified.
|
||
|
||
`RUNNING.md` is 107 lines and every command in it was run before it was written:
|
||
build the tree, `--boot --play` for the cold-start walk, `--menu=` to skip the
|
||
157 s intro, and a table of **what a human should see at each press** so the gate
|
||
is a judgement about the port rather than about whether they drove it right.
|
||
|
||
Three sections exist because a reader would otherwise report the container as a
|
||
defect:
|
||
|
||
* **What is knowingly missing** — four of five main-menu destinations are
|
||
*measured but in other archives*, and the port prints what it would have opened
|
||
and why it cannot. `NEW GAME`'s skipped chain is a stated gap, not a sequence.
|
||
* **What this container distorts** — 720p decodes **+6.7 %…+6.9 %** slower than
|
||
real time here, **you will hear nothing** (dummy audio driver, so *"I heard
|
||
it"* is not available in this box), and the leak line at exit is engine-side,
|
||
measured 8 → 8.
|
||
* **Modding** — overrides are announced as read, and inert ones listed at the end.
|
||
|
||
⚠️ It does not claim P5 is met. It removes the excuse that the gate was hard to
|
||
attempt.
|
||
|
||
## Their `BGM_103` report: the row was already corrected, and it carries their diagnosis
|
||
|
||
They reported `BLOCKED.md`'s *"which BGM the menu plays — not on the disc"* as
|
||
wrong and themselves as the source. **The row has been struck and corrected for
|
||
days**, and I am telling them rather than silently "fixing" a live row — the
|
||
asymmetry they themselves named: *a wrongly-superseded row removes a live question
|
||
from both views, and nobody re-checks something already marked handled.*
|
||
|
||
📌 **And the correction already contains the diagnosis they have just made.** My
|
||
row says the negative is bounded — *"the **tables** (`SOUNDS`, `FILES`, bank
|
||
headers) name no screen"* — cites `li r5, 1103`, the byte-for-byte wave match, and
|
||
ends:
|
||
|
||
> *"a row here must quote the reach of a negative, because a negative summarised
|
||
> without its bound reads as a bigger negative than it is."*
|
||
|
||
Their message says the same thing arrived at independently: *"the negative was
|
||
true of the CUE TABLE and I wrote it as a negative about the disc."*
|
||
|
||
🔴 **Fourth instance of the connection failure, and the sharpest yet:** the
|
||
correction was *about their page*, written in my file, and neither of us connected
|
||
it. The three before were an answer sitting unread; this one was an answer sitting
|
||
**addressed**.
|
||
|
||
✅ Their method note is the transferable part and it inverts my own v1→v3 story:
|
||
their impossibility sweep printed 40 candidates with a known false-positive rate,
|
||
and my *"a report with a standing false positive is one nobody reads"* nearly made
|
||
them **filter it**. Instead they measured what the false positives actually were —
|
||
guessed infrastructural nouns, 5 of 40; the real category was *"not on the disc"*
|
||
used as a **classification legend** — and reading those turned up the one that was
|
||
not legend at all. **The noisy report was worth reading carefully exactly once
|
||
before being made quiet.** My three-version story is about reports that are
|
||
*permanently* noisy; theirs is about the single careful read that must happen
|
||
first.
|
||
|
||
## The shared-state problem is two gaps, and only one of them needs a human
|
||
|
||
The Decoder's correction, and it reframes something I have been filing wrongly for
|
||
a week:
|
||
|
||
| | needs |
|
||
|---|---|
|
||
| what a peer **holds** | **nothing** — `git show <ref>:<path>`, from any topic branch, on refs already fetched |
|
||
| what a peer must be **told** | a human merge to `main` |
|
||
|
||
**I had been treating both as blocked on the merge.** Half never was.
|
||
|
||
The symmetry is exact and unflattering to both of us. I read `main`'s 926-line
|
||
HANDOFF for two days while the live one sat on a branch **I was already citing by
|
||
sha**. They read this port's `BLOCKED.md` at a copy **234 commits behind** and
|
||
reported a corrected row as stale, with the live file one `git show` away on a ref
|
||
already in their checkout. Same gap, opposite directions, one command in both.
|
||
|
||
📌 Their addition to the fourth connection-failure instance is the sharpest
|
||
statement of it yet: that answer was **addressed, fetchable, and cited a commit of
|
||
theirs**. *Three affordances, and neither of us used them.*
|
||
|
||
### So the command exists rather than the intention
|
||
|
||
`tools/port/peer-head` prints, for each file this port depends on and another
|
||
agent writes, the newest commit touching it **on any ref**, whether the working
|
||
tree has it, and the exact `git show` line. Report-only in `check-all`: being
|
||
behind a peer's topic branch is the normal state, and a red line for it would be
|
||
scenery inside a day.
|
||
|
||
✅ It confirms the anchored checks were already current by construction —
|
||
`contract-check` reads HANDOFF and `navigation.md` from the newest ref rather than
|
||
the working tree, which is why my *checks* were right while my *tree* was 115
|
||
commits behind.
|
||
|
||
### 🔴 And it caught a defect in itself on its first run
|
||
|
||
`PROTOCOL.md` showed **mine == newest** and yet **"1 unread"**, with an
|
||
instruction to `git show` **my own version**. The count was true — one commit
|
||
touching that path is outside my ancestry — and the *label* was wrong: two
|
||
branches can each carry an unrelated commit to a file while my copy is still the
|
||
newest. **A real number with a fabricated meaning**, which is the family this
|
||
project keeps paying for, appearing in the tool written to close a different
|
||
instance of it.
|
||
|
||
Staleness is now decided by whether the **newest** commit is reachable from
|
||
`HEAD`, and divergence is reported separately as *"(n commit(s) elsewhere, none
|
||
newer)"*.
|
||
|
||
⚠️ **The rule, which is not an instrument:** *read the peer's branch head before
|
||
reporting a defect in their file.* They stated it, and it is the one that would
|
||
have prevented both incidents. The tool only makes it cost one command instead of
|
||
one memory.
|
||
|
||
## The mirror of `peer-head`: my register was judging their files from my stale tree
|
||
|
||
They checked their `check_refuted.py` against the exposure I had just described
|
||
and found it scans `docs/` — including files I author, from copies days behind.
|
||
**Mine had the same shape**, and measuring it first (their discipline, after their
|
||
impossibility sweep taught them their first guess at a category was wrong) gave a
|
||
result that then changed under the fix:
|
||
|
||
| | |
|
||
|---|---|
|
||
| scanning **my working tree** | 33 files match a registered claim, **0 in a peer-owned root** — "latent, not active" |
|
||
| scanning **their branch head** | **6 occurrences**, in four of their files |
|
||
|
||
📌 **So the exposure was not latent — my copy was just too old to see it.** `docs/re/`
|
||
is **246 commits** behind their head here, `docs/agents/` 13, `docs/game/` 9. A
|
||
verdict about one of their files would have been a verdict about my copy of it,
|
||
and the failure direction is the false positive: flagging something they have
|
||
already corrected — **which is exactly what they did to me by hand, reading my
|
||
`BLOCKED.md` 234 commits behind.**
|
||
|
||
✅ Fixed by the only structural pattern either of us has found: **read the ref, not
|
||
the tree.** Peer-owned roots are now scanned with `git grep` against the newest
|
||
blob on any ref. It is the same reason `contract-check` stayed correct while this
|
||
working tree sat 115 commits behind.
|
||
|
||
### 🔴 And the first version of the fix over-claimed
|
||
|
||
It put those six hits in the failure count, so the run went red. **That applies my
|
||
marking convention to their corpus**: `[refuted]` is a token *this port* uses in
|
||
*its own* files; their pages mark corrections their own way. Three of the six are
|
||
in their `METHOD.md` and one in an audit log — **pages whose subject is the
|
||
corrections**, so the phrase appearing there is what a correction looks like, not
|
||
a revival.
|
||
|
||
Now reported and not counted: *a prompt to look, never a verdict* — the same
|
||
conclusion the withdrawal hook reached about its own candidates. **A checker that
|
||
failed on another agent's file for not using this one's punctuation would be noise
|
||
inside a day, and I would have been the one to file it.**
|
||
|
||
⚠️ What this does **not** establish: whether any of the six is a live revival in
|
||
their corpus. That is a judgement about their pages, made with their conventions,
|
||
and it is theirs. What changed here is that the question can now be asked from the
|
||
right copy.
|
||
|
||
## Their zero held, mine was six, and the difference is structural rather than hygiene
|
||
|
||
They re-ran their cross-scan from my ref as I prompted. **Their zero held** — and
|
||
they controlled it, because *a zero from a broken reader looks identical to a real
|
||
one*: they probed my live `BLOCKED.md` for a string they knew was in it and got a
|
||
hit over 99 KB.
|
||
|
||
📌 **The asymmetry is expected, not a difference in care.** My register holds
|
||
claims about **port decisions**, which their `METHOD.md` quotes constantly because
|
||
they write up our joint corrections. Theirs holds **decoder-domain phrasing** my
|
||
files rarely quote verbatim. **My six and their zero are the same phenomenon from
|
||
two directions.**
|
||
|
||
✅ And parsing my register properly — it is a heredoc, twelve rows — they find
|
||
**three** of my claims in their files: `no loop-point field has been identified`
|
||
[refuted], `AUDIBLY WRONG AT THE SEAM` [refuted], `goes against the port` [refuted]. **None is a live revival**:
|
||
all sit on pages whose subject *is* the corrections, plus their own stale copy of
|
||
my `BLOCKED.md`. **Which is what I predicted and deliberately did not assert** —
|
||
the judgement was theirs to make with their conventions.
|
||
|
||
### 🔴 Their false zero, and the control it earned on my side
|
||
|
||
Their first attempt regexed quoted strings out of `check-claims`, produced **63
|
||
phantom phrases**, and found **zero** — *a false zero from a reader invented in
|
||
the same minute*. The same family as everything else this week: an instrument
|
||
whose clean run is indistinguishable from not looking.
|
||
|
||
**My peer scan had exactly that hole.** It found six hits today, so it is
|
||
demonstrably live *now* — but on the run where their pages no longer contain any
|
||
of these phrases, a wrong ref, a wrong pathspec or a renamed directory would all
|
||
produce the same clean line. It now asserts a **known positive** first: how many
|
||
files it can see at their ref, refusing with **exit 2** below ten. Verified both
|
||
ways — **623 files live; a blinded pathspec exits 2.**
|
||
|
||
📌 **The line I would keep from their message is about my restraint, not my
|
||
scan.** My first fix counted their six as failures and went red, applying my
|
||
`[refuted]` token to a corpus that marks corrections its own way. Their reaction
|
||
to that hypothetical is the part I could not have supplied: *"I would have argued
|
||
with it rather than fixing my pages, which is the worst of both outcomes."*
|
||
|
||
**A false positive aimed at another agent does not merely get ignored — it gets
|
||
disputed, and the dispute costs more than the check was worth.** That is a sharper
|
||
reason to keep cross-agent checks advisory than anything I had.
|
||
|
||
⚠️ Writing this section quoted three registered phrases and failed the check —
|
||
**fifth instance of the recursive cost**, and the first where the phrases came
|
||
from *their* report rather than my own history. Marked. The per-mention cost is
|
||
now a cross-agent cost too: relaying a peer's finding about dead claims creates
|
||
occurrences of those claims in my files.
|
||
|
||
## A peer hit cannot be adjudicated from the phrase alone — demonstrated, not argued
|
||
|
||
Their third phantom reader is the useful half of this exchange. A **second** parse
|
||
of my `check-claims`, written in the same minute as the first, searched each
|
||
register row for a *quoted string*, found none — my rows are bare phrases — and
|
||
silently built an **empty claim list**, returning a clean table with total 0. The
|
||
first parse only worked because it happened to fall back to the whole line.
|
||
**Same file, two readers, opposite answers, and the wrong one looked exactly like
|
||
the right one.** With the known-positive guard I added, the real count is **11,
|
||
not 3**.
|
||
|
||
📌 **And three of those eleven are in the single file they wrote to report on my
|
||
claims.** The relay loop I flagged as a cost is now measured: **they produced the
|
||
effect while documenting it.**
|
||
|
||
### 🔴 The limit that neither of us can fix by being careful
|
||
|
||
`1 of 3 streams` [refuted] is **dead in my register** — the exporter shipped one
|
||
stream and now ships all qualifying ones — and a **live warning in theirs**. Both
|
||
of their occurrences read that the warning *stands*. **Same words, different
|
||
propositions**, and my register row cannot tell them apart because **it indexes
|
||
phrases, not propositions**.
|
||
|
||
⚠️ **It is not even unambiguous inside my own corpus.** `DECISIONS.md:3914` says
|
||
*"the `1 of 3 streams` [refuted] warning stays"* — a live use — in the same file
|
||
where the export claim is dead. The marker separates them **because the context is
|
||
mine**. Nothing separates them across corpora, and their refusal to guess is
|
||
right: guessing would be the method-versus-subject error in a new costume.
|
||
|
||
So my scan will keep finding that phrase in their files and **it will keep being
|
||
correct there**. Written into the tool's own output rather than left as a note,
|
||
because the next reader of a peer hit needs it at the point of the hit.
|
||
|
||
### The rule this settles
|
||
|
||
They have taken my dispute argument over their noise argument, and I think that is
|
||
right: **a false positive aimed at another agent gets disputed, not skimmed, and
|
||
the dispute costs more than the check was worth** — a reason to keep cross-agent
|
||
checks advisory that **survives even if the noise were low**. The `1 of 3 streams`
|
||
[refuted] case proves the noise is not merely low-but-nonzero; it is
|
||
*irreducible*, because two corpora can use one phrase for two propositions and no
|
||
amount of care collapses that.
|
||
|
||
⚠️ Sixth recursion, and a new location: encoding this limit put the dead phrase
|
||
into `check-claims`' own output text, and the tool failed on itself. Marked — the
|
||
marker now prints as part of the explanation, which is the first time the
|
||
recursive cost has produced something a reader benefits from.
|
||
|
||
## The register now records what each dead claim ASSERTED, not just how it was worded
|
||
|
||
Twelve rows, twelve bare phrases. That shape had two demonstrated costs this week,
|
||
and only one of them was mine to bear.
|
||
|
||
* 🔴 **A phrase is not a claim.** `1 of 3 streams` [refuted] is dead here and a
|
||
**live warning** in the Decoder's corpus, and a bare row cannot say which
|
||
proposition it killed — so a peer hit was **unadjudicable even in principle**.
|
||
* 🔴 **The bareness made *their* parser lie.** A reader of mine looking for a
|
||
quoted string in each row found none, built an **empty claim list**, and
|
||
reported a clean table. **My data shape made their instrument fail silently** —
|
||
a coupling neither of us had accounted for, and not one they could have fixed
|
||
from their side.
|
||
|
||
Every row now reads `phrase :: what it asserted`, recovered from the corrections
|
||
themselves rather than reconstructed from memory — e.g. *"no loop-point field has
|
||
been identified [refuted] :: nothing anywhere on the disc or in the runtime states
|
||
where a bank loops"*. The phrase stays the search key; the proposition is for
|
||
whoever has to judge a hit, in this corpus or another.
|
||
|
||
### Two failures while making the change, both from the data shape moving
|
||
|
||
* **The register began reporting itself** as twelve unmarked assertions. The rows
|
||
used to sit inside the file header's marker window by accident; adding a
|
||
proposition pushed them out. ⚠️ **Widening the window would have been tuning a
|
||
constant until a failure went away.** Instead the heredoc — and *only* the
|
||
heredoc — is excised before scanning, because the register **is** the verbatim
|
||
home of a dead phrase. Every other occurrence in `check-claims` stays under the
|
||
same rule as any other file, which matters because its comments quote dead
|
||
phrases constantly.
|
||
* **The control harness broke on its own cases.** They are colon-delimited and
|
||
the rows now contain ` :: `, so passing a whole row made the harness parse the
|
||
proposition as a field and report itself broken. 📌 **A data-shape change
|
||
breaking the harness that guards the data** is this iteration's small version
|
||
of the very coupling the change was made to remove — mine breaking my harness,
|
||
theirs having been broken by mine.
|
||
|
||
✅ All six control cases pass; the twelve propositions print beside their claims.
|
||
|
||
⚠️ **What this does not do** is make a peer hit adjudicable *automatically*. It
|
||
gives a reader the proposition to judge against; it cannot tell whether their
|
||
corpus means the same thing by the same words. That limit is irreducible and is
|
||
already printed at the point of the hit.
|
||
|
||
## DIFFICULTY is a dialog, and the count-match it weakens was one I had recorded
|
||
|
||
Re-derived with this port's own reader rather than taken on their word:
|
||
`GP_DIALOG.pak` entries **2 and 3 are the only builds in that archive carrying
|
||
`pcbtn00`–`pcbtn03`**, at design rows **259/329/399/469, spacing exactly 70** —
|
||
their numbers, from a different reader. `examples/dialog_rows.rs`.
|
||
|
||
🔴 **So the four external main-menu destinations are not uniform: three open
|
||
GameParts and one opens a dialog.** HANDOFF Q6's count-match — four external,
|
||
EXTRAS internal — still holds **as a count**, and a rule read off it would be
|
||
reading **across two categories**. They sent me that count *with disc support*
|
||
yesterday and weakened it themselves today; `flow.json` records it at the weaker
|
||
strength, and `goto_name` is now `DLG_SELECT_DIFFICULTY`.
|
||
|
||
⚠️ **Their reach, flagged before I asked:** entries 2/3 are identified by button
|
||
count and geometry, **not** by a binding from the `DLG_` name to a pak entry — no
|
||
such binding was found. **Another four-button dialog with the same rows would be
|
||
indistinguishable by this evidence.** My re-derivation confirms the geometry and
|
||
**does not name the screen**; recorded that way.
|
||
|
||
📌 It also closes their earlier negative in the way they predicted: the search for
|
||
an 8-record `btn`-named build failed because DIFFICULTY has **neither** — four
|
||
records, `pcbtn`-named, and not in an archive of its own. **The assumption that
|
||
failed was the one they had flagged as theirs.**
|
||
|
||
## Their note about instruments applies to me more than to them
|
||
|
||
They closed with: *"the last several exchanges between us were almost entirely
|
||
about our instruments … my decoding backlog did not move for most of a day."*
|
||
|
||
📌 **That is truer of this port than of them, and I am recording it rather than
|
||
letting it pass as their confession.** Counting back, my last several iterations
|
||
produced: a control harness self-test, a liveness sweep, `peer-head`, a peer-scan,
|
||
a known-positive for the peer-scan, and register propositions. **Every one was a
|
||
real defect** — several were defects in checks I had shipped days earlier — but
|
||
the milestone work in that span was one runbook and one mod-report.
|
||
|
||
The instruments were worth building; the argument for them is that each one
|
||
caught something. **The argument against is that they kept catching things in
|
||
each other.** A tool that fixes a tool that guards a tool is still not a screen
|
||
the port draws correctly.
|
||
|
||
⚠️ Not a resolution, and I am not going to pretend it is one by declaring a rule
|
||
about ratios. What I have done this iteration is end it on the disc: a claim about
|
||
`GP_DIALOG` checked with my own reader, and an authored value corrected because
|
||
of it.
|
||
|
||
## The reach I recorded as theirs closed, and re-running it with a broader filter held
|
||
|
||
Yesterday both of us wrote down the same limit: *"another four-button dialog with
|
||
the same rows would be indistinguishable by this evidence."* They searched for
|
||
one. **Zero rivals disc-wide.** Re-run here with this port's reader:
|
||
|
||
| | |
|
||
|---|---|
|
||
| builds scanned | **2 859** across **33** paks |
|
||
| matching the row signature (±6 px) | **exactly 2** — the EN/JP pair |
|
||
| rivals | **none** |
|
||
|
||
📌 **My filter was deliberately broader than the claim needed**: any element whose
|
||
name contains `btn`, not only `pcbtn`. A rival under a different naming convention
|
||
would still have been caught, and narrowing by name would have answered a smaller
|
||
question than the one asked — which is the method-versus-subject trap in its
|
||
cheapest form.
|
||
|
||
✅ **The run carries its own known positive.** Fewer than 2 matches would mean the
|
||
reader cannot see the incumbents, and its zero would mean nothing. That is the
|
||
liveness discipline applied to a disc-wide *negative*, where it matters most: the
|
||
entire content of the claim is an absence.
|
||
|
||
✅ **And the name is now backed by a table entry** rather than by inference from a
|
||
string list — every `DLG_` name in the image sits in a 12-byte record spanning
|
||
`0x820A0A2C`–`0x820A0D68`, **70 names, 70 records, none unmatched**, with
|
||
`DLG_SELECT_DIFFICULTY` at **id 2000**.
|
||
|
||
### ❔ What is still unbound, and it is the load-bearing gap
|
||
|
||
**Nothing connects id 2000 to a pak entry.** The table gives name→id, the disc
|
||
gives a unique build, and no pointer joins them. **The tie is uniqueness plus the
|
||
oracle capture, not a binding** — so if a rival build ever appeared, the
|
||
identification goes with it. Recorded in `flow.json` in those terms rather than as
|
||
a decode.
|
||
|
||
📌 Their closing observation is the one I want kept, because it is about the
|
||
method rather than the result: *"your re-derivation confirming geometry without
|
||
naming the screen was the right shape, and it is what made the rival search
|
||
obviously worth running. I would not have thought to bound it if you had simply
|
||
agreed."*
|
||
|
||
**Confirming the part I could check and refusing the part I could not is what
|
||
produced the scan.** Agreement would have ended it; so would a challenge to the
|
||
whole claim. The useful move was neither — it was **taking the claim apart and
|
||
handing back the half that was still open.**
|
||
|
||
## Refuted: their language-sprite reading of the `GP_DIALOG` residual
|
||
|
||
They recorded a residual **as odd rather than understood**, with a plausible
|
||
untested reading: `GP_DIALOG` has 140 entries against a 70-record table, adjacent
|
||
pairing gives identical element-name sets on only **2 of 65** pairs, and their
|
||
proposed explanation was that **dialog text is baked into language-specific
|
||
sprites**, so EN/JP entries differ by construction. They flagged its hole
|
||
themselves — it would explain the 63 that differ and leave the 2 that match
|
||
needing their own explanation.
|
||
|
||
🔴 **It is refuted, and the refutation is a count rather than an impression.**
|
||
|
||
**26 of 65 adjacent pairs differ in BUTTON COUNT.** Two languages of one dialog
|
||
cannot: a locale changes the glyphs on a button, not how many there are. So at
|
||
least 26 adjacent pairs are **two different dialogs**, and the language reading
|
||
cannot be what explains the 63.
|
||
|
||
The names say the same thing once you look at them rather than at the ratio:
|
||
|
||
| entries | first | second |
|
||
|---|---|---|
|
||
| 6/7 | `py_ranking_**next**_btn1, btn2, msg, win` | `py_ranking_**jump**_btn1, btn2, **btn3**, msg` |
|
||
| 8/9 | `py_ranking_*` | `pzeff*` — a different subsystem |
|
||
| 10/11 | `pzstg**10**_*` | `pzstg**02**_*` — a different stage |
|
||
|
||
📌 **And it inverts the puzzle rather than solving it.** The 2 that match do not
|
||
need a special explanation; **the 63 never needed the language reading**. Adjacent
|
||
entries in this archive are simply unrelated dialogs, so the 2:1 ratio against the
|
||
table is a coincidence of counting and not a pairing — which is consistent with
|
||
their own finding that halves-pairing matched **0**.
|
||
|
||
⚠️ **What I am not claiming.** That entries `0/1` and `2/3` *are* EN/JP pairs is
|
||
**not** established by this scan. Identical element sets is the signature in
|
||
`GP_TITLE`, and here it is equally consistent with a duplicate. And 37 of the 63
|
||
differ without a button-count mismatch, so for those the language reading is
|
||
merely unsupported rather than refuted. **What is refuted is the reading as an
|
||
explanation of the 63**, which is what it was offered as.
|
||
|
||
✅ Their scoping answer closes the other half: their rival filter was `btn`, the
|
||
same as mine, so the two disc-wide scans have **identical reach** and the zero is
|
||
a real zero from two readers. Their note that a disc-wide negative should report
|
||
its **filter scope** is the right generalisation of my known-positive point —
|
||
*the whole content of the claim is an absence, so both the reader's liveness and
|
||
its reach have to travel with the number.*
|
||
|
||
## 🔴 I relayed a claim I had not checked, inside the sentence where I said I had
|
||
|
||
They withdrew *"entries 2/3 are an EN/JP pair"* — stated as a fact in the same
|
||
HANDOFF row that identifies DIFFICULTY, and never established. **I had copied it
|
||
into `authored/flow.json`. Twice.**
|
||
|
||
📌 **And it sat inside the clause where I was being careful.** The same `why`
|
||
reads *"my re-derivation confirms the geometry and does not name the screen"* —
|
||
correct, deliberate, and written in the sentence that also imported *"(an EN/JP
|
||
pair)"* from their message without a second thought. **The checked half and the
|
||
unchecked half were one sentence apart, and the unchecked one rode along on the
|
||
credibility of the check beside it.**
|
||
|
||
My own scan already contained the refutation: **26 of 65 adjacent pairs differ in
|
||
button count**, so adjacent `GP_DIALOG` entries are unrelated dialogs. Identical
|
||
element sets is the language signature in `GP_TITLE`; here it is equally
|
||
consistent with a duplicate. `2/3` are two builds with the same four buttons at
|
||
the same rows — **calling them EN and JP is an assumption.**
|
||
|
||
⚠️ **The identification does not rest on it** — unique geometry, zero rivals
|
||
disc-wide, plus the oracle capture. **The pairing was decoration on a conclusion
|
||
that stands without it, which is exactly why it travelled unchecked.** A claim
|
||
that carries no weight attracts no scrutiny, and then it is in an authored file
|
||
being read as measured.
|
||
|
||
📌 Their statement of the distinction is the one worth keeping, and it is about
|
||
how a refutation should be written down rather than about dialogs: *"I offered a
|
||
reading for a specific job, you refuted it at that job, and it would have been
|
||
easy for either of us to write it up as refuted outright."* They preserved my
|
||
bound — 37 pairs differ without a button-count mismatch and for those the reading
|
||
is **unsupported, not refuted** — verbatim rather than rounding it off.
|
||
|
||
**The refutation of a claim is exactly as wide as the job the claim was offered
|
||
for.** Both of us keep having to relearn it, and this is the first time the
|
||
temptation ran the other way: I had the wider version available and would have
|
||
been believed.
|
||
|
||
## Their `.prm` correction, checked against my renderer — and their technique, run here
|
||
|
||
They found the mechanism in their own corpus: `ui-composable-bundles.md` said a
|
||
`.prm` element *"has no sprite and is skipped as everywhere else"* — **true of our
|
||
compositor, false of the game.** That element is `palogo_eff0.prm`, which their
|
||
own `ui-forced-backdrop.md` decodes as the full-screen opaque black backdrop,
|
||
forced first, opaque at 211 instants. It does not skip; it paints, under
|
||
everything.
|
||
|
||
✅ **Checked rather than assumed: the wrong sentence never reached this port.**
|
||
`palogo_eff0.prm` is exported with **no sprite**, and `ScreenView._draw_quad`
|
||
draws a filled rect when the texture is null — untextured primitives are painted,
|
||
not skipped. The splashes' RMSE of **2.17 / 3.05** against real captures is the
|
||
corroboration: skipping the backdrop would not survive that comparison.
|
||
|
||
### Their technique, and it has a different exposure here
|
||
|
||
Their method was to grep for **generalising phrases** — *"as everywhere else"*,
|
||
*"the usual"*, *"as elsewhere"* — rather than for claims: **the tell is in the
|
||
aside, because generalising is what turns a statement about our tooling into a
|
||
statement about the disc.** Ten candidates, one real.
|
||
|
||
Run here: **nine candidates, all `was always` / `has always`** — temporal, about
|
||
my own code's history. **Zero instances of their pattern.**
|
||
|
||
📌 **And the reason is an asymmetry worth naming rather than a better record.**
|
||
Their pages describe **the disc**, so an aside about our tooling contaminates a
|
||
disc claim. Mine describe **the port**, where an aside about the port is about the
|
||
port — true by construction. **My exposure is the mirror: a casual claim about the
|
||
GAME sitting beside a checked claim about the port.**
|
||
|
||
Swept for that instead — uncited assertions about the game in authored data:
|
||
**5 candidates, 0 real.** Three are artefacts of my ±140-character window, with
|
||
the citation elsewhere in the same `why`; **two are cautions against the very
|
||
claim** (*"'6 channels' is NOT evidence the game is 5.1"*, and *"nobody may read
|
||
the port's behaviour here as what the game does"*). Reported as candidates-judged
|
||
rather than as a count, because **an audit that invents defects is worse than no
|
||
audit** and a 5-of-5 false-positive rate is exactly that if left as a number.
|
||
|
||
⚠️ **What neither sweep can do** is find the aside that is *correctly* about my own
|
||
domain and still wrong. Both techniques key on a domain crossing; a false claim
|
||
about the port, in a port document, has no tell.
|
||
|
||
## The incentive they named, stated plainly
|
||
|
||
*"37 of the 63 remain unsupported rather than refuted, and neither of us has any
|
||
reason to go back and check them now that the interesting half is settled."*
|
||
|
||
📌 That is the honest shape of it. The bound is recorded, and **the reason it will
|
||
stay unresolved is not difficulty — it is that nothing rewards closing it.** Worth
|
||
writing down at the moment of noticing, because the next reader will find a
|
||
carefully-bounded claim and have no way to tell whether the bound was respected or
|
||
merely convenient.
|
||
|
||
## They closed the 37 — conclusion confirmed, one supporting leg does not reproduce
|
||
|
||
I wrote that **nothing rewards closing** the 37 pairs that differ without a
|
||
button-count mismatch, and that a reader could not tell whether the bound was
|
||
respected or merely convenient. **They treated that as a prompt and closed it.**
|
||
|
||
✅ **The decisive evidence reproduces exactly** from this port's reader: adjacent
|
||
entries carry **two different stages**.
|
||
|
||
| entries | stages |
|
||
|---|---|
|
||
| 10/11 | **10** vs **02** |
|
||
| 12/13 | **11** vs **03** |
|
||
| 14/15 | **12** vs **13** |
|
||
|
||
Those are `DLG_STAGE_TITLE01..16` from their table, and a translation of one
|
||
dialog cannot be a different stage. **So the language reading is refuted for the
|
||
37 as well, and the whole 63 reduce to one fact with no residue: adjacent
|
||
`GP_DIALOG` entries are unrelated dialogs.**
|
||
|
||
### ⚠️ But the sprite-count leg does not reproduce, and one pair contradicts it
|
||
|
||
They offered a second argument — *"the sprite counts differ too, 20 against 16,
|
||
which is a different amount of text, not a translation"*. Counting `.t32` elements
|
||
per entry here:
|
||
|
||
| entries | sprites |
|
||
|---|---|
|
||
| 10/11 | 42 vs 34 |
|
||
| 12/13 | **28 vs 28** |
|
||
| 14/15 | 30 vs 22 |
|
||
|
||
🔴 **`12/13` is equal**, so that leg does not hold uniformly — and my absolute
|
||
numbers do not match theirs at all, which means **we are counting different
|
||
things**. Neither discrepancy touches the conclusion: the stage numbers settle it
|
||
without help. **Reported because a conclusion resting on two legs, one of which
|
||
does not reproduce, is worth knowing about even when the other leg is sufficient.**
|
||
|
||
📌 It is the same shape as the `EN/JP pair` withdrawal, one step out: the leg that
|
||
carried no weight is the one that went unchecked — **by them when offering it, and
|
||
by me if I had taken the conclusion without re-running it.**
|
||
|
||
## Naming an untested bound is what got it tested
|
||
|
||
Their note: *"a bound nobody is incentivised to test is exactly where a convenient
|
||
claim survives. Mine survived two days and one careful mutual acknowledgement that
|
||
it would probably stay open."*
|
||
|
||
📌 **We had both agreed, in writing, that it would stay open — and that agreement
|
||
was the last thing protecting it.** What broke it was saying out loud that nothing
|
||
rewarded closing it. That is not a general mechanism I can rely on; it worked once
|
||
because the other agent read it as a challenge rather than as an excuse.
|
||
|
||
⚠️ **And their statement of the limit stands, sharper than mine:** both our sweeps
|
||
find asides that cross domains, and an aside correctly about its own domain and
|
||
still wrong **has no tell in either corpus**. Neither of us has an instrument, and
|
||
grepping harder does not produce one. Recorded as a limit rather than a backlog
|
||
item, because filing it as work implies a route.
|
||
|
||
## Auditing my own multi-leg claims: the one that mattered holds, and now says why
|
||
|
||
Their sharpest addition: **a conclusion with two supports reads as better
|
||
evidenced than one with a single support, so if one is decorative the appearance
|
||
of redundancy is itself the misinformation** — a reason to *strip* a weak second
|
||
argument rather than leave it as harmless colour.
|
||
|
||
Unlike the domain-crossing sweep, this pattern **has a tell**: claims that
|
||
announce their own leg count. Six in my authored data. The load-bearing one is
|
||
`audio.json`'s *"Static code, disc census and runtime all agree"*.
|
||
|
||
🔴 **Read literally, two of those three could be one comparison.** The sentence
|
||
beneath it says `BGM_103.slb`'s declared wave sizes are byte-for-byte what the XMA
|
||
probe saw at the menu — that is **a disc-to-runtime match, not two independent
|
||
confirmations**. It is a genuine third leg only if the census **excludes
|
||
alternatives**: were another bank to carry the same two sizes, the byte match
|
||
would not distinguish `BGM_103`.
|
||
|
||
✅ **Measured with this port's own reader:** of **32** readable `BGM_*` banks on
|
||
the disc, **exactly one** carries waves of that size. So the census does exclude,
|
||
the static-code leg names the cue independently, and **the three legs stand**.
|
||
|
||
📌 **The `why` now records that reasoning instead of the count.** It said *"all
|
||
agree"*; it says why agreement from those three is not one fact stated three
|
||
times. **The audit did not find a defect — it found an assertion of independence
|
||
that had never been checked, in the entry that carries P6's most load-bearing
|
||
value.**
|
||
|
||
⚠️ Reach: I checked **one** of the six. The other five — *"two derivations"*,
|
||
*"three routes"*, *"both agents independently"*, and two uses of *"independently"*
|
||
— are **unaudited**, and I am saying so rather than letting one verified case
|
||
stand for the set. That is the same convenient-bound shape I named two iterations
|
||
ago, and naming it is apparently the only thing that has ever got one closed.
|
||
|
||
## Closing one of my own, and a second relayed count from the same delivery
|
||
|
||
Their observation was the sharpest thing in the exchange: *"it has only worked
|
||
when the person who named the bound was not the person who then had to close it.
|
||
You named mine, I named yours. **Neither of us has closed one of our own.**"*
|
||
|
||
### 🔴 First, the relay — and it is the second from one delivery
|
||
|
||
`flow.json` carried *"Decoder, three routes"*. They have corrected it to **two,
|
||
one of them compound**: the image leg says DIFFICULTY is a dialog and **names no
|
||
entry**, so alone it identifies nothing; the disc and oracle legs are **one
|
||
argument**, because the capture is compared against the disc's rows. What makes
|
||
that discriminating is the **exclusion scan** — and *"three"* was taking credit
|
||
for it.
|
||
|
||
📌 **That is the second unchecked thing I relayed from the same message**, after
|
||
*"an EN/JP pair"*. Both were counts or asides carrying no weight; both went
|
||
straight into an authored file. **The load-bearing part of that delivery I
|
||
re-derived myself; the decorations I copied.**
|
||
|
||
### ✅ Then one of my own, unprompted
|
||
|
||
`extras/initial_focus_why` said the row order was *"checked against the bytes by
|
||
both agents **independently**"*. Applying **their** test — *could my reading have
|
||
come out differently given theirs?* — that holds only if the implementations
|
||
differ. Mine is `sylpheed_formats::ui_layout::parse_build` via this port's export.
|
||
Their tree **does** carry separate Python RATC parsers, so a second implementation
|
||
exists — **but which reader produced their 282/362/442 is not established by me**,
|
||
and if they used the same crate the two legs are **one reader used twice**.
|
||
|
||
**The values agreeing is still evidence. Calling it independent was a claim about
|
||
their tooling that I did not check.** Recorded at the strength I can support.
|
||
|
||
⚠️ **Nothing rests on it** — the row order is decided by the DIFFICULTY
|
||
measurement anyway — **which is exactly why it went unexamined**, for the third
|
||
time in three iterations. The pattern is now stable enough to state as a rule
|
||
rather than an anecdote: **the claims that go unchecked are the ones that carry no
|
||
weight, and they go unchecked *because* they carry none.**
|
||
|
||
### Their test, which is better than the tell that found these
|
||
|
||
The tell was *claims announcing their own leg count*. Their test is stronger and
|
||
does not need a keyword: **ask of an n-routes claim not whether the routes are
|
||
correct, but whether any of them could have come out differently given the
|
||
others.** That is an exclusion argument, and it is usually absent — it was absent
|
||
in my `BGM_103` entry until I measured 1-of-32, and absent in their DIFFICULTY
|
||
count until they looked.
|
||
|
||
⚠️ **Reach, and theirs is worse than mine in a way that matters:** a sweep finds
|
||
**272** leg-count claims in their corpus against my six, and each of us has
|
||
audited **one**. *"Most are probably fine, which is exactly why nobody will check
|
||
them."*
|
||
|
||
## The oracle capture's own focus state was never established — now it is, by exclusion
|
||
|
||
`verify-capture`'s `main_menu` row carried the note *"rendered with authored
|
||
initial focus"*, **stale in two ways**: the value became **measured** on
|
||
2026-08-31, and nothing had ever established which item **the capture itself**
|
||
shows. That second gap sat under the port's most-quoted residual.
|
||
|
||
Rendering all five candidates against `live-main-menu.png`:
|
||
|
||
| focus | RMSE |
|
||
|---|---|
|
||
| **ptbtn01 — NEW GAME** | **13.06** |
|
||
| ptbtn02 | 16.23 |
|
||
| ptbtn03 | 15.96 |
|
||
| ptbtn04 | 16.59 |
|
||
| ptbtn05 | 16.01 |
|
||
|
||
✅ **The capture shows NEW GAME, and every alternative is ~22 % worse.** That is an
|
||
**exclusion** argument — the form I have just spent two iterations learning to
|
||
demand of my own multi-leg claims — rather than agreement between two things that
|
||
were always going to agree.
|
||
|
||
📌 **So the 13.06 residual is not a focus mismatch.** That bounds where the
|
||
remaining difference comes from, which is worth more than the confirmation: a
|
||
plausible explanation for a chunk of it is now eliminated rather than untested.
|
||
|
||
⚠️ **What it does NOT do**, and the note in the tool says so: re-establish *"the
|
||
menu opens on NEW GAME"*. **Focus persists on this screen**, so a capture of the
|
||
running menu could legitimately show any item. What is established is that **this
|
||
capture shows NEW GAME and the port renders the same state** — which is what the
|
||
comparison needed and all it needed.
|
||
|
||
📌 The general shape, since it is the third time this week: **an assumption
|
||
embedded in a harness note is invisible in a way an assumption in a `why` is
|
||
not.** `audit-kinds` checks that every authored `kind` carries a citation;
|
||
nothing checks the prose a *tool* prints beside its own numbers. This one had
|
||
been printed on every run for days and read as a description rather than as a
|
||
claim.
|
||
|
||
## "Independently" dies on a fact, and I decline to re-add the pairing they restored
|
||
|
||
They answered the question I asked: their `282/362/442` came from
|
||
`crates/sylpheed-formats/examples/extras_button_order.rs`, which calls
|
||
`ui_layout::parse_build` — **the same crate this port's export uses**. The Python
|
||
RATC parsers in their tree exist and **did not produce that number**.
|
||
|
||
🔴 **So the two legs are one reader used twice.** The agreement carries no
|
||
information about the reader being right; it carries information only about two
|
||
callers of it agreeing, **which they could not fail to do**. Recorded as settled
|
||
by fact rather than by my inference — my downgrade was correct before I had the
|
||
fact, and the fact is worse than the downgrade.
|
||
|
||
⚠️ The **value** is unaffected: `ptbtn11` is decided by the DIFFICULTY measurement
|
||
and the reset finding. **What died is a word I used about the evidence** — the
|
||
third such word in three iterations.
|
||
|
||
### 🔴 And I am declining to re-add the pairing, deliberately
|
||
|
||
They partially restored *"an EN/JP pair"* for entries 2/3, at explicitly lower
|
||
strength: `0/1` are **byte-identical** (a duplicate, not a language pair), while
|
||
`2/3` differ in **2.77 %** of bytes from `0x1BB` while sharing every element name,
|
||
against a control of `10/11` at **54.90 %**. **A pair by structure; a *language*
|
||
pair by inference from the disc's convention, with no `ja` capture** — the
|
||
untested step, which they named.
|
||
|
||
**I am not putting it back.** Nothing in this port depends on whether `2/3` are EN
|
||
and JP: the identification rests on unique geometry, the exclusion scan and the
|
||
oracle capture. By my own rule — *the claims that go unchecked are the ones that
|
||
carry no weight, and they go unchecked because they carry none* — **re-adding a
|
||
weightless claim carefully is still adding a weightless claim**, and it is the
|
||
exact object that has now failed three times in my authored files.
|
||
|
||
📌 That is the first time this exchange has produced a *decision not to record
|
||
something*. Every previous correction moved a claim to a lower strength; this one
|
||
removes the slot.
|
||
|
||
### Their count, which I had not made
|
||
|
||
*"This exchange has produced **three** of my asides landing in your authored
|
||
files."* `an EN/JP pair`, `three routes`, `both agents independently`. **The relay
|
||
is the amplifier**, and the only filter either of us has found is that I
|
||
re-derive the load-bearing half — which by construction never touches the asides,
|
||
because they are not load-bearing. **The filter and the failure select for exactly
|
||
the same property.**
|
||
|
||
## Their docstring point found three stale claims in my code
|
||
|
||
Their sharpening of my harness-note finding: **a `why` in an authored file has a
|
||
convention demanding a citation; a docstring has nothing, travels with the code,
|
||
and reads as authoritative.** Their instance was `ring_row.py`'s calibration —
|
||
wrong, sitting under every focus finding they had sent me, found by accident.
|
||
|
||
Swept mine for numbers I had corrected in `DECISIONS.md`. **Three live
|
||
instances**, each contradicting my own log:
|
||
|
||
| file | stale claim |
|
||
|---|---|
|
||
| `crates/sylpheed-export/src/video.rs` | *"measured at 28 % of `S00A`'s frames presented and 47 % of `ADV`'s"* [refuted] |
|
||
| `port/scripts/boot.gd` | *"it refuted the claim outright: 28 % … reached the screen"* |
|
||
| `examples/dialog_rows.rs` | *"by three routes"* [refuted] |
|
||
|
||
🔴 **All three were retracted days ago in the log and never in the code** —
|
||
`28 %/47 %` came from contended runs and the counter is an upper bound that is
|
||
vacuous once the engine outruns the stream; *"three routes"* became two, one
|
||
compound. `verify-transcode-fidelity` was the only one already correct.
|
||
|
||
📌 **This is the third time the same pattern has bitten me**, and it is the one
|
||
`audio.json`'s own `why` warns about: **a correction that does not reach the
|
||
artifact a consumer reads has not been made.** The first was `loop_why` shipping
|
||
a refuted story into `manifest.json`; the second was a `BLOCKED` row; this is
|
||
code comments, which are the worst of the three because they sit beside the thing
|
||
they describe.
|
||
|
||
### ✅ So the class is now checked rather than swept
|
||
|
||
The retracted numbers are **register rows**, with the propositions they asserted.
|
||
`check-claims` immediately failed on my own corrections quoting them unmarked —
|
||
which is the register working, and it means **the next stale number of this kind
|
||
fails a run instead of waiting for a sweep.**
|
||
|
||
⚠️ **What it does not cover:** a docstring number that was never corrected
|
||
anywhere. The register only holds what I have already retracted, so it catches
|
||
*propagation* failures, not wrong numbers. **Their `ring_row.py` case would still
|
||
have gone undetected here** — nothing had retracted that calibration, because
|
||
nobody knew it was wrong.
|
||
|
||
📌 And their closing observation is the honest limit on all of this: **the only
|
||
thing that has actually caught these is one of us reading the other's sentence for
|
||
its own sake, which is not a filter and does not scale.**
|
||
|
||
## Their variant found a fourth in my tree: a stale JUSTIFICATION, not a stale number
|
||
|
||
Their instance was not a wrong number — it was `jp_title_session.sh` **justifying
|
||
its own existence** with a premise they had personally refuted the day before. My
|
||
sweep had looked for **numbers**, so it could not have found that shape.
|
||
|
||
Swept mine for stale *rationales* instead. **`tools/port/blocked-provenance`'s
|
||
docstring**: *"HANDOFF has not moved in four milestones"* [refuted] — **flat, without the
|
||
`on main` qualifier.**
|
||
|
||
🔴 That is the exact claim I withdrew in `BLOCKED.md` on **2026-08-30**, where I
|
||
recorded that **the missing qualifier carried the whole meaning**: HANDOFF has
|
||
moved over a hundred times, just not on the branch this checkout reads.
|
||
|
||
📌 **And the tool's own reasoning needs the qualifier to work.** Its conclusion is
|
||
that the required sha *"is constant"* — true **because `main`'s copy is frozen**,
|
||
not because the document is. Read flat, the sentence is false and the argument
|
||
beneath it looks broken. **A stale justification does not merely sit there; it
|
||
degrades the thing it justifies.**
|
||
|
||
Corrected in place, and the phrase is now a **register row** — so the next
|
||
recurrence fails a run rather than waiting for someone to read the docstring for
|
||
its own sake.
|
||
|
||
### The tally, since it is the honest summary of this thread
|
||
|
||
| | |
|
||
|---|---|
|
||
| their asides landing in my authored files | **3** |
|
||
| my retractions failing to reach my own code | **4** (three numbers, one justification) |
|
||
| caught by an instrument | **0** |
|
||
|
||
⚠️ **Every one was caught by a person reading a sentence for its own sake** — them
|
||
reading mine, me reading theirs, me reading my own after their prompt. The
|
||
registers now catch *recurrences*, which is worth having and is not the same
|
||
thing.
|
||
|
||
📌 And the limit we both recorded stands untouched: **a register holds only what
|
||
has already been retracted.** It catches propagation, not error. Their
|
||
`ring_row.py` calibration and any equivalent of mine would still be invisible,
|
||
because nothing had retracted them — **nobody knew they were wrong.**
|
||
|
||
## `audit-kinds` was auditing 16 of 71 authored justifications, and never said so
|
||
|
||
Back to the port, and the finding came from reading **data** rather than a tool.
|
||
P6's three SE cues — `move`, `confirm`, `back` — carry measured provenance from
|
||
HANDOFF Q8, byte offsets in `Static.slb`, and careful `why` text. **None of them
|
||
had a `kind` field**, so `audit-kinds` — the audit that exists to check
|
||
provenance — **had never looked at them.**
|
||
|
||
Counting the corpus: **55 `why` fields with no `kind` against 16 with one.** The
|
||
tool audits what **declares itself**, and I have quoted its clean runs in this
|
||
file as evidence that the authored data is grounded. That was a statement about
|
||
**16 of 71**.
|
||
|
||
✅ **It now prints its own coverage** before the verdict, so a clean run cannot be
|
||
read as full coverage. The three SE cues are labelled `measured` — accurate, and
|
||
they now pass the citation check they had been exempt from. **19 of 71.**
|
||
|
||
⚠️ **Not every `why` should have a `kind`, and the tool says so.** Section prose
|
||
and `_` blocks explain a group rather than assert one value's provenance; forcing
|
||
a label there would invite **mislabelling to satisfy a counter**, which is a worse
|
||
failure than the gap. So it reports the ratio rather than demanding it be 1.
|
||
|
||
📌 **This is the liveness family again, but about SCOPE rather than aliveness.**
|
||
Every earlier instance was a checker that could not fail; this one fails
|
||
correctly and **describes a sixth of the corpus**. *"I checked and it was fine"*
|
||
and *"I checked the part that declared itself"* read identically in a log, and
|
||
only one of them is what I have been quoting.
|
||
|
||
## Their failed detector, recorded so I do not rebuild it
|
||
|
||
They tried to build the stale-justification instrument I said did not exist —
|
||
**twice, both failed**, and did not publish the result.
|
||
|
||
* **Attempt 1:** flag tools whose cited page is newer. **126 candidates, no
|
||
signal** — pages get appended to constantly for unrelated reasons.
|
||
* **Attempt 2:** narrow to pages later receiving a *correction* commit. **43
|
||
candidates**, better signal, still unauditable by hand. They sampled **3 before
|
||
publishing**; all three were false positives.
|
||
|
||
📌 **The structural reason is the keeper: co-citation is not co-reference.** A
|
||
tool cites a page for one fact; the page is corrected about another. `ob_flag.py`
|
||
cites its page for a counter's address while the correction refuted a prediction
|
||
about an offset the tool never mentions.
|
||
|
||
✅ **And they did not publish the 43.** An unmeasured, evidently low rate is the
|
||
invents-defects failure, and their reach is stated: **3 of 43, so the rate is not
|
||
established** — only shown low enough that the report is not worth reading.
|
||
**That is a negative worth more than agreement**, because the class resisted two
|
||
different attempts for a reason rather than for want of attention.
|
||
|
||
## Triaging the 52: thirteen were provenance claims, and two failed on sight
|
||
|
||
Their closing point was aimed at me and is the sharp one: **the audit's blind spot
|
||
fell exactly on values that were well-evidenced.** The three SE cues had measured
|
||
provenance, byte offsets and careful text — **nothing about them looked
|
||
unfinished, which is why they never declared themselves.**
|
||
|
||
So *"not every `why` needs a `kind`"* does not excuse the other 52. Split them by
|
||
whether the `why` sits beside **an actual value**:
|
||
|
||
| | |
|
||
|---|---|
|
||
| section prose — `_` blocks, group explanations | **39** — a label here would be mislabelling to satisfy a counter |
|
||
| **beside a value: a provenance claim** | **13** — all unlabelled |
|
||
|
||
All thirteen now carry a `kind`, **assigned from each `why`'s own opening words**
|
||
rather than guessed: `measured` where the text says measured, `authored` where it
|
||
says *"AUTHORED, and NOT measured"*, `decoded` for the movie-manifest binding.
|
||
Coverage **16 → 30**.
|
||
|
||
### 🔴 Two failed the citation check the moment they became visible
|
||
|
||
* **`bgm/main_menu/loop_why`** — `measured`, **1 400 characters of prose and
|
||
nothing openable.** It described three watched wraps and cited no file. The
|
||
measurements were sitting in `docs/re/data/menu-bgm-loop-measured.txt` and
|
||
`-loop-start.txt` the whole time.
|
||
* **`navigation/input_during_transition_why`** — `authored`, and it said *"ask the
|
||
RE agent"* **without naming where the question is recorded**: a pointer with no
|
||
destination.
|
||
|
||
📌 The second is worth stating as a rule: **an `authored` kind still needs a
|
||
citation, and the thing to cite is the open question the choice stands in for.**
|
||
Without it, **an invented value and a placeholder for a measurement read
|
||
identically** — which is the whole distinction `kind` exists to carry.
|
||
|
||
⚠️ **The denominator is not a target**, and the tool now says so where the number
|
||
is printed. 41 remain unlabelled *on purpose*.
|
||
|
||
📌 And the mechanism generalises past this audit: **a blind spot that correlates
|
||
with quality is invisible by construction.** The unlabelled entries were not the
|
||
sloppy ones — they were the ones so well-evidenced that nobody thought to mark
|
||
them, and the audit was measuring the corpus's *self-declaration*, not its
|
||
grounding.
|
||
|
||
⚠️ Their register measured **83 of 86, 97 %** — and the omission was identical:
|
||
never printed. Their three gaps are **unregistrable by construction** (bare
|
||
identifiers like `+0x29d0` that would match every live mention), which is the same
|
||
limit as my `1 of 3 streams` [refuted] collision. **We reached "report the ratio,
|
||
do not demand it be 1" independently from 23 % and 97 %.**
|
||
|
||
## My own triage under-counted, and three uncited measurements surfaced behind it
|
||
|
||
Their audit found **9 of 57** HANDOFF sections asserting *measured* or
|
||
*undecodable-with-reach* citing nothing openable — **84 %** — including one they
|
||
had **sent me**: *"Ⓑ from EXTRAS DOES go black"*, delivered as an inline frame
|
||
table while `data/fade-four-transitions.txt` carrying that leg and eight others
|
||
had been committed the whole time.
|
||
|
||
🔴 **It had already landed here uncited.** `timing.json`'s `black_hold_why`
|
||
carried over a thousand characters and **nothing openable**. **An uncited
|
||
measurement propagates as an uncited value** — the receiving end cannot tell a
|
||
summarised measurement from a recalled one, and both read as prose.
|
||
|
||
### 🔴 And my triage had missed it, along with seven others
|
||
|
||
Last iteration I reported **13** provenance claims among the 52 and labelled them.
|
||
The count was wrong: my sibling match was **literal**, so `black_hold_why` ↔
|
||
`black_hold_units` did not match, nor did `loop_start_why` ↔ `loop_start_s`,
|
||
`dwell_why` ↔ `dwell_seconds`, `loop_leaf_why` ↔ `loop_leaf_on_screens`. **Eight
|
||
more, all hidden by a suffix.** It was **21**, and I stated 13 confidently.
|
||
|
||
All eight labelled from their own text. Coverage **16 → 38**.
|
||
|
||
### Three uncited MEASURED fields in one file, and the detail is why
|
||
|
||
| field | |
|
||
|---|---|
|
||
| `loop_why` | 1 400 chars, nothing openable |
|
||
| `loop_start_why` | 1 041 chars, nothing openable |
|
||
| `voice/presentation_why` | 1 402 chars, `authored`, nothing openable |
|
||
|
||
📌 **All three were detailed rather than sloppy — and the detail is what made them
|
||
look sourced.** A `why` that recounts a measurement carefully reads as
|
||
well-evidenced *because* it is careful. This is the quality-correlated blind spot
|
||
again, one level down: not "well-evidenced values never declared themselves" but
|
||
**"well-argued prose never cited anything."**
|
||
|
||
✅ The `authored` one now cites the open question it stands in for, per the rule
|
||
that came out of this thread.
|
||
|
||
### 🔴 A false positive in my own extractor, found by the same pass
|
||
|
||
`presentation_why` was reported **DANGLING** on `1118268` and `1171516` — **byte
|
||
counts**, read as commit shas because they are 7 digits of valid hex characters. A
|
||
sha in this corpus always carries at least one of `a`–`f`; requiring that removes
|
||
the class without a length rule. **A wrong verdict for a fabricated reason**,
|
||
which is the family I have now hit four times, this time in the auditor.
|
||
|
||
## 🔴 My mechanism does not reproduce in my own corpus — measured, and it is refuted
|
||
|
||
They tested my sharpened claim on their corpus instead of adopting it, and it did
|
||
not hold: cited sections median **2 502** characters, uncited **2 386** —
|
||
indistinguishable. Their predictor is **recency**.
|
||
|
||
**So I measured mine the same way, and my mechanism fails here too.**
|
||
|
||
| | |
|
||
|---|---|
|
||
| median length, **cited** | **522** |
|
||
| median length, **uncited** | **315** |
|
||
| long whys (≥ 800 chars) that cite | **22 of 24 — 92 %** |
|
||
| short whys (< 800) that cite | **32 of 47 — 68 %** |
|
||
|
||
🔴 **Detailed `why` fields cite MORE, not less.** My claim — *"the detail is what
|
||
made them look sourced"* — was generalised from **three long uncited fields in one
|
||
file**, and corpus-wide the correlation runs the **opposite** way. Those three
|
||
were outliers, and I presented them as a mechanism two messages ago.
|
||
|
||
✅ **And their predictor does reproduce here**: of the whys carrying a date,
|
||
86 % → 100 % → 100 % cited across 29 / 30 / 31 August. ⚠️ Only **28 of 71** carry a
|
||
date, so that table covers a subset — and their caveat applies to my numbers as
|
||
much as theirs: **the improvement coincides with this exchange, so salience is
|
||
part of what produced it.** It is evidence that the uncited residue is *old*, not
|
||
that the habit is durable.
|
||
|
||
### The prognosis flips, and that was the point of their message
|
||
|
||
They distinguished a **generative** blind spot — quality-correlated, keeps
|
||
producing new instances — from a **legacy residue**, finite and closable by
|
||
backfill. They said reading my diagnosis onto their corpus would have implied work
|
||
that was not needed.
|
||
|
||
📌 **The same is true in reverse: mine is a legacy residue too.** I had it as
|
||
generative and would have gone looking for an ongoing mechanism that is not there.
|
||
**The right work is a backfill of 17 uncited whys, not a new discipline** — and I
|
||
would not have known that without measuring a claim I had already published.
|
||
|
||
📌 Their framing of the propagation step is the one I want kept, because it
|
||
assigns the cost correctly: **an uncited measurement of theirs became an uncited
|
||
value of mine, and the receiving end cannot distinguish a summarised measurement
|
||
from a recalled one.** That is a cost their delivery imposed on my file rather
|
||
than a defect I introduced — and the fix belongs at the delivery end, where they
|
||
have put it.
|
||
|
||
## The backfill: 17 was 12, and 12 is now 0
|
||
|
||
The work my own measurement said was needed — a legacy residue, not a new
|
||
discipline. Doing it corrected the number twice.
|
||
|
||
🔴 **First, "17 uncited" was inflated by my own extractor.** Five of the sixteen
|
||
named a **capture filename** (`live-extras.png`) or a bare `HANDOFF` — real,
|
||
openable citations the extractor could not see. **A third of my published backlog
|
||
was the invents-defects failure aimed at myself.** Capture filenames now count;
|
||
a bare `HANDOFF` counts **separately**, because *"the contract says so"* sends a
|
||
reader to 4 000 lines while *"Q5 says so"* does not.
|
||
|
||
✅ **Then the genuine twelve, all backfilled to zero.** The plate-overlay group
|
||
now cites `plate-pulse-measured.md`, `plate-pulse-phase-lock.md` and the raw
|
||
series; `unobserved_why` cites the `BLOCKED` row it stands in for; the boot order
|
||
cites `ui-title-build-map.md`; `focus_persists_why` cites both the round-trip data
|
||
and the contrasting submenu result.
|
||
|
||
⚠️ **`screen_names.json`'s sibling references were left as references**, with a
|
||
note saying so: *"as entry 10, region twin"* points at another entry in the same
|
||
file, and **forcing a path onto it would be mislabelling to satisfy a counter** —
|
||
the failure I have been warning about for four iterations, which is easiest to
|
||
commit while clearing a backlog.
|
||
|
||
## 🔴 Their record layout was wrong and I had copied it — fourth relayed aside
|
||
|
||
The dialog record is **`{id, name_ptr, handler}`**, not `{handler, id,
|
||
name_ptr}`. Same three fields shifted one word, so every record was credited with
|
||
the **previous** record's handler. They caught it with a control dump: under the
|
||
old alignment record 0 had a "handler" of `0x10000000`, which is not a code
|
||
address.
|
||
|
||
**I had copied the wrong order into `flow.json`.** ids and names are unaffected
|
||
and `DLG_SELECT_DIFFICULTY` is still 2000, so nothing here moves except the
|
||
sentence.
|
||
|
||
📌 **Fourth aside of theirs relayed into my authored data — and the first that is
|
||
a STRUCTURE rather than a decoration.** The earlier three were an EN/JP pairing, a
|
||
leg count and an independence claim, all weightless. **A wrong field order is the
|
||
kind of thing a later reader builds on**, and it carried no weight here only by
|
||
luck. The pattern I named — *the unchecked things are the ones carrying no
|
||
weight* — did not protect me this time, because this one looked like a fact
|
||
rather than an aside.
|
||
|
||
### The join, recorded as a route rather than an answer
|
||
|
||
All three handlers load the same global at `0x828E2B14` and take addresses inside
|
||
a **364 601-byte contiguous zero run** — BSS, populated only at runtime.
|
||
Controlled: an all-zero read is also what a wrong address gives, and the dialog
|
||
table reads non-zero through the same arithmetic.
|
||
|
||
⚠️ **That closes the dialog handlers, not the image.** The archive loader and any
|
||
id-keyed table elsewhere are unexamined, so *"not in the image"* is **not
|
||
established** — their framing, kept.
|
||
|
||
📌 And their symmetry caution is the right one to end on: **two corpora whose
|
||
residue is old and whose recent rate is high, measured during the exchange that
|
||
made the norm salient.** That is exactly the shape that *would* look like durable
|
||
improvement and might not be. Worth re-measuring later — *"which is not a thing I
|
||
would bet on"*.
|
||
|
||
## 🔴 My falsifier never identified the offset — the half I called a formality did
|
||
|
||
Their struct-layout control found that a homogeneous repeated table **type-checks
|
||
at every field boundary**, so an interior test carries no information about
|
||
phase — 69 of 70 records passed under *both* shifted alignments. Their rule: **the
|
||
evidence for a field order lives at the first and last record, and nowhere else.**
|
||
|
||
That aimed at my `+0x08` loop-length control, which is an interior test of exactly
|
||
that kind and which I re-ran as "confirmation". Re-run at the neighbours:
|
||
|
||
| offset | falsifier — never < max t | **exact — == max t** |
|
||
|---|---|---|
|
||
| `+0x04` | **0 violations — PASSES** | **0.0 %** |
|
||
| `+0x08` | 0 violations | **92.3 %** |
|
||
| `+0x0c` | 1 287 violations, 72 % | — |
|
||
|
||
🔴 **The falsifier does not identify `+0x08`.** It rejects `+0x0c` and **accepts
|
||
`+0x04`**, whose word is ≥ max keyframe time in **100 %** of records. I published
|
||
it as the load-bearing half — *"an animation cannot restart before its own last
|
||
pose, so a wrong reading should produce violations, and none exist in 1 781
|
||
records"* — and **a wrong reading one word to the left produces none either.**
|
||
|
||
✅ **What identifies the offset is the half I described as merely guarding against
|
||
triviality:** `+0x08` equals the largest keyframe time **exactly** in 92.3 % of
|
||
records; `+0x04` does so in **0 %**. No unrelated word reproduces that
|
||
coincidence.
|
||
|
||
📌 **So the value is right and my argument for it was wrong** — and this is the
|
||
second time this week I have had the weight on the wrong leg. Last time the count
|
||
was taking credit for an exclusion argument; this time the falsifier was taking
|
||
credit for the exactness statistic. **Both were cases where the impressive-sounding
|
||
control was the one carrying nothing.**
|
||
|
||
⚠️ Their generalisation of the boundary rule does not transfer literally — a
|
||
per-record header has no first-and-last-record phase question — but the underlying
|
||
point does: **an interior consistency check is satisfied by any reading that is
|
||
internally consistent, and "internally consistent" is what a wrong offset into a
|
||
regular structure usually is.**
|
||
|
||
📌 And their observation about *when* I found my extractor inflating my own backlog
|
||
is worth keeping: **while clearing it, not while building the tool.** Clearing put
|
||
me in contact with the individual items; building had only put me in contact with
|
||
the rule.
|
||
|
||
## The 92.3 %-versus-49.6 % gap: same numerator, and their filter is not applied
|
||
|
||
Reproducing my offset result, they reported the same discrimination over a
|
||
**different population — 3 311 records against my 1 781** — with exactness at
|
||
**49.6 %** against my **92.3 %**, attributing the difference to *"this scan takes
|
||
every pak and requires a timed keyframe"*. Both scans are described identically,
|
||
so at least one was narrower than its own description. Counting my survivors at
|
||
each filter:
|
||
|
||
| filter | survivors |
|
||
|---|---|
|
||
| records declared by `parse_build` | **3 311** |
|
||
| within the entry's bounds | 3 311 |
|
||
| carrying the `RATC` magic | 3 311 |
|
||
| parsing as a nested build | 3 311 |
|
||
| **with at least one timed keyframe** | **1 781** |
|
||
|
||
📌 **3 311 is the count *before* the timed filter.** And the arithmetic closes it:
|
||
|
||
```
|
||
1643 / 1781 = 92.3 % (mine)
|
||
1643 / 3311 = 49.6 % (theirs, exactly)
|
||
```
|
||
|
||
**Same numerator.** So their denominator includes the **1 530 records with no
|
||
timed keyframe at all**, where *"does `+0x08` equal the largest keyframe time?"*
|
||
has no meaning — there is no largest keyframe time, `max t` is 0, and every one of
|
||
them counts as "not exact" by construction.
|
||
|
||
🔴 **So their stated filter is not applied**, and the 49.6 % is not a weaker
|
||
version of my 92.3 % — it is **1 643 successes divided by a denominator containing
|
||
1 530 questions that were never asked.**
|
||
|
||
✅ **The discrimination is untouched**, as they said: `+0x04` gives **0 %** under
|
||
either denominator, so the offset conclusion stands on both scans.
|
||
|
||
⚠️ **And my number needs its own qualifier, which it did not carry.** 92.3 % is
|
||
*"of the records where the question is meaningful"*, not *"of nested records"*.
|
||
I have been quoting it bare since 2026-08-30, including into `screen.rs`'s doc
|
||
comment — **a population-scoped statistic reported without its population**, which
|
||
is the same shape as a negative reported without its reach.
|
||
|
||
📌 Two agents, one number, and the disagreement was **entirely in the denominator**
|
||
— neither of us was wrong about the disc. That is a cheaper failure than the
|
||
offset one and a more common one: **the numerator agreed to the unit, which is
|
||
exactly what makes a denominator mismatch invisible.**
|
||
|
||
## 🔴 Correcting my own correction: none of the 1 530 is a question without content
|
||
|
||
I told them their denominator held *"1 530 questions that were never asked"* —
|
||
records with no timed keyframe, where *"does `+0x08` equal the largest keyframe
|
||
time?"* has no meaning. **I did not check that, and it is wrong.**
|
||
|
||
| of the 1 530 excluded | |
|
||
|---|---|
|
||
| **no timed keyframe at all** | **0** |
|
||
| **timed, every pose at t = 0** | **1 530** |
|
||
|
||
**Every one of them has a largest keyframe time. It is 0.** So the question is
|
||
well-formed there, and the answer is *"not exact"* — because a **static record
|
||
still declares a cycle length**, and a nonzero `+0x08` against a largest time of 0
|
||
is a real disagreement, not an absent one.
|
||
|
||
📌 **Which makes their 49.6 % defensible rather than mistaken.** Two statistics
|
||
over two populations:
|
||
|
||
* **92.3 %** — of records whose largest keyframe time is **> 0**.
|
||
* **49.6 %** — of **all** nested records, static ones included.
|
||
|
||
**Neither is the corrected version of the other.** I framed mine as the correct
|
||
one and theirs as an artefact; the truthful statement is that they answer
|
||
different questions and **both need their population attached** — which was my own
|
||
point one message earlier, applied to their number and not to my reading of it.
|
||
|
||
⚠️ Their cause diagnosis is still right about the **mechanism** — `.max()` returns
|
||
`Some(0)` rather than `None` — but *"records with no timed keyframe"* describes
|
||
**zero records on this disc**. The mechanism they found is real and the population
|
||
they attributed it to does not exist.
|
||
|
||
📌 **Third-order, and worth naming as such:** they corrected an argument, I
|
||
corrected their denominator, and this corrects my characterisation of what was in
|
||
it. Each step was checkable in one scan, and each of us stated the *interpretation*
|
||
confidently while only the *number* had been measured. **The numbers have agreed
|
||
throughout; every disagreement has been about what they were counting.**
|
||
|
||
✅ What survives untouched, and is the only part the port depends on: `+0x08`
|
||
equals the largest keyframe time exactly where that time is nonzero, `+0x04` does
|
||
so **0 %** of the time under either denominator, and the offset identification
|
||
stands on both scans.
|
||
|
||
## The one load-bearing thing in the denominator thread, checked against the port
|
||
|
||
Their substantive point was not about counting: **a static record still declares a
|
||
cycle length**, and a nonzero `+0x08` against a largest keyframe time of 0 is a
|
||
real disagreement. That is a *rendering* question for this port, and it had not
|
||
been asked.
|
||
|
||
Scoped to the archive the port exports:
|
||
|
||
| | |
|
||
|---|---|
|
||
| nested records in `GP_TITLE` | **65** |
|
||
| declaring a cycle while every pose sits at t = 0 | **20** |
|
||
| **of those, with any element carrying more than one pose** | **0** |
|
||
|
||
✅ **So the declared cycle is visually inert on every one of them.** A record whose
|
||
elements each hold a single pose renders identically whether looped or held —
|
||
there is nothing to move between. **The port holds nothing still that the disc
|
||
says moves**, and that is now measured rather than assumed.
|
||
|
||
⚠️ It includes `ptbtn11`/`12`/`13` — EXTRAS' own buttons — declaring 120-unit
|
||
cycles. Had any carried two poses, the port would have been holding a menu button
|
||
the disc says animates, on the one submenu P5's gate walks. **The check cost one
|
||
scan and the answer could have gone the other way.**
|
||
|
||
📌 **And this is the thread's whole yield stated honestly.** Three rounds of
|
||
correction ran over an interpretation that was **never load-bearing** — the offset
|
||
stood on both scans throughout, so the cost of being wrong at each step was a
|
||
paragraph. What came out of it that was worth having: the **population
|
||
distinction**, and this one check, which exists because they pushed on what the
|
||
1 530 *mean* rather than on how they are counted.
|
||
|
||
⚠️ Their framing of why it was safe is the caveat I would attach to repeating it:
|
||
**nothing the port depends on moved at any point.** That made three rounds cheap.
|
||
It does not make three rounds a good default, and I would not have spent them if a
|
||
shipped value had been waiting on the outcome.
|
||
|
||
## Quantifying the one thing neither agent can move
|
||
|
||
They closed with the fact that gates everything: **`main` is ~234 commits behind
|
||
their branch, so nothing either of us decided this week is reachable from it.**
|
||
That is also where P5's gate sits, and it is the one open item I own the framing
|
||
of.
|
||
|
||
Measured from this side: **256 commits ahead of `origin/main`, 0 behind** — so
|
||
`main` is an ancestor and **the merge is a fast-forward with nothing to resolve**.
|
||
58 files: the exporter and its examples, the Godot scripts, `authored/`,
|
||
`tools/port/`, `docs/port/`.
|
||
|
||
`RUNNING.md` gains a §6 saying what a person is actually being asked to do, in
|
||
three lines: run `--boot --play`, walk the table, **say whether it behaves as
|
||
described — not whether it matches the game**, because that comparison is the
|
||
oracle's and is already asserted by `check-all`.
|
||
|
||
📌 **That distinction is the point of writing it down.** A reviewer who thinks
|
||
they are being asked to judge fidelity will either decline or over-claim; the gate
|
||
asks something narrower and answerable in five minutes. **P5 has been open partly
|
||
because nobody had written what "clicking through it" means.**
|
||
|
||
⚠️ What this does **not** do is meet the gate. It removes the last excuse that is
|
||
mine to remove — the commands existed only in a header comment, then only in a
|
||
log, and now the ask itself was unstated. **The remaining blocker is a person, and
|
||
before that, a merge neither agent may perform.**
|
||
|
||
## Verified their merge-state claim rather than relaying it — and it improves the ask
|
||
|
||
They extended the merge quantification and I checked it from this side, because it
|
||
is a **structural claim about my own repository** and I have relayed four of their
|
||
asides unchecked this week.
|
||
|
||
| | |
|
||
|---|---|
|
||
| their branch vs `main` | **329 ahead, 0 behind** — fast-forward ✅ |
|
||
| files in common between the two change sets | **0** ✅ |
|
||
| `git merge-tree --write-tree` of the two heads | single tree, **no conflicts** ✅ |
|
||
|
||
Read-only: **nothing was merged, and merging stays the human's.**
|
||
|
||
📌 **Their point about the sentence is the useful part, and it corrects my own
|
||
framing from last iteration.** I wrote *"257 ahead, `main` 234 behind"* — which
|
||
sounds like a backlog someone has to schedule. **The true sentence is "two
|
||
fast-forwards over disjoint file sets, mergeable in either order with zero
|
||
conflicts"**, and that is a decision that takes a minute. `RUNNING.md` §6 now says
|
||
the second thing.
|
||
|
||
⚠️ I had the numbers and still described the situation in the harder-sounding way.
|
||
**A quantity is not a framing**, and I supplied one while leaving the other at the
|
||
level of "this is blocked".
|
||
|
||
### Closing the exchange
|
||
|
||
Their last observation is the one I would keep from the whole week: **the counting
|
||
question felt like the rigorous one to both of us and produced three rounds of
|
||
correction over an interpretation nothing depended on; the meaning question
|
||
produced a check that could have gone the other way on a screen this port ships.**
|
||
|
||
📌 And the P5 finding is its companion: **the gate was partly open because the ask
|
||
itself was unstated.** The commands existed, the log existed, the question lived
|
||
nowhere — and **no instrument either of us built would ever have surfaced that.**
|
||
Both are the same shape: the thing missing was not evidence, it was *what the
|
||
evidence was for*.
|
||
|
||
## The number in my decision document was stale the moment I committed it
|
||
|
||
Their last finding lands on `RUNNING.md` §6, which I wrote for the person who has
|
||
to certify P5: **a count written into a document meant to inform a decision decays
|
||
with every commit either agent makes.**
|
||
|
||
🔴 **Self-demonstrating: §6 said "256 commits ahead". By the time it was worth
|
||
reading, the answer was 258 — and the commit that added the sentence is one of the
|
||
two that made it wrong.** The act of recording the number changed the number.
|
||
|
||
✅ **Rewritten to invariants plus the commands to re-derive**, because the counts
|
||
were never the claim. What does not move:
|
||
|
||
| invariant | |
|
||
|---|---|
|
||
| `main` is an **ancestor** of this branch | ✅ |
|
||
| `main` is an ancestor of the Decoder's branch | ✅ |
|
||
| the two change sets touch **zero files in common** | ✅ |
|
||
| `merge-tree` of both heads → **one line, no conflicts** | ✅ |
|
||
|
||
**Every check in the table was run as written before it was published** — a
|
||
documented command that has never been executed is the same class as a control
|
||
that does not execute.
|
||
|
||
📌 **And it closes the exchange on the shape it kept producing.** Three times this
|
||
week I supplied a measured quantity and left the *thing it was for* unstated: the
|
||
merge described as a backlog when it is a one-minute decision; the P5 gate open
|
||
because the ask was never written; and now a count standing in for an invariant.
|
||
**In each case the evidence existed and what it was evidence *for* did not.**
|
||
|
||
⚠️ Their closing judgement is the one I would repeat rather than improve: **no
|
||
instrument either of us built has any purchase on that class, and neither of us
|
||
should try to build one.** The only thing that has ever caught it is one agent
|
||
reading the other's sentence for its own sake — which does not scale, and is not a
|
||
process, and is the entire mechanism behind every correction in this file this
|
||
week.
|
||
|
||
## A command without a pass condition is half a check
|
||
|
||
Their standard applied back to my §6: **a reader gets a number and no way to know
|
||
whether it is the right one.** Two of my four rows were worse than that —
|
||
`git merge-base --is-ancestor` **prints nothing at all** on success, so a reader
|
||
running it as written sees an empty line and cannot distinguish success from
|
||
failure.
|
||
|
||
Each row now carries `; echo $?` where the answer is an exit code, a stated pass
|
||
condition, and the last observed run: **`0`, `0`, `0`, `1`**. All four were
|
||
executed as written before publishing.
|
||
|
||
## What every failure this week actually was
|
||
|
||
Their closing observation is the best summary of the exchange and I would not have
|
||
assembled it:
|
||
|
||
> **None of them was a wrong measurement.** Every one was a **correct measurement
|
||
> doing a job it could not do.**
|
||
|
||
* a **count** standing in for an **invariant** — §6's "256 commits"
|
||
* a **falsifier** standing in for a **discriminator** — `+0x08` vs `+0x04`
|
||
* a **leg count** standing in for an **exclusion argument** — "three routes"
|
||
* a **denominator** standing in for a **population** — 92.3 % vs 49.6 %
|
||
* a **capture's assumed focus** standing in for **an excluded one** — the oracle row
|
||
|
||
📌 **That is a narrower failure than being wrong, and it survives every instrument
|
||
either of us built — because the number is right and the instruments check
|
||
numbers.** `audit-kinds` checks that a claim cites something. `check-claims`
|
||
checks that a dead phrase is marked. `contract-check` checks that a value matches
|
||
the contract. **Not one of them can ask whether the quantity answers the question
|
||
it is placed under.**
|
||
|
||
⚠️ **And that is where I am leaving it**, because the alternative is building the
|
||
instrument we spent a day establishing cannot exist. The Decoder tried twice and
|
||
published neither attempt; my own version would have been "flag claims whose
|
||
supporting statistic is not an exclusion argument", which is a judgement, not a
|
||
test.
|
||
|
||
📌 The one durable thing is a habit rather than a tool: **ask what job a number is
|
||
doing, not whether it is correct.** Every entry in the list above was caught by
|
||
somebody asking that about somebody else's sentence — and in four of the five, the
|
||
somebody was the other agent.
|
||
|
||
## The remaining multi-leg claims audited — and the pattern I predicted is not there
|
||
|
||
I have listed *"four unaudited multi-leg claims"* as open for several iterations,
|
||
and after the week's findings I said I **expected the same inversion in them**: a
|
||
count standing in for an exclusion argument. Applying the test — *could any leg
|
||
have come out differently given the others?*
|
||
|
||
| claim | verdict |
|
||
|---|---|
|
||
| `loop_start_why` — *"two derivations, neither converts bits to seconds"* | ✅ **holds.** (a) depends on a measured **rate**, (b) on the **cycle** — a wrong rate breaks (a) and leaves (b), a wrong cycle does the reverse. They fail **independently**. ⚠️ Bound: one trace, so they exclude arithmetic error and not trace error — which the existing *"one boot, one bank"* caveat already says |
|
||
| `arithmetic_why` — *"the corpus had independently measured 28.5 fps"* | ✅ **holds.** A different quantity, measured **before** these runs, so it could have disagreed. It agrees to **1.4 %** |
|
||
| `black_hold_why` — *"I checked independently…"* | ✅ **holds, and needed nothing** — it was already an exclusion argument: a constant excluded, outgoing-screen keying excluded, and four declared quantities each shown not to separate the pairs |
|
||
|
||
📌 **So the prediction was wrong, and that is worth recording as loudly as a
|
||
confirmation would have been.** Two of the original five *were* the bad shape and
|
||
were corrected when they came up — *"three routes"* and *"both agents
|
||
independently"*. The three that remained are sound, and **two of them were already
|
||
exclusion arguments before I had the vocabulary for it.**
|
||
|
||
⚠️ **The lesson I nearly drew was that my corpus is riddled with count-shaped
|
||
support.** It is not: it had **two**, both relayed from a peer's message, and the
|
||
claims I built from my own measurements state what they exclude. **That is a
|
||
different diagnosis and it changes what to watch** — the exposure is at the relay,
|
||
not in the authoring.
|
||
|
||
✅ Each `why` now states **what its legs discriminate**, rather than how many there
|
||
are. That is the whole content of the week's finding applied to my own data: *the
|
||
count was never the claim.*
|
||
|
||
## Their JP menu capture, corroborated from the disc — and the legs are genuinely different
|
||
|
||
They captured the JP main menu for the first time: initial focus **新規 (NEW
|
||
GAME)**, top item, ring **y 225.5** — matching six English boots — and reported
|
||
that the JP build places its buttons where the English one does.
|
||
|
||
✅ **Checked from my export, and it is exact:**
|
||
|
||
| screen | button rows |
|
||
|---|---|
|
||
| `main_menu` | 162, 242, 322, 401, 482 |
|
||
| `main_menu_jp` | **162, 242, 322, 401, 482** |
|
||
| `extras` | 282, 362, 442 |
|
||
| `extras_jp` | **282, 362, 442** |
|
||
|
||
📌 **And this time the legs really are independent, which is worth stating after a
|
||
week of finding they were not.** Theirs is a **runtime capture** of where the ring
|
||
lands; mine is the **disc's declaration** of where the buttons rest. Different
|
||
instruments, different quantities — my rows are button rest positions, their
|
||
225.5 is a ring row, **so these are not the same number** — and either could have
|
||
disagreed: the disc could declare different JP rows while a runtime ring landed at
|
||
225.5 for some other reason.
|
||
|
||
**What each establishes, separately:** the disc declares identical rows for EN and
|
||
JP; the running JP menu's ring sits where English's does. Together, declaration
|
||
and runtime agree *and* the languages agree.
|
||
|
||
⚠️ **It does not settle their dialog question**, and they said so before I could:
|
||
this is **language-pair structure at the menu, not at the dialog**. `GP_DIALOG`
|
||
2/3 remains exactly where it was — the ja capture of DIFFICULTY did not happen,
|
||
the round trip failed at the final Ⓐ.
|
||
|
||
📌 Their self-correction on the way is the same shape as everything this week:
|
||
they assumed the JP menu detector had failed because a log line showed glyph
|
||
11654; it had not — JP detects at 320 against English's 327, both inside the band,
|
||
and the 11654 was a later phase. **Diagnosing from the log line rather than from
|
||
the capture.**
|
||
|
||
## They have taken the relay finding, and it now has a direction
|
||
|
||
*"The exposure is at the relay, and I am the source end of it."* They hold **271
|
||
unaudited leg-count claims**, and my result predicts that is the problem set
|
||
rather than mine — **a prediction with a direction, which is better than a backlog
|
||
item**, and they said plainly they are not going to pretend it is scheduled.
|
||
|
||
📌 That is the correct disposition and I would not push it. **A backlog nobody is
|
||
incentivised to clear is exactly what we established survives**; the honest move is
|
||
to say so rather than to file it and let the filing stand in for the work.
|
||
|
||
## The independent pair was an accident — the rule that would make it deliberate
|
||
|
||
Their observation, and it is the one thing left in the exchange worth converting
|
||
into a habit: **the first genuinely independent pair either of us produced this
|
||
week happened by accident.** I checked a different quantity because it was **the
|
||
one my export had**, not because independence was designed for.
|
||
|
||
📌 **The rule that would make it deliberate: when corroborating a peer's claim,
|
||
re-derive it from what YOUR OWN DOMAIN gives you, not from the quantity they
|
||
measured.** Independence then follows by construction, because the domains differ
|
||
— mine is a static export of the disc, theirs is a runtime capture of the
|
||
emulator. Reaching for *their* quantity is what produces one reader used twice,
|
||
which is precisely how `extras/initial_focus`'s "both agents independently" died.
|
||
|
||
⚠️ **And it explains why that one failed while this one held.** For `ptbtn11` I
|
||
reached for the *same* quantity — button rows via `parse_build` — and got their
|
||
crate back. For the JP menu I reached for what my export happened to carry —
|
||
declared rest positions — and got a different instrument answering a different
|
||
question. **The difference was not care. It was whether the obvious check was
|
||
inside my own domain or inside theirs.**
|
||
|
||
📌 Their note on the cost of the log-line slip is the sharper half of that
|
||
finding: the wrong diagnosis would have entered the corpus as *"the detector is
|
||
locale-specific"* — **plausible, tidy, false, and exactly the kind of instrument
|
||
finding nothing downstream questions.** An instrument's own limitations are the
|
||
claims least likely to be challenged, because challenging them requires doubting
|
||
the tool everyone is using.
|
||
|
||
## A workflow defect of mine, on its fourth occurrence
|
||
|
||
`check-all` failed on `decisions-index` again — stale because **I appended to
|
||
`DECISIONS.md` while the suite was running.** That is the **fourth** time, and I
|
||
have regenerated and moved on each of the previous three without naming it.
|
||
|
||
📌 **The check is right and the habit is wrong.** A stale index is a real defect
|
||
for a reader — it answers *"is this already decided?"* with a confident no — so
|
||
weakening the assertion to accommodate my ordering would be exactly the
|
||
tuning-to-pass failure this file is full of warnings about.
|
||
|
||
✅ **The rule is one line: write the entry, then start the suite.** Recorded here
|
||
rather than left as a thing I keep rediscovering, because four silent
|
||
regenerations is how a recurring defect becomes invisible — each individual one
|
||
looks like a trivial fix.
|
||
|
||
⚠️ And the substantive result of the run, which is why it was worth doing: after
|
||
an iteration of authored-data edits across `audio.json`, `flow.json`,
|
||
`timing.json` and `screen_names.json`, **the oracle numbers are unchanged** —
|
||
`main_menu` 13.21, `extras` 13.38, `title` 14.16. **Nothing I did to the `why`
|
||
fields touched what the port renders**, which is what `authored/` being separate
|
||
from `export/` is supposed to guarantee and is now checked rather than assumed.
|
||
|
||
## The menu residual, decomposed — and half of 13.06 is tone
|
||
|
||
`verify-capture`'s header has said for weeks that *"RMSE is reported and is NOT a
|
||
target: the capture carries the game's own tone ramp, so it has a floor."* **The
|
||
floor has never been measured.** The splash residual was localised in detail; the
|
||
menu's — the port's most-quoted number — never was.
|
||
|
||
| | luma RMSE |
|
||
|---|---|
|
||
| `main_menu` render vs capture, aligned | **10.60** |
|
||
| after fitting one monotone transfer curve | **4.77** |
|
||
| **removed by tone alone** | **55 %** |
|
||
| ⚠️ control: same fit, menu render vs the **EXTRAS** capture | 28.92 → 20.98, **27 %** |
|
||
|
||
📌 **The control is what makes the 55 % mean anything.** A fitted curve always
|
||
removes *something* — on an unrelated pair it removes 27 %. Twice that on the
|
||
matched pair, so the tone difference is **real and specific**, not the fit
|
||
absorbing variance.
|
||
|
||
**So roughly half the menu residual is a tone/gamma difference between the port's
|
||
render and the captured surface, and half is not.** The fitted curve is an
|
||
S-shape rather than a gain — 64→72 brightens, 128→119 and 192→184 darken — which
|
||
is the shape of an output pipeline, not of a wrong colour.
|
||
|
||
⚠️ **What this does NOT do, and I am not going to do it:** identify which side is
|
||
wrong, or "fix" the tone. The mission's instruction is explicit — *say which is
|
||
wrong rather than tuning until they match* — and I cannot say which. The capture
|
||
carries the emulator's output path; the render carries Godot's. **Nothing in the
|
||
port changes on the strength of this.** What changed is that a number quoted for
|
||
weeks now has a decomposition and a bound: **4.77 of it is not tonal**.
|
||
|
||
### 🔴 And my first attempt at it was misaligned in exactly the documented way
|
||
|
||
Before this I ran a raw full-frame comparison and got *"75.6 % of pixels differ,
|
||
uniformly across the frame"* — a nonsense result, because **the capture is
|
||
1279×675 and the render is 1280×720**. `verify-capture` handles that in a comment
|
||
I had read: *"the captures are a 1279×675 top-left crop of the guest surface, so
|
||
the render is cropped to match and nothing is scaled."*
|
||
|
||
📌 **I bypassed the harness that encodes the correction and reproduced the
|
||
uncorrected error.** Same shape as the week: the instrument existed, its comment
|
||
said why, and I reached past it for a quicker check. The tell was that the answer
|
||
was **uniform** — a residual spread evenly over an entire frame is a comparison
|
||
fault, not a rendering one, and that is the reading I should have applied before
|
||
the dimensions.
|
||
|
||
## Refutation: the peer's tone/geometry positive control rests on a number of mine that cannot carry it
|
||
|
||
`docs/re/structures/title-residual-tone-vs-geometry.md` (`d0eae04`) argues that at
|
||
least 68 % of the *title's* residual is spatial. Its instrument is a per-level LUT
|
||
fitted on the screen itself, and it validates that instrument with a **positive
|
||
control on the main menu** — *"where the port measures only 0.06 % of pixels
|
||
differing, so geometry is essentially right"* — closing 70.3 % there.
|
||
|
||
📌 **That 0.06 % is mine, and it does not mean what the control needs it to mean.**
|
||
`verify-capture` counts pixels surviving `-threshold 25%`: differing by **more
|
||
than ~64 levels**. That is deliberate — the tool's job is to catch a missing or
|
||
misplaced element, which is a large connected blob. **A one-pixel offset, a soft
|
||
edge slightly out of place, an antialiasing difference: none of those move a pixel
|
||
64 levels, and none of them are visible to that column.** It establishes *no gross
|
||
displacement*, not *geometry is right*.
|
||
|
||
And sub-threshold spatial error is precisely what a per-level LUT also cannot
|
||
close — so if the menu has any, the control is not measuring what it claims.
|
||
|
||
### It does. Measured, with a known negative
|
||
|
||
After fitting the LUT, splitting the remaining residual by local gradient:
|
||
|
||
| main_menu, LUT-corrected residual | value |
|
||
|---|---|
|
||
| on **edge** pixels (4.3 % of frame) | **6.94** |
|
||
| on **flat** pixels | **2.20** |
|
||
| **concentration** | **3.16×** |
|
||
| ⚠️ known negative — render vs itself under a pure gamma 0.78 | **0.00 / 0.00** |
|
||
|
||
✅ **The known negative is exact, by construction:** a residual that really is a
|
||
per-level tone effect is inverted *perfectly* by a per-level LUT, so any nonzero
|
||
edge concentration is spatial. The menu's is 3.2×.
|
||
|
||
### What this does and does not refute
|
||
|
||
* 🔴 **Refuted: the positive control's premise.** The main menu is not a
|
||
geometry-free screen, so "the instrument closes 70 % of a tone-dominated
|
||
residual" is not demonstrated — that 70 % was closed on a residual that also
|
||
contains spatial error.
|
||
* ✅ **Their conclusion survives, and I want to be clear about it.** The title's
|
||
32 % figure never depended on the control: whatever a fitted per-level LUT
|
||
cannot close is, by construction, not a per-level effect. That argument is
|
||
self-standing. **The refutation lands on the validation, not on the result.**
|
||
* 📌 **The actionable half is mine.** A number this tool prints was quoted by
|
||
another agent as evidence for something it cannot support. I have written the
|
||
limit into `verify-capture`'s header next to the number, because the misreading
|
||
was reasonable — the column is called `diff` and nothing said what it thresholds.
|
||
|
||
⚠️ **Still not settled:** what the menu's spatial error *is*. 3.2× edge
|
||
concentration is the signature of sub-pixel misregistration, of antialiasing, or
|
||
of a genuinely misplaced soft element, and this measurement does not separate
|
||
those three. I am not guessing between them.
|
||
|
||
## The menu's edge residual is **not** a misregistration — the Decoder's discriminator, run
|
||
|
||
They proposed the test and said plainly they had not run it: *"the discriminator
|
||
would be whether the edge excess is signed — a misplaced element gives a residual
|
||
with a consistent direction along the edge, antialiasing does not. That is a real
|
||
experiment and I have not run it, so it is a route, not an answer."*
|
||
|
||
📌 **It needed my render beside the capture, so it was mine to run.** Made concrete:
|
||
a shift by `(dx,dy)` makes the signed residual track the **gradient**, and the
|
||
fitted slope *is the shift in pixels*; a blur makes it track the **Laplacian**,
|
||
symmetric and directionless. New tool: `tools/port/edge-residual-kind`.
|
||
|
||
### The controls come first, and they are not a flag
|
||
|
||
| render vs a deliberately damaged copy of itself | d/dx | d/dy | laplacian |
|
||
|---|---|---|---|
|
||
| known **+1 px horizontal shift** | **r +0.789, slope +0.938 px** | −0.008 px | −0.306 |
|
||
| known **blur, no shift** | +0.003 px | +0.004 px | **r −0.896** |
|
||
|
||
✅ **A 1-px shift reads back as +0.938 px.** The instrument recovers both what it
|
||
is for, and neither control leaks into the other's channel.
|
||
|
||
### The result
|
||
|
||
| `main_menu` render vs oracle capture | value |
|
||
|---|---|
|
||
| horizontal shift | r −0.027, **slope −0.010 px** |
|
||
| vertical shift | r −0.027, **slope −0.009 px** |
|
||
| blur / sharpness | r +0.103 |
|
||
|
||
🔴 **Flat on all three. The menu is not globally misregistered** — any whole-frame
|
||
translation is under **a hundredth of a pixel**, against a control that reads a
|
||
true 1 px at 0.938. That excludes the most worrying of the three candidates, and
|
||
the one a renderer can silently acquire. The blur channel at +0.103 is weak *and
|
||
the opposite sign to the blur control*, so the capture is not a softened render
|
||
either.
|
||
|
||
⚠️ **Reach, and it is the whole reach:** this is a **whole-frame** fit. A single
|
||
misplaced element is a small share of 38 752 edge pixels and would not move these
|
||
numbers. **This excludes a global translation, not a local one.** Of the peer's
|
||
three candidates it kills misregistration and weakens uniform antialiasing; a
|
||
misplaced soft element is untouched by it, and I am not claiming otherwise.
|
||
|
||
📌 Exit codes are **0 or 2, and there is no 1** — the tool classifies, it does not
|
||
judge. If either control fails it prints nothing but the failure: verified by
|
||
raising the thresholds to 0.99, which suppresses the report and exits 2. A guard
|
||
nobody has watched fail is decoration.
|
||
|
||
## `GP_DIALOG` 2/3 restored to `authored/flow.json` — on a measurement this time
|
||
|
||
I withdrew *"an EN/JP pair"* as an unchecked relay and **declined to re-add it**
|
||
because nothing depended on it. The Decoder has now taken the `ja` capture that
|
||
was missing (`HANDOFF` at `5a7f34d`, dated today): EN and JP differ in **1.82 % of
|
||
pixels in four bands and nowhere else** — heading, the ring by 2 px, `BACK`, the
|
||
footer. `EASY`/`NORMAL`/`HARD` are *not* in the differing set; the Japanese release
|
||
leaves them in Latin script, which is why the disc figure is only **2.77 % of
|
||
bytes**.
|
||
|
||
📌 **My objection was not wrong and is not withdrawn.** It was that *identical
|
||
element sets do not imply a language pair* — 26 of 65 adjacent `GP_DIALOG` pairs
|
||
differ in button count, so adjacency proves nothing. That still holds. What changed
|
||
is that the claim now rests on a direct locale capture instead of on that
|
||
inference. **A bad argument for a true claim is still a bad argument**, and the
|
||
claim was correctly out of the file until somebody went and looked.
|
||
|
||
⚠️ Their reach, carried across: one JP boot, one screen, does not generalise —
|
||
`GP_TITLE` 4/7 differs by more than text. Nothing in the port keys off locale
|
||
today, so this is recorded, not consumed.
|
||
|
||
## The residual map: no local displacement either, and the split I expected is not there
|
||
|
||
The Decoder proposed the division and it is the right one: *"the map is yours and
|
||
the element inventory is mine."* `tools/port/edge-residual-map` tiles the frame at
|
||
64 px and runs the same shift discriminator **inside each tile**, which is the
|
||
thing `edge-residual-kind` said it structurally could not do.
|
||
|
||
### 🔴 The first control failed, and that is the useful part
|
||
|
||
A known **+2 px** displacement localises perfectly — the displaced region is the
|
||
top four tiles — but reads back **+0.839 px**. The slope is a linearisation,
|
||
`residual ≈ dx · gradient`, valid only while `dx` is small against the width of an
|
||
edge. **The estimator saturates.** Reporting that as a distance would have
|
||
understated a real displacement by more than half.
|
||
|
||
So there are now **two** controls, each asserting only what it can:
|
||
|
||
| control | localises | magnitude |
|
||
|---|---|---|
|
||
| **+1 px** (linear regime) | ✅ top 4 tiles | ✅ **+0.949** |
|
||
| **+2 px** (saturating) | ✅ top 4 tiles | ⚠️ +0.839 — **a lower bound** |
|
||
|
||
📌 **A hot tile's slope is a floor on the displacement, never a ceiling.**
|
||
|
||
### The result
|
||
|
||
| tile | edge | flat | e/f | dx | dy |
|
||
|---|---|---|---|---|---|
|
||
| 512,128 | **18.80** | 10.20 | 1.84 | **−0.001** | **−0.000** |
|
||
| 576,64 | 15.11 | 9.09 | 1.66 | −0.012 | +0.029 |
|
||
| 384,256 | 14.12 | 4.40 | 3.21 | +0.003 | +0.003 |
|
||
| 512,192 | 11.91 | 3.53 | 3.37 | −0.038 | −0.090 |
|
||
| 448,128 | 11.78 | 9.53 | 1.24 | +0.084 | +0.036 |
|
||
|
||
median tile 5.13 · hottest 3.66× median · **every dx and dy under 0.1 px**
|
||
|
||
🔴 **No tile in the top ten is displaced.** Against a control that reads a true
|
||
1 px at +0.949 and finds a 2 px one even while understating it, **nothing in the
|
||
hot region has moved.** The peer's third candidate — a misplaced soft element —
|
||
now has no support anywhere on this screen, globally or locally.
|
||
|
||
The hot tiles cluster: **x 384–704, y 64–256**, a wide upper-centre band, plus one
|
||
outlier at **640,576**. Those are coordinates. **This tool names nothing** — what
|
||
sits under them is the Decoder's, and I have sent them the list.
|
||
|
||
### ⚠️ And a structure I expected, went looking for, and did not find
|
||
|
||
I added the `flat` column expecting two families: tiles hot *only* at edges (an
|
||
edge-rendering difference) against tiles hot *everywhere* (a local tone the global
|
||
LUT mis-serves). Reading the first ten rows, that split looked obvious.
|
||
|
||
It is not there. The hot tiles run **continuously from 1.24 to 3.37** across a
|
||
median of **1.84**.
|
||
|
||
📌 **What nearly manufactured it:** I had the frame-wide *pooled* edge/flat ratio,
|
||
**3.16**, from the earlier work, and against 3.16 the rows at 1.2–1.8 look like a
|
||
distinct low family. But the pooled figure is dominated by the tiles carrying the
|
||
most edge pixels; **the per-tile median is 1.84**, and against *that* the same rows
|
||
are unremarkable. **Same quantity, wrong population** — the week's pattern again,
|
||
caught this time only because I computed the baseline before writing the claim
|
||
rather than after.
|
||
|
||
**So the hot region is not one anomalous element with a character of its own**, and
|
||
that is a finding, not an absence of one.
|
||
|
||
## Suppression beats coordinates: the menu residual is two frame elements, drawn too dark
|
||
|
||
The Decoder named what sits under my hot tiles (`docs/re/data/menu-hot-tile-inventory.txt`)
|
||
and was careful to test both coordinate readings rather than assume one, because
|
||
design space and the comparison frame differ by the capture transform.
|
||
|
||
📌 **I did not need the transform.** The port has a mod tree, so an element's real
|
||
footprint can be *measured*: shadow its sprite with a transparent PNG, render, and
|
||
diff my own two renders. The pixels that change are the element, in my comparison
|
||
frame, with **no coordinate convention assumed at all**. That is the method I
|
||
should have reached for before handing over tile coordinates.
|
||
|
||
### The ranking, by residual density on each element's own visible pixels
|
||
|
||
| element | footprint | mean \|resid\| | vs frame mean 2.40 |
|
||
|---|---|---|---|
|
||
| **`ptframe1`** | 0.45 % | **22.72** | **9.47×** |
|
||
| **`ptframe2`** | 0.50 % | **13.09** | 5.46× |
|
||
| `ptmsg` | 0.46 % | 8.46 | 3.52× |
|
||
| `pteff12` | 17.31 % | 4.98 | 2.07× |
|
||
| `ptbase` | 3.98 % | 3.34 | 1.39× |
|
||
| `pteff10` | 52.15 % | 3.34 | 1.39× |
|
||
|
||
### 🔴 This refutes the hypothesis I came in with
|
||
|
||
I predicted **the effect element**: `screen.rs` records blend mode as undecoded,
|
||
an effect composited wrongly would be tonal and displace nothing, and `pteff12`
|
||
sat in the hot band. The measurement says **the frames** — 9.47× against the
|
||
effect's 2.07×, and `pteff12`'s ratio is largely inherited from *containing*
|
||
`ptframe1` (excluding the frame's pixels drops it from 4.98 to 4.61).
|
||
|
||
The mechanism I proposed may still be right. **The element I proposed it for was
|
||
wrong**, and the only reason I know is that suppression ranks elements rather than
|
||
confirming the one I was looking at.
|
||
|
||
### Not an edge effect — and that is what makes it specific
|
||
|
||
| | edge px | \|r\| edge | flat px | \|r\| flat |
|
||
|---|---|---|---|---|
|
||
| **`ptframe1`** | 1868 | 19.85 | 1986 | **25.41** |
|
||
| **`ptframe2`** | 2201 | 9.82 | 2086 | **16.54** |
|
||
| `pteff12` | 12102 | 7.73 | 137306 | 4.73 |
|
||
| `ptbase` | 14092 | 4.69 | 20229 | 2.40 |
|
||
|
||
🔴 **The two frames are the only elements whose residual is higher on FLAT pixels
|
||
than on edges.** Everything else is edge-weighted, as any render/capture pair is.
|
||
So this is the elements' **body intensity**, and signed it is one-directional:
|
||
`ptframe1` renders at **88.4 against the capture's 129.1**, with **0.1 % of its
|
||
pixels render-brighter**. The port draws them too dark, nearly everywhere, after a
|
||
global tone LUT is already applied.
|
||
|
||
Filed as an ask in `BLOCKED.md` against HANDOFF `5a7f34d`. **I am not brightening
|
||
them** — that is tuning until they match, and the blend bits are the Decoder's.
|
||
|
||
### ⚠️ And a defect I nearly reported that was not one
|
||
|
||
Suppressing `ptbtn01` — the *focused* button — changed **zero pixels**, and an
|
||
**opaque magenta** replacement changed zero too. `ptbtn02` and `ptbtn03` change
|
||
9 331 and 7 654. All five buttons carry the same rest fade, so the difference is
|
||
focus, and the obvious reading was "the port never draws the focused button".
|
||
|
||
`screen_view.gd:746` says otherwise, deliberately: *"A FOCUSED button draws its
|
||
record INSTEAD of its base sprite — measured, the focused sprite covers the base
|
||
at 100.0 % of base-visible pixels."*
|
||
|
||
✅ So the null is a **confirmation**, at a stricter standard than the claim it
|
||
confirms: not "100 % of sampled base-visible pixels are covered" but "replacing
|
||
the base with opaque magenta changes the frame in zero pixels, exactly". I went
|
||
looking for a bug and independently re-derived a documented measurement.
|
||
|
||
## The frames generalise, premultiplied alpha is refuted, and the shortfall tracks the background
|
||
|
||
`tools/port/element-residual` generalises last iteration's suppression method: pose
|
||
a screen as `verify-capture` does, shadow each sprite with a transparent PNG, and
|
||
rank elements by residual on the pixels they actually paint. Two controls, both
|
||
mandatory — the metric's zero on identity, and **a mod that shadows nothing must
|
||
move zero pixels**, or a footprint is the harness rather than the element.
|
||
|
||
### It generalises: four frames, two screens, ranks 1 and 2 on both
|
||
|
||
| screen | element | foot % | \|resid\| | ×frame | edge | flat | signed |
|
||
|---|---|---|---|---|---|---|---|
|
||
| `main_menu` | **`ptframe1`** | 0.45 | 22.72 | **9.47×** | 19.85 | 25.41 | **−22.72** |
|
||
| `main_menu` | **`ptframe2`** | 0.50 | 13.09 | 5.46× | 9.82 | 16.54 | −12.31 |
|
||
| `extras` | **`ptframe3`** | 0.40 | 34.80 | **14.23×** | 32.41 | 41.93 | **−34.80** |
|
||
| `extras` | **`ptframe4`** | 0.40 | 25.58 | 10.46× | 23.29 | 33.55 | −25.36 |
|
||
|
||
📌 **And the sign splits, so this is not the port being globally dark.** Frames are
|
||
negative; `ptmsg` **+5.02**, `ptmsg2` **+8.83**, `pttitle` **+7.88** and every
|
||
button **+1.45…+4.07** are *too bright*. A global tone error cannot do that.
|
||
|
||
### 🔴 Premultiplied alpha: my hypothesis, refuted by its own prediction
|
||
|
||
The obvious exporter-side cause is a premultiplied-alpha texture decoded as
|
||
straight alpha, which darkens exactly where alpha is partial. **It predicts error
|
||
∝ partial-alpha fraction. The opposite holds:**
|
||
|
||
| element | % partial alpha | signed |
|
||
|---|---|---|
|
||
| `ptframe1` | **7.3 %** | **−22.72** |
|
||
| `ptframe3` | **6.7 %** | **−34.80** |
|
||
| `pteff10` | **100 %** | −1.33 |
|
||
| `ptbase` | 0.9 % | +1.04 |
|
||
|
||
✅ **Dead.** The most-darkened elements have the *least* partial alpha, and the one
|
||
element that is entirely partial is almost exactly right.
|
||
|
||
### What the frames actually are, and where the shortfall goes
|
||
|
||
**Neither frame has a single fully-opaque pixel** — 0 % at alpha ≥ 99 %, against
|
||
`ptbase`'s 99.1 %. They are wholly semi-transparent overlays, the one class where
|
||
the compositing equation decides the result.
|
||
|
||
That gives a falsifiable prediction. Under alpha-over, the shortfall against a
|
||
background-scaling blend is `a · background`, so it scales with **what is behind
|
||
the frame** — whereas a too-dark texture would scale with **the frame's own
|
||
contribution**. Using only the render, the frame-suppressed render and the capture,
|
||
so no placement or coordinate convention is assumed:
|
||
|
||
| | r(shortfall, **background**) | r(shortfall, frame's contribution) |
|
||
|---|---|---|
|
||
| `ptframe1` (`main_menu`) | **+0.772** | +0.244 |
|
||
| `ptframe3` (`extras`) | **+0.797** | +0.237 |
|
||
|
||
✅ **Replicated on two elements on two screens.** The missing light scales with the
|
||
background, which is what an additive or screen blend predicts and what a bad
|
||
texture does not. Implied `a` medians 0.316 and 0.532.
|
||
|
||
### ⚠️ What I am NOT doing about it
|
||
|
||
The Decoder has established there is **no blend mode on the disc** for `.t32` — both
|
||
frames are kind 0, declared identically to elements the port draws almost exactly
|
||
right (`ptbase` ×1.31, `pteff05` ×0.92). **So any blend I choose is authored**, and
|
||
adopting one on my own authority is precisely what the mission forbids. I am
|
||
proposing it, not taking it.
|
||
|
||
📌 **The measurement does say something their negative does not cover, and I want
|
||
it on the record as an extension rather than a challenge:** the behaviour exists
|
||
and is large and replicated, and if nothing in the *data* selects it, then it is
|
||
selected in **code** — the executable's draw path, which they named as the route
|
||
they have not taken. Their negative and this measurement are consistent; together
|
||
they locate the remaining question rather than closing it.
|
||
|
||
⚠️ Not settled: whether it is additive, screen, or something else — `+0.77` and
|
||
`+0.80` say "scales with the background", not which curve. Two screens; I have not
|
||
checked the title.
|
||
|
||
## Which blend? Additive halves the error, on both frames — proposed, not adopted
|
||
|
||
Last iteration ended with *"`+0.77` and `+0.80` say 'scales with the background',
|
||
not which curve."* That is decidable without any RE, because **an element rendered
|
||
over two different backgrounds gives two equations in `a` and `aC`**:
|
||
|
||
```
|
||
base - bg = a(C - bg ) the frame over background 1
|
||
b2 - bg2 = a(C - bg2) the same frame over background 2
|
||
--------------------------------------------------------------
|
||
a = [(base-bg) - (b2-bg2)] / (bg2 - bg) aC = (base-bg) + a·bg
|
||
```
|
||
|
||
Both backgrounds are produced by the mod tree — suppress `pteff10`/`pteff12` and
|
||
the background under the frame changes by a mean of 26 levels. **No placement, no
|
||
coordinate transform, no texture decoding assumed.**
|
||
|
||
### ✅ The control is exact, and it is what makes the rest usable
|
||
|
||
Rebuilding **alpha-over** from the solved `a` and `aC` reproduces the port's actual
|
||
render at **RMSE 0.0000** on both screens. The recovered per-pixel values are
|
||
right; they are not a fit that happens to land nearby.
|
||
|
||
### The result, replicated
|
||
|
||
RMSE against the capture, all four candidates mapped the same way:
|
||
|
||
| composite | `ptframe1` (`main_menu`) | `ptframe3` (`extras`) |
|
||
|---|---|---|
|
||
| **additive** | **34.305** | **28.948** |
|
||
| screen | 50.052 | 50.368 |
|
||
| **alpha-over — what the port does** | **65.046** | **71.299** |
|
||
| frame not drawn at all | 90.916 | 109.801 |
|
||
|
||
📌 **Same ordering on both, and additive roughly halves alpha-over's error.** The
|
||
frame is definitely drawn in the capture (absent is worst by a wide margin), and
|
||
of the three standard composites additive is the only one that closes most of the
|
||
gap. Solved on 1 743 and 1 999 pixels; median `a` 0.429 and 0.594.
|
||
|
||
### ⚠️ What this is not
|
||
|
||
**Additive is not established as the answer.** It still leaves 28.9–34.3, so
|
||
*none* of the three reproduces the capture — this ranks three candidates, it does
|
||
not identify the equation. The absolutes are inflated by mapping the capture
|
||
through the fitted LUT's inverse; the **ranking** is fair because all four
|
||
candidates go through the same mapping, and the ranking is the claim. Grayscale
|
||
only.
|
||
|
||
🔴 **And I have not adopted it.** The Decoder established that nothing on the disc
|
||
selects a blend for `.t32`, so any blend the port picks is **authored** — and
|
||
`PORT-MISSION`'s rule is that a runtime dependency is *proposed*, not taken on my
|
||
own authority. The renderer is unchanged. What exists now is a measurement that
|
||
says: alpha-over is wrong here, additive is much closer, and the choice is a
|
||
human's.
|
||
|
||
## Refutation attempt: the Decoder's kind-0 claim survives, checked from my own data
|
||
|
||
They reported both frames as **kind 0, identical to `ptbase`, `pteff05`, `pteff10`,
|
||
`pteff12` and `ptmsg`** — read off the 60-byte `.t32` declaration.
|
||
|
||
My exporter decodes that field independently and stores it as `kind_raw`. Every
|
||
sprite decoration on both screens is `0x0` — `ptframe1`…`ptframe4` included — and
|
||
every button is `0x3002`. ✅ **Survives.** Two independent decodes of the same
|
||
field agree, and the claim is now stronger than when only one side had read it.
|
||
|
||
📌 That matters because it is what makes the blend question sharp: the frames are
|
||
declared *identically* to `ptbase` (drawn at 1.31× the frame mean) and `pteff05`
|
||
(0.92×). Same declaration, opposite accuracy — so whatever distinguishes them is
|
||
not in the field either of us can read.
|
||
|
||
## The blend is measured, so the port draws it — main_menu 13.21 → 10.67
|
||
|
||
The Decoder took the draw-path route and logged `RB_BLENDCONTROL0` per draw in
|
||
Canary on both screens. `0x01010101` is `src=ONE dst=ONE`: **additive**. That
|
||
turns my proposal into a transcription, and they said so explicitly — *"withdraw
|
||
the instruction I gave you last time; additive is transcribed now, not authored."*
|
||
|
||
📌 **Their control is what licenses the change:** one pixel shader,
|
||
`0xE59B2B3DA4AA9008`, runs with **both** blend states on the main menu — 12 draws
|
||
additive, 18 alpha-over. The frames and `ptbase` share a shader. **Only the blend
|
||
register differs**, so this is a blend result and not a shader result.
|
||
|
||
Recorded in `authored/rendering.json` as `additive_elements`, per screen, with
|
||
every id being a measured draw and the reach written next to it.
|
||
|
||
### The result, and a neutrality control that came free
|
||
|
||
| screen | before | after |
|
||
|---|---|---|
|
||
| **`main_menu`** | 13.21 / 0.06 % | **10.67 / 0.02 %** |
|
||
| **`extras`** | 13.38 / 0.20 % | **11.43 / 0.07 %** |
|
||
| main menu, focus `ptbtn04` | 13.82 / 0.15 % | 11.36 / 0.11 % |
|
||
| title / title_plate / title_band | 14.16 / 13.04 / 12.86 | 14.10 / 13.03 / 12.85 |
|
||
| **`publisher_logo`** | **2.17** | **2.17** |
|
||
| **`developer_logos`** | **3.05** | **3.05** |
|
||
|
||
✅ **The last two rows are the control.** They are the screens `verify-capture`
|
||
marks *"no free-running element — absolute, means what it says"*, and they have no
|
||
additive element. They did not move **at all**. The rewrite routed every draw in
|
||
the project through `RenderingServer` canvas items, so "did the plumbing change
|
||
the picture?" was a live question; those two rows answer it. The improvement is
|
||
the blend, not the refactor.
|
||
|
||
Per element on `main_menu`: `ptframe1` **22.72 → 4.17** (signed −22.72 → −3.51),
|
||
`ptframe2` 13.09 → 3.32 (−12.31 → **+1.80**), whole-screen mean 2.40 → 1.55.
|
||
|
||
### 🔴 The change ran, produced a number, and the number was wrong by looking right
|
||
|
||
First run after wiring it all up: `ptframe1` moved from **22.72 to 22.69**. Bands
|
||
were created, ordered and assigned correctly; the screen composited exactly as
|
||
before. I had written `CanvasItemMaterial.new()` and **never set `blend_mode`**, so
|
||
every band was MIX.
|
||
|
||
📌 Nothing errored. A 0.03 move is a *plausible* outcome — I could have written
|
||
"additive does not help after all, the two-background solve overstated it" and it
|
||
would have read as a careful negative result. **It was caught only because the
|
||
measurement predicted a large move and 0.03 is not one.** The comment now sits on
|
||
that line.
|
||
|
||
### Why it is `RenderingServer` and not child nodes
|
||
|
||
Godot sets blend mode per **canvas item**, not per draw call. The obvious
|
||
implementation — a child `Node2D` per band with a `CanvasItemMaterial` — **loses a
|
||
frame**: `boot.gd` calls `view.queue_redraw()` from nine places and none reaches a
|
||
child node, so bands would paint the *previous* pose. Under `--script=wait` that
|
||
surfaces as a plausible wrong capture, not an error. The bands are canvas items
|
||
filled synchronously inside `_draw()` instead.
|
||
|
||
The runs are recomputed every frame rather than cached. The additive elements are
|
||
consecutive in paint order on both measured screens — **that is an accident of
|
||
those two screens**, and a cache keyed on "the additive block" would be right today
|
||
and silently wrong on the first screen that interleaves.
|
||
|
||
### ⚠️ What I did not do: `ptframe4`
|
||
|
||
`ptframe4` is now the worst element on EXTRAS (31.90, 16.19× the frame mean) and
|
||
additive would plainly help it. **It is not in the measured table and it is not in
|
||
the file.** Filed in `BLOCKED.md` with `pteff21`/`22`/`23`, which are also absent
|
||
from every captured draw. Where the blend is measured the element is near-exact
|
||
(`ptframe3` signed **−0.61**); where it is not, it is the worst thing on screen.
|
||
That contrast is the argument for asking rather than inferring.
|
||
|
||
## 🔴 Refuted: my "no fully-opaque pixel" sharpener
|
||
|
||
I offered, as the thing that distinguished the frames, that **neither frame has a
|
||
single fully-opaque pixel** against `ptbase`'s 99.1 %. The Decoder refuted it with
|
||
a census (`docs/re/data/menu-sprite-alpha-census.txt`): **`pteff10` has max alpha
|
||
130, is 100 % partial, has no opaque pixel either — and I measure it as nearly
|
||
exact.** `pteff12`, `pteff20` and `pteff21`–`23` likewise.
|
||
|
||
✅ **The observation was true and it was not the discriminator.** My *direction*
|
||
survived — the draw path was the right place to look, and it answered — but the
|
||
reason I gave for looking there was wrong. Recording it because the conclusion
|
||
being vindicated is exactly the circumstance in which a bad supporting argument
|
||
survives unexamined.
|
||
|
||
## The sweeps: a measured blend, a corroborated identification, and a confound in my own evidence
|
||
|
||
### Transcribed: both rotated sweep strips are additive
|
||
|
||
`ui-blend-mode-measured.md`'s summary table names them additive in the same row as
|
||
the frames. They are `ptloop01`/`ptloop02` here, now in `additive_elements` for
|
||
both measured screens.
|
||
|
||
⚠️ **It changes nothing visible today.** On the menus the port runs the leaf group
|
||
once and parks it off-screen, so both paint zero pixels at every pose the port can
|
||
be put in — checked by suppressing both sprites at leaf-time 100, 200 and 300:
|
||
**0 px changed each time**. The entry is there because it is measured, not because
|
||
it does anything. What it *does* do is repair the instrument below.
|
||
|
||
### ✅ Refutation attempt on their identification — survives, with a number of mine
|
||
|
||
The automatic name matcher reports **"no match"** for those two draws; they are
|
||
identified only by the Decoder's control, which reproduces heights **1134** and
|
||
**1303** from a different tool in a different session. A claim resting on one
|
||
control is worth attacking.
|
||
|
||
It survives, and my own geometry corroborates it independently: rendering the menu
|
||
at a phase where the sweeps are on screen and suppressing them gives a footprint
|
||
of **884 × 720**, against the log's **889.6** wide — **0.6 % apart**, and I did not
|
||
use their number to produce mine. The heights differ (720 against 1134) exactly as
|
||
they should: my bounding box is clipped by the screen, theirs is the untruncated
|
||
quad extent.
|
||
|
||
### 🔴 And the blend was a confound in my *own* prior evidence
|
||
|
||
`loop_leaf_why` scoped leaf-looping to the title partly on a measurement of mine:
|
||
sweeping the phase against `live-main-menu.png`, the port matched best with the
|
||
sweeps **off-screen (0.061 %)** and three times worse mid-screen (0.183 %).
|
||
|
||
**That sweep drew them alpha-over.** They are additive. So an on-screen sweep was
|
||
being composited the wrong way and scored against the capture — "mid-screen is
|
||
worse" could have been an artefact of my own renderer rather than of the sweeps
|
||
being absent. Re-run with the correct blend and looping switched on for the menu,
|
||
through a scratch export root so nothing in the repo changed:
|
||
|
||
| | diff vs capture | sweeps |
|
||
|---|---|---|
|
||
| phase 0 | **0.0208 %** | 0 px — off screen |
|
||
| phase 150 | 0.0851 % | **58 027 px**, bbox 884×720 |
|
||
| phase 300 | **0.0205 %** | 0 px — off screen |
|
||
| phases 75/225/375/450/525 | 0.086–0.122 % | on screen |
|
||
| run-once-and-park (what ships) | **0.0208 %** | parked |
|
||
|
||
✅ **The conclusion held and strengthened** — the ratio was 3× with the wrong blend
|
||
and is **4–6×** with the right one. The capture still matches best with the sweeps
|
||
not visible, so the scoping stays and the *correction* is what got recorded.
|
||
|
||
⚠️ Still one capture, and "best match" is still a weak instrument for an absence.
|
||
Fixing the blend cleared one confound; it did not repair that.
|
||
|
||
### 📌 A draw is not a visible element
|
||
|
||
The new log shows both sweep strips **submitted on the main menu, in every frame
|
||
group**. It would be easy — and wrong — to read that as "the sweeps animate on the
|
||
menu", which is exactly the question `loop_leaf_why` left open and would have
|
||
contradicted the pixels for no reason. **A quad parked off-screen at x=1521 is
|
||
still a draw call.** The log settles the *blend*; it does not settle *visibility*,
|
||
and those two came in the same artefact.
|
||
|
||
## A leak I introduced, and a reach sentence that understates its own gap by four elements
|
||
|
||
### 🔴 The `RenderingServer` rewrite leaked five canvas items per run
|
||
|
||
Every run printed `5 RIDs of type "CanvasItem" were leaked` — exactly the number of
|
||
paint-order runs on the main menu. **Canvas items created through `RenderingServer`
|
||
are not owned by the node**; a child `Node2D` would have been collected for me, and
|
||
the reason for not using one (`_band`) is also the reason this had to be paid for.
|
||
Freed in `_exit_tree`.
|
||
|
||
📌 It was found by looking, not by anything failing: `verify-capture` was green
|
||
across every screen, the pictures were right, and the leak line sat in a log
|
||
alongside `N ObjectDB instances were leaked at exit` — which `BLOCKED.md` records
|
||
as **engine-side and not ours**, investigated at `91ada14`, where releasing every
|
||
reference the port owns moved the count *not at all*. A new leak line arriving next
|
||
to a known-benign leak line is close to the best possible camouflage. The
|
||
distinguishing fact was the number: **5**, which is a count of my bands and not of
|
||
anything the engine owns.
|
||
|
||
### 🔴 Refutation: the measurement's reach is right for one screen and wrong for the other
|
||
|
||
`ui-blend-mode-measured.md` closes with: *"Every element on the two screens the
|
||
port ships is in the table except the two above and `pteff10`, which did not appear
|
||
as an identifiable quad."*
|
||
|
||
Checked element by element against my own export — counting an element as covered
|
||
if it appears in the per-draw log **or** in the summary table's prose rows (which
|
||
name `pteff05`, "every button" and "both rotated sweep strips"):
|
||
|
||
| screen | in neither |
|
||
|---|---|
|
||
| `main_menu` | `pteff10` — ✅ exactly as claimed |
|
||
| **`extras`** | `pteff10`, **`ptframe4`, `pteff21`, `pteff22`, `pteff23`** |
|
||
|
||
🔴 **Five, not one.** The sentence is accurate for the main menu and understates the
|
||
EXTRAS gap by four elements — and they are not arbitrary four. **They are precisely
|
||
the elements the port now measures as the worst on that screen**: `ptframe4` at
|
||
16.19× the frame mean, with `pteff21`/`22`/`23` immediately behind it.
|
||
|
||
⚠️ **This is a reach statement, not a result** — every measured row stands, and the
|
||
port has already transcribed all of them. But a reader of that page would conclude
|
||
the coverage is complete but for one unidentifiable quad, and on EXTRAS it is not:
|
||
**a quarter of what the port draws there is unmeasured, and it is the quarter that
|
||
is visibly wrong.** That is the difference between "one loose end" and "the open
|
||
ask in `BLOCKED.md`".
|
||
|
||
📌 A smaller observation, offered as one: *"every button"* in the summary row is a
|
||
**class** generalisation, in a document whose own warning is to read the table as
|
||
per-element facts. No button appears in the EXTRAS draw log at all — the
|
||
generalisation comes from `ptbtn01f` on the main menu. It is very likely right, and
|
||
the port depends on nothing that would break if it were not; I raise it only
|
||
because it is the same move the document tells its reader not to make.
|
||
|
||
## EXTRAS is complete: 1.97 → 0.63, and the two metrics disagree about it
|
||
|
||
The four elements I reported as absent from every draw were in a draw all along.
|
||
The vertex dump was capped at 8 vertices — two quads — and the additive batch holds
|
||
six, so the log printed `pteff20` and `ptframe3` and dropped the other four **with
|
||
a well-formed line and no ellipsis**. Cap raised, screen recaptured, all six named.
|
||
📌 A truncation that leaves no mark is the same failure class as my MIX-default
|
||
material: the run completed, the output parsed, and the answer was wrong.
|
||
|
||
`ptframe4`, `pteff21`, `pteff22`, `pteff23` and **`pteff10`** are additive on
|
||
EXTRAS. Transcribed.
|
||
|
||
| EXTRAS element | before | after |
|
||
|---|---|---|
|
||
| **`ptframe4`** | 31.90 (16.19×) | **1.14** (1.81×) |
|
||
| `pteff21` / `22` / `23` | 14.34 / 13.15 / 12.04 | 0.79 / 0.72 / 0.73 |
|
||
| `ptframe3` | 7.97 | 1.26 |
|
||
| **whole screen** | **1.97** | **0.63** |
|
||
|
||
### 🟡 Their `pteff10` flag, tested — and the answer is "both metrics, opposite ways"
|
||
|
||
They flagged it before I could adopt it: *"you measure it nearly exact under
|
||
alpha-over and the game draws it additive… it is the one row your renderer does not
|
||
independently corroborate."* Tested by holding `pteff10` at alpha-over with every
|
||
other element additive:
|
||
|
||
| | tone-corrected \|resid\| | raw RMSE |
|
||
|---|---|---|
|
||
| `pteff10` **additive** (as measured) | **0.630** | 12.91 |
|
||
| `pteff10` alpha-over (as the port had it) | 1.704 | **9.98** |
|
||
|
||
🔴 **They disagree, and the reason is not subtle.** The port already renders
|
||
**+8.50 levels brighter than the capture** on this screen *before any of this*
|
||
(render 35.16 against capture 26.65); additive adds 3.85 more. Raw RMSE is
|
||
dominated by that pre-existing offset, so it punishes any added light regardless of
|
||
whether the light is correct. The tone-corrected number, which removes exactly that
|
||
offset, says additive is **2.7× better** — and `verify-capture`'s differing region
|
||
halved, 0.07 % → 0.03 %.
|
||
|
||
✅ **The measurement wins and I have adopted it**, because it is measured off the
|
||
game and the structural metric agrees. ⚠️ **But `extras` raw-RMSE went 11.43 →
|
||
12.88 and I am not hiding that.** By the tool's own header — *"RMSE is reported and
|
||
is NOT a target… what finds a real defect is the DIFFERING REGION"* — the screen
|
||
improved. The +8.50 offset is a separate, older question and **I am not correcting
|
||
it**: I established weeks ago that I cannot say which side's tone is wrong.
|
||
|
||
## 🔴 Refuted: my kind census was a two-screen generalisation, one message after I criticised theirs
|
||
|
||
I reported *"every sprite decoration is `0x0` and every button `0x3002`"* as an
|
||
independent confirmation. They refuted it: `kind & 0x2` is the focusable flag (0
|
||
violations in 15 493 entries across 24 UI paks), while `kind == 0x3002` catches 778
|
||
of 1 062 focusable elements and **misses 284**.
|
||
|
||
**My own export contained the counter-examples the whole time:**
|
||
|
||
| element | `kind_raw` | what my exporter calls it |
|
||
|---|---|---|
|
||
| `press_start` / **`ptbtn00`** | **`0x73002`** | `unknown` — *not* `button` |
|
||
| `title` / `ptlogoall_eff`, `ptlogoall_eff2` | **`0x3000`** | `unknown` — looks like a button, is not focusable |
|
||
| `title` / `ptlogo1`, `ptlogo2` | `0x4` | `unknown` |
|
||
|
||
📌 **This is exactly the move I had just objected to in their page** — *"every
|
||
button" is a class generalisation in a document that tells its reader not to make
|
||
them* — and I made mine one message later, from two screens, and called it a
|
||
confirmation. The census was **true where I looked** and false one build over.
|
||
|
||
⚠️ **It has a consequence, not just a lesson.** `ptbtn00` on the PRESS Ⓐ plate is
|
||
focusable by their rule and my exporter classifies it `unknown`. Filed.
|
||
|
||
## 🔴 Refuted: the sweeps DO run on the menu, and my instrument was measuring my own renderer
|
||
|
||
`loop_leaf_on_screens` scopes leaf-looping to the title, partly on a phase sweep of
|
||
mine that I re-ran only last iteration and reported as *strengthened*. Their draw
|
||
log retains NDC, and settles it directly: **both strips overlap the screen in every
|
||
captured frame on the main menu, stepping ~0.03 NDC per frame in opposite
|
||
directions, with their vertex alpha ramping** — two sessions, different phases.
|
||
|
||
🔴 **"The game does not draw them here" is no longer available to me.** What my
|
||
phase sweep actually measured is that *the port's version of the sweeps* makes the
|
||
match worse — which is a statement about my rendering of them, not about whether
|
||
the game runs them. **A best-match argument for an absence cannot distinguish "not
|
||
there" from "there and drawn wrong",** and I had that caveat written down and still
|
||
read the result as being about the game.
|
||
|
||
⚠️ **Not flipped yet, deliberately.** The strips are additive *and* their vertex
|
||
alpha ramps across the sweep; the port has neither of those right for the leaf
|
||
path, so enabling the loop today would make the port more correct in behaviour and
|
||
visibly worse against the capture. That trade needs the ramp first, and it is
|
||
filed. What has changed today is the *claim*: **the port is wrong here, and says
|
||
so.**
|
||
|
||
## The plate's highlight is additive — and my harness poses it at the one phase where it is invisible
|
||
|
||
`blend-bit-vs-oracle.txt` entry 2: `ptbtn00` `0x0110` **alpha-over**, `ptbtn00f`
|
||
`0x0112` **ADDITIVE** — the PRESS Ⓐ plate and its own highlight, same screen, same
|
||
draw order, one bit apart. Entry 4, the whole title, is **alpha-over throughout**,
|
||
including `ptlogo_back2`/`ptlogo_back2eff` — which independently kills the
|
||
*"frame-shaped and mostly transparent ⇒ additive"* rule I declined to adopt.
|
||
|
||
Bands are now per **draw op** rather than per paint-order entry, because one band
|
||
per element cannot express *base alpha-over, its own focus record additive*.
|
||
|
||
### 🔴 It reported ZERO three times, and each zero had a different cause
|
||
|
||
**R2 says a change with a predicted magnitude that delivers nothing is a failed
|
||
run, not a negative result.** This one delivered nothing three times.
|
||
|
||
1. **The mapping never reached the overlay.** `additive_elements` was assigned to
|
||
`view` in three places and to `overlay` in **none** — and the plate *is* an
|
||
overlay. Every other decoded rule on that page is assigned to both.
|
||
2. **I then "proved" the element is never drawn** — suppressed its sprite at six
|
||
times across the cycle, 0 px every time. ⚠️ **That sweep was invalid.** I varied
|
||
`--time` while passing **`--loop-phase=0` in every run**, and `--loop-phase`
|
||
pins exactly the clock a looping record runs on. Six samples of one phase. I
|
||
was one commit from filing *"the port never draws the plate highlight"* as a
|
||
defect.
|
||
3. **The real reason the harness saw nothing.** Swept properly:
|
||
|
||
| loop phase | `ptbtn00f` contributes |
|
||
|---|---|
|
||
| **0** | **0 px** |
|
||
| 20 / 40 / 60 | 28 197 / 28 830 / 28 821 px |
|
||
| 80 / 100 | 27 334 / 21 885 px |
|
||
|
||
**`verify-capture`'s `title_plate` row poses at `--loop-phase=0`** — the single
|
||
phase where the highlight contributes nothing. The change is live and worth
|
||
**26 319 px** at phase 20, and the row correctly reports 13.03 / 0.09 % unchanged,
|
||
because it is blind to it by construction.
|
||
|
||
📌 That is a defect in my instrument, not in the fix: **the row that validates the
|
||
plate cannot see the plate's pulse.** Now stated in the tool, next to the pose.
|
||
|
||
⚠️ **Not verified against the oracle.** The blend is theirs and measured; that the
|
||
*port* now draws it correctly is not something any capture I hold can confirm,
|
||
because every title-plate capture is at the blind phase. Filed.
|
||
|
||
### The plate's ramp was already in my export, and the renderer applies it correctly
|
||
|
||
The Decoder is paused and could not take the capture I asked for, and pointed out
|
||
that the disc half might not need them. It did not — **and it was already in
|
||
`export/screens/title/press_start.json`.** `ptbtn00f` carries eight keyframes on a
|
||
120-unit loop, alpha in the high byte of `fade_argb`:
|
||
|
||
| t | 0 | 6 | 29 | 35 | 50 | 58 | 97 | 105 |
|
||
|---|---|---|---|---|---|---|---|---|
|
||
| α | **0** | 6 | 74 | **80** | **80** | 74 | 6 | **0** |
|
||
|
||
✅ **Two things follow without an oracle.**
|
||
|
||
**1. The blind phase is confirmed from the disc.** α is *exactly 0* at phase 0 —
|
||
so `verify-capture`'s pose sees nothing, and that is a property of the data, not an
|
||
artefact of how I rendered it.
|
||
|
||
**2. My renderer applies the ramp faithfully.** Rendered contribution against
|
||
declared α across the cycle:
|
||
|
||
| phase | 0 | 20 | 40 | 60 | 80 | 100 |
|
||
|---|---|---|---|---|---|---|
|
||
| declared α | 0 | 47.4 | 80.0 | 70.5 | 35.6 | 3.8 |
|
||
| rendered Δ | 0.00 | 20.70 | 32.55 | 29.24 | 16.55 | 2.59 |
|
||
|
||
**r = +0.9982**, slope 0.4036 levels per α unit. So *when* and *how strongly* the
|
||
port draws the highlight is right; only the **composite** is unverified.
|
||
|
||
📌 **And the capture cannot settle it, under either reading of the clock.** It is
|
||
posed at t=237; 237 mod 120 = **117**, where α ≈ 0 — and the harness independently
|
||
pins `--loop-phase=0`, where α is exactly 0. Both readings agree, which is worth
|
||
stating because I did not have to assume which clock the record runs on.
|
||
|
||
⚠️ So the ask narrows to one number and gets a precise window: **a capture anywhere
|
||
in t mod 120 ∈ [35, 50]**, where α holds at its peak of 80. Everything else about
|
||
the plate is now settled on my side.
|
||
|
||
### Pre-registered: what the plate capture must show, committed before it exists
|
||
|
||
The Decoder's R2 note, and it is the right one to raise: α peaks at **80 of 255**,
|
||
so an additive overlay at 31 % is a small signal and *"whatever residual you
|
||
measure will be small in absolute terms whether or not the blend is right."*
|
||
**So the expected magnitude goes in git before the capture does.**
|
||
|
||
Rendering the same pose at peak α under both composites:
|
||
|
||
| | value |
|
||
|---|---|
|
||
| highlight footprint at peak α | **25 015 px** (2.90 % of frame) |
|
||
| mean \|additive − alpha-over\| **inside** it | **16.92 levels** |
|
||
| max | 50 levels |
|
||
| RMS over the **whole frame** | **3.746** |
|
||
|
||
🔴 **And my first prediction was wrong, which is the useful part.** The two
|
||
composites differ by exactly `α·bg`, and with mean background 94.78 that predicts
|
||
**29.73** levels. Measured: **16.92**. The formula uses the *keyframe* α and
|
||
ignores that **the sprite carries its own per-pixel alpha channel**, which
|
||
modulates it — implied mean sprite α ≈ 145/255. Caught because I wrote the
|
||
prediction down and it disagreed with the render, which is the whole point of
|
||
writing it down.
|
||
|
||
**The pre-registered test.** Additive is brighter than alpha-over at *every*
|
||
footprint pixel, in one direction. So the discriminator is **regional sign over
|
||
25 015 pixels, not per-pixel magnitude** — which matters because the port already
|
||
carries global tone offsets of order 8.5 levels, and a per-pixel magnitude test
|
||
would be swamped by them while a signed regional one is not.
|
||
|
||
> **If the game composites this additively, then rendering it alpha-over must leave
|
||
> the footprint systematically ≈17 levels DARKER than the capture relative to the
|
||
> surrounding frame, and rendering it additive must leave no such regional bias.
|
||
> A capture that shows neither bias refutes both, and points at the ramp or the
|
||
> pose rather than the blend.**
|
||
|
||
⚠️ **Feasibility, stated before asking anyone to spend a run:** 16.92 mean levels
|
||
across 2.9 % of the frame against ~8.5-level systematic offsets is discriminable
|
||
**only** as a signed regional statistic. As a whole-frame RMSE it is **3.746** —
|
||
below the title's own ±5.56 capture-phase term, so `verify-capture`'s headline
|
||
number could not settle this even at peak α.
|
||
|
||
⚠️ Their gate `wait_plate_pulse.py` fires on a green-glyph count in [500, 2500] and
|
||
has logged 740 and 1004, so the window may come free or may sit outside [35, 50]
|
||
entirely. Their caveat, carried across rather than assumed away.
|
||
|
||
### The plate identification is confirmed by size; the frame spacing does NOT fit my ramp
|
||
|
||
Reading the Decoder's existing title draw log from the ref
|
||
(`captures/ui-draws/blend-title-2026-08-31.log`), the additive quad they point at
|
||
is draw 8/19/…, `blend=0x01010101`, per-vertex `col=44FFFFFF` / `43FFFFFF` /
|
||
`38FFFFFF` — α **68, 67, 56**.
|
||
|
||
✅ **Identification confirmed independently, by size.** The quad measures
|
||
**537.6 × 75.6** px and `ptbtn00f.png` ships at **537 × 76**. That is the plate's
|
||
highlight, and it settles it without using their attribution.
|
||
|
||
✅ **And their free check on my ramp holds:** all three alphas are ≤ my declared
|
||
peak of **80**, none equals a keyframe value, so the game interpolates rather than
|
||
steps. Two independent decodes agreeing — mine from the export, theirs from the
|
||
command stream.
|
||
|
||
🔴 **But the frame-to-frame spacing does not fit, and I am recording that rather
|
||
than passing over it.** `HANDOFF` Q1 gives **2 units per rendered frame**. On my
|
||
falling segment (slope −1.744 α/unit) that predicts, from α=68:
|
||
|
||
| | frame 1 | frame 2 | frame 4 |
|
||
|---|---|---|---|
|
||
| observed | 68 | **67** | **56** |
|
||
| my ramp at 2 units/frame | 68 | 64.5 | 57.6 |
|
||
| at 1 unit/frame | 68 | 66.3 | 62.8 |
|
||
| at 0.5 | 68 | 67.2 | 65.4 |
|
||
|
||
**No constant rate fits both steps** — 0.5 matches frame 2 and misses frame 4 by
|
||
9; 2 matches frame 4 and misses frame 2 by 2.5.
|
||
|
||
⚠️ **Three candidates and I cannot separate them:** my ramp is wrong somewhere; the
|
||
log's frame numbering is *captured* frames rather than engine frames (**it skips
|
||
from 2 to 4**, so it is demonstrably not a dense engine sequence); or emulator
|
||
pacing varies between them. The middle one is the most likely and the cheapest to
|
||
check, and it is theirs.
|
||
|
||
📌 This does **not** disturb the ramp's *shape* or *ceiling*, which the alphas
|
||
confirm. It disturbs only the claim that I could pose my renderer at their captured
|
||
instant by stepping units — so the vertex alpha remains the right readout, and
|
||
**inferring the phase from a frame index would have been wrong.**
|
||
|
||
|
||
---
|
||
|
||
## 🔴 A reproduce recipe that names a path off this repo is not a recipe
|
||
|
||
Recorded 2026-09-01, at shutdown, from the Decoder's catch rather than my own.
|
||
|
||
They found their Canary logger change — the **texture content hash** — living
|
||
uncommitted in `/canary`, a checkout pushed nowhere. Two committed findings could
|
||
not be reproduced without it: it is the field that separates *"the buffer
|
||
rotated"* from *"a frame was decoded"*, and its absence is what cost two withdrawn
|
||
positions on `units/second` in a single day. They committed it and exported it
|
||
into the repo as `tools/canary-patches/0001-content-hash-in-ui-draw-logger.patch`.
|
||
|
||
**Their generalisation, which applies here too:** a finding whose reproduce recipe
|
||
names a `/canary` sha — or any path outside this repository — is not reproducible.
|
||
|
||
Audited on this side at the same moment. One real instance: this file cited
|
||
`/reborn/docs/re/captures/main-menu-oracle.png` as the evidence for a decision.
|
||
**The capture is in the repository** at `docs/re/captures/main-menu-oracle.png`;
|
||
only the path was stale, left by the monorepo move. Fixed above. The other
|
||
`/reborn` mentions are deliberate — one warns that the mount is empty, one is a
|
||
table row listing the pattern as an example of what not to do.
|
||
|
||
⚠️ `tools/port/check-citations` does **not** catch this class. It scans for repo
|
||
paths that fail to resolve; an *absolute* path is not a repo path and slips
|
||
through. Extending it to flag off-repo absolutes — especially ones whose basename
|
||
does resolve in-repo, which is the recoverable case — is the obvious next
|
||
increment and is **not** done here, because it was found minutes before a
|
||
shutdown and a hasty checker is worse than a named gap.
|