Answered against the pre-registration committed before the capture.
Units per second was the last open number on the PRESS (A) plate, and both
prior measurements of it were wall-clock readings off an emulator that runs
the guest slow by an unknown factor. They disagreed by 2.9x, because a 30 Hz
guest at full speed and a 60 Hz guest at half speed look identical on a wall
clock.
The ruler here is not a clock. ADV.wmv declares 30.0000 fps in its own ASF
header, so a decoded movie frame is a tick the emulator's speed cannot
stretch. Presented frames per decoded movie frame is guest_fps/30 with no
wall clock in the chain.
predicted H_A 30 fps -> 60 units/s -> 1.0
H_B 60 fps -> 120 units/s -> 2.0
measured 1.0000
Both pre-registered guards pass. Guard 2: a perfect repeating 3-buffer
cycle, 52 uses each (exactly 156/3), 2 chroma planes per luma on 156 of 156.
Guard 1: run lengths are 156 runs ALL of length 1 -- no smear, so the
dropped-movie-frame bias that would have pushed the answer toward 120 is
measurably absent rather than argued away.
So H_A. The port keeps its 60 and changes nothing.
REFUTES the live H3 hypothesis that 120 units/s explains the play-test's
late plate. That hypothesis was well-formed and attractive precisely because
it would have explained the complaint, which is why it needed a ruler that
is not a clock.
Which means finding 3 still has no cause. The strongest remaining candidate
is decoded rather than speculative: the plate's declared onset is t=214, not
t=236 -- a 22-unit fade, matching the T=22 the oracle confirmed by measuring
+23 alpha per presented frame on that element.
The pre-registered control could NOT be run: this logger build emits vb=
addresses, not vertex contents, so there was no alpha to check +34 against.
A weaker control is substituted and labelled -- the splash shader/blend
census, which validates the log's structure (what this measurement uses) and
not alpha extraction (which it does not).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
7.6 KiB
✅ The guest presents at 30 fps, so the UI clock runs at 60 units/s — the port's value stands
Status: ✅ measured. Instrument: ⟨capture⟩ — the real game in Xenia Canary,
per-draw, one boot, 2026-09-01. Ruler: ⟨disc⟩ — ADV.wmv's own ASF header.
Answered against
guest-frame-rate-preregistration.md,
committed before the capture was taken.
Data: data/guest-frame-rate-cadence.txt.
The result
| predicted | measured | |
|---|---|---|
| H_A guest 30 fps ⇒ 60 units/s | 1.0 labels per movie frame | — |
| H_B guest 60 fps ⇒ 120 units/s | 2.0 | — |
| 1.0000 |
H_A, and not marginally. The plate's t = 236 is 3.93 s.
🔴 This closes the question the port was blocked on, and the answer is that the
port was already right. HANDOFF.md §H3 said "units per second is still open,
and it is now the only place the disagreement lives", and warned "do not change
your 60 on my account yet." Good: 60 is correct and nothing should change.
⚠️ So the play-test's "the plate arrives late" is NOT a units-per-second error. The leading candidate is eliminated. See "what this re-opens" below.
Why this measurement does not have the defect the previous two had
Canary presents at ~27–28 fps and runs the guest slower than real time by an unknown factor, so a 30 Hz guest at full speed and a 60 Hz guest at half speed produce the identical wall-clock observation. Both earlier readings were wall clock, and they disagree by 2.9× (2.13 s vs 0.73 s for the same interval).
The ruler here is not a clock at all. ADV.wmv — the boot intro and the attract
movie, the same asset — declares its video rate in the shipped file:
ExtendedStreamProperties stream #2 avgTimePerFrame = 333333 x100ns -> 30.0000 fps
⚠️ Stream #1 is the audio and its avgTimePerFrame is 3 276 559, which reads
as 3.05 fps. Reading the wrong stream is the obvious way to get this wrong, so it
is recorded rather than silently avoided.
A decoded movie frame is therefore a tick that the emulator's speed cannot
stretch: however slowly Canary runs, the guest still decodes 30 movie frames per
second of movie time. Counting presented frames per decoded movie frame gives
guest_fps / 30 with no wall clock anywhere in the chain.
The measurement, and both guards
One presented frame = one RESOLVE to dest=0x14570000. There are 595 of
them against the logger's own FRAMES=600 budget, so the two notions of "frame"
agree and the count is not the harness's.
movie luma draws : 156
movie spans frames : 439..594 (156 frames)
presented frames per movie fr : 1.0000
Guard 2 — is it really a buffer cycle? Yes, and stronger than asked:
distinct 1280x720 luma bases : 3 0x11590000 0x11720000 0x118B0000
uses of each : 52, 52, 52 (156/3 exactly)
perfect repeating 3-cycle : True (not merely 3 distinct)
chroma planes per luma draw : 2, on 156 of 156 (YUV420)
Guard 1 — a spike, not a smear. The pre-registration said H_A is a spike at 1, H_B a spike at 2, and frame-dropping a smear with a tail:
run lengths (frames holding one luma base):
1 frame(s): 156
156 runs, all of length 1, nothing else. No tail, no smear, no dropped movie frames. The bias that would have pushed the answer toward H_B is measurably absent rather than argued away.
🔴 The control was NOT the one I pre-registered, and that is stated plainly
The pre-registered control was "the splashes' established +34/frame alpha step
must reproduce in the same log." It could not be run: this logger build emits
vertex-buffer addresses (vb=0x14CD00BC), not vertex contents, so there is no
alpha to extract. I did not discover that until the log was in hand.
Substituted, and weaker in a way worth naming: the three splash pixel shaders and their blend states must reproduce against the committed census from an independent boot.
| committed census | this capture | |
|---|---|---|
| sprite shader | 0xE59B2B3DA4AA9008 0x07010701 |
✅ ×446 |
| the clear | 0x2E372EA28CC404B7 0x00010001 |
✅ ×223 |
| black backdrop | 0x5773DC18083C4C20 0x07010701 |
✅ ×223 |
This validates the log's structure — frame delimitation, shader and blend fields — which is what the cadence measurement actually uses. It does not validate alpha extraction, and the cadence measurement does not use alpha. That is why the substitution is acceptable here and would not have been for a claim about a ramp.
Independent corroboration, from data already committed
data/attract-frame-match.txt matched captured
attract frames to timestamps inside ADV.wmv. Over its fourteen high-confidence
ADV matches, movie time advances ≈ 5.77 movie-seconds per capture step
against a harness that sleeps 5 s plus grab overhead — i.e. the movie plays at
roughly real time. A movie playing at real time, with one present per movie
frame, is a guest presenting at ≈ 30 fps. Consistent, from a different capture,
a different instrument, and a measurement taken for a different purpose.
🔴 What this re-opens: finding 3 still has no cause
Units per second was the leading candidate for "the PRESS Ⓐ plate arrives
late" and it is now eliminated. The play-test's remaining candidates were:
the clock origin, rest.t, and the record layout. One of them is now the most
likely, and it is decoded, not speculative:
The plate's declared onset is
t = 214, nott = 236.
$ sylpheed-cli screen info --build 2 --geometry $SYLPHEED_DISC/dat/GP_TITLE.pak
ptbtn00.t32 0: a=0 214: a=0 236: a=255 238: a=255 244: a=0
A keyframe is the start of a ramp (an established ✅ law), so the plate fades
in across 214 → 236 — a 22-unit ramp, which is exactly the T = 22 the
oracle independently confirmed by measuring +23 alpha per presented frame on
this element (255 × 2 / 22 = 23.18).
At 60 units/s the plate begins appearing at 3.57 s and is full at 3.93 s. A
port that shows nothing until t = 236 is 22 units — 0.367 s — late at
onset, and replaces a 22-unit fade with a pop. A human watching judges a fade by
when it starts.
⚠️ I am not claiming that is what the port does. The play-test reports the
port raising the plate at t = 236; whether that is its onset or its completion
is the port's to check. What is decoded here is the disc fact and the arithmetic.
Reach
⟨capture⟩ over one boot, English locale, one machine, and the movie region of one log. The ratio is exact and both guards pass, but a second independent boot would make it ⟨capture⟩×2 — worth having before anything irreversible rests on it, though nothing needs to, because the answer is "keep 60". The 30.000 fps ruler is ⟨disc⟩ and generalises.
Refutation attempt, recorded per the adversarial duty
Target: my predecessor's own H3 note that "2 × 60 fps is 120 units/s, which
puts the plate at 1.97 s instead of 3.93 s — and 'about two seconds early' is the
size of what the human reported." That is a live hypothesis with a plausible
motive, and it is the one I expected to confirm.
Result: REFUTED. The guest presents one frame per decoded movie frame, on a perfect 3-buffer cycle with no dropped frames. 120 units/s requires two presents per movie frame and there are none. The hypothesis was well-formed and wrong, and the reason it was attractive — it would have explained the play-test — is exactly why it needed a ruler that is not a clock.