diff --git a/authored/flow.json b/authored/flow.json index 462545f6..87dd6ef2 100644 --- a/authored/flow.json +++ b/authored/flow.json @@ -34,9 +34,9 @@ "screen": "press_start", "clock": "shared", "why": "MEASURED, 2026-08-29, docs/re/title-plate-delay-measured.md on branch auto/no-disc-and-menu-captures at 5b0a6e6 (NOT on main when this was written). The boot title shows build 4 ALONE and the `PRESS (A) BUTTON` plate -- build 2 -- arrives later. This is the ONE case in the port where two builds are drawn at once.", - "no_constant_why": "THERE IS NO AUTHORED DELAY HERE, AND THERE WAS ONE FOR ONE ITERATION. The first version of this block carried `after_settle_seconds: 2.13`, taken from the RE agent's instruction. The port refuted that instruction with arithmetic off the disc -- build 2 has a group of its own, and starting it at settle put the plate 3.97 s late -- and the corrected answer needs no constant at all: BOTH BUILDS RUN ON ONE CLOCK, STARTED TOGETHER, and the plate arrives at its own declared t=238. `clock: \"shared\"` is that, spelled out rather than implied by the absence of a delay field. πŸ“Œ SOURCES, added 2026-09-01 in the uncited-why backfill: the plate's arrival is docs/re/title-plate-delay-measured.md and its pulse is docs/re/structures/plate-pulse-measured.md.", - "arithmetic_why": "Why one clock reproduces the measurement, checked against this export rather than taken on trust: build 4's visible build-in ends at t=118, where `pteff01`, `pteff02` and `ptlogoall_eff` finish together; `ptbtn00` reaches alpha 255 at t=238; the difference is 120 units = 2.000 s at 60 units/s. The oracle measured 2.138 s and 2.132 s. The gap is presentation rate: 120 units in 2.135 s is 56.2 units/s, i.e. the emulator presenting at 28.1 fps against a nominal 30, and the corpus had independently measured the idle title at 28.5 fps before these runs. πŸ“Œ SOURCE, added 2026-09-01: the pulse period and its phase behaviour are in docs/re/structures/plate-pulse-measured.md and docs/re/structures/plate-pulse-phase-lock.md, with the raw series in docs/re/data/plate-pulse-timeseries.txt. βœ… AUDITED 2026-09-01: the corpus's 28.5 fps is a genuinely independent leg -- a different quantity (idle-title presentation rate), measured BEFORE these runs, so it could have come out disagreeing. It agrees to 1.4 %. That is corroboration rather than a restatement, which is the distinction the week's audits were about.", - "the_premise_that_failed_why": "The port's own, and it is worth keeping because it will bite again: `rest.t` IS NOT WHEN A SCREEN SETTLES. It is the last hold keyframe before the exit. `ptlogo1` has rest.t=251 and stops MOVING at t=42, then creeps 5 px and 31 alpha steps over the next 209 units. Reading rest.t as the settle put build 4's arrival at 4.35 s instead of 1.97 s, and every reconciliation computed from it came out wrong by exactly that error. `ScreenView.settle_time()` still uses rest.t -- see docs/port/BLOCKED.md.", + "no_constant_why": "THERE IS NO AUTHORED DELAY HERE, AND THERE WAS ONE FOR ONE ITERATION. The first version of this block carried `after_settle_seconds: 2.13`, taken from the RE agent's instruction. The port refuted that instruction with arithmetic off the disc -- build 2 has a group of its own, and starting it at settle put the plate 3.97 s late -- and the corrected answer needs no constant at all: BOTH BUILDS RUN ON ONE CLOCK, STARTED TOGETHER, and the plate arrives at its own declared t=236 (CORRECTED 2026-09-01 from t=238, which is the last opaque frame rather than the arrival). `clock: \"shared\"` is that, spelled out rather than implied by the absence of a delay field. πŸ“Œ SOURCES, added 2026-09-01 in the uncited-why backfill: the plate's arrival is docs/re/title-plate-delay-measured.md and its pulse is docs/re/structures/plate-pulse-measured.md. \ud83d\udd34 AND `clock: \"shared\"` IS AUTHORED FROM OUR OWN ARITHMETIC, NOT MEASURED. Nobody has watched whether build 2's group starts with build 4's; it is the reading that reconciles the oracle's 2.13 s IF the settle anchor is t=118. See docs/port/plate-arrival-halves.md and BLOCKED.md H3.", + "arithmetic_why": "Why one clock reproduces the measurement, checked against this export rather than taken on trust: build 4's effect quads `pteff01`, `pteff02` and `ptlogoall_eff` end their ramps together at t=118; `ptbtn00` reaches alpha 255 at t=236; the difference is 118 units = 1.967 s at 60 units/s. The oracle measured 2.138 s and 2.132 s. The gap is presentation rate: the emulator presents at 28.1 fps against a nominal 30, and the corpus had independently measured the idle title at 28.5 fps before these runs. πŸ”΄ CORRECTED 2026-09-01: this said `ptbtn00` reaches 255 at t=238 and that the difference is 120 units = 2.000 s. It reaches 255 at t=236 and HOLDS to 238, so 238 is the last opaque frame, not the arrival; 236 - 118 = 118. The port printed the contradiction in one sentence on every boot. The correction moves the reconciliation by 0.033 s and overturns nothing -- see docs/port/plate-arrival-halves.md. πŸ”΄ AND THE ANCHOR IS NOW OPEN. The oracle defines \"title settled\" operationally, as its glyph counter first reading the no-plate value 154. This export offers TWO anchors 42 units apart: t=118 (the effect quads) and t=160 (`ptcopyright` at full alpha -- the LAST element to finish building in, and the only one made of glyphs). This line picked 118, while `ScreenView.settle_time()` returns 160 and the boot prints `settles at t=160`, so one binary holds both. Asked in BLOCKED.md H3; not guessed here. πŸ“Œ SOURCE: the pulse period and its phase behaviour are in docs/re/structures/plate-pulse-measured.md and docs/re/structures/plate-pulse-phase-lock.md, with the raw series in docs/re/data/plate-pulse-timeseries.txt. βœ… AUDITED 2026-09-01: the corpus's 28.5 fps is a genuinely independent leg -- a different quantity (idle-title presentation rate), measured BEFORE these runs, so it could have come out disagreeing. It agrees to 1.4 %.", + "the_premise_that_failed_why": "The port's own, and it is worth keeping because it will bite again: `rest.t` IS NOT WHEN A SCREEN SETTLES. It is the last hold keyframe before the exit. Reading it as the settle put build 4's arrival at 4.35 s instead of 1.97 s, and every reconciliation computed from it came out wrong by exactly that error. `ScreenView.settle_time()` still uses rest.t -- see docs/port/BLOCKED.md. πŸ”΄ THE EXAMPLE THIS CITED IS GONE, THOUGH THE CONCLUSION IS NOT. It read \"`ptlogo1` has rest.t=251 and stops MOVING at t=42\". In the CURRENT export `ptlogo1.rest.t` is 42 -- equal to when it stops moving. The record-layout fix repaired precisely that element, and the entry was never re-derived under it (REFUTED.md now carries this at 🟑 ⟨our-reader⟩). rest.t is still wrong for transients -- `ptlogo_back2eff1` is a two-frame flash whose rest.t=54 is the flash PEAK -- and for `pteff00`, whose rest.t=16 sits at the end of the fade-FROM-black while a fade-TO-black runs 261..269. Re-derived 2026-09-01: docs/port/plate-arrival-halves.md. πŸ”΄ AND IT IS NOT THIS DEFECT'S CAUSE. The plate's ARRIVAL is a declared keyframe (transparent to t=214, opaque at t=236), not a rest pose; rest.t=236 only chooses where `holding` parks it, and 236 is that ramp's own peak. Confirmed on a filmed boot with rest.t untouched: the onset is bracketed within one frame of 214.", "scope_why": "Attached to the BOOT STEP, not to the `title` screen, and that is deliberate. What was measured is the boot title. Whether the title shows the plate when it is REACHED AGAIN -- by (B) from the main menu, or after the attract movie -- is not measured, and putting the overlay on the screen would quietly claim it is. πŸ“Œ SOURCE, added 2026-09-01: the plate belongs to the boot's overlay step rather than to the title screen because its arrival is measured against the boot clock -- docs/re/title-plate-delay-measured.md.", "no_pulse_why": "The port draws the plate arriving and then holding. It does not pulse it. The RE agent identifies the pulse as the plate's FOCUS RECORD `ptbtn00f` -- a glow ramping 0x00 to 0x50 and back, t=6..105 -- not as a loop of `ptbtn00`'s own group, which was the port's earlier reading and was wrong. Looping that record is a candidate the port has NOT taken: its group is 105 timed units plus an AUTHORED 24-unit exit ramp, and hitting the measured 2.24 s mean requires composing that authored constant with a loop assumption, which is tuning rather than measuring. Filed in BLOCKED.md. πŸ“Œ SOURCE, added 2026-09-01: docs/re/structures/plate-pulse-measured.md, and the phase-lock caveat that bounds what a gated capture can show is docs/re/structures/plate-pulse-phase-lock.md." }, diff --git a/docs/port/BLOCKED.md b/docs/port/BLOCKED.md index d650a87c..03fd70de 100644 --- a/docs/port/BLOCKED.md +++ b/docs/port/BLOCKED.md @@ -105,8 +105,8 @@ method finding that applies to the Decoder's harness as much as this one: | # | ask | why the port cannot answer it | |---|---|---| | **H1** | **Does a held direction REPEAT in the menus, and at what rate?** Initial delay and repeat interval, for the d-pad and for the left stick separately if they differ. | Behavioural. The port now does **one step per deflection** β€” `authored`, and deliberately the conservative half: it cannot run away and it invents no rate. If the game repeats, a human will feel it as *"I have to flick it again"*. | -| **H2** | **The splash blur/fade-in does not match the game.** A human watching both says the game's is **more pronounced** than the port's. | Needs a capture of the publisher/developer splash across its fade, at a known frame cadence. The port's ramp comes from the declared keyframes; whether the game applies a **blur** on top β€” and whether what we read as a fade is one β€” is not answerable from the export. | -| **H3** | **The `PRESS β’Ά` plate arrives LATE.** The port raises it at `t=236`, derived `238 βˆ’ 118 = 120 units = 2.000 s`. | 🟑 **Half of this may be the port's, and that half must be settled here first.** Three candidate causes: the **unitβ†’seconds constant** (Decoder's), the **clock origin** β€” do builds 2 and 4 really start together? β€” and **`rest.t`** (both ours). `REFUTED.md` records that believing `rest.t` already put a plate **3.97 s late** once. Establish which half it is *before* asking, and say how. | +| **H2** | βœ… **ANSWERED 2026-09-01, and the port's own premise was wrong.** The Decoder found **no post-process pass** on either splash (`auto/frame-blend-draw-path` `3cc1b51`). The blur is **in the asset**: every logo ships a pre-blurred copy ~21Γ—20 px larger and concentric, drawn alone first and crossfaded out as the sharp logo fades in β€” which is the human's *"blurred to clear/sharp, in about a second"*, with no shader. | πŸ”΄ **"The port applies no blur at all" was FALSE and this page said it.** The port draws all seven quads of the developer splash, the three companions included, and always has. Verified by a frozen sweep, 3 units a step: companions alone at u=3–15, all seven at u=18–42, sharp only from u=45. Writing down what the renderer was *believed* to do instead of running it is what produced the claim. What remains is the **extent**, and it is the same unit question as H3 β€” asked there, once. See [`plate-arrival-halves.md`](plate-arrival-halves.md). | +| **H3** | 🟒 **NARROWED 2026-09-01, port half eliminated. The ask is now one question: what does your "title settled" anchor correspond to on the declared timeline?** You define it operationally β€” *glyph counter first reads its no-plate value 154*. Our export offers **two** anchors 42 units apart: **t=118** (`pteff01`, `pteff02`, `ptlogoall_eff` end their ramps together) and **t=160** (`ptcopyright` reaches full alpha β€” the *last* element to finish building in, and the only one made of **glyphs**). At 118 the shared-clock reading reproduces your 2.135 s to βˆ’8 %; at 160 it misses by βˆ’41 % and `clock: "shared"` collapses. πŸ”΄ **AND A SECOND, SHARPER HALF, ADDED AFTER YOUR SPLASH CAPTURE LANDED: your two captures imply keyframe rates a factor of 2.7 apart.** `splash-quad-timeline.txt` gives **5 units per guest frame** on three independent intervals of the publisher splash (companion onsetβ†’logo onset, companion onsetβ†’peak, logo onsetβ†’full: 3 frames each, against 15 declared units each) β€” against the declared **2**. At 30 fps that is ~150 units/s, the plate would land at **1.57 s** instead of 3.93 s, and this port would be **~2.4 s late**, which is the right magnitude for what the human saw. But `title-plate-delay-measured.md` implies **~55 units/s** from the same game. Both are yours; we cannot pick. ⚠️ Caveat we can see from here: the splash capture's frame index **skips** (5, 8, 12 are absent from Q7's eight draws), and whether those are unsubmitted quads or dropped presents changes the arithmetic. | All three candidates the play-test named are eliminated, with evidence, in [`plate-arrival-halves.md`](plate-arrival-halves.md) β€” derived from `export/` at port `HEAD` `6db49f5`, HANDOFF at `9ca1eb5`. **`rest.t`**: not in the causal path β€” the plate's onset is a declared keyframe (transparent to t=214, opaque at t=236), and rest.t only picks the parking pose. **Clock origin**: the two builds share one clock exactly β€” 85 of 85 filmed title frames have `view_units == overlay_units` to 3 dp. **Unit constant**: eliminated by *sign* β€” making the port earlier needs units/s **above** 60, while 28.1 fps presentation and 28.5 fps idle title both say the game's units run slower. And the port is **0.17 s early** on the interval you measured (1.967 s vs 2.138/2.132) and **between your two runs** on first-drawnβ†’plate (3.933 s vs 3.781/4.263). πŸ”΄ Also corrected: `ptbtn00` reaches a=255 at **t=236**, not 238 β€” so the cited `238 βˆ’ 118 = 120` is `236 βˆ’ 118 = 118`. | **On H2, three things the port can say that narrow it**, none of which settle it: diff --git a/docs/port/plate-arrival-halves.md b/docs/port/plate-arrival-halves.md new file mode 100644 index 00000000..f3492e21 --- /dev/null +++ b/docs/port/plate-arrival-halves.md @@ -0,0 +1,315 @@ +# H3 β€” the `PRESS β’Ά` plate: which half the lateness is in + +**Status:** the port's half is **eliminated**; the Decoder's half is **live and now has a number**. +Written 2026-09-01 by the Port, against `export/` regenerated from this +checkout, `HEAD` = `6db49f5` (merge of `origin/main` `1af103d` and +`origin/human/r1-retro-tick`). HANDOFF on this branch answers `9ca1eb5`. + +The [play-test](../agents/PLAYTEST-2026-09-01.md) finding 3 says the plate +arrives late and names three candidate causes: the **unitβ†’seconds constant** +(the Decoder's), the **clock origin** and **`rest.t`** (both ours). The brief +asks which half it is, and how that was established. This page is the answer. + +> **Short version: the two candidates that were ours are eliminated, and the one +> that is the Decoder's is not.** The port renders the plate exactly where its +> own export declares it, on a clock verified identical to build 4's frame by +> frame β€” so neither `rest.t` nor the clock origin can be the cause. The +> **unitβ†’seconds constant** survives, and a capture the Decoder landed the same +> day puts it at **5 units per guest frame against the declared 2**, which would +> make this port ~2.4 s late β€” the right magnitude. That reading collides with +> the oracle's own plate measurement by a factor of 2.7, so it is asked, not +> acted on. + +## What the export actually declares + +`export/screens/title/press_start.json`, build 2, one element `ptbtn00`: + +| t | alpha | note | +|---|---|---| +| 0 | `0x00` | invisible, y = 560 | +| 214 | `0x00` | still invisible, y = 550 β€” it has drifted up unseen | +| **236** | `0xff` | **full** | +| 238 | `0xff` | last opaque frame | +| 244 | `0x00` | gone again | + +`rest.t = 236`. `settle_window = [214, 236, 225]`. + +πŸ”΄ **The corpus says the plate reaches `a=255` at `t=238`. It reaches it at +`t=236`.** 238 is the last frame at full alpha, not the arrival. Every +derivation quoting `238 βˆ’ 118 = 120 units = 2.000 s` is really `236 βˆ’ 118 = +118 units = 1.967 s`. The port has been printing both halves of the +contradiction in one sentence on every boot β€” *"plate reaches full alpha at +t=236 … 120 units after …"* β€” which is what an unchecked literal beside a +computed value looks like. Corrected in `boot.gd` and in `authored/flow.json`. +It moves the reconciliation by 0.033 s and overturns nothing. + +## Candidate 1 β€” `rest.t`. **Eliminated.** + +`rest.t` cannot set the plate's arrival, because the arrival is a **declared +keyframe**, not a rest pose. `ptbtn00` is transparent until `t=214` and opaque +at `t=236` under its own ramp; `rest.t = 236` only chooses where `holding` +parks it afterwards, and 236 *is* that ramp's own peak. Setting `rest.t` to any +other value moves where the plate stops, never when it starts. + +Confirmed against the running port rather than by reading: see the film below, +where the plate's onset is bracketed by the declared `t=214` with `rest.t` +untouched. + +⚠️ This does **not** rehabilitate `rest.t` generally. It is still wrong for +transients β€” `ptlogo_back2eff1` is a two-frame flash (0 at t52, `0xff` at +t54–56, 0 by t58) and its `rest.t = 54` is the flash *peak*, so `rest()` leaves +five of these burning at once. It is simply not in this defect's causal path. + +## Candidate 2 β€” the clock origin. **Eliminated, and measured.** + +Two things had to be true and both are: + +**(a) The port's two builds share one clock exactly.** `_advance` sets +`view.time_units = 0.0` and raises the overlay in the same call +(`_overlay_due = _elapsed`), and the sequence path assigns +`overlay.time_units = view.time_units` outright rather than integrating a second +delta. Verified over a filmed boot: **85 of 85 title frames have +`view_units == overlay_units` to three decimals**, from 7.812 to 679.182. There +is no drift and no offset to find. + +**(b) The title step begins when the previous step ends.** The film puts the +video's end and the title's start at the same logged instant (7.86 s / 7.92 s +across two runs), with no black hold between them. + +## Candidate 3 β€” the unitβ†’seconds constant. **NOT eliminated. It is the live one.** + +πŸ”΄ **This section said "eliminated by sign" and that was wrong.** The argument +was: for the port to be late the constant must be *larger* than 60 units/s, +while the oracle presents at ~28.1 fps and the corpus measured the idle title at +28.5 fps β€” both slower than nominal, so the game's units run slower, not faster. + +**That conflates two different quantities.** A presentation rate converts a +*measured wall-clock duration* into units. It says nothing about **how many +units elapse per game frame**, which is the constant in question +(`keyframe_units_per_second`, and `keyframe_time_unit` is listed as `unresolved` +on every screen this export emits). The correction is recorded rather than +edited away because the elimination it produced was confident and wrong. + +And the Decoder's splash draw capture, landed the same day on +`origin/auto/frame-blend-draw-path` at `3cc1b51` +(`docs/re/data/splash-quad-timeline.txt`), gives an **independent handle on that +constant** β€” from a screen with no bearing on the plate: + +| interval, publisher splash | guest, observed | this export, declared | units/frame | +|---|---|---|---| +| companion (`Q7`) onset β†’ sharp logo (`Q0`) onset | f4 β†’ f7, **3 frames** | t=0 β†’ t=15, **15 units** | **5.0** | +| companion onset β†’ its own peak | f4 β†’ f7 (a=240), **3 frames** | t=0 β†’ t=15 (a=255), **15 units** | **5.0** | +| sharp logo onset β†’ full alpha | f7 β†’ f10, **3 frames** | t=15 β†’ t=30, **15 units** | **5.0** | + +Three intervals, three ways, **5 units per guest frame** β€” against the declared +**2**. At 30 fps that is ~150 units/s rather than 60, and the plate at t=236 +would land at **1.57 s**, not 3.93 s. The port would be **~2.4 s late**, which +is the magnitude of a thing a human notices without a stopwatch. + +⚠️ **Two caveats, and they are why this is asked rather than acted on.** The +capture's frame index **skips** β€” 5, 8 and 12 are absent from Q7's eight draws β€” +and whether those are frames where the quad was genuinely not submitted or +presents the logger dropped changes the arithmetic. And the present rate during +a splash is not established. + +### πŸ”΄ And it collides with the oracle's own plate measurement + +The two captures cannot both be read at face value: + +| capture | implies | +|---|---| +| `title-plate-delay-measured.md` β€” 118 units in 2.135 s | **~55 units/s** | +| `splash-quad-timeline.txt` β€” 15 units in 3 frames | **~150 units/s** at 30 fps | + +A factor of **2.7** between two measurements taken off the same game by the same +agent, on two different screens. Either the two screens' keyframes are in +different units, or one of the two readings has an anchor wrong β€” which is the +same shape as the `t=118` / `t=160` question below. **Both are oracle +measurements and neither is the port's to resolve.** Asked in +[`BLOCKED.md`](BLOCKED.md) H3. + +## The film β€” and the instrument had to be fixed first + +`--film` scheduled frame `n` for `n Γ— interval` and reported nothing. One +1280Γ—720 `save_png` under llvmpipe costs ~0.24 s, so **a request for 0.05 s +delivered 247 frames in 60 s where 1 200 were asked for** β€” an achieved 4.1 fps +against a requested 20 β€” and the deficit accumulated silently into the frame +index. `f_071.png` still looked exactly like the frame that was meant to be +3.55 s in. + +[`TEMPORAL-VERIFICATION.md`](../agents/TEMPORAL-VERIFICATION.md) Β§1 is explicit +that this is not a slow capture but a *different* one, and that an instrument +which cannot report its own completeness may not be trusted (R3). So `--film` +now writes `_frames.tsv` β€” one row per frame carrying the elapsed second +it was **actually** taken at, the second it was **requested** for, the lag +between them, and both builds' clocks β€” appended and flushed as it goes, so a +run killed by `timeout` still leaves a complete index. It prints the achieved +rate against the requested one every 40 frames. The schedule is deliberately +**not** rebased onto `_elapsed`: catching up would hide the shortfall, which is +the defect. + +The run this page rests on: requested 20 fps, **achieved 15.9 fps for the first +120 frames and 11.3 fps by frame 200**, lag reaching 8.7 s. Stated because the +numbers below are quoted against `view_units` β€” the port's own timeline, which +the index records per frame β€” and never against a wall-clock instant. + +### Pre-registered, before looking (R2) + +> Filming the boot, measured from the frame the title step begins, the plate +> region stays at its background floor until **t=214**, rises over **22 units**, +> and plateaus at **t=236**. Accept Β±2 frames at each end. + +### What it did + +Region = the plate's own quad, x 127–640, y 525–575 (`pos [383,550]`, +`pivot [256,25]`, sprite 513Γ—50). Mean of the crop, one row per filmed frame: + +| `view_units` | region mean | | +|---|---|---| +| 7.8 β†’ 143.8 | 0.13765, **flat to 5 decimals** | nothing there | +| 151.9 β†’ 207.8 | 0.13773 β†’ 0.14341 | a slow 4 % drift, build 4's own | +| **207.8 β†’ 215.3** | 0.14341 β†’ **0.15348** | β¬… the step; brackets the declared **214** | +| 215.3 β†’ 239.8 | β†’ 0.21254 | the ramp | +| 239.8 β†’ 279.3 | β†’ 0.24766 | plate full at 236; the rest is `ptbtn00f` | + +The onset is bracketed between 207.8 and 215.3 at a frame spacing of ~7.5 units, +i.e. **within one frame of the declared 214**, and `rest.t` was never touched. +Prediction held. + +After 236 the region keeps oscillating β€” peaks near `u` 279 and 407, troughs +near 343 and 479, a period of **~128 units** against `ptbtn00f`'s declared +`loop_length_units = 120`. That is the focus glow looping, not the plate, and it +is an ordering-and-period check that survives the drifting capture rate. + +## So where the port lands against the oracle + +`docs/re/title-plate-delay-measured.md` is **not in this checkout** β€” it is on +`origin/auto/no-disc-and-menu-captures`, added at `fb536df` and cited by +`authored/flow.json` at `5b0a6e6`. Read there: + +| | run 1 | run 2 | **the port** | +|---|---|---|---| +| settled β†’ plate | 2.138 s | 2.132 s | **1.967 s** (118 units at 60/s) | +| first drawn β†’ plate | 3.781 s | 4.263 s | **3.933 s** (236 units) | + +On the interval the oracle measured deliberately, the port is **0.17 s early**. +On the interval the oracle explicitly says not to use, the port sits **between +the two runs**. No number anyone has taken makes this port's plate late. + +**The human watched both and says late, and that observation stands.** At +60 units/s the port reproduces every figure the plate capture carries β€” which is +the point: *reproducing that capture is not the same as being right*, because +the splash capture says the unit is 2.5Γ— off and would put the port 2.4 s late. +The two cannot both hold. The human's eye agrees with the splash capture. + +## A second anchor question, also the Decoder's + +**What does the oracle's "title settled" correspond to on the declared +timeline?** It is defined operationally β€” *"glyph counter first reads its +no-plate value 154"* β€” and our export offers **two** anchors, 42 units apart: + +| anchor | what it is | settled β†’ plate | vs measured 2.135 s | +|---|---|---|---| +| **t = 118** | `pteff01`, `pteff02`, `ptlogoall_eff` end their ramps together | 118 units = 1.967 s | βˆ’8 % | +| **t = 160** | `ptcopyright` reaches full alpha β€” the **last** element to finish building in, and the only one made of **glyphs** | 76 units = 1.267 s | βˆ’41 % | + +The port's reconciliation picked 118. The port's own `settle_time()` returns +**160** and the boot prints `settles at t=160` β€” so the two notions disagree +inside one binary. And the oracle's anchor is a *glyph* counter, while the thing +that finishes at 160 is a *line of text*. + +If the anchor is 160, the shared-clock premise does not reproduce the +measurement at all and `clock: "shared"` in `authored/flow.json` β€” which the +port authored from arithmetic and nobody has ever measured β€” is open. If it is +118, the reconciliation stands and H3 is not a timing defect. + +**This cannot be settled here.** It is a question about what the Decoder's +instrument was looking at, and it is asked in [`BLOCKED.md`](BLOCKED.md) H3. + +## Refutation attempts this iteration + +| claim | whose | outcome | +|---|---|---| +| the title's settle window is `[160, 236]` | Decoder, `5b0a6e6` | βœ… **survived** β€” our exporter still computes `[160, 236, 198]` under the corrected record layout | +| *"`ptlogo1` rests at t=251 and stops moving at t=42"* | Decoder, `5b0a6e6`, and the headline evidence for `rest.t β‰  settle` | ❌ **refuted on its evidence** β€” in the current export `ptlogo1.rest.t` is **42**, equal to when it stops moving. The record-layout fix repaired exactly this element. The *conclusion* survives on other elements (`ptlogo_back2eff1`, `pteff00`); the example no longer supports it | +| *"`ptbtn00` reaches a=255 at t=238"* β†’ `120 units` | the Port's own | ❌ **refuted** β€” 236, so 118 units | +| *"build 4 is still fading up from black until t=261; `pteff00` is 7 % opaque at 243"* | the Port's own | ❌ **refuted** β€” `pteff00` is opaque at t=0, clear by **t=16**, transparent until 261, then fades **to** black by 269. At t=243 it is 0 % opaque. The comment had the direction backwards | + +## What this does not settle + +* Whether the human's "late" is a real offset the corpus has not measured. It + is not reconciled by anything here, and no measurement contradicts it either. +* The `t=118` / `t=160` anchor. Asked, not guessed. +* Whether the plate **pulses** after arrival. The port holds it; the corpus + measures a ~2.24 s pulse. Filed already in `flow.json`'s `no_pulse_why`. +* Finding 4 (the splash fade/blur) is the Decoder's this iteration β€” it said so + on the message channel and is on `auto/frame-blend-draw-path`. + + +--- + +# The splash blur is an ASSET, not a pass β€” and this port already draws it + +Added the same iteration, after the Decoder answered play-test finding 4 from +GPU state (*no post-process on either splash*, `auto/frame-blend-draw-path` at +`3cc1b51`) and the human added the observation that **the logos go from blurred +to clear/sharp, in about one second**. + +Those two are not in tension. They resolve each other: + +> **Every logo ships a second, pre-blurred copy of itself, ~21 Γ— 20 px larger +> and concentric, which is drawn alone first and crossfades out as the sharp +> logo fades in.** That is "blurred β†’ sharp" with no post-process pass, no +> blur shader and no second render target. + +Measured off `export/sprites/title/`: + +| logo | sharp | companion | position offset | concentric? | +|---|---|---|---|---| +| `palogo_gamearts` | 500 Γ— 71 | 521 Γ— 91 | (βˆ’11, βˆ’10) | βœ… | +| `palogo_seta` | 240 Γ— 89 | 261 Γ— 110 | (βˆ’10, βˆ’11) | βœ… | +| `palogo_anima` | 388 Γ— 136 | 407 Γ— 156 | (βˆ’11, βˆ’9) | βœ… | +| `palogo_sqex` | 666 Γ— 68 | 686 Γ— 89 | (βˆ’10, βˆ’11) | βœ… | + +## πŸ”΄ "The port applies no blur at all" is false, and it came from this page's own side + +[`BLOCKED.md`](BLOCKED.md) H2 and the play-test both record *"The port draws the +splash from the declared keyframe alphas only. It applies **no blur at all**."* +The port does not apply a post-process blur β€” but it draws all seven quads of +the developer splash including the three blurred companions, and it has been +doing so all along. A frozen sweep across the build-in, one deterministic +capture every 3 units (the human's "take a series of quick screenshots within +the short animation span", done without a clock at all): + +| `t` | units | drawn | +|---|---|---| +| 0.00 s | 0 | background only | +| 0.05 – 0.25 s | 3 – 15 | background + **the three blurred companions alone** | +| 0.30 – 0.70 s | 18 – 42 | **all seven** β€” the crossfade | +| 0.75 – 0.80 s | 45 – 48 | background + the three sharp logos | + +Blurred first, then both, then sharp. The mechanism is reproduced. The claim +that it was not came from describing the renderer instead of running it. + +## Cross-check of the Decoder's quad mapping β€” βœ… survived, independently + +`splash-quad-timeline.txt` names its quads by NDC rectangle off the guest's +vertex stream. This export's declared rectangles, converted independently: + +| | this export | the guest | agreement | +|---|---|---|---| +| `palogo_sqex` | x[βˆ’0.517, +0.523] y[βˆ’0.106, +0.083] | `Q0` x[βˆ’0.520, +0.520] y[βˆ’0.100, +0.080] | **~2 px** | +| `palogo_sqex_eff` | x[βˆ’0.533, +0.539] y[βˆ’0.133, +0.114] | `Q7` x[βˆ’0.530, +0.540] y[βˆ’0.130, +0.120] | **~4 px** | + +Two decoders, two paths β€” a `.pak` read statically and a vertex stream logged +live β€” agreeing to a few pixels on both the sharp quad and the blurred one. +That is corroboration of the mapping and of `Q7` being the companion. + +## What is still open on the splash + +* **The extent.** The rectangles agree; the *alphas* are where the 5-units-per- + frame discrepancy above came from, and until that is settled it is not + possible to say whether the game holds the blurred copy longer than we do. + That is the same question as H3 and is asked once, there. +* Whether `palogo_eff0` (kind `0x10`, a full-screen quad, like the title's + `pteff00`) is the splash's fade veil. Not examined this iteration. diff --git a/port/scripts/boot.gd b/port/scripts/boot.gd index ea25ca99..081bd32a 100644 --- a/port/scripts/boot.gd +++ b/port/scripts/boot.gd @@ -83,10 +83,20 @@ var audio: MenuAudio = null ## 2 -- arrives later. ## ## **They share one clock, started together, and there is no authored delay.** -## The plate arrives at its own declared `t = 238`; build 4's visible build-in -## ends at `t = 118`; the 120-unit difference is 2.000 s, against an oracle that -## measured 2.138 s and 2.132 s at an emulator presenting 28.1 fps rather than -## 30. A port running at a true 30 Hz wants the declared 120, not the wall clock. +## The plate reaches full alpha at its own declared `t = 236`; build 4's effect +## quads end their ramps together at `t = 118`; the 118-unit difference is +## 1.967 s, against an oracle that measured 2.138 s and 2.132 s at an emulator +## presenting 28.1 fps rather than 30. +## +## πŸ”΄ THIS SAID `t = 238` AND `120 units = 2.000 s`, AND BOTH ARE OFF BY TWO. +## `ptbtn00`'s declared alpha reaches `0xff` at **t = 236** and *holds* it to +## t = 238, then ramps back to 0 by t = 244 -- so 238 is the last opaque frame, +## not the arrival. `236 - 118 = 118`. The port has been printing the +## contradiction in one sentence on every boot: *"plate reaches full alpha at +## t=236 ... 120 units after build 4's last build-in ramp at t=118"*. The +## correction moves the reconciliation by 0.033 s and changes no conclusion, +## which is why it survived; a cited number that is wrong is still wrong. +## See `docs/port/plate-arrival-halves.md`. ## ## A second ScreenView rather than a second screen inside one, because that is ## what "two builds at once" actually is: each has its own timeline, its own @@ -1053,14 +1063,59 @@ func _capture(path: String) -> void: ## A frame every 0.25 s for the whole run, so an unattended boot leaves a ## filmstrip behind rather than requiring someone to be watching it. +## +## πŸ”΄ A FRAME INDEX IS NOT A CLOCK, AND THIS TOOL WAS BEING READ AS ONE. +## +## `_film_next += _film_interval` schedules frame `n` for `n * interval`. When a +## frame costs more than the interval -- and it does; one 1280x720 `save_png` +## under llvmpipe measured ~0.24 s, so any `--film-interval` under a quarter of +## a second is unreachable -- the deficit accumulates silently and every later +## frame is taken late by a growing amount. Asking for 0.05 s over 60 s +## requested 1 200 frames and produced **247**, an achieved 4.1 fps against a +## requested 20. Nothing in the output said so, and `f_071.png` still looks +## exactly like the frame that was meant to be 3.55 s in. +## +## `TEMPORAL-VERIFICATION.md` Β§1 is explicit that a capture that asked for one +## rate and delivered another "is not a slow capture, it is a **different** +## capture", and that an instrument which cannot report its own completeness may +## not be trusted (R3). So the film now carries its own timebase: one TSV row per +## frame with the elapsed second it was ACTUALLY taken at and both builds' clocks +## at that moment, appended as it goes so a run killed by `timeout` still leaves +## a complete index. +## +## The schedule is deliberately NOT rebased onto `_elapsed`. Catching up would +## hide the shortfall, which is the defect; falling behind and saying so is the +## fix. The achieved rate is printed every 40 frames and the row is the record. func _film_capture() -> void: + var index_path := "%s_frames.tsv" % _film + var index := FileAccess.open(index_path, FileAccess.WRITE) + if index == null: + push_error("cannot write %s (%d)" % [index_path, FileAccess.get_open_error()]) + return + index.store_line("frame\telapsed_s\trequested_s\tlag_s\tscreen\tview_units\toverlay_units") + print("film: %s, requested one frame every %.3f s; index -> %s" + % [_film, _film_interval, index_path]) while true: await RenderingServer.frame_post_draw if _elapsed >= _film_next: + var at := _elapsed var img := viewport.get_texture().get_image() img.save_png("%s_%03d.png" % [_film, _film_frame]) + # The overlay's clock, or -1 where there is no second build. A blank + # would read as zero, and zero is a real instant on that timeline. + var over := -1.0 + if overlay != null: + over = overlay.time_units + index.store_line("%d\t%.4f\t%.4f\t%.4f\t%s\t%.3f\t%.3f" % [ + _film_frame, at, _film_next, at - _film_next, + String(view.screen.get("name", "?")) if view != null else "?", + view.time_units if view != null else -1.0, over]) + index.flush() _film_frame += 1 _film_next += _film_interval + if _film_frame % 40 == 0 and at > 0.0: + print("film: %d frames in %.2f s -- achieved %.2f fps against a requested %.2f" + % [_film_frame, at, _film_frame / at, 1.0 / _film_interval]) # Godot passes everything after `--` through untouched; take `--key=value`. @@ -1447,16 +1502,27 @@ func _raise_overlay(name: String) -> void: # no log line. if _sequence.is_empty(): return + # %d - 118, computed rather than spelled: the literal "120" here disagreed + # with the "t=236" printed beside it in the same sentence, for weeks. print(" plate reaches full alpha at t=%d (%.2f s on the shared clock), \ -120 units after build 4's last build-in ramp at t=118" - % [int(overlay.settle_time()), visible_at]) +%d units after build 4's effect quads end together at t=118" + % [int(overlay.settle_time()), visible_at, int(overlay.settle_time()) - 118]) if not _play and _film == "": - # The LATER of the two, not the overlay's alone. The plate arrives at - # t=238 and build 4 is still fading up from black until t=261 -- its - # `pteff00` quad is 7 % opaque at 243 -- so quitting when the plate - # lands photographs a title that has not finished presenting. The first - # capture taken this way was visibly darker than the one before it, and - # nothing in the log said why. + # The LATER of the two, not the overlay's alone. + # + # πŸ”΄ THE REASON GIVEN HERE WAS BACKWARDS AND IS REPLACED. It said build 4 + # "is still fading up from black until t=261 -- its `pteff00` quad is + # 7 % opaque at 243". `pteff00` is the screen's black veil and it does + # BOTH fades: opaque at t=0, clear by **t=16**, transparent all the way + # to t=261, then back to opaque by t=269. At t=243 it is 0 % opaque, not + # 7 %, and 261..269 is the fade-OUT. So build 4 finishes arriving 16 + # units in, not 261. + # + # The line still stands, for the reason underneath it rather than the + # one that was written: build 4's own last hold is t=160 (`ptcopyright` + # reaching full alpha) and the plate's is t=236, so ending on the + # overlay alone would still be ending on the earlier of two clocks. The + # darker capture that prompted this was real; the explanation was not. var ends_at := maxf(view.settle_time(), overlay.settle_time()) / view.units_per_second _overlay_quit_at = _elapsed - (view.time_units / view.units_per_second) + ends_at print(" boot ends at %.2f s, once both builds have arrived (t=%d)" diff --git a/tools/port/check-all b/tools/port/check-all index a6c9b5ad..0838983b 100755 --- a/tools/port/check-all +++ b/tools/port/check-all @@ -36,6 +36,35 @@ step() { # name, expectation, command... esac } +# πŸ”΄ GODOT'S SCRIPT CLASS LIST IS A BUILD CACHE, AND IT IS GITIGNORED. +# +# `port/.godot/global_script_class_cache.cfg` is what resolves a `class_name`, +# and `.gitignore` excludes `port/.godot/` -- correctly, it is derived. So a +# checkout that MERGES a commit adding a new `class_name` keeps a cache that +# does not list it, and every script referencing the new class fails to parse: +# +# SCRIPT ERROR: Parse Error: Identifier "Gamepad" not declared in the current scope. +# ERROR: Failed to load script "res://scripts/boot.gd" with error "Parse error". +# +# The whole project then refuses to load, from `--screen` to `--boot`, and the +# error names the symbol rather than the cache -- so it reads as a missing file +# or a bad merge. This is exactly what merging the human's input fix did on +# 2026-09-01: `gamepad.gd` arrived with `class_name Gamepad`, the cache in this +# container was warm and predated it, and the port did not run at all. +# +# A fresh clone has no `.godot/` and Godot builds one on first run, so nobody +# hits this until they merge into a working tree -- which is every iteration of +# this loop. Reimporting is cheap and idempotent, so it runs unconditionally +# rather than behind a staleness test that would itself need to be right. +echo "godot: reimporting so class_name resolves against a fresh cache" +DISPLAY="$DISPLAY" godot --headless --path port --import >"$OUT/godot-import.log" 2>&1 \ + || { echo " πŸ”΄ godot --import FAILED -- see $OUT/godot-import.log"; fail=1; } +for c in $(grep -ho '^class_name [A-Za-z_][A-Za-z0-9_]*' port/scripts/*.gd | awk '{print $2}'); do + grep -q "\"$c\"" port/.godot/global_script_class_cache.cfg 2>/dev/null \ + || { printf ' %-24s πŸ”΄ class_name %s is not in the class cache\n' class-cache "$c"; fail=1; } +done +echo + echo "asserting checks:" step format-validator must-pass "$BIN" check # The contract lives on a branch this checkout does not merge: HANDOFF on `main`