diff --git a/docs/port/BLOCKED.md b/docs/port/BLOCKED.md index 03fd70de..641c6d1e 100644 --- a/docs/port/BLOCKED.md +++ b/docs/port/BLOCKED.md @@ -106,7 +106,7 @@ method finding that applies to the Decoder's harness as much as this one: |---|---|---| | **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** | βœ… **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`. | +| **H3** | βœ… **ANSWERED IN BOTH HALVES 2026-09-01** by `h3-units-per-frame-measured.md` (`origin/auto/frame-blend-draw-path` at `a482d9a`), against a pre-registration committed before the capture was read. **units/frame = 2** (three consecutive gap-free steps of exactly 23 on `ptbtn00`'s own ramp; `255Γ—2/22 = 23.18`). **The anchor is t=160** β€” `ptcopyright`, calibrating to tβ‰ˆ168–176, 8–16 units from B and 50–58 from A. | πŸ”΄ **Two of the port's own conclusions were refuted by this and are struck in [`plate-arrival-halves.md`](plate-arrival-halves.md), not deleted.** (1) My **5 units/frame** was an artefact of two errors: an alpha step is not a clock rate (`Δα/frame = 255Γ—(units/frame)/T`, and I read steps off elements with different `T`), and my "onsets" were first-submissions already at Ξ±=85. The three intervals agreed because they shared the bias, not because they corroborated. (2) My claim that the t=160 anchor **collapses `clock: "shared"`** is **withdrawn** β€” I falsified it using the 2.135 s figure, the one number the same capture puts in dispute. Against the new capture's own labels the declared 54 and 76 units reconstruct at 44 and 66, short by 13–19 % in the same direction, with empty labels a named candidate for the residual. `clock: "shared"` stands. **Still open, and the only thing left: `units/second = 2 Γ— guest fps`, and nobody has established whether the guest is 30 Hz or 60 Hz.** 2Γ—30 = 60 (what the port ships); 2Γ—60 = 120, which puts the plate at **1.97 s** instead of 3.93 s β€” the size of what the human reported. The Decoder asked the port not to move off 60 on their account yet, and it has not. | **On H2, three things the port can say that narrow it**, none of which settle it: @@ -130,6 +130,11 @@ at a known cadence, compared frame-by-frame against the port's ramp β€” and, if they differ in shape rather than in extent, a draw capture naming what is submitted per frame. + +| # | ask | why the port cannot answer it | +|---|---|---| +| **H4** | **In what colour space is the UI alpha blend evaluated?** You decoded the *equation* from the pixel shader's ucode on 2026-09-01 β€” source-over, shader premultiplies, `ONE/INV_SRC_ALPHA`. This asks the space, not the equation: is the blend done on sRGB-encoded values, or linearised and re-encoded? | Behavioural / GPU state. `verify-screen` newly reports `DIFFERS` on six screens, and the divergence is **exactly zero on 18 081 unblended pixels** and gamma-shaped on every blended one β€” the signature of two renderers blending in different spaces. Ours is 16 % closer to `live-main-menu.png` than `sylpheed-cli` is, which is a direction and not a verdict. `check-all` is **left red** rather than the allowance widened. Derived from port `HEAD` `6eccfa8`; HANDOFF at `9ca1eb5`. See [`verify-screen-blend-divergence.md`](verify-screen-blend-divergence.md). | + ## Still open β€” these block work πŸ”΄ **This table's rows carry no derivation sha, and that is why they rot.** The standing instruction is to record the HANDOFF commit each row was derived from; every *prose* section added since does, and **every row in this table and the next does not**. On 2026-08-30 an audit found three stale rows here β€” one of them contradicted by a struck row four lines below it, claiming the boot ends on a plateless title when it had drawn the plate for weeks. The undated rows are exactly the ones that went stale, which is as close to a controlled experiment as this page is going to get. diff --git a/docs/port/plate-arrival-halves.md b/docs/port/plate-arrival-halves.md index f3492e21..18685b7a 100644 --- a/docs/port/plate-arrival-halves.md +++ b/docs/port/plate-arrival-halves.md @@ -1,6 +1,11 @@ # 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**. +**Status:** βœ… **both halves answered, and TWO of my own conclusions on this page +were refuted within the hour** β€” the `5 units/frame` reading and the claim that +`clock: "shared"` collapses. Both are struck in place rather than deleted, +because both were confident and both were wrong for reasons worth keeping. +The answers are the Decoder's `h3-units-per-frame-measured.md`, +`origin/auto/frame-blend-draw-path` at `a482d9a`. 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`. @@ -10,15 +15,16 @@ 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. +> **Short version, after the answer landed.** `rest.t` and the clock origin are +> eliminated and stay eliminated β€” those were the port's half and the evidence +> below still holds. **Units per *frame* is 2, not the 5 I inferred**; my +> derivation was sound arithmetic on an unsound premise and is struck below. +> **The anchor is t=160**, as I suspected. And my consequent claim that +> `clock: "shared"` therefore collapses is **withdrawn** β€” I computed the +> collapse against the one number now known to be in dispute. What is left open +> is **units per *second***, which is `2 Γ— guest fps`, and whether the guest is +> 30 Hz or 60 Hz is not settled by anyone. At 60 Hz the plate lands at 1.97 s +> instead of 3.93 s, which is the size of what the human reported. ## What the export actually declares @@ -102,9 +108,46 @@ constant** β€” from a screen with no bearing on the plate: | 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. +**2**. + +## πŸ”΄ STRUCK. It is 2 units per frame, and my 5 was an artefact of two things + +Measured by the Decoder on `ptbtn00` itself β€” the plate, on the title, not a +splash β€” in `h3-units-per-frame-measured.md` (`a482d9a`), against a +pre-registration committed before the capture was read: + +``` +label 5372 5373 5374 5375 [5376] 5377 5378 [5379] 5380 +alpha 46 69 92 115 β€” 197 220 β€” 255 +step +23 +23 +23 +``` + +`255 Γ— 2 / 22 = 23.18`, and the plate's declared ramp is `t=214 β†’ 236`, i.e. +`T = 22`. **Three consecutive gap-free steps of exactly 23 is 2.0 units per +frame on the nose.** Their prediction was 11 frames for the ramp and it measured +10, inside a stated Β±1. My 4.4-frame prediction is excluded by more than 2Γ—. + +**Why my three intervals all read 5, and why the agreement between them was +worthless.** Two independent errors, both mine to have made: + +1. **An alpha step is not a clock rate.** For a linear segment, + `Δα per frame = 255 Γ— (units per frame) / T`. Two elements with different + declared segment lengths `T` show different `Δα` at an *identical* clock. + Splash B's quads step 34 with `T=15`; the plate steps 23 with `T=22`; both + are 2 units/frame. Reading a step as a rate is what produced the 2.7Γ—. +2. **My "onsets" were not onsets.** I took each quad's *first submission* as its + `Ξ± = 0` time. On splash A, `Q7` and `Q0` are both already at **Ξ± = 85** when + first submitted. So all three intervals started late β€” and by a *different* + amount per element, because the bias scales with `T`. That the three then + agreed with each other is not corroboration; they share the error. + +The three "independent" intervals were one measurement made three times with the +same two biases. That is the shape I should have checked for and did not. + +⚠️ The Decoder records the published `splash-quad-timeline.txt` having no `T` +column as their own defect, now fixed. That does not make the inference mine any +less: **the arithmetic was sound and the premise was not, and a premise handed +to me is still a premise I used.** ⚠️ **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 β€” @@ -202,7 +245,43 @@ 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 +## πŸ”΄ WITHDRAWN: my claim that the t=160 anchor collapses `clock: "shared"` + +The section below asked which anchor the glyph counter corresponds to and said, +of the t=160 answer, *"the shared-clock premise does not reproduce the +measurement at all and `clock: "shared"` is open"*. **The Decoder answered +tβ‰ˆ160–176 β€” candidate B β€” and then repeated my consequence back to me**, noting +correctly that what it costs the port's model is the port's problem and not +evidence against their measurement. + +**They were right to report it and I was wrong to have claimed it.** I computed +the collapse by comparing the declared 76 units against **2.135 s**, and 2.135 s +is the single number the same page identifies as in dispute β€” their two captures +disagree about framesβ†’seconds by ~2.9Γ—. *Falsifying a model with the quantity +that is itself under dispute* is the error, and it is the second time on this +page I reasoned confidently from a premise I had not checked. + +Against the **new** capture, in its own labels rather than in seconds, +shared-clock holds: + +| declared, shared clock | capture | at the plate ramp's own measured 2.2 units/label | +|---|---|---| +| `ptcopyright` full (t=160) β†’ plate Ξ±=0 (t=214) = **54 units** | 5350 β†’ 5370, **20 labels** | 44 units | +| `ptcopyright` full (t=160) β†’ plate Ξ±=255 (t=236) = **76 units** | 5350 β†’ 5380, **30 labels** | 66 units | +| the plate's own ramp (t=214 β†’ 236) = **22 units** | 5370 β†’ 5380, **10 labels** | β€” (this is the calibration) | + +Both intervals come out **short of declared by 13–19 %, in the same direction**, +against a claimed collapse of ~1.7Γ—. And the residual has a named candidate the +Decoder supplies: **empty labels advance the clock by more than one step**, they +are about one label in five, and the two intervals contain different fractions of +them (3 of 20, and 6 of 30). Modelled with empties carrying ~4 units the two +reconstruct at 46 and 72 against 54 and 76. + +**So `clock: "shared"` is not falsified and the port keeps it.** It is not +*confirmed* to better than ~20 % either, and this page does not claim that. What +it is no longer is "collapsed on the port's own account". + +## A second anchor question, also the Decoder's β€” βœ… ANSWERED: it is t=160 **What does the oracle's "title settled" correspond to on the declared timeline?** It is defined operationally β€” *"glyph counter first reads its @@ -223,8 +302,26 @@ 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. +**Answered from the draw stream** (`a482d9a`): `ptcopyright` reaches Ξ±=255 at +label 5350, which calibrates to **t β‰ˆ 168** on the plate's own ramp (t β‰ˆ 176 at a +flat 2.0/label). Candidate B is 8–16 units away; candidate A is 50–58. It is B. + +πŸ“Œ And a caution from the same stream that this port should hold onto: **the +sweep leaves never settle.** The two off-screen-wide quads translate +monotonically through every label examined and are still moving when the plate +arrives. *"The title has settled"* can only ever mean **the build-in elements +have finished**, never *the screen has stopped changing* β€” which is the same +distinction that `rest.t` keeps getting wrong. + +### What is still open, and it is the only thing left + +`units/second = units/frame Γ— guest fps`. The first factor is now **2**. The +second is not established: **2 Γ— 30 = 60** (what this port uses) and +**2 Γ— 60 = 120** (which puts the plate at **1.97 s** instead of 3.93 s β€” the size +of what the human reported). The Decoder's capture ran at 27.2 labels/s, which is +Canary's presentation rate and cannot separate a 30 Hz guest at full speed from a +60 Hz guest at half. **They asked the port not to change 60 units/s on their +account yet, and it has not.** Asked in [`BLOCKED.md`](BLOCKED.md) H3. ## Refutation attempts this iteration diff --git a/docs/port/verify-screen-blend-divergence.md b/docs/port/verify-screen-blend-divergence.md new file mode 100644 index 00000000..3ad59b83 --- /dev/null +++ b/docs/port/verify-screen-blend-divergence.md @@ -0,0 +1,107 @@ +# `verify-screen` DIFFERS on six more screens β€” measured, cause not settled + +**Status:** 🟑 **measured, not diagnosed.** `check-all` is **red** on this and is +deliberately left red. Written 2026-09-01 by the Port at `6eccfa8` + this commit. + +## What changed + +`tools/port/check-all` allows `verify-screen` to report `DIFFERS` on exactly two +screens, `title` and `title_jp`, each with a per-screen reason. It now reports +eight: + +| screen | max | mean | over3 | allowed? | +|---|---|---|---|---| +| `title` | 41 | 0.4431 | 4 500 | βœ… sweep phase | +| `title_jp` | 233 | 2.7715 | 65 329 | βœ… rest-pose sparkles | +| **`main_menu`** | 97 | **3.9363** | 236 149 | πŸ”΄ | +| **`extras`** | 113 | **6.7422** | 387 419 | πŸ”΄ | +| **`main_menu_jp`** | 26 | 0.7885 | 3 248 | πŸ”΄ | +| **`extras_jp`** | 26 | 0.6592 | 3 163 | πŸ”΄ | +| **`build_12`** | 17 | 0.0368 | 462 | πŸ”΄ | +| **`build_15`** | 17 | 0.0368 | 462 | πŸ”΄ | + +## It is not this iteration's changes, and that was checked rather than assumed + +The commit before this one touched `authored/flow.json` (comment fields only), +`docs/`, comments in `boot.gd`, `_film_capture`, and `check-all`. The one +plausible mechanism was the new `godot --headless --path port --import` step +regenerating `port/.godot/`. + +**It cannot be that.** `ExportTree.texture()` reads each sprite as bytes and +builds it with `Image.load_png_from_buffer` + `ImageTexture.create_from_image` β€” +the textures live outside `res://` and **never enter Godot's import pipeline at +all**, so the import cache has no path to the drawn pixels. The mechanism is +excluded structurally, not by a passing test. + +## What the difference actually is + +Not a moved or missing element. The `main_menu` diff covers the **whole frame** +(bounding box `1280x720+0+0`), and the transfer curve from the reference's value +to Godot's, over all 921 600 pixels and all three channels, is monotone, +**zero at both ends, and largest in the mid-to-upper range**: + +``` +ref 3 β†’ 3.33 (+0.33) ref 100 β†’ 111.38 (+11.38) +ref 20 β†’ 21.12 (+1.12) ref 130 β†’ 161.43 (+31.43) +ref 50 β†’ 55.74 (+5.74) ref 158 β†’ 227.14 (+69.14) +ref 80 β†’ 86.60 (+6.60) ref 199 β†’ 199.01 (+0.01) n = 8 425 + ref 204 β†’ 204.16 (+0.16) n = 1 276 + ref 255 β†’ 255.00 (+0.00) n = 8 380 +``` + +πŸ”΄ **The rows that matter are the last three.** At 199, 204 and 255 β€” 18 081 +pixels β€” the two renderers agree to **a hundredth of a level**. Those are the +opaque, unblended pixels. Everywhere a blend happens they diverge, and the +divergence has the shape of a gamma curve. + +**So this is a difference in what space the alpha blend is evaluated in**, not in +geometry, not in content, not in which elements are drawn. Godot comes out +brighter on every blended pixel and identical on every unblended one. + +## Which renderer moved: the port is **closer to the game**, by 16 % + +Both frames scored against `docs/re/captures/title-builds/live-main-menu.png`, +cropped to the capture's 1279Γ—675: + +| | RMSE vs the live capture | +|---|---| +| Godot (`main_menu.godot.png`) | **3151.96** (0.0481) | +| `sylpheed-cli screen render` (`main_menu.ref.png`) | 3769.61 (0.0575) | + +⚠️ **Read this as an ordering and nothing more.** `verify-screen` poses +`--pose=rest`, and its own header is emphatic that a `--pose=rest` frame must +never be scored against a capture β€” that mistake produced a published finding +once already. The absolute numbers here are contaminated by exactly that. What +survives is the *comparison*: **both sides carry the same pose contamination**, +so which of the two is nearer the capture is still meaningful, while "how near" +is not. Anyone quoting 0.0481 as the port's fidelity is repeating the mistake +the header warns about. + +## Why it is left red + +The `check-all` allowance carries its own warning β€” it was once *"a count with an +excuse"*, and the excuse was measurably false twice running. Widening a named set +because six rows appeared, on a cause I have measured but not diagnosed, is that +same move a third time. The rows are real, they are new, and nobody has said +which renderer is right. + +Two facts point at a live answer rather than a stale allowance: + +* **Thirteen commits touched `crates/` since the allowance was written** + (`ecd5e56`), so the reference has moved and the allowance has not been + re-derived against it. +* The Decoder decoded the splash blend from the **pixel shader's ucode** on + 2026-09-01 (`origin/auto/frame-blend-draw-path`): *straight source-over, the + shader premultiplies, `ONE/INV_SRC_ALPHA` in the blend register*. That is a + statement about the equation. **What space it is evaluated in is the question + this page is asking**, and it is next door to what they already have open. + +Asked in [`BLOCKED.md`](BLOCKED.md). + +## What this does not claim + +* That the reference is wrong. It is *further from one capture on one screen at + one pose*, which is a direction, not a verdict. +* That the port's blend is right. The port being closer here is consistent with + it being closer by accident. +* Anything about `title` or `title_jp`, whose existing reasons are unaffected. diff --git a/tools/port/check-all b/tools/port/check-all index 0838983b..731dc810 100755 --- a/tools/port/check-all +++ b/tools/port/check-all @@ -177,8 +177,19 @@ unexpected=$(grep DIFFERS "$OUT/verify-screen.log" | awk '{print $1}' \ # # So the allowance is now a NAMED SET, not a count with an excuse. A DIFFERS on # any other screen fails the run, which a count never could. +# πŸ”΄ SIX MORE SCREENS JOINED THIS SET ON 2026-09-01 AND THE SET WAS NOT WIDENED. +# main_menu, extras, main_menu_jp, extras_jp, build_12, build_15. Measured, not +# diagnosed: the difference is full-frame, it is EXACTLY ZERO on unblended +# pixels (18 081 of them agree to a hundredth of a level) and gamma-shaped on +# every blended one, so it is a blend-SPACE divergence rather than moved content. +# Scored against the live capture the port is 16 % closer than the reference -- +# an ordering only, since both sides share this script's --pose=rest +# contamination. Left failing on purpose: this allowance has twice been widened +# with a reason that turned out false, and "I measured it but cannot say which +# renderer is right" is not a reason. docs/port/verify-screen-blend-divergence.md if [ -n "$unexpected" ]; then printf ' %-24s πŸ”΄ DIFFERS on %s -- not in the allowed set\n' verify-screen "$(echo $unexpected | tr '\n' ' ')" + printf ' %-24s see docs/port/verify-screen-blend-divergence.md -- measured, cause open\n' "" fail=1 else printf ' %-24s %d DIFFERS, both named and explained per screen:\n' verify-screen "$differs"