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
This commit is contained in:
@@ -206,3 +206,33 @@ XEX decrypt + LZX decompress, and the disassembly-to-database step — belongs i
|
||||
`crates/sylpheed-formats`.** Until then, every static finding rests on an
|
||||
artefact this project cannot rebuild, and that is a real gap in the corpus rather
|
||||
than a convenience.
|
||||
|
||||
## 🔴 Pressing Ⓐ on the title faults the guest — and the fault fills the disk
|
||||
|
||||
Three attempts to capture the main menu on 2026-08-29 ended the same way. Every
|
||||
run that tapped Ⓐ **on the title** faulted; every run that tapped nothing there
|
||||
completed and produced its capture.
|
||||
|
||||
| run | input on the title | outcome |
|
||||
|---|---|---|
|
||||
| 1 | Ⓐ, then Ⓐ again on the transition | guest fault, **519 MB** of register dump |
|
||||
| 2 | one Ⓐ | drifted to a `flight` classification, 97 MB |
|
||||
| 3 | one Ⓐ | guest fault, **223 MB** of register dump |
|
||||
| 4–6 | none (`NOTAP=1`) | 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". ⚠️ The corpus's existing
|
||||
menu measurements (Q4, Q5, the focus ring) were taken by some route that survived
|
||||
this; what differs has not been found. **Menu-side dynamic RE is blocked until it
|
||||
is.**
|
||||
|
||||
⚠️ **A guest fault writes an UNBOUNDED register dump to stdout.** Xenia runs with
|
||||
`break_on_unimplemented_instructions = true`, and the dump is `vN = [...]` / `rN =
|
||||
...` lines at roughly 100 MB per 30 s. The filesystem here sits at **91 %**. Any
|
||||
scripted run that presses a button must watch `canary.stdout` and kill on growth —
|
||||
`ls -la` on it before trusting a long run.
|
||||
|
||||
📌 Two knobs added to `ui_draw_capture.sh` for boot-side work: `GRACE=1` (the fixed
|
||||
8 s wait before arming means an `ARM=early` capture otherwise misses both splashes,
|
||||
which run at ~1.2–9.5 s of guest time) and `NOTAP=1` (no input at all — the movie
|
||||
tap fires on "the screen changed a lot", which is also true of a fading splash).
|
||||
|
||||
@@ -2297,3 +2297,47 @@ sharper form is that they were not two witnesses. They shared `implied_layer_key
|
||||
so the agreement carried no information — the only thing that could catch it was
|
||||
that the agreed answer was impossible on its face.
|
||||
|
||||
## 2026-08-29 — the 114-vs-120 gap was mine, and it is closed
|
||||
|
||||
✅ **The declared 120 stands. Nothing you ship changes.** The gap I flagged as
|
||||
unexplained was a category error in my own arithmetic.
|
||||
|
||||
**What I found in the draw stream:** `GP_TITLE` build 4 declares `t = 0…269` —
|
||||
about 120 presented frames at this run's pacing — and the title dwell lasted
|
||||
**~1 100**. `ptcopyright` declares alpha ≥ 1 for **106 units** and is drawn for
|
||||
**1 050 frames**; `ptlogo1` declares an exit at t=264 and is drawn for 1 095. Both
|
||||
disappear within three frames of the dwell ending.
|
||||
|
||||
> **The top-level clock advances through the build-in, stops inside the settle
|
||||
> window `[160, 236]`, and holds. The exit ramp is not on a timer — it plays when
|
||||
> something makes the screen leave.**
|
||||
|
||||
That is the settle-time decode observed from the other side, in the game rather
|
||||
than in the file — and it is worth having explicitly if you drive transitions:
|
||||
**do not schedule a screen's exit off its own timeline.**
|
||||
|
||||
🔴 **And it explains the 114.** My 2.231 units/frame was regressed over *build-in*
|
||||
events — the only stretch where the top-level clock advances — and I applied it to
|
||||
the glow's period, measured over the settled dwell where that clock is frozen and
|
||||
only the record's own clock runs. Two different clocks. The 120 was never in doubt
|
||||
from the dark-fraction test, which needs no conversion at all.
|
||||
|
||||
✅ The 51.158-frame period is now confirmed by a **second independent estimator**
|
||||
(autocorrelation: lag 51, harmonics at 102 and 154).
|
||||
|
||||
❔ **The sweeps' period is still unmeasured**, and I would rather say so than give
|
||||
you a number: the same estimator disagrees between two dwells of the same screen
|
||||
(515 vs 452 frames). Combined with the zero-slack `+0x08`, neither the file nor
|
||||
this capture settles whether they loop. **On the title they demonstrably do not
|
||||
park**; the menu remains open.
|
||||
|
||||
🔴 **Blocker you should know about, because it bounds what I can answer:** a single
|
||||
Ⓐ press on the title **faults the guest** in this container. Three menu-capture
|
||||
attempts, two ending in register dumps of 223 MB and 519 MB, against three runs in
|
||||
the same session that pressed nothing and all completed. It is the crash the
|
||||
capture script's own header records from 2026-08-18. Menu-side dynamic RE is
|
||||
blocked here until that is understood; the corpus's existing menu measurements
|
||||
predate it.
|
||||
|
||||
Detail: [`docs/re/structures/ui-clock-freezes-at-settle.md`](../re/structures/ui-clock-freezes-at-settle.md).
|
||||
|
||||
|
||||
@@ -175,3 +175,4 @@ files, which is how the same ground got covered twice.
|
||||
| [`structures/boot-splash-gap-measured.md`](structures/boot-splash-gap-measured.md) | The black gap between the two boot splashes | ✅ **measured** in the guest's **draw stream**, which separates true black from a fade tail where luminance cannot: the publisher's last sprite is frame 125 (alpha 7), then **frames 126–129 submit NO sprite quad at all**, then the developer fades in at alpha 34. **The gap is 4 presented frames.** Converted with the disc as its own clock — `palogo_sqex` declares alpha≥1 for **239.8 units** and is drawn in **105** frames → **2.284 units/frame** (the title capture independently gave 2.231) — that is **~9.1 units ≈ 0.152 s**, against the **12** the port authored; ⚠️ and the true black is *shorter*, since both boundary frames still carry picture. 🔴 **RETRACTED**: "the developer splash is ONE composited 525×259 quad" — the same batching artefact. It draws three logos and three glows as separate quads in one `indices=24` call; the 525×259 was `gamearts_eff` merged with `seta_eff`. The port refuted it with arithmetic (a 259-tall box cannot hold logos spanning y 164…585) before I checked. ⚠️ The gap measurement is unaffected — those glows are the developer splash's first draw. ❌ Not declared on the disc: `palogo_eff0.prm` is a single static keyframe, and the top-level `+0x08` is a **family constant** (300 / 60) whose slack ranges 12–226 units. ❔ The executable is **not** looked at — named, not claimed. 🔴 The instrument was perturbing the measurement: the capture script taps Ⓐ on "screen changed a lot", which is also true of a fading splash — it tapped through the publisher and the developer never appeared. `GRACE=1` and `NOTAP=1` knobs added |
|
||||
| [`structures/ui-forced-backdrop.md`](structures/ui-forced-backdrop.md) | Where a keyless primitive paints, when the file forces it | ✅ **decoded**, partly closing `ui-prm-primitives.md`'s standing blocker: **an element covering the screen and fully opaque at some instant cannot paint above anything visible then**, and where that set is *every* other element its position is **forced first**. Disc-wide **80** instances forced, 50 constrained but not forced, 0 unconstrained. ✅ **Two controls, both measured orders from the running game**: it reproduces `palogo_eff0.prm` = FIRST (opaque 211 instants, below 6/6) — which a **name**-based rule gets wrong, since it is named like an overlay — and permits `pteff00.prm` on top (opaque 2 instants, below 3/23), which is where it is measured. ✅ Answers the port's `build_12`/`build_15` blank-screen contradiction: `pgloading_eff00.prm` is forced first, 4/4. ✅ Explains 36 builds the corpus recorded as "one colour" with no cause — `pzeff00.prm` forced first 32/32, so **our own sort wiped them**. 🔴 The rule's limit was found by its own test failing: applied to `.t32` sprites it claimed 22 must sort first against their own keys (`pneff01` 0xd850 at #8/13, `pbfriendly` 0x9230 at #17/49) — a sprite's *element* alpha says nothing about its *texture*'s coverage, so it is now restricted to untextured primitives. ⚠️ Assumes straight alpha-over; blend mode is still ❔. ⚠️ A lower bound, not an ordering. ⚠️ No new oracle run — the controls are prior measurements |
|
||||
| [`structures/ui-forced-backdrop.md`](structures/ui-forced-backdrop.md) *(span sensitivity)* | How much of the forced-backdrop rule rests on the timeline convention | ✅ **decoded**: the span is `0..=max keyframe time over every element`, and an element **holds** its final pose — decoded, not assumed ([`ui-keyframe-time-unit.md`](ui-keyframe-time-unit.md), [`ui-record-loop-length.md`](structures/ui-record-loop-length.md)). Sensitivity over the 130 keyless full-screen primitives with an opaque interval: using the header's declared **`+0x08`** instead changes **0** verdicts (interchangeable); using the primitive's **own** last keyframe changes **72**; counting elements **gone** after their last keyframe changes **72**. 🔴 So the hold decides **55 %** of verdicts — and dropping it is **refuted by a measured order**: `palogo_eff0.prm` is a single keyframe at t=0, so without the hold it is opaque for one instant, nothing else is up, and the rule calls it *free* against a game measured painting it first. ✅ The verdicts that matter are convention-independent — `pgloading_eff00.prm` is FIRST under all four, `pteff00.prm` FREE under all four. ⚠️ The port's **256 vs 211** was a **bundle mismatch, not a definitional one**: `palogo_eff0.prm` runs to t=255 on the publisher splash (entries 10/13) and t=210 on the developer (11/14) |
|
||||
| [`structures/ui-clock-freezes-at-settle.md`](structures/ui-clock-freezes-at-settle.md) | The top-level clock stops at the settle point — observed in the running game | ✅ **measured**: `GP_TITLE` build 4 declares `t = 0…269`, about 120 presented frames at this run's pacing, and the dwell lasted **~1 100**. `ptcopyright` declares alpha≥1 for **106 units** (t=138…244) and is **drawn for 1 050 frames**; `ptlogo1` declares an exit at t=264 and is drawn for 1 095. Both vanish within three frames of the dwell ending. **The clock advances through the build-in, stops inside the settle window `[160,236]`, and holds; the exit ramp plays when the screen leaves, not on a timer** — [`ui-settle-time.md`](structures/ui-settle-time.md)'s decode observed from the other side. A nested record keeps looping on its own clock throughout. 🔴 **This closes the 114-vs-120 gap, and it was my arithmetic**: 2.231 units/frame was regressed over *build-in* events (the only stretch the top-level clock advances) and applied to a period measured during the freeze — 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, lag 51 with harmonics at 102/154) — ⚠️ whose first version **failed its control**, returning 48, because it indexed by sample position where the log's frame numbers have gaps. ❔ The **sweeps'** period stays unmeasured: the same validated estimator disagrees between two dwells of one screen (515 vs 452 frames). 🔴 **Blocker: a single Ⓐ on the title faults the guest** — 3 attempts, 2 register dumps of 223 MB and 519 MB, against 3 no-input runs that all completed; bounds menu-side dynamic RE here, and any scripted button press needs a `canary.stdout` size guard |
|
||||
|
||||
107
docs/re/structures/ui-clock-freezes-at-settle.md
Normal file
107
docs/re/structures/ui-clock-freezes-at-settle.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# 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`](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`](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
|
||||
600–800 % 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
|
||||
|
||||
```bash
|
||||
python3 tools/re-capture/quads_per_frame.py <log> 200 1200
|
||||
```
|
||||
@@ -204,9 +204,13 @@ observed frame of five events against their declared keyframe times (residuals
|
||||
at **frame 106.1** — and the composite spike, which was not part of the fit, is
|
||||
frame **107**.
|
||||
|
||||
❔ **The glow's period implies a 114-unit cycle at that slope**, against a declared
|
||||
120. The dark-fraction test below settles 120 against 105 without any calibration,
|
||||
but the 5 % gap in the period is unexplained.
|
||||
✅ **The 114-unit gap is CLOSED, and it was my arithmetic.** The 2.231 units/frame
|
||||
is regressed over *build-in* events — the only stretch in which the top-level clock
|
||||
advances — while the 51.158-frame period is measured over the settled dwell, where
|
||||
that clock is **frozen** and only the plate's own record is running. Two different
|
||||
clocks; there was never a reason they should agree. The declared 120 was never in
|
||||
doubt from the calibration-free dark-fraction test. See
|
||||
[`ui-clock-freezes-at-settle.md`](ui-clock-freezes-at-settle.md).
|
||||
|
||||
❔ **The absolute frame rate of this run was not measured** — Canary logged no fps
|
||||
and the log has no timestamps — so nothing here is stated in seconds. It did not
|
||||
|
||||
Reference in New Issue
Block a user