94d761ebff02c61bded367e2b3acfb788f1ceaa6
59 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
94d761ebff |
port: the (A) snap RESTARTS the leaves at t=0, not just the shared clock
Measured by the Decoder with the full-quad reader: at the snap frame both sweeps enter at their DECLARED OPENING alphas -- pteff03 at 255, pteff03a at 0 rising 1,2,3,4,6,11,17 from x=1721. Those are the leaves' own t=0 poses. My implementation advanced the shared clock and left the leaf phase untouched, so the sweeps would have sat mid-travel at the right clock value and the wrong position. A defect visible only in a film -- no still and no clock reading would show it, because the clock was correct. The field already existed (leaf_start_units, from the F6 work); it simply was not being set at the snap. VERIFIED, pre-registered, via --probe-leaf on a real boot with a real press: snap frame -> leaf_t 0.0, pteff03 x=-639, pteff03a x=1721 (declared t=0) next frame -> leaf_t 0.5, both advanced by 2 px Their separate note on my snap target: 236.0 survives their challenge -- at the snap the sweeps are at alpha 255, putting the title clock in [100,238) -- but their data cannot separate 236 from 200. Recorded as surviving, not confirmed; the bound in the code comment already says so. |
||
|
|
2b802e06b6 |
port: F5 -- (A) during the title build-in snaps the ONE shared clock to the settle
Measured by the Decoder: the plate reaches alpha 255 in a single frame against ~11 frames of ramp with no input, and the sweep enters at 255 with no ramp where an untouched run obeys its parent's declared t=70..100 gate. A cut, not an acceleration. It advances the ONE SHARED CLOCK, so authored/flow.json's clock: 'shared' stands. 🔴 I IMPLEMENTED THE OPPOSITE FIRST AND REVERTED IT. Their initial report had the artwork animating across the press, which would mean two clocks and an overlay-only jump; I built that (_plate_offset). It rested on a 5-frame window that sat entirely inside the 11-12 frame lag between a scripted press and its effect -- it compared frames where the input had not been acted on yet. A wider pre-registered test refuted it: three elements mid-fade vanish in one frame. 🔴 AND MY FIRST VERSION NEVER FIRED, SILENTLY. It gated on overlay.settle_instant, which is -1 for press_start: that screen declares a 22-unit settle window against SETTLE_WINDOW_MIN=30. The boot has always printed 'settles at t=236' from settle_time(), a DIFFERENT quantity, and I took the printed number as evidence for the field I was testing. Caught only by filming and seeing overlay-view stay flat at 0.0 across the press. ⚠️ The target is BOUNDED, not pinned: the capture puts it inside [100,238] -- past the sweep's gate at 100, not past 250 or ptloop01's exit ramp would return the sweep to alpha 0. settle_time()=236 sits inside that and is where the plate reaches full alpha. New diagnostic --press-at=S[,S...] presses (A) at wall-clock moments anywhere in a boot; --skip-at lives inside the movie branch and fires once, so it cannot reach the title. Marked fragile by construction. VERIFIED on a filmed boot: shared clock jumps 109.4 -> 236.0 and view_units and overlay_units move together across the press. |
||
|
|
af10a2ec3c |
port: F6 -- adopt the sweep onset as a measured RATIO, and fix the guard that disabled it
Onset: 0.500 of (title first visible element -> plate onset), measured by the
Decoder as 0.489 and 0.507 across two independent captures, 3.7% apart. A RATIO,
which is the point -- it needs no clock, and every unit-valued figure from those
captures has been withdrawn: the rate because frames are presents (1168 vs 600
for the same animation), the '+40 units' because its conversion put
title-start->plate at 75 units where the declared data puts the plate at 238, a
3.2x conflict that is still open and is F4's.
Resolved against THIS export: 0.500 x (214 - 0) = 107.0 units.
tools/port/check-leaf-onset recomputes it and fails if a re-timing moves the
anchors; it has a selftest in both directions and is in check-all.
🔴 AND THE ADOPTION WAS A NO-OP UNTIL THIS COMMIT. leaf_clock() read
'if start < 0.0 OR rate <= 0.0: return screen_units', so when the withdrawn rate
went back to null the adopted OFFSET stopped applying too -- silently, while
authored/rendering.json still stated it. The two fields are independent now.
It was caught only because the offset was re-verified by PROBING THE RENDERER
instead of re-reading the file I had just edited. Both my earlier verifications
of this feature passed while it did nothing: one compared frames that were all
being forced to the same pose, the other ran when both fields happened to be set.
Verified, pre-registered before running, via --probe-leaf:
title u=236 -> leaf_t 129.0, x -123 (predicted 129, -123)
title u=400 -> leaf_t 293.0, x 533 (predicted 293, 533)
Effect: at the plate's arrival the sweep sits at x=-123, just entering the frame,
where before it was at x=305, well across it.
|
||
|
|
4d533185e7 |
port: leaf clock mechanism -- origin and rate, both UNSET
F6 is a clock question, not an effect question: the human confirms the light animation itself looks like the game's and only starts earlier, and separately that the port may be running it faster (flagged by them as an impression). The port passed time_units straight to the leaf, which is an assumption -- offset 0, rate 1 -- that nobody measured and that the Decoder's capture contradicts: the game's leaf t=0 is its first drawn frame, 40 frames after the title's first element, at a rate measuring well below the title's. ScreenView.leaf_clock(screen_units) applies an origin and a rate; both default to -1.0 meaning unmeasured, in which case it is the identity and behaviour is unchanged. Fed from authored/rendering.json leaf_clock per screen, currently null with the provenance recorded. No placeholder values, per F1: an invented constant here is indistinguishable from a measured one later. Verified inert: title rendered at t=2/3/4 s, 0 differing pixels against captures taken before the change. Also corrected in the process: my earlier 'the sweep enters the viewport at t=61' used the UNROTATED sprite width. The leaf carries a 30 deg rest rotation, so its AABB is 886 px against a 399 px sprite -- matching the Decoder's measured ~890 to within 4 px. Port and game both put the quad on screen at leaf t~0, so the whole discrepancy is the leaf clock's origin and rate. |
||
|
|
937f05595c |
port: the menu repeat mechanism, with NO rate -- deliberately inert
F1: the human watched the real game and it repeats on a held direction, on the stick AND (confirmed separately) the d-pad. gamepad.gd had predicted this exact refutation in its own words, so one-step-per-deflection stops being the conservative reading and becomes a known defect. Mechanism: Gamepad.held_direction() polls the DEVICES -- not Input.is_action_pressed, because ui_up/ui_down sit on the stick at Godot's 0.50 deadzone while the port steps at the game's measured 0.61, so polling the action would repeat through the exact band ENTER exists to exclude. Boot._menu_repeat() re-applies the same guards a real press gets, rather than sharing them, because a second input path is where this port's defects hide. The RATE is NOT shipped, on instruction: an invented interval is indistinguishable from a measured one later. An earlier draft of this change had 0.40/0.20 with a why attached; that was the named failure mode and the numbers are removed, not commented out. repeat_due() returns 0 until both are set. Flagged at the adoption site: verify-input's 'a held stick is ONE step, not six' asserts the ABSENCE of this feature and will go red when a rate lands -- for the right reason, and looking exactly like the jitter bug returning. |
||
|
|
08ed3dd17e |
port: the splash never animated -- pose_at ASSIGNED the settle instant instead of clamping to it
The 2026-09-02 play-test: "the logos just switch, I cannot discern any animation
at all." Reproduced, diagnosed, fixed, and gated by a film.
REPRODUCED FIRST, as instructed. tools/motion-census needed Pillow, which this
container has no pip for, so it got an ImageMagick fallback that shims only the
four Pillow calls it uses -- the census arithmetic, the MOVED floor and the GRID
are untouched. Its --selftest passes on that backend with the human's own
numbers: fade 97.4 %, switch 2.6 %, frozen 0.0 %. A shim that distorted pixels
would fail its own control.
publisher 0.30 s moving then 3.30 s FROZEN (human: 0.30 then 3.20)
developer 0.40 + 0.25 split then 2.45 FROZEN (human: 0.35 + 0.25 then 2.40)
Matched to within a frame.
THE CLOCK WAS NEVER THE PROBLEM. view_units advances 2.8-3.0 per frame, smooth,
~60 units/s, no stalls -- the play-test's candidate list can drop "the group
clock not integrating" and "advancing by keyframe index".
THE POSE WAS. Measuring the sharp logo's own rect frame by frame: 0.40549 flat
from unit 7.9 through 28.2 -- the same value it holds at 45 and beyond. It was
already FULL before its declared ramp (15 -> 30) began.
Cause, in ScreenView.pose_at:
t = settle_instant if settle_instant >= 0.0 else minf(t, settle_units(element))
The comment above it says "stop at the hold". The else-branch clamps. This half
ASSIGNS, so from a screen's first frame every element was posed at the settled
instant and no build-in was ever drawn. The asymmetry is the whole defect, and
`--time` sets `frozen`, which skips the clamp -- which is exactly why my frozen
sweep "proved" the companions were drawn and proved nothing about running.
Fix: `minf(t, settle_instant)`. One operator.
⚠️ AND THE HOLD IS NOT THE BUG. The Decoder measured the game holding one picture
for 3.34 s on this screen -- LONGER than the port's 3.30 -- because palogo_sqex
declares 205 of its 255 units as a flat plateau. The play-test's "a fade does not
hold one picture for 3.20 s" would have sent me to delete the one correct part.
Clamping keeps the plateau exactly.
GATED BY A FILM, not a still:
before after game (Decoder)
publisher build-in 0.30 s 0.60 s
developer build-in 0.40+0.25 0.95 s continuous
splash moving 12.0 % 24.8 % 21.2 % / 27.8 %
distinct luma states 120 152
longest frozen 3.30 s 3.30 s 3.34 s
🔴 AND IT LOOKED LIKE A 10x REGRESSION AGAINST THE ORACLE, WHICH IT WAS NOT.
verify-capture went publisher 2.17 -> 22.58, title 14.11 -> 67.07. Cause: it
shoots two frames after load and got the settled pose ONLY because pose_at
assigned it. Its own comment says so -- "the 0.01 % agreements on both splashes
were measured through that accident."
So the `--screen --capture` path now advances the clock to the settle instant
explicitly before shooting, which is what the tool was always asking for. Guarded
on `not _frozen`: `--time` means the caller wants THAT instant, and overriding it
would reintroduce the silent-ignore this replaces.
Every oracle row is back to its pre-fix value to the digit: publisher 2.17
(0.01 %), developer 3.05 (0.01 %), title 14.11, main_menu 13.02, extras 13.10,
title_plate 13.04. The port animates AND still matches the settled captures.
Not settled: motion-census is not yet wired into check-all -- next, and
deliberately not rushed at the end of a long iteration.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX
|
||
|
|
975d24def2 |
port: --linger, so a scripted walk can observe what arrives after it settles
Last iteration I inferred the returned plate's fade from code-path identity because the harness could not watch it: `--script` quits the moment the last step settles, and the plate arrives on its declared ramp ~3.6 s later. A film of `--script=cancel` stopped at 168 units and never reached 214. `--linger=SECONDS` holds the run open past the walk. `_script_settled` still waits for a hold before shooting -- a shot taken mid-fade is a photograph of a fade -- this only changes what happens after the shot, which was "exit". Pre-registered: the returned plate sits at its floor until 214 units, then rises to full by 236, the same ramp the boot shows. Measured: units=208.75 0.145404 floor units=216.58 0.158596 units=224.41 0.179036 units=232.25 0.203050 units=240.08 0.215005 full -- boot path gives 0.2142 at t=236 So the return fades, identically to the boot, and it is now OBSERVED rather than argued from the branch it takes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX |
||
|
|
1c2782247d |
port: the plate POPPED on a return to the title where the boot fades it
The Decoder warned that my port "presumably models one title". Checking that found a real behavioural split, and the code was right where I expected it wrong and wrong where I did not. My prediction FAILED first: I expected the port to be inventing a plate on a (B)-reached title, because flow.json's scope_why says that is deliberately not claimed. It is not inventing -- the call site cites a measurement from 2026-08-30, the plate IS re-drawn after (B). scope_why was the stale thing, and is corrected. 🔴 BUT THE TWO PATHS DIFFER, AND A `why` CLAIMED THEY DO NOT. That call site says "the plate re-appears by the SAME path, with the same shared clock, as it does on boot. Whatever the boot does, the return does." Filmed: before: el=1.39 view_u=0.00 overlay_u= 0.00 plate absent el=1.53 view_u=8.67 overlay_u=244.67 plate present The overlay clock jumped 0 -> 244.67 in ONE frame. The plate POPPED, where the boot fades it across its declared 214->236. Cause: `_overlay_process` detected "static diagnostic mode" as `_sequence.is_empty()`, and `_sequence` is populated only by `--boot`. So `--menu` matched it too and the menu's return took the `--screen --overlay` diagnostic branch, which poses the overlay at settle_time() by design. A proxy for one mode that silently caught another. Fixed by gating on the flag itself -- `_static_overlay`, set only by `--overlay=` without `--boot`. Verified both directions: diagnostic still poses: --screen=title --overlay=press_start --time=4 -> "overlay press_start at t = 240.00 units, drew 2" return now shares the clock: overlay_u == view_u on every filmed frame, plate at its 0.1377 floor through 168 units boot path unchanged: plate reaches full alpha at t=236, boot completes 10.46 s ⚠️ WHAT I DID NOT OBSERVE, stated rather than glossed: the plate actually RISING on the return path. `--script` quits when the walk settles, so the film stops at ~168 units and never reaches 214. The rise is established on the BOOT path (measured earlier: 0.1457 at 210 u, 0.2142 at 236 u) and the return now provably takes that same branch with an identical clock -- but the final rise on this path is inferred from path identity, not filmed. ⚠️ And whether the GAME fades the returned plate is still unmeasured. The 7.3 s between (B) and the pulse returning is consistent with a transition plus the declared fade, but that is consistency, not a measurement of the ramp on this path. Recorded in scope_why. Not settled: finding 3, no surviving cause; the clock origin, which the Decoder reports blocked -- no capture contains the title, because it sits on the far side of a 137.7 s movie and the runs were too short; the ~1.0-1.2 menu residual. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX |
||
|
|
70799488fe |
port: delete the authored blend map for the decoded field, and find a counter-example doing it
PORT-MISSION §3: "When the RE agent later decodes something you had authored, delete the authored entry and let the exporter emit it. That deletion is the measure of progress." This is that deletion. authored/rendering.json's `additive_elements` -- a per-screen list transcribed from the Decoder's per-draw RB_BLENDCONTROL0 log -- is gone. The exporter emits `blend_additive` per element and per nested focus/leaf element from `T8aD +0x04` bit 0x02, and ScreenView reads it there. Both accessor spellings are needed: `ptbtn00f.t32` is in build.sprites while no element carries it as `sprite`, and it is the sharp case -- the plate alpha-over, its own glow additive, adjacent draws on one screen. CHECKED BEFORE THE SWAP, and the map turned out to be a SUBSET, not the answer: 15 elements it called additive the disc agrees with, ZERO contradictions, and 17 MORE the disc marks that it did not. Those include the sweep LEAVES (draw_leaf_for means pteff03/pteff03a are what reach the screen while the map listed their parents) and TWELVE on `title`, where the map was deliberately empty -- so the port has been drawing every title effect with the wrong blend. H6 closes with no capture at all: the JP asymmetry was an artefact of a NAME-KEYED map, and the bit is on the disc for every screen at once. 🔴 AND IT INTRODUCED A REGRESSION, WHICH IS REPORTED, NOT HIDDEN. Against the oracle captures on the GPU: main_menu 10.88 -> 13.02, main_menu_options 11.56 -> 13.57. Deterministic to the digit over three runs, so not sampling noise. Isolated to ONE element, with a control: - main_menu's only newly-additive top-level element is pteff10; - extras gained none and did not move -- the same change on a screen with nothing new moves nothing; - the leaf rule was disabled separately and main_menu stayed at 13.02, so pteff03/pteff03a are NOT the cause. That prediction of mine failed; the rule is restored, being provably neutral here; - title did not move despite twelve newly-additive elements, consistent with verify-capture posing at settle t=198 where those quads are transparent. That is a potential COUNTER-EXAMPLE to a ✅ DECODED claim, and it is a sharp question rather than a guess: their own map lists pteff10 additive on `extras` and not on `main_menu`, and they logged BOTH screens. Asked in BLOCKED.md H6. Shipped anyway, for reasons stated rather than assumed: +2.14 is inside the harness's own ±3.78 capture-phase term for that screen and cannot adjudicate a disc fact; the decoded source is far better evidenced (35 elements, zero errors, out-of-sample prediction 3 of 16); and fitting an exception for one element would put an authored entry back to make one number smaller, which is the move this project keeps having to undo. It is a KNOWN regression, not an unnoticed one. Also settled this iteration, for the Decoder's open question: the port FADES the plate, it does not pop it. Frozen sweep of the plate region -- 210u 0.1457, 216u 0.1573, 222u 0.1727, 228u 0.1900, 236u 0.2142 -- a clean monotone ramp across the declared 214->236. So t=236 is the port's COMPLETION, not its onset, and the 0.367 s "late plus a pop" reading does not apply. Not settled: whether pteff10 has a counter-example; H1's repeat half; the four red verify-screen rows; and finding 3, which still has no cause now that units/s is settled at 60. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX |
||
|
|
170d255e82 |
port: the port never reported its own frame rate, and the menu draws at 9.7 fps
TEMPORAL-VERIFICATION §1 requires every instrument to state its achieved rate against its requested one. That has been applied to --film (which I fixed for exactly this), to the Decoder's harnesses and to the oracle. It had never once been applied to the thing being shipped. The port had no idea what rate it drew at and no way to say. It matters now because the splashes are the focus and the open complaint is that ours is LESS PRONOUNCED than the game's. A fade drawn in 45 frames and the same fade drawn in 12 are different animations, and nothing here could tell them apart. boot.gd now counts frames per screen and reports at every boot transition, at the end of the boot, and at every menu arrival. `worst gap` sits beside the mean because a hitch is what reads as wrong and a mean hides one by construction. Measured in this container, three boots: publisher_logo 17.3 / 19.6 / 25.0 fps worst gap 100-115 ms developer_logos 16.7 / 21.9 / 22.8 fps worst gap 103-138 ms title 17.2 / 14.2 / 12.7 fps worst gap 150 ms, all three main_menu 9.7 fps worst gap 150 ms 🟡 A LIVE CANDIDATE FOR PLAY-TEST FINDING 4, and the first one that is not dead. The timeline is delta-driven so durations stay correct at any rate; what changes is how many alphas the fade is DRAWN at. At the measured rates the 45-unit build-in gets 12-17 distinct alphas instead of 45, and the pre-blurred companion glow -- the thing that IS the splash's blur -- rises over 15 units and is drawn at FOUR TO SIX steps instead of fifteen. ⚠️ It is a candidate, not a cause: this is llvmpipe under Xvfb and not the human's hardware. The point is that the line now prints on every boot, so the next play-test answers it for free. Every other candidate for finding 4 is already dead -- keyframes vindicated against the vertex stream, companion quads drawn, blend space matching, settled pose at 0.01 % against the capture, no post-process pass to add. ✅ And nothing published is invalidated, which was worth checking rather than assuming: every timing result here comes from `_elapsed` (+= delta) or `time_units` (the same sum scaled), so all are correct at any frame rate. The splash dwells were measured across runs whose rates differed by 2x and agreed to ±0.03 s. Had the timeline been frame-counted, every number in this corpus would have been wrong by a factor that changed between runs -- which is precisely the failure the Decoder found in the emulator's rate and withdrew a finding over. Two defects in the instrument itself, both caught and fixed before it was trusted: - its first version printed "-9223372036854775808 requested". DisplayServer.screen_get_refresh_rate() returns a FLOAT and is -1.0 when the display cannot say, which Xvfb cannot, and %d underflows to INT64_MIN. It now names the cap or says `uncapped`. - it was BOOT-ONLY and said so nowhere -- `--menu` arrives through _menu_arrive, not _advance, so the mode a human spends time in reported nothing. That is the shape this port keeps finding in other people's work, and it lasted one measurement here. Refutation attempt: I checked whether the port re-decodes PNGs per frame, which would have been a real defect. It does not -- _load_textures caches at load_screen. Hypothesis dead, cheaply, and recorded. Not settled: what rate the human's machine manages; whether 9.7 fps on the menu is llvmpipe or something in our draw path; H6's +0x04 exposure; H1 (the Decoder is taking it this iteration). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX |
||
|
|
977965e92d |
port: the splash rate is withdrawn, and timing the shipping boot caught a why describing code we do not have
H7 closed: the Decoder withdrew the per-GamePart rate the same day (
|
||
|
|
6eccfa84d8 |
port: the plate's lateness is the unit, not our clock -- and the splash blur is an asset we already draw
H3, the PRESS (A) plate. Established which half it is, as the brief asked, and
the answer moved once during the iteration -- both readings are recorded because
the first one was confident and wrong.
Eliminated, ours:
rest.t not in the causal path. The plate's ARRIVAL is a declared keyframe
(transparent to t=214, opaque at t=236); rest.t=236 only picks
where `holding` parks it, and 236 is that ramp's own peak.
clock origin the two builds share one clock exactly -- 85 of 85 filmed title
frames have view_units == overlay_units to 3 dp.
NOT eliminated, the Decoder's: the unit->seconds constant. I first ruled it out
"by sign" using the emulator's 28.1 fps presentation rate. That conflates a
wall-clock conversion with units-per-game-frame; the correction is written down
rather than edited away. The Decoder's splash draw capture (
|
||
|
|
b70b638cb4 |
port: Ⓐ was never bound to the pad, and the stick is not an edge
Both found by a human playing the port on a real controller. Both were
invisible to every check this port has, for one reason:
`--script` sends InputEventAction, which BYPASSES the input map.
So the harness asserted every line of code AFTER the map and nothing about the
map itself. Measured on this Godot, not remembered -- the remembered answer was
wrong:
ui_accept key:Enter, key:Kp Enter, key:Space <- no joypad at all
ui_cancel key:Escape <- no joypad at all
ui_up key:Up, JOYBTN:11, JOYAXIS:1- <- d-pad AND left stick
ui_down key:Down, JOYBTN:12, JOYAXIS:1+
Four actions worked on the pad and two did not, which presents as a broken
controller: navigation moved, Ⓐ skipped nothing and opened nothing. Godot
4.7.2 binds no joypad button to ui_accept or ui_cancel.
Gamepad.bind_missing() ADDS the two buttons to the built-in actions rather than
redefining them in project.godot, which would replace the built-ins wholesale
and drop the keyboard bindings silently.
Second defect, same blind spot: an InputEventAction is not an analog axis. The
left stick is bound to axis 1, and an axis is not an edge -- held at deflection
it emits an event per jitter, each reporting the action pressed. That was one
cursor step per jitter ("moves the cursor too fast"). The stick is now latched
to one step per deflection, with hysteresis so a stick resting near the
threshold does not chatter.
AUTHORED, and deliberately the conservative half: whether the game REPEATS a
held direction, and how fast, is an oracle question. One deflection one step
cannot run away and invents no rate. Logged as BLOCKED H1.
tools/port/verify-input asserts the map and the latch, with a control that
removes each check's OWN subject -- its first version inverted all nine
assertions when only two depended on the fixup, and reported seven correct
checks as broken. Three rows say plainly they are not controllable (they assert
Godot's own bindings) and one is a negative carrying a positive control (R4),
rather than faking an inversion for either.
Also logged BLOCKED H2, unguessed: the splash blur/fade-in is more pronounced
in the game than in the port. The port applies no blur at all. Noted there that
the two splashes are the only screens reaching the rest() plateau-less
fallback, which the R1 pass just re-opened in both directions.
|
||
|
|
d45b23ebbe |
port: draw the plate's highlight additive, and find my harness poses it where it cannot be seen
blend-bit-vs-oracle.txt entry 2: ptbtn00 alpha-over, ptbtn00f ADDITIVE -- the PRESS (A) plate and its own highlight, 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 means additive" rule I declined to adopt. Bands are now per DRAW OP rather than per paint-order entry: one band per element cannot express base alpha-over with its own focus record additive. The change reported zero three times and each zero had a different cause. First, 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 goes to both. Second, I then measured that the element is never drawn, suppressing its sprite at six times across the cycle for 0 px every time, and was one commit from filing "the port never draws the plate highlight" as a defect. 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. Third, swept properly, ptbtn00f contributes 0 px at phase 0 and 22-29k px at phases 20-100 -- and verify-capture's title_plate row poses at loop-phase 0. The row that validates the plate is blind to the plate's pulse by construction. It correctly reports 13.03 / 0.09 % unchanged while the fix moves 26 319 px at phase 20. Stated in the tool next to the pose. Not verified against the oracle: every title-plate capture we hold is at the blind phase, so no capture here can confirm the port now draws it right. Asked. |
||
|
|
c453d8dade |
port: draw the measured additive blend -- main_menu 13.21 -> 10.67
The Decoder logged RB_BLENDCONTROL0 per draw in Canary on both screens. 0x01010101 is src=ONE dst=ONE, additive. That makes the blend a transcription rather than my proposal, and they withdrew the "any blend you choose is authored" instruction explicitly. Their control is what licenses the change: one pixel shader, 0xE59B2B3DA4AA9008, runs with BOTH blend states on the main menu -- 12 additive draws and 18 alpha-over. The frames and ptbase share a shader; only the blend register differs. authored/rendering.json gains additive_elements per screen. Every id is a measured draw and the reach is written beside it. verify-capture: main_menu 13.21 -> 10.67 (0.06 % -> 0.02 %), extras 13.38 -> 11.43, main menu with ptbtn04 focused 13.82 -> 11.36. Per element, ptframe1 22.72 -> 4.17 and ptframe2 13.09 -> 3.32. Neutrality control, free with the table: publisher_logo 2.17 and developer_logos 3.05 are unchanged to the digit. Those are the screens whose metric is absolute and they carry no additive element, so the rewrite that routed every draw through RenderingServer canvas items did not change the picture. The improvement is the blend, not the plumbing. RenderingServer rather than child Node2Ds because boot.gd calls view.queue_redraw() from nine places and none reaches a child node -- bands would paint the previous pose, which under --script=wait is a plausible wrong capture rather than an error. Runs are recomputed per frame: the additive elements are consecutive on both measured screens, and that is an accident of those two screens. And the change first ran with the material left at its default MIX, moving ptframe1 from 22.72 to 22.69. Nothing errored and a 0.03 move is a plausible negative result. It was caught only because the measurement predicted a large move. Not done: ptframe4 is now the worst element on EXTRAS at 16.19x the frame mean and additive would plainly help it. It is not in the measured table, so it is not in the file. Filed in BLOCKED.md with pteff21/22/23, which are also in no captured draw. Refuted, mine: "neither frame has a fully-opaque pixel" was true and was not the discriminator -- pteff10 has max alpha 130, no opaque pixel, and measures nearly exact. The direction survived; the reason for it did not. |
||
|
|
2759f3e719 |
port: 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. video.rs asserted '28 % of S00A's frames presented and 47 % of ADV's' as measured; boot.gd asserted that the same numbers 'refuted the claim outright'; dialog_rows.rs said 'by three routes'. All three were retracted days ago in the log and never in the code -- the percentages came from contended runs and the counter is an upper bound that goes vacuous once the engine outruns the stream, and three routes became two, one compound. verify-transcode-fidelity was the only one already correct. Third time this 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. First was loop_why shipping a refuted story into manifest.json, second a BLOCKED row, this is code comments -- 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 carrying the propositions they asserted, and check-claims immediately failed on my own corrections quoting them unmarked. The next stale number of this kind fails a run instead of waiting for a sweep. What it does not cover is a docstring number that was never corrected anywhere. The register holds only what I have already retracted, so it catches propagation failures rather than wrong numbers -- their ring_row.py case would still have gone undetected here, because nothing had retracted that calibration. Their closing observation is the honest limit: 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. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
a3ec9d03fa |
port: a mistyped mod override was silent -- the liveness defect, in the product
Every checker fix this week was about a tool that could not tell 'I checked and it was fine' from 'I checked nothing'. The port 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 and one in a mistyped directory: the correct one is announced and the typo produces NO OUTPUT AT ALL. The modder sees the port load, run, and say nothing about the file that did nothing -- MODDING rule 4's own failure mode, since base-and-overrides is only usable if an override that misses says so. ExportTree.unused_mods() and a report at run end now list them. Controlled both directions: one inert file with the typo present, silent with it removed. Getting the category right took three tries and that is the point. v1 'never used' flagged data/mods/README.md on every run, and a report with a standing false positive is one nobody reads -- precisely the failure it exists to fix. v2 'no such path in the export' was correct and still flagged the README. v3 excludes by extension with the rule checked rather than assumed: the export tree contains only png, json, ogg, ogv and cmd, verified zero .md anywhere, so a .md in data/mods could never be an override by construction. The report also separates what v1 conflated: a file whose path exists in the export but was not read this run is NOT listed. Every line printed is an override that can never apply, whatever the run does. 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 rejects it. Their P3 delivery is taken at the strength given: Q6's count-match has disc support for its structure -- every button record across all 16 GP_TITLE entries is ptbtn00, ptbtn01-05, ptbtn11-13 -- but it does not show that event 3 is a particular row, and they said not to author from it. flow.json already binds buttons by measured screen rather than event index, so nothing changes. Their own negative is narrower than 'not found': the DIFFICULTY search assumed four items pair with f variants, so what is established is 'not an 8-record btn-named build anywhere'. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
1e8c457a8b |
port: make the frame count permanent, then correct what I read from it twice
The Decoder's closing point -- the inference is cheap and the measurement looks expensive right up until someone does it -- is actionable, so the probe I reverted is now permanent. The exporter records each transcode's duration and frame rate in the manifest (probed from the file it wrote, not the source), and every video run prints what it showed against what the media holds. An instrument that has to be added before the question can be asked will not be there the next time somebody reasons instead. Then the instrument corrected me twice more. It is an UPPER BOUND, not a count. It counts engine frames, and the engine renders the UI at its own rate: on a quiet box ADV drew 6480 frames across a 4123-frame video, 44 fps against the media's 30. Above that crossover it constrains nothing, and '157% presented' is the counter used outside its range. The report now says so instead of printing a percentage. So 'the player skips, heavily' is not supported. At 8.3 engine fps under contention S00A could not have shown more than 28% -- a valid bound under contention and nothing more. Quiet, the bound is 88-90%, permitting anything from no drops to a tenth. And the 720p-versus-432p contrast is refuted -- the finding I sent them twice. I reported ADV +6.7% against S00A -0.5% and built 'heavy decode falls behind, light 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 schedule. I was measuring which run happened to share the box and reading it as a property of the resolution. What survives is sturdier than either: playback runs +6.7%...+6.9% long on this container, five runs, both videos, quiet, resolution-independent. Three corrections in three iterations, all mine, all the same shape: argued from an absence; measured and over-read; then found the measurement was taken under a confound I introduced myself by running the suite alongside it. Their rule needs a companion -- ask what the quantity can be skipped by, and ask what else was running. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
7df386e07d |
port: running it as a player finds two defects reading it did not
--boot --script= parsed, was stored, and did nothing. The script only starts at _menu_enter, and a --boot run without --play never enters a menu -- it holds on the title and quits. The run completed, exit 0, no menu line, no press: a clean result to a question never asked. This file already warns about that exact shape 600 lines above the bug, where --capture used to photograph the first frame of a scripted run. The warning was written, kept, and did not stop the same class recurring in the neighbouring flag. Now push_errors and exits 2, naming both working forms, refusing rather than implying --play since the two runs differ by 157 s of intro. Verified: --boot --play --script walks power-on through splashes, ADV, title, (A), main menu, down, (A). A comment above audio.play_bed described the port as CHOOSING the menu track, which HANDOFF Q10 refuted a week ago -- BGM_103 is measured on three independent legs and audio.json says so. Third instance of the drifted-comment trap. The dead phrase is now a check-claims register row, controlled: a planted revival fails and removing it passes. And the boot's wall-clock seconds are a property of this container. ADV takes 146.6 s of wall clock for 137.44 s of media, +6.7%, while S00A runs real time at -0.4%. Not a post-roll and not a general deficit: ADV is 1280x720 and S00A is 768x432, this box has no GPU, and 720p Theora decodes below real time here. The transcode is faithful against a 137.71 s source and the exporter does not rescale. P3/P7 artifacts quote seconds containing that deficit -- reproducible here, not a statement about the port or the game. Comparisons with the Decoder's measurements must go through media length, not wall clock; they carry an explicit emulator pacing factor for the same reason and I had been quoting mine as exact. Their negative result on LOAD GAME, TUTORIAL and OPTIONS leaves guard_focus_scope right to count them UNMEASURED rather than 'resets'. The transferable part is their instrument story: a narrow calibrated reader failed, so they generalised to a whole-frame comparison, which died the moment a crash dialog overlaid the frame while the narrow reader kept working. contract-check is deliberately narrow, individually anchored checks for the same reason, and the temptation after an ANCHOR LOST will be to loosen the matching -- trading a failure I can see for one I cannot. Every asserting check passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
6e3347a338 |
port: the main menu remembers its cursor -- a measured P5 defect, fixed and scoped
Measured by the Decoder today: (B) from the menu to the title and (A) back returns to the item you left, not to a default; their control passed first, two delivery-confirmed DOWNs moving the cursor exactly two items before the round trip. The port reset to initial_focus on every entry, so a player who moved to EXTRAS, pressed (B) then (A) 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 (B)'s restore -- and a memory updated at only one of them is right until the player uses the other. focus_persists is true on main_menu and nowhere else, and the scope is the authored part. wrap generalised because it was measured on two screens; this was measured on one. Here that is stronger than a preference: extras opens on MISSION SELECT as a MEASURED initial focus, so a menu-wide memory would have silently replaced a measured value with a derived one. Both halves are in one artifact, because a one-sided test passes a port that quietly generalised: the menu returns to ptbtn05 after the round trip, and extras opens on ptbtn11 both times despite being left on ptbtn12. contract-check asserts the pair -- on where measured, off elsewhere -- and fails its known negative. Eleven checks. Not assumed: whether the memory survives a reboot, or whether any other screen has it. Their reach is one boot, one round trip, one direction. The finding also reframes this morning's initial-focus warning without settling it -- if focus persists, a reading not taken on a fresh boot's first entry is measuring history. NEW GAME stays authored, on its own reasoning. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
3a9d0223f3 |
port: test the half-guard they named -- it found a real gap on first use
They flagged that my pose line reports the pins from the variables in force, never checked against a pin set but not reaching the view. I had recorded the same doubt and not acted on it. The case is the overlay: a second ScreenView with its own pins, while the announcement read view.* only -- and the plate carries a looping focus record, the clock in question, drawing from overlay.*. Extended the line to report the overlay's pins, and its first use printed overlay(loop-phase=0.0, leaf=free): overlay.loop_phase_units was wired and overlay.leaf_time_units was not. A run requesting both had one pin 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. Their half-guard precisely. Currently inert -- press_start carries no leaf, so the render is byte-identical before and after. The gap was real, live for any overlay carrying a leaf, and cost nothing today. Fourth instance of their remedy of putting the qualifier in the text rather than the reader's memory, and it caught something within a minute of existing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
c1092e25b5 |
port: verify every documented invocation -- one runs forever and did not say so
Flagged the --boot family as unverified three iterations running, deferring each
time on cost. Done: --boot terminates at 156 s on title+plate; --skip-at=1 puts
the title at 7.80 s against 152.54, so the skip is real and quantified;
--film with --film-interval=0.5 writes 375 frames; --play hands over with 'menu on
title' at 7.77 s and stays live by design.
And --boot --film= never terminates. The boot-quit branch is gated on _film ==
at line ~499, and a second quit path on the same condition, so a filming run keeps
capturing past the title forever -- measured still filming at 300 s. verify-dwell
wraps it in timeout so the behaviour was known to whoever wrote that tool, 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 twelve lines away. Fixed with the measurement.
The deferral was the mechanism: three times I judged the cost too high and
recorded the judgement honestly, which kept a non-terminating documented
instruction alive for three iterations. 'Too expensive to verify' and 'unverified'
are the same state and only one sounds like a decision.
Also records their correction -- the menu spans {0,1}, so even a per-outgoing-screen
key would not be single-valued, making 'not modelled' more robust; and EXTRAS is
structurally stuck at n=1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
|
||
|
|
9cf04a4fc6 |
port: branches 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. Assertions catch the edit; log lines catch the execution. Two of my own failures were of exactly this shape. --no-hold under --time produced byte-identical renders because --time sets frozen and pose_at tests 'holding and not frozen' -- a request silently overridden reads exactly like one that worked. And I enumerated three free-running clocks, wired two, and a run pinning two of three looked identical to one pinning all three. Both now announce. --no-hold prints INERT with the reason when --time is present, and the pose line carries the effective configuration of all three clocks: 'pose = timeline [frozen, loop-phase=free, leaf=free]' against '[running, loop-phase=0.0, leaf=free]'. The second prevents precisely the failure I shipped -- pinning a subset and reading the result as pinned. Verified the harnesses are unaffected: nothing under tools/port/ parses that line. Also accepts their scope correction: a claim about code needs its ref attached, the same way a number needs what it is a number of. With main 145 behind and both of us on topic branches, 'the code contains X' is underspecified by default, which is how we were both correct about SYLPHEED_KF_TIME_SHIFT simultaneously. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
9e7bca6de9 |
port: three live-but-undocumented flags, and a dead instruction I wrote while fixing them
Their newest class -- the instruction is dead AND the working one is undocumented -- inverts last iteration's sweep. I checked documented->parsed; the reverse is parsed->documented, and it enumerates, so it completes rather than samples. Eighteen flags parsed, fifteen documented, three live and undocumented: --film-interval and --skip-at (used by verify-dwell, in no usage example) and --no-hold, which 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. A capability that exists only in an 11000-line record does not exist to anyone reading the interface. Then I documented it wrong in the same command. I wrote the example as --screen=title --no-hold --time=6 and tested it: the renders are byte-identical because --time sets frozen and pose_at tests 'holding and not frozen', so an explicit instant makes --no-hold inert. Without --time the pair differs by max 253. I wrote a dead instruction inside the commit fixing dead instructions, and it only failed to ship because I ran the example rather than trusting that a parsed flag works -- the gap I had named one iteration earlier. Strongest evidence yet for their ranking: a wrong description costs a reader's belief, a wrong instruction hands them a null result that looks like a finding. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
d725f8e2f8 |
port: the dead-rule grep found two more, and the cause is my correction habit
Their generalisation of my 'untimed' marker -- search for the vocabulary the dead rule needed -- is the cheap version and it works. Swept for the nouns of every rule refuted this session. Two real hits: verify-screen:57 still asserting 'all four are COMPOSITED rather than standalone', the reading withdrawn after they tested it disc-wide at 7.9%; and boot.gd:197 opening with the pre-fix 'no time slot' claim before retracting it. Third and fourth instance after spin_period_units and exit_ramp_units, and in all four the correction sits below the false claim in the same block, with both written by me. The diagnosis is a habit: my corrections are ADDITIVE. I append a CORRECTION block and leave the original standing, which is right for a record and wrong for a statement -- a reader takes the first assertion and the retraction three lines later has already lost. The habit that creates these is the same one I adopted to make corrections honest. Fix: keep quoting the original but demote it grammatically, leading with 'what this used to say'. Both rewritten. Verified comment-only by artifact rather than by reading -- the main_menu render is byte-identical before and after. Also records agreement with their caution: the failed gap+clear rule was rejected, not narrowed to menu transitions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
e005016751 |
port: count the fallbacks instead of inspecting them; black_hold's absence is now audible
The Decoder sharpened the 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: rotation_deg -> 0 fires 0 times in 866 keyframes and 178 rest poses, and ramp is present in authored/. So rotation is read, not invented -- the same conclusion they reached for design size, reachable only by counting. The count exposed one I had waved through twice: black_hold_units defaults to 0.0 and its authored value IS 0, so deleting the entry would be invisible -- same behaviour, no error, and the reasoning in black_hold_why (four measured gaps, why 0 over the better-fitting 4 or 6, the tripwire) silently stops applying. Fixed the same way as exit_ramp_units: fallback is -1.0 and an absent key raises an error naming what was lost. The control is the demonstration: key present 0 errors, key deleted 1 error, and the render byte-identical either way. No output inspection could have detected the deletion. Does not change the value: still 0, still wrong by 4-6 units on three of four measured transitions, still no rule. Only its absence is now audible. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
0be8348a6f |
port: the refuted 24-unit constant was living in a default; make it loud instead
ScreenView.exit_ramp_units defaulted to 24.0 -- the constant HANDOFF ask 2 told this port to author and that it refused, since the file's own ramp is 10 units. The authored entry was deleted as progress when the corrected record layout removed the unknown, and the default plus boot.gd's timing.get(..., 24.0) made that deletion a no-op. Both use sites are unreachable on today's export (866 keyframes, 0 untimed), so the branch is kept for an older export but no longer invents: the default is -1.0 meaning not supplied, and an untimed group now raises an error naming the screen rather than fabricating a duration. My first verification accused the change: main_menu 641941 px and extras 226009 px changed, on a branch that cannot execute and with no error raised. The cause was --screen=X --capture= firing at an uncontrolled instant -- t=9.00 in the earlier run against t=8.00 in the later one, one unit apart mid-build-in. Three runs now are byte-identical, so it is not noise; the instant is stable within a session and moves between them. Re-run with --time=1.0 pinned, old against new is byte-identical on all four screens. Records the harness limitation: --screen=X --capture= cannot be used for before/after comparison on an unsettled screen, which also explains the earlier settle-vs-rest confound. Also corrects my overstatement that other tools call the CLI -- verify-screen is the only one, checked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
8ae0ec2287 |
port: verify-screen was nondeterministic; pin the pulse phase in the harness
Running the full set after the plate fix, 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 fell -- while the reference renderer cannot pulse at all. The port is not the thing that is wrong: the pulse is measured and a thing that pulses does not stop because the screen arrived. ScreenView.loop_phase_units pins it, negative means free-running and stays the default everywhere, and only the harness passes --loop-phase=0. Controlled: pinned, 3 runs identical; free-running, 3 of 4 identical and one different. That 3-of-4 is why it survived -- it looks deterministic most of the time, and without the negative control a no-op flag would have been indistinguishable from a fix. With the phase pinned press_start reads max 1 / over3 0 OK -- the recorded baseline exactly. Fifteen of sixteen rows now match. The sixteenth, title_jp, has genuinely drifted: 155/20498 -> 233/61208, deterministic, on the Godot side, localized to one 350x396 block at (405,74). There is no capture of the Japanese title, so I can say the renderers moved apart but not which moved. Recorded as an ask, not resolved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
b9667c6c9a |
port: the PRESS A plate comes back after B, and it did not here
The Decoder measured that after B from the menu the plate is re-drawn (
|
||
|
|
64b76b0892 |
port: the runtime now says what the voice export is missing, at the moment it plays it
The manifest has carried the gap for weeks and the runtime printed '+ voice ADV' and nothing else. A reader of manifest.json gets a paragraph; a person LISTENING gets clean dialogue and no way to learn a stream is absent. NEW GAME already announces the screens it jumps over; audio had no equivalent. ManifestAudio gains -- one line naming what is KNOWN missing, absent meaning nothing is known rather than nothing is wrong -- and MenuAudio carries it so _play_video can print it. Verified on the boot's ADV and P7's S00A. The first version of the message was FALSE for one of the two assets: it said 'one is a start-truncated stream', which is ADV's story, where S00A's dropped chunks are digitally silent. Caught by reading the output for both, which I nearly skipped because the ADV line was obviously right. Now states the counts and points at the entry's why. A message generated once from a template but true only for the case it was written against is harder to see than a wrong number -- the sentence is well-formed and confident in both places. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
922d93df5b |
port: a static overlay advances instead of freezing; and their pulse floor is unreproducible
--screen=X animates X but froze an overlaid Y -- my earlier fix overshooting, replacing a frozen-too-early overlay with a frozen-at-arrival one. The plate pulse made it visible: oscillating on the boot path, flat here. Now offset, not pinned: the overlay starts at its settle and takes the main view's delta. Static path now pulses 95.85 -> 115.41 against the boot's 95.68 -> 115.52; still frames unaffected and title_plate holds at 0.00%. Both halves were mine a week apart, and the over-correction was invisible until a third change gave it something to be wrong about. Refutation attempt on their pulse floor: 159/714/1520 is NOT reproducible from the published description. My counts on the same capture are 3-5x theirs at every threshold, so their region must be a tighter crop; neither region nor threshold is stated. The RATIO survives robustly -- 1:10.4-10.9 across a wide band, bracketing their 1:9.6 -- so 'steady base plus pulsing glow' stands, which is all the port's implementation rests on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
fdb33a9abc |
port: wire flow.json's dwell, assert the three authored values the port hardcodes
Applying the prior from six prior findings to authored/ itself: five keys had no reader. dwell, ramp, left_right, input_during_transition, stems. dwell is the one that mattered. Its own text says a measured hold goes there and a number placed there did nothing -- and two iterations ago I asked the Decoder for measurements destined for that slot. Wired now, and it stays EMPTY: the splash dwells are declared on the disc and measured to agree. I wired it to the wrong branch first and it did nothing, silently -- holding longer after settle is absorbed because the screen still leaves at exit_time + black_hold. A dwell must delay the departure. Caught only by testing the control: +120 units moves the transition 4.46 -> 6.43 s. ramp, left_right and input_during_transition describe hardcoded behaviour and are written like switches. Rather than invent the missing implementations, they are now asserted against the value the port was built for, naming the file -- which is the distinction left_right's own why claims to make and was not making. The validator is called, not merely defined. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
a758f9b247 |
port: --focus was ignored on the menu path; focus rendering now verified against the oracle
live-main-menu-options-focused.png -- the only capture of a known focus state -- was untestable because --focus= parsed, was stored, and was overwritten by the authored initial focus on every _menu_enter. Every run logged focus ptbtn01 whatever was asked for. Now pushed into the menu model so navigation continues from where it was forced. With it working, each capture picks out exactly one button: ptbtn04 at 0.1355% against 0.70-0.82% for the others on the OPTIONS capture, and ptbtn01 at 0.0705% against 0.72-0.84% on the plain one. 5x and 10x discrimination. First time the port's focus rendering has been checked against the game at all -- the existing main_menu row uses an authored focus and could never have caught a focus error. Records in flow.json that live-main-menu.png shows NEW GAME focused, so the authored initial_focus matches the one frame it can be checked against -- and that this does NOT overturn Q5's measured instability. It stays authored. Adds main_menu_options to verify-capture at 0.13%. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
69e9043b96 |
port: a second capture closes the sweep-geometry question; title+plate matches at 0.00093%
live-title-press-a.png was unused in the corpus. Posed at t=237 -- inside the plate's 8-unit window -- the port matches it at 0.00093%, against 0.0124% for the no-plate capture at leaf phase ~400. Two captures, two different phases, both under 0.013%: a systematic sweep-geometry error would leave a floor in both, so last iteration's caveat is closed. Sweeping the whole screen's instant against capture 1 gives at best 0.148% at t=230 -- 10x worse than the leaf-only fit. So that capture is the screen SETTLED with the sweeps still looping, which is the first independent evidence for the authored loop_leaf decision. Fixes the cause of a flat 1% floor: --screen=X --overlay=Y pushed the raw elapsed clock into the overlay (9 units at capture), so press_start drew nothing -- the flag whose purpose is 'put the plate on the title'. A static overlay now poses at its own arrival; the --boot shared clock is untouched. Adds title_plate to verify-capture at 0.00%, the most sensitive row in it. Its instant is FITTED and labelled as such. Also records that I nearly committed a wrong cause for the overlay bug: I wrote that nothing drives the overlay's clock outside a sequence. It is driven, every frame, from view.time_units. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
96fe0eac51 |
port: add --leaf-time, and the title's residual is the sweep phase (~400 units, not 357.7)
The Decoder's refined sweep fit had never been testable: verify-capture passed it as a whole-screen --time that pose_at discarded, and asking for it honestly poses past the title's group end. --leaf-time separates the leaf's clock from the screen's. Controls: the renderer is deterministic (3 runs bit-identical) and the sweeps move 0.40% of the frame between phases, so the comparison can see them. Sweeping the full 600-unit span gives a sharp basin at 390-415 units (0.0124%) against 0.2532% at t=357.7 -- 20x. So the title's 0.21% residual is the sweep phase, not structure: at the fitted phase it matches the capture as well as the splashes do. NOT adopted: the port loops the leaf freely and re-posing the harness to the fitted value would be tuning until they match. Filed instead, with the question of whether 357.7 and this are even the same quantity. Also verified last iteration's settle-window change was surgical: only press_start and its twin moved, 14 screens unchanged including title's Decoder-confirmed [160,236]. Settle-window ties exist on 4 screens but all sit under the 30-unit bar, so the arbitrary tie-break never reaches the runtime. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
3c71962698 |
port: the PRESS (A) plate could not be drawn at any instant -- four faults, and a misquoted number
1. --time= was silently ignored on any screen with a settle window >= 30 units: pose_at overwrote the requested instant with settle_instant. ScreenView.frozen now marks an explicit instant and skips both clamps. 2. press_start's settle window was [0,214] -- the dead stretch BEFORE the plate exists -- so its settle instant was t=107, where the element is alpha 0. The exporter now rejects intervals in which nothing is visible. title keeps [160,236], the interval the Decoder's draw stream confirmed. 3. My authored looping_focus_records entry for press_start/ptbtn00 drew a dim focus record INSTEAD of the plate's own sprite: max 0 vs max 252.5. Deleted -- an authored guess that overrides a decode with a worse answer is removed. 4. verify-capture passed --time=5.9617 for the title and it was never applied. Every title figure it has printed, including the 0.26% quoted to the Decoder, was measured at the settle instant under a note claiming t=357.7. Both rows now pose by omission and the note matches. title is 0.21% honestly; splashes unchanged at 0.01%. The boot's end artifact now contains the plate (region mean 95.7 vs 33.6). Corrects last iteration's BLOCKED row, which had the entry's effect backwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
09188ccbb2 |
port: fix two capture bugs, and the second was hiding a missing PRESS (A) plate
1. --capture with --script photographed before the first press (t=0.133 s, 10 of 16 elements transparent). Two runs differing by two presses came out bit-identical and I read it as 'runtime focus never changes'. Deferred to the end of the script; verified max 235 and t=82 units. 2. --boot --capture= wrote NO FILE: _finish_boot() is reachable only from the overlay-quit branch, but line 412 quit first because _overlay_spec is cleared when the overlay is raised. Pre-existing, confirmed by stashing. Fixed by also requiring _overlay_quit_at < 0.0. 3. The artifact that now exists shows the boot's end frame is bit-identical to the title alone -- no plate. ptbtn00 is opaque for 8 units (236-244) and the boot captures at 246.54, because it waits for build 4 to finish fading at t=261. Both halves of that are sound and they are incompatible. NOT changed; filed, since what settles it is what the game does after t=244. Defect 3 was invisible while defect 2 existed: a capture flag that writes nothing cannot show a missing element. Also records that runtime focus is FINE -- my contrary reading came from 410 files whose names did not match the flag I passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
fce921d5c9 |
port: add wait:<seconds>, observe the bed's loop, and measure the seam at 3.4 s of silence
The port could not be asked to run for a stated duration -- a bare step is a no-op that returns at settle -- so nothing after the settle point was observable. An 87.7 s bed on a harness whose longest menu run was 7 s. The bed loops at 87.8 s against the track's 87.7 (r=0.947 and 0.885 on a clean bed-only recording): loop: restart behaves exactly as authored. First end-to-end observation of P6 looping. The authored 'audibly wrong at the seam' is confirmed and quantified: 36 consecutive near-silent 50 ms windows, 84.40-87.80 s, about 3.4 s of silence after a fade from RMS 2057 to 431. Recorded in authored/audio.json. It does NOT license trimming, which would still invent a loop point. My first wait: used create_timer and ran 39% long (30 s requested, 41.7 s wall) because an idle scene throttles the delta it counts down on. Now polls Time.get_ticks_msec: +4.6%. Checked before generalising: over a boot the port's clock tracks wall clock within 4%, so animation timing is sound and the earlier splash-dwell agreement stands. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
92369c0776 |
port: the menu bed plays under the cutscene -- announced, deliberately not fixed
MenuAudio.stop_bed() exists and is called from nowhere, so the bed started on the main menu runs through S00A and loops on past it, putting two unrelated music tracks on the bus at once. Established from the source and authored data, not from measurement. NOT silenced: MISSION says leave an unmeasured detail plainly wrong rather than plausibly invented, and music over a cutscene is caught by any listener in a second where ducking would sound right and be a guess. _play_video announces it instead, and stop_bed is kept as the one line to change. Also records that the envelope correlator is unreliable for music under music -- 0.15-0.42 for every candidate, peaks moving with window and template. I was drafting '46 s of unexplained audio' when the cause was the authored loop: restart. A margin needs a control at the SAME SNR. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
e9073750ac |
port: make ScreenView say what it could not draw; refute 'first-declared paints first'
skipped[] has been tracked and read by nobody since P1, under a comment saying a silently missing element looks like art. _note_structural prints from inside ScreenView rather than returning a value for a caller -- routing it through a caller is exactly what did not happen. Structural skips only; transparent-at- rest is ordinary animation. Zero found today: a guard, not a fix. The first version of that scan was a FALSE PASS: screen_view.gd did not parse (a line inserted at three tabs inside a four-tab block -- the substring assert matched a shallower indent), so grep counted zero from a dead script. The scan now counts the summary line as a positive control. Refutes 'the first-declared element paints first', which would have made the forced-backdrop rule redundant since all six forced elements are index 0. False on 8 of 16 screens -- decisively on main_menu, where index 0 is pteff00, painted LAST, and pteff00 is a measured control. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
be643ba58a |
port: withdraw 'the boot is known too fast' -- the splash dwells are declared and the port already played them
The Decoder measured both splashes over 3 cold boots: publisher t=0..255, developer t=0..210, the developer agreeing with wall clock to 1.1%. The port emits each declared value plus the 9-unit black hold, exactly. No code change. My error was the generalisation, not the arithmetic: build 4 is the title, whose exit is caused from outside its timeline, so it holds; a splash's exit is caused by nothing, so it plays out. I used the one boot screen the port is unaffected by to overturn the two it governs. Declining to scale by 9x while adopting the conclusion that implied was half a caution. Also refutes their two splash boundaries as not comparably anchored: 2.237 vs 2.414 units/frame in one boot, and the publisher has a glow symmetric with the developer's three. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
b366e404ee |
port: the clock freezes at settle -- my window is the game's, and the boot is known too fast
The Decoder measured build 4's top-level clock stopping inside [160,236]. The exporter computes title's settle window as [160,236,198] from the file alone. Same interval, two independent methods -- the first evidence for the settle instant that does not come from our own renderer. ptcopyright reaching alpha 255 exactly at t=160 agrees from a third direction. Corrects a claim in three places: timing.json, flow.json and boot.gd all said a screen's dwell IS its keyframe group and the port reproduced 'the disc's own pacing'. Build 4 declares ~120 presented frames and dwelled ~1100. The decision to hold zero extra stands; the claim that it was faithful does not. Checks their two declared spans against the file: both exact, with a 106-vs-105 interval-convention quibble that changes nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
39209eab05 |
port: the title's sweeps loop, the black hold is 9 units, and one claim refuted
THREE THINGS FROM THE DECODER, one of which I am not taking. REFUTED: "the developer splash is one composited quad, the bounding box of the three logos". The observed quad is 525x259 at (378,155). The three logos' bounding box is 500x421 at (390,164) -- a 259-tall quad CANNOT contain them, and palogo_anima alone starts at y=449, thirty-five pixels below that quad's bottom edge. The observed quad matches the union of gamearts_eff and seta_eff, 521x261 at (379,154), to about four pixels in every dimension -- and both of those are TRANSIENTS my own census flagged, dark by t=45, so a frame containing that quad is a build-in frame rather than the settled screen. I cannot see their draw stream, so I sent the arithmetic rather than a verdict, and the port keeps drawing three: I will not 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. I authored 12 from Q7's luminance plateau of 0.17-0.23 s, supported by the menus' transition quad. The Decoder counted SUBMITTED QUADS instead -- luminance cannot separate the outgoing fade's tail from true black. Four frames with no sprite quad at all, at 2.284 units/frame derived from the disc as its own clock, gives 9.1 units = 0.152 s (6.9-11.4). That overlaps the luminance figure only at the top, and the true black is SHORTER still since both boundary frames carry picture. My 12 was supported by analogy -- a different screen's quad on a different path -- and a number that fits by analogy loses to one measured in place. verify-dwell's bound moved with it; both screens still agree. THE TITLE'S SWEEPS LOOP. The oracle shows the quad oscillating over its whole x range and resetting hard, one reset in the first title dwell and two in the second. The loop-length field could NOT have settled it, correcting 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. Verified on the two sweeps' LCM, since their periods differ: 600 and 720 realign at 3600 units, mean diff 0, against 0.438 at half that. Scoped to the title. The menus declare the same lengths but the oracle measurement is of the title, and my own weak evidence points the other way there -- best match with the sweeps off-screen, three times worse mid-screen, against a 73% on-screen duty cycle if they looped. Two weak signals in opposite directions is a reason to scope, not to pick. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
6c78b3cda9 |
port: the publisher residual was a missing black hold -- we had both dismissed it
I carried this as "0.03 s outside a composite bound, probably a property of the bound rather than the game", and the Decoder agreed. 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. 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 the boot path has nothing to read it from: publisher_logo and developer_logos each carry a single palogo_eff0, a 1280x720 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 mid-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, then the developer logos at 4.45. publisher interval 4.26 DIFFERS -> 4.47 agrees; developer 3.62 -> 3.73, still agrees. Settled-frame comparisons untouched, as they should be. THE LESSON IS 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 plausible, was accepted by both of us, and was wrong. The composite bound is why the miss looked small -- the underlying gap was 0.2 s -- and a plausible explanation for a small number is how a real defect stays hidden. The film cost one command. Also recorded: the Decoder has reproduced across two build-ins that the console NEVER draws ptlogo_back2eff3 (0 draws against ~5 expected), with sampling phase, invisible draws and position error all ruled out -- but WHY is not established, and nothing in eff3's record differs from its neighbours. The port keeps drawing it, deliberately: dropping an element the disc declares on a measurement with no mechanism is authoring a behaviour neither agent can derive, and nothing this port gates on would notice, since the flashes live only in the build-in and verify-capture compares the settled frame. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
d57366a35f |
port: the focus ring had silently stopped, and the plate's period is now the disc's
BLOCKED said the record-layout change touches five things: pose_at, settle_units, spin_period_units, exit_ramp_units and the plate. I checked two, 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: 120 - 0 = 120 units, the same number the old rule produced, which is evidence the corrected layout is self-consistent rather than merely different. Verified the way P5 verified it, by bit-identity one period apart on the ring's own 60x60 box so the ptloop sweeps cannot confound it: 0 at +120 units (twice), 8.61 at a quarter period, 8.88 at half. Three wrong instruments on the way, and the sequence is the lesson. A whole-frame `max` saturates on one rotating edge (adjacent frames scored 131 with a mean of 0.022). A live --menu filmstrip jitters by up to a frame, which is ~3 degrees of ring. 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. THE PLATE'S PERIOD IS NOW 105, THE DISC'S OWN GROUP LENGTH, and it disagrees with the measurement. The ambiguity the entry carried is gone -- it used to say the cycle might restart at t=6 rather than 0 and that nothing separated them; the group now runs t=0 to t=105, both at alpha 0, and there is one reading. But 105 units is 1.750 s, or 1.906 s scaled by the factor the ring shows between its declared 120 and its measured 2.177 s -- about 17% below all four corpus timings (2.12 / 2.19 / 2.34 / 2.31). The old 129 gave 2.34 s, at the top of the range, which is why it looked right. 129 was the last timed keyframe plus exit_ramp_units, and that constant is deleted. A period built on 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. Verified bit-identical 105 units apart, 0.83 at 30 units. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
5dbc9aeac0 |
port: delete exit_ramp_units, invert the format's own rule, and guard a scale-0 leaf
FOUR THINGS, and the first is what MISSION section 3 calls the measure of
progress.
DELETED `exit_ramp_units` AND `exit_ramp_seconds`. They were authored because the
disc had no time slot on a group's final keyframe, so the ramp into it was the
one unknown duration per screen. Under the corrected record layout that keyframe
does not exist -- a group is an 8-byte header then frames x {u32 time; 36-byte
pose} and every pose is timed. VERIFIED DEAD BEFORE DELETING: setting it to 9999
(166 s) moved the boot's transitions by 0.04 s, which is wall-clock jitter, and
both uses in ScreenView are gated on a condition that no longer fires on any of
the export's 866 keyframes.
INVERTED THE FORMAT'S OWN RULE. `check.rs` enforced "the final keyframe has no
`t`; the disc has no time slot there" and FORMAT.md stated it. Both are now
backwards, and the validator fired 150 times on a re-export. I had not run
`check` between pinning the tag and measuring against the oracle -- the pixel
harness was green while the format validator was failing on every screen with a
multi-keyframe group. A correctness harness does not replace a format one; they
fail at different layers.
GUARDED A SCALE-0 LEAF, which the Decoder hit in its own renderer: its leaf
branch marked the element drawn unconditionally while the blit returned early on
zero scale, so a scale-0 leaf suppressed its parent and blanked the element --
live on all four loading screens. This port did not have the bug only because
authored/rendering.json happens not to list pgloading_loop5. That is an accident
of a gate written for another reason, not a defence, so `_draw_leaf` now reports
whether it drew and `_draw` falls back to the parent.
ISOLATED THE PACING QUESTION rather than leaving it as a suspected regression.
Legacy association: publisher 4.70 agrees, developer 3.92 DIFFERS. Corrected:
publisher 4.26 DIFFERS, developer 3.62 agrees. Both misses are ~0.03 s outside a
composite bound. The association traded which screen is marginally out; it did
not regress the pacing.
Bumped the pin c -> d for the parser and audio changes. Its headline renderer
change does not reach this port: sylpheed-cli builds from the workspace crate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
|
||
|
|
9f6959c7ca |
port: implement the decoded leaf composition -- 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 rather than unsupported -- at the fitted time the parent has expired, so leaf x parent predicts zero for both quads and the sweeps would be invisible. They are drawn. 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% against the oracle: 1.82 at t=261, 1.81 at t=355, 1.79 at t=420. 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 it 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 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 not sufficient. TWO FLAGGED ELEMENTS DELIBERATELY NOT DRAWN, in authored/rendering.json with reasons. title_jp/ptlogo_eff2 (parent 125%, leaf 100%) is the same shape and is the element DECISIONS has recorded since P1 as the largest render disagreement -- but the Decoder said plainly "I have not tested it", and drawing it would extend a decode past the case it was fitted on. pgloading_loop5's leaf is scale (0,0), and scale-0 is one of the three historical failures this corpus names. Neither can be adjudicated here: title_jp has no oracle capture, and verify-screen compares against a renderer that draws no leaves at all, so ANY leaf drawing increases that divergence whether right or wrong. Its max went 155 -> 232 when they were drawn, and that number is not evidence in either direction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
8bb9238632 |
port: the title residual is a horizontal redistribution, and three of my own explanations are dead
`title` is the port's largest disagreement with the oracle at 1.82%, and last iteration I attributed it to the moving ptloop sweeps without checking. Wrong, and so were the two hypotheses I formed after it. NOT THE SWEEPS. ptloop01/02 are 399x180 at (441,270) -- small and central -- and their exported keyframes hold pos, scale and rotation constant. The difference peaks at x~1088. NOT AN OVER-HELD ELEMENT. Added `--no-hold` to render the alternative: playing the title's groups past rest fades the 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 gives 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. WHAT IT IS: a horizontal redistribution. Signed difference by cell shows the port DARKER centre-left (-13.1, -8.3, -6.6) and BRIGHTER right (+16.0, +9.9), nearly cancelling -- whole-frame means 63.8 against 62.5. Brightness in the wrong place, not a level error or a tone ramp. It falls in the rows spanned by the two wide elements ptlogo_back2 (1118x262) and ptlogo_back2eff (1133x280), with the column profile falling off past x~1152 against their right edges at 1189 and 1197. AND THE EXPORT CARRIES NO BLEND MODE. ptlogo_back2eff's 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, and the port composites everything with normal alpha. If the game draws `_eff` layers additively, a wide gradient sprite would produce exactly this signature and nothing in the export would reveal it. Asked, not assumed; I have not tested it, and I am recording it because the three I could test are dead. Recorded and NOT acted on: pteff02's rest.t is 46, where its fade is 25% black, while its own group reaches 0x00000000 at t=236 -- so the port holds a black veil the timeline removes. Third instance of rest.t naming a hold that is not the settled state. It does not explain the residual: removing a darkening veil would make the port brighter still, and it is already brighter where it disagrees. `--no-hold`'s first version set the flag thirty lines before `view` exists and silently rendered nothing, caught because the loop found no files rather than because anything reported an error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
7c918006e8 |
port: the PRESS (A) plate pulses -- authored per element, because 82 of 212 share its shape
The human listed pulsation as first-class and the port drew nothing: the plate's focus record `ptbtn00f` was never reached, because press_start has no `buttons` and nothing is focused. That it LOOPS is measured -- the corpus timed the period 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 DIED IN THE CENSUS. The spinning ring is a rule in the renderer because it has a disc-wide check: 16 of 212 elements match its shape and all 16 are focus rings. The analogous shape for a pulse -- keyframes varying only in alpha, first alpha equal to last -- matches 82 OF 212, including ptcopyright, palogo_sqex, ptmsg and every _eff fade. A renderer rule on it would make the copyright notice pulse. Narrowed to focus records it matches exactly one distinct element, and a rule justified by n=1 is a special case wearing a rule's clothes. So it is a LOOKUP in authored/timing.json keyed <screen>/<element>, with the census recorded beside it so nobody widens it later. The period is 129 units -- the element's own group under the port's existing model: last timed keyframe t=105 plus the authored exit_ramp_units of 24. No new constant. 2.150 s at 60 units/s, 2.295 s at the ~28.1 fps the emulator presents, against measurements of 2.12-2.34. IT IS A CHOICE AND THE ALTERNATIVE IS STATED: restarting at the group's first keyframe (t=6) instead of 0 gives 123 units = 2.050 / 2.189 s, also inside the measured spread. Nothing separates them. t=0 is taken because it is where every other group starts -- consistency, not evidence. Verified the way the ring was, by bit-identity one period apart. 20 periods is 43.00 s = exactly 172 film frames: frames N and N+172 differ by 0-1/255, while the control a quarter-second off (43.25 s) differs by 58.7/255. On the held boot title the glow-box mean swings 26.0 <-> 37.7. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |
||
|
|
53a93e2e9e |
port: the intro had no dialogue because the voice is a separate asset, and I concatenated it wrongly first
A human play-test heard music under the boot intro and no voices. The obvious reading -- the 5.1 fold dropped the centre channel -- is wrong. `ADV.wmv` carries music and effects only; a cutscene's voice is a separate continuous XMA stream in `sound.pak`, bound to the movie by the manifest in `tables.pak`. Nothing was dropped. The exporter had never been asked for it, so every fidelity measurement in AUDIO-VERIFICATION.md would have come back clean. `audio::export_voice` resolves it with `media::resolve_movie_voice_region` and never by filename: `RT01A`'s voice lives inside `VOICE_ADV.slb`, so a name match is correct on exactly the two movies this port would have spot-checked. Decoded, not authored -- so it runs outside the `authored/audio.json` block. THE FIRST VERSION CONCATENATED THE REGION'S CHUNKS AND WAS WRONG. It produced 359 s of dialogue for a 137 s movie. Decoding and timing each chunk shows two of them equal to six decimals and each spanning the whole movie -- HANDOFF Q10's decoded two-stem shape on a second asset kind -- so they are summed at 1/n. The error was visible only because the first version recorded the decoded length against the movie's instead of clamping to it; the clamp `media`'s own doc comment invites, and which `sylpheed-viewer` applies, would have produced a file of exactly the right duration containing the wrong audio. The dropped leading chunk matches no duration in its region and is NOT closed here. It is the same signature as `BGM_103`'s third sub-wave, already open in BLOCKED.md, now corroborated on an independent asset kind. Raised with the Decoder; the manifest names every chunk dropped and its length. Also in this commit, and separable: * `--skip-at=SECONDS` -- `--script` structurally cannot press during a movie, because `_script_settled` waits while `_player != null`. That is why "does (A) skip the intro" had been read out of the source rather than measured. * MISSION section 6 pins a 5.1->stereo matrix and this exporter has shipped a different one since P4 -- the same weighting, 7.65 dB quieter -- and said so nowhere. Re-measured with the right instrument (float decode, whole file, count the samples that would clamp, not a peak reading): the pinned matrix puts ADV at +4.26 dBFS on 4406 samples, while S00A never clips. So the pin overloads one movie and the constant is over-broad for the other. NOT changed -- the level of a mix is what section 6 reserves to a human. The export now carries a warning with the numbers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF |