Files
Sylpheed/docs/re/structures/ui-clock-freezes-at-settle.md
sylph-decoder 78328c5022 re: the top-level clock freezes at the settle point -- closing the 114-vs-120 gap
Measured in the title draw capture, re-read with the per-quad parser.

GP_TITLE build 4 declares t = 0..269, about 120 presented frames at this run's
pacing. The title dwell lasted ~1100. ptcopyright declares alpha >= 1 for 106
units (t=138..244) and is drawn for 1050 frames; ptlogo1 declares an exit at
t=264 and is drawn for 1095. Both vanish within three frames of the dwell
ending.

So the top-level clock advances through the build-in, stops inside the settle
window [160,236], and holds. The exit ramp is not played on a timer -- it plays
when something makes the screen leave. That is ui-settle-time.md's decode seen
from the other side and observed in the running game rather than inferred from
the file. A nested record keeps looping on its own clock throughout.

This closes the 114-vs-120 gap, and it was my arithmetic rather than a
discrepancy in the decode. The 2.231 units/frame was regressed over BUILD-IN
events -- the only stretch in which the top-level clock advances -- and applied
to a period measured over the settled dwell, where that clock is frozen and
only the plate's own record is running. Two different clocks. The declared 120
was never in doubt from the calibration-free dark-fraction test.

The 51.158-frame period is now confirmed by a second independent estimator:
autocorrelation returns lag 51 with clean harmonics at 102 and 154. Its FIRST
version failed its control, returning 48 for a period known to be 51.158,
because it indexed by sample position where the log's frame numbers have gaps.
Recorded, because the failure is the reason the second version can be trusted.

Not settled: the sweeps' period. The same validated estimator disagrees between
two dwells of one screen -- 515 vs 452 frames for the same family -- and a 14%
disagreement within one screen is not a period. The +0x08 field cannot settle
it either, since ptloop01/ptloop02 have zero slack.

Blocker recorded in CONTAINER-NOTES: a single A press on the title faults the
guest. Three menu-capture attempts, two ending in register dumps of 223 MB and
519 MB, against three no-input runs in the same session that all completed. And
a guest fault writes an UNBOUNDED register dump to stdout on a filesystem at
91%, so any scripted button press needs a size guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
2026-08-29 21:21:27 +00:00

5.2 KiB
Raw Blame History

The top-level clock freezes at the settle point — and that closes the 114-vs-120 gap

Classification: measured. From the title draw capture of 2026-08-29 (ui_draw_capture.sh ARM=early), re-read with the per-quad parser.

The observation

GP_TITLE build 4 declares a timeline of t = 0 … 269. At this run's build-in pacing that is about 120 presented frames. The title dwell lasted ~1 100.

element declared alpha ≥ 1 drawn in frames span
ptcopyright t = 138 … 244 (106 units) 168 … 1217 1 050 frames
ptlogo1 t = 26.6 … 264 (237 units) 125 … 1220 1 095 frames

If the top-level clock ran on to the end of its declared timeline, ptcopyright would fade out at t=244 — around frame 216 — and ptlogo1 at t=264. Neither does. Both are drawn continuously until the dwell ends, and disappear within three frames of it.

The top-level clock advances through the build-in, stops at the settle point, and holds there. The exit ramp is not played on a timer — it plays when something makes the screen leave.

That is ui-settle-time.md's decode seen from the other side, and observed in the running game rather than inferred from the file. The freeze lands inside the settle window [160, 236]: ptcopyright reaches full alpha at its start (t=160) and never moves again.

Meanwhile the plate's focus record keeps looping throughout — a nested record runs on its own clock, which does not stop when the parent's does.

🔴 What this closes: the 114-unit period, which was my error

ui-record-loop-length.md recorded an unexplained gap: the plate glow's period of 51.158 presented frames, multiplied by 2.231 units/frame, gives 114 units against a declared 120.

The two numbers come from different clocks. The 2.231 was regressed over five build-in events — the only stretch in which the top-level clock advances. The 51.158 was measured over the settled dwell, where that clock is frozen and only the record's own clock is running. Applying one to the other compares two different stretches of wall time, and there was never a reason they should agree.

The 120 was never in doubt from evidence that needs no conversion: the dark-fraction test (17.7 % measured, 14.4 % predicted by 120, 2.2 % by 105, with a culling threshold of 1 read off the data) refutes 105 by a factor of eight without any units/frame at all. The gap was an artefact of my arithmetic, not a discrepancy in the decode, and the doc no longer carries it as open.

The period itself, confirmed twice

51.158 frames was originally measured by detecting cycle starts. A second, independent estimator — autocorrelation of the glow's alpha series — returns lag 51, with clean harmonics at 102 and 154.

⚠️ And the first version of that estimator failed its control, which is why it is worth recording. Indexed by sample position it returned 48 for a period known to be 51.158 — 6 % low, with the top three lags (47, 48, 49) not even bracketing the truth. The log's frame numbers have gaps, so a lag of n samples is not n frames. Indexed by frame number with missing frames masked, the control passes.

What this does not settle: the sweeps' period

The same validated estimator, applied to the two light sweeps, gives periods that disagree between two dwells of the same screen:

sweep dwell 1 dwell 2
tall quad, h ≈ 1134 515 frames 452 frames
tall quad, h ≈ 1303 549 frames 538 frames

A 14 % disagreement within one screen is not a period. The likely reason is that the quad's x is a composite of the parent's placement with the leaf's rotation and 600800 % scale, so "the quad's left edge" is not a clean phase variable — but that is a hypothesis, not a finding. The sweeps' period is unmeasured, and the +0x08 field cannot settle it either: ptloop01/ptloop02 have zero slack, and a zero-slack record cannot distinguish "loops at 600" from "runs once and stops".

🔴 Blocker: a single Ⓐ on the title faults the guest

Three attempts to capture the main menu ended the same way.

run input outcome
1 Ⓐ on title, then Ⓐ again on the transition guest fault, 519 MB of register dump
2 one Ⓐ on title (guard added) drifted to a flight classification, 97 MB
3 one Ⓐ on title guest fault, 223 MB of register dump

Against three runs in the same session that tapped nothing on the title and all completed normally. This is the crash ui_draw_capture.sh's own header records from 2026-08-18 — "a stray A there sends the guest into the save-data probe".

⚠️ It bounds menu-side dynamic RE in this container, and it is not a contradiction of the corpus's existing menu measurements, which were taken before and by some route that survived. What differs has not been found.

⚠️ And a guest fault writes an unbounded register dump to stdout — 223 MB and 519 MB here, on a filesystem at 91 %. Any scripted run that presses a button needs a size guard on canary.stdout.

Reproducing

python3 tools/re-capture/quads_per_frame.py <log> 200 1200