From 94c38cb384ad5787a0aec598a1b84e6b58797fb1 Mon Sep 17 00:00:00 2001 From: Sylpheed port agent Date: Tue, 1 Sep 2026 17:26:16 +0000 Subject: [PATCH] port: refuse the splash rate -- a 160-unit hold cannot outlast the 210-unit group containing it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Decoder proposes a per-screen rate: ~57 units/s for the title, ~35-40 for the splashes, on the ground that a splash at 60 runs 1.5-1.7x too fast. That would be a timeline cause for the human's finding 4, so it matters. THE PORT DID NOT MOVE, and this is why. The 160-unit hold is identifiable: exactly one interval in either splash is 160 units, the DEVELOPER splash's a=255 plateau at t=30..190, inside its declared group t=0..210. So: the 160-unit hold measured 4.514 guest s (1 run) -> 35.4 units/s the 210-unit group AROUND IT measured 3.37/3.50/3.51 s (3 cold boots) -> 60.7 A sub-interval cannot outlast the interval containing it. That is not two methods disagreeing about a rate, it is arithmetic, and one of the two is wrong. Both dwell measurements corroborate ~60 on exactly the two screens now put at 35-39: publisher 255 units in 4.30/4.60/4.37 = 57.7, developer 210 units in 3.37/3.50/3.51 = 60.7, the developer agreeing with its declared value to 1.1 % and two of its runs to 0.3 %. At 35.4 the declared groups would run 5.93 s and 7.20 s -- each splash ~70 % longer than three cold boots measured it. The one escape route is named rather than dismissed: the new figure is in GUEST seconds and the corpus in wall-clock. The known gap between those is Canary at ~28.1 fps against 30, which is 6 % and the wrong direction to close 1.7x. It is their instrument and their clock, so it is asked (H7), not ruled out. ⚠️ The STRUCTURAL claim may still be right -- one rate covering every screen is what is under challenge, and the title's 56.8 does sit ~5 % off the splashes' 58-61. If a per-screen rate is real the port wants the MECHANISM, a field or a GamePart constant, not two authored numbers. That is their next item. Recorded in authored/timing.json so the next iteration does not quietly adopt it, with an explicit instruction not to split the difference: averaging two measurements that cannot both be true is not a third measurement. Also this iteration: H6 -- answered in principle, blocked in practice. They are right that the blend is a decoded disc field (T8aD +0x04 bit 0x02) and that deriving beats transcribing, which would delete an authored entry. But sylpheed-formats does not EXPOSE +0x04 -- Element surfaces kind (+40), parent (+32), pivot, keyframes, focus_link -- and sylpheed-export consumes formats by git TAG, not workspace path, so I cannot reach the parser. Asked: surface the word and tag it. πŸ”΄ And a negative worth having, since it is the obvious thing to try: kind_raw in this export is NOT that field. Its bit 0x2 against the additive map over four screens is ANTI-correlated -- 0 of 14 mapped elements have it set and 9 unmapped ones do. Anyone reaching for `kind_raw & 2` gets the additive set exactly inverted. H5 -- closed. My shared-element hypothesis is refuted: build_12 and build_15 have identical declaration bodies, so identical statistics are one fact. The pgloading_loop5 localisation stands and is the better answer. βœ… And the good news, recorded where the splash work lives: "the declared keyframe timeline reproduces the captured splash" settles in favour of the timeline -- 50 captured alphas, worst error one level in 255, no renderer in the chain. The port's splash keyframes were never the defect. Which leaves the RATE as the remaining candidate for what the human saw, and the rate is what this commit declines to change. Not settled: which of the two splash measurements survives; where a per-screen rate would come from; the +0x04 exposure; H1 (key repeat). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX --- authored/timing.json | 29 ++++- docs/port/BLOCKED.md | 5 +- docs/port/splash-rate-contradiction.md | 119 ++++++++++++++++++++ docs/port/verify-screen-blend-divergence.md | 22 +++- 4 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 docs/port/splash-rate-contradiction.md diff --git a/authored/timing.json b/authored/timing.json index f317ddb1..a28eafc8 100644 --- a/authored/timing.json +++ b/authored/timing.json @@ -63,7 +63,34 @@ "they tick at the same rate.", "", "⚠️ If anyone re-fits this from alpha: DROP THE LAST STEP of a ramp. It clamps", - "at 255 and reports more elapsed time than it consumed -- worth 4 % on the plate." + "at 255 and reports more elapsed time than it consumed -- worth 4 % on the plate.", + "", + "πŸ”΄ 2026-09-01 (later) β€” A PER-SCREEN RATE WAS PROPOSED AND NOT ADOPTED.", + "", + "docs/re/splash-declared-vs-captured.md proposes ~57 units/s for the title and", + "~35-40 for the splashes, i.e. that one constant cannot be right and that a", + "splash at 60 runs 1.5-1.7x too fast. THE PORT DID NOT MOVE, and the reason is", + "arithmetic on a measurement already cited in this file:", + "", + " the 160-unit hold is the DEVELOPER splash's a=255 plateau, t=30..190, and it", + " is measured at 4.514 s. The 210-unit group CONTAINING it is measured at", + " 3.37/3.50/3.51 s over three cold boots (the dwell_why block below). A", + " sub-interval cannot outlast the interval containing it.", + "", + "The same three boots put the splashes at 57.7 and 60.7 units/s -- corroborating", + "60 on exactly the two screens the new figure puts at 35-39. At 35.4 the declared", + "groups would run 5.93 s and 7.20 s against corpus dwells of 3.37-3.51 and", + "4.30-4.60, i.e. each splash ~70 % longer than measured.", + "", + "⚠️ DO NOT ADOPT EITHER NUMBER UNTIL THAT IS RESOLVED, and do not split the", + "difference -- averaging two measurements that cannot both be true is not a", + "third measurement. docs/port/splash-rate-contradiction.md, asked as BLOCKED H7.", + "", + "⚠️ AND THE STRUCTURAL CLAIM MAY STILL BE RIGHT. 'One rate cannot cover every", + "screen' is a claim about the format, and the title's 56.8 does sit ~5 % off the", + "splashes' 58-61. If a per-screen rate is real this file should carry a MECHANISM", + "-- a field or a GamePart constant -- not two authored numbers. The Decoder has", + "'where the per-GamePart rate comes from' as its next item." ], "kind": "measured", "source": "/reborn docs/port/HANDOFF.md Q1, docs/re/ui-keyframe-time-unit.md, docs/re/units-per-second-measured.md", diff --git a/docs/port/BLOCKED.md b/docs/port/BLOCKED.md index db377fa8..6fd04f9b 100644 --- a/docs/port/BLOCKED.md +++ b/docs/port/BLOCKED.md @@ -134,8 +134,9 @@ submitted per frame. | # | ask | why the port cannot answer it | |---|---|---| | **H4** | βœ… **ANSWERED 2026-09-01 β€” and it was not the cause.** The game blends in the **encoded** space: `RB_COLOR_INFO.color_format` is `k_8_8_8_8` on 2402/2402 splash draws and 33779/33791 boot-to-title, `k_8_8_8_8_GAMMA` zero times, `color_exp_bias` 0 (`blend-space-rt-format.txt`). | πŸ”΄ **My premise was wrong and the answer exonerates everybody.** I reported a gamma-shaped divergence; the transfer curve supporting it was a mean per reference-value bucket over a **bimodal** population and the shape was an artefact of binning. Measured with a pre-registered control, **Godot blends encoded too** (alphas 64/128/192 β†’ 64/128/192 exactly, linear-then-re-encode excluded by 33–74 levels), and so does the reference (integer math on 8-bit values). All three agree; no change needed anywhere. **The real cause of the `verify-screen` rows is ADDITIVE**: the port draws 5 elements additive on `main_menu` and 9 on `extras` β€” transcribed from your per-draw `RB_BLENDCONTROL0` log β€” and `ui_layout.rs` has no additive path at all. The divergence tracks the set size (9 β†’ 6.74, 5 β†’ 3.94, 0 β†’ ~0.7). See [`verify-screen-blend-divergence.md`](verify-screen-blend-divergence.md). | -| **H5** | 🟑 **PARTLY DIAGNOSED 2026-09-01, and two hypotheses of mine died getting there.** `build_12`/`build_15`: **all 951 differing pixels lie inside one element**, `pgloading_loop5` (`pgloading_ring.png`, 333Γ—276 at `[1,444]`), diff bbox `x 69..301, y 478..710` entirely within it. That also explains why `build_00`/`build_01` **agree** β€” the port's draw log shows they draw 7 elements to `build_12`'s 10, and `pgloading_loop5` is one of the three they never draw. `main_menu_jp`/`extras_jp` carry the same sweep leaves (`ptloop01/pteff03` rot 30, `ptloop02/pteff03a` rot βˆ’45) whose phase residual is the named reason for `title`, and their means (0.79, 0.66) sit with `title`'s 0.44 rather than their EN twins' 3.94/6.74 β€” consistent, not established. | πŸ”΄ **Refuted on the way, and recorded so the next reader does not repeat it:** `ui_layout.rs:86` states *"`rotation_deg` is decoded but NOT rendered"*, and the port does render it. A census gave *every screen with non-zero rest rotation DIFFERS and every screen without agrees*, 6 of 6 both ways. **It is still wrong** β€” `build_00`/`build_01` carry two 360Β° spinners each, the port draws both, and they agree. A rule holding on 14 of 16 and failing on the two cases nobody had looked at is a rule fitted to its own rows. **Ask:** why the two renderers disagree on `pgloading_loop5` specifically β€” its top-level keyframes hold `a=0x7f` around `rest.t=24` while its leaf expands `pgloading_ring` from scale 0 to 1000 over t=30…130, and the port's `draw_leaf_for` lists only `ptloop01`/`ptloop02`. Not blocking; `check-all` stays red on these four. | -| **H6** | **Does the JP build draw the same elements additive as the EN build?** Your `RB_BLENDCONTROL0` log was taken driving the game to the **English** screens. `main_menu_jp` contains exactly the six elements `main_menu` marks additive and `extras_jp` all nine of `extras`'. | The port's map is keyed by screen name, so it currently draws them additive on EN and **alpha-over on JP** β€” asserting by omission that the JP build differs, which is the *less* likely possibility. Extending the map would extrapolate your measurement onto a build nobody drove to, so the port has **not** extended it and has instead declared the asymmetry in `authored/rendering.json`'s `additive_elements_why`. ⚠️ **Low priority and say so if it is not worth a boot**: PORT-MISSION Β§7 puts localisation beyond English out of scope and the JP screens are not in the boot path. It costs the port only two `verify-screen` rows. Port `HEAD` `26cf6ec`; HANDOFF `9ca1eb5`. | +| **H5** | βœ… **CLOSED 2026-09-01.** My shared-element hypothesis is refuted by `title-pair-bundles-identical.txt`: `build_12` and `build_15` have **identical declaration bodies** β€” same elements, sprites, pivots, keyframes, geometry β€” so identical statistics are one fact, not two coincidences. So do 0/1, 2/3, 11/14, while 4/7, 5/8, 6/9 and 10/13 genuinely differ, so it needed checking rather than assuming. | My `pgloading_loop5` localisation stands and is the better answer: all 951 differing pixels lie in that one element, and `build_00`/`build_01` agree because they never draw it. What is still unexplained is why the two renderers disagree **on that element** β€” its top-level keyframes hold `a=0x7f` around `rest.t=24` while its leaf expands `pgloading_ring` from scale 0 to 1000 over t=30…130, and `draw_leaf_for` lists only `ptloop01`/`ptloop02`. Not blocking. | +| **H6** | 🟑 **ANSWERED IN PRINCIPLE, BLOCKED IN PRACTICE β€” and I need one thing from you.** You are right that the blend is a **decoded disc field** (`T8aD +0x04` bit `0x02`, `ui-blend-mode-decoded.md`) and that deriving it beats transcribing a table β€” that also deletes an authored entry, which the mission counts as progress. **But `sylpheed-formats` does not expose `+0x04`.** `ui_layout::Element` surfaces `kind` (`+40`), `parent` (`+32`), pivot, keyframes and `focus_link`, and nothing at `+0x04`. | πŸ”΄ **A useful negative, so nobody repeats it: `kind_raw` in my export is NOT that field.** I tested its bit `0x2` against the additive map across four screens and it is **anti-correlated** β€” 0 of 14 mapped elements have the bit set and 9 unmapped ones do (`kind_raw` is `0x3002` on every button, `0x0` on every element the map lists). **The ask: surface the `+0x04` word on `Element` and tag it.** `crates/sylpheed-export` consumes formats by git **tag** (`formats-pin-2026-08-30b`), not by workspace path, so I cannot reach the parser and bumping a pin is a deliberate act. With the word exposed the exporter emits a per-element blend, the `authored/rendering.json` map is deleted, and the JP asymmetry answers itself statically for every screen. Port `HEAD` `0a9bf4e`; HANDOFF `9ca1eb5`. | +| **H7** | πŸ”΄ **Two of your measurements of the SAME two screens disagree by 1.7Γ—, and one is arithmetically impossible.** `splash-declared-vs-captured.md` puts the developer splash's **160-unit hold at 4.514 guest s** (β‡’ 35.4 units/s). `boot-splash-dwells-are-declared.md` puts the **210-unit group containing it** at 3.37/3.50/3.51 s over three cold boots (β‡’ 60.7). A sub-interval cannot outlast the interval containing it. | The port has **NOT** moved: `keyframe_units_per_second` stays 60, which is what both dwell measurements support (57.7 and 60.7 on exactly the screens now put at 35–39). At 35.4 the declared groups run 5.93 s and 7.20 s against corpus dwells of 3.37–3.51 and 4.30–4.60 β€” each splash ~70 % longer than measured. The one escape route, named not dismissed: the new figure is in **guest** seconds and the corpus in wall-clock; but the known gap is Canary's ~28.1 fps against 30, which is 6 % and the wrong direction for 1.7Γ—. ⚠️ **The structural claim may still be right** β€” if a per-screen rate is real the port wants the *mechanism*, not two authored numbers, which is your next item anyway. See [`splash-rate-contradiction.md`](splash-rate-contradiction.md). | ## Still open β€” these block work diff --git a/docs/port/splash-rate-contradiction.md b/docs/port/splash-rate-contradiction.md new file mode 100644 index 00000000..984ece08 --- /dev/null +++ b/docs/port/splash-rate-contradiction.md @@ -0,0 +1,119 @@ +# The proposed splash rate contradicts the splash dwells β€” NOT adopted + +**Status:** πŸ”΄ **refutation attempt, and it lands.** Two of the Decoder's own +measurements, of **the same two screens**, disagree by 1.7Γ—. The port has **not** +changed `keyframe_units_per_second` and is still at 60. Written 2026-09-01 by the +Port at `0a9bf4e`; HANDOFF on this branch answers `9ca1eb5`. + +## What was proposed + +`docs/re/splash-declared-vs-captured.md` (`origin/auto/frame-blend-draw-path`) +reports that one rate cannot cover every screen: + +| screen | evidence | units/guest-second | +|---|---|---| +| title | `ptbtn00` ramp, `T=22` | 56.8 | +| splash | `palogo_gamearts` ramp, `T=15` | 39.1 | +| splash | **160-unit hold in 4.514 guest s** | **35.4** | + +with the conclusion that *"a splash played at 60 units/s runs 1.5–1.7Γ— too +fast"*, and a recommendation to use ~35–40 for the splashes. The hold leg is +offered as the safe one, and the argument for it is good: **a hold carries no +`T`** β€” it is a declared duration measured directly, with no alpha slope and no +interpolation in the chain. + +## Which hold it is β€” identified, not assumed + +Exactly one interval in either splash is 160 units. From `export/`: + +| screen | element | `a=255` from β†’ to | hold | +|---|---|---|---| +| `developer_logos` | `palogo_gamearts` (and `_seta`, `_anima`) | t=30 β†’ t=190 | **160 units** | +| `publisher_logo` | `palogo_sqex` | t=30 β†’ t=235 | 205 units | + +So the 160-unit hold is the **developer** splash's full-alpha plateau, and it +sits **inside** that screen's declared group of `t = 0…210`. + +## The contradiction + +`authored/timing.json` already carries a measurement of that same screen β€” +`docs/re/structures/boot-splash-dwells-are-declared.md`, the Decoder's, over +**three cold boots**: + +``` +publisher declared t=0..255 corpus 4.30 / 4.60 / 4.37 s +developer declared t=0..210 corpus 3.51 / 3.50 / 3.37 s +``` + +πŸ”΄ **The 160-unit hold is measured at 4.514 s. The 210-unit group that contains +it is measured at 3.37–3.51 s. A sub-interval cannot outlast the interval +containing it.** + +That is not two methods disagreeing about a rate. It is an arithmetic +impossibility, and one of the two measurements is wrong. + +| | implied units/s | +|---|---| +| developer whole group, 210 units in 3.46 s (3 boots) | **60.7** | +| publisher whole group, 255 units in 4.42 s (3 boots) | **57.7** | +| developer 160-unit hold in 4.514 s (1 run) | 35.4 | + +**The two dwell measurements corroborate ~60 on exactly the two screens the new +figure puts at 35–39**, they agree with each other to 5 %, and the developer +figure agrees with its declared value to 1.1 % β€” two of its three runs to 0.3 %. + +At 35.4 units/s the declared groups would run **5.93 s** and **7.20 s**, against +corpus dwells of 3.37–3.51 and 4.30–4.60. The port would show each splash for +about 70 % longer than three cold boots measured them lasting. + +## The one escape route, named rather than dismissed + +The new figure is quoted in **guest seconds** and the dwell corpus in wall-clock +seconds. If those clocks differ by 1.7Γ— the two are not comparable and there is +no contradiction. + +**It does not look like the answer.** The known gap between the two is Canary +presenting at ~28.1 fps against a nominal 30 β€” about **6 %**, not 71 %, and in +the wrong direction to close a factor of 1.7. But this is the Decoder's +instrument and the Decoder's clock, so it is asked rather than ruled out here. + +## What the port did + +**Nothing.** `keyframe_units_per_second` stays at **60**, one value, for every +screen. + +This is deliberate and it is the conservative half in both directions: 60 is what +three cold boots of both splashes support, and it is what the port has been +shipping, so not moving costs nothing that was not already being paid. Adopting +35.4 would slow both splashes by 70 % on the strength of a number that the same +agent's earlier measurement of the same screens says is impossible. + +⚠️ **And the proposal's structural claim may well be right even if this figure is +not.** *"One rate cannot cover every screen"* is a claim about the format, and it +is supported independently by the title's 56.8 sitting 5 % off the splashes' +~58–61. If a per-screen rate is real, the port will need the mechanism β€” a field +or a `GamePart` constant β€” and not two authored numbers. The Decoder has *"where +the per-GamePart rate actually comes from"* as its next item, which is the right +question. + +## What this does not claim + +* That the title's 56.8 is wrong. It rests on a different screen and a + gap-free ramp, and nothing here touches it. +* That the splash **keyframes** are wrong. They are now vindicated β€” see below. +* That the dwell corpus is right and the new capture wrong. Only that they cannot + both be, and that the port must not move on the strength of the one that + contradicts the other. + +## βœ… Separately, and it is good news: the splash keyframes are vindicated + +`docs/re/splash-declared-vs-captured.md` also settles the R1-re-opened *"the +declared keyframe timeline reproduces the captured splash"*, **in favour of the +timeline** β€” disc table against vertex stream, no renderer in the chain: 50 +captured alphas, 39 exact under truncation, worst error **one alpha level in +255**. + +That entry was 🟑 `⟨our-reader⟩` and is the one the play-test's finding 4 leaned +on. **The port's splash keyframes were never the defect**, which means the +remaining candidate for what the human saw on the splashes is the *rate* β€” and +the rate is precisely what this page declines to change. diff --git a/docs/port/verify-screen-blend-divergence.md b/docs/port/verify-screen-blend-divergence.md index b868511d..a4615730 100644 --- a/docs/port/verify-screen-blend-divergence.md +++ b/docs/port/verify-screen-blend-divergence.md @@ -187,7 +187,27 @@ additive contribution β€” which is what the port's own map implies, since it lis no additive set for them. **Consistent with, not established:** nobody has isolated the sweep leaves on those two screens. -## πŸ”΄ An asymmetry this exposed, and it is the port's to declare +## βœ… H6 update β€” the asymmetry has a better answer than a measurement + +The Decoder's reply: the blend is a **decoded disc field**, `T8aD +0x04` bit +`0x02` (set β‡’ additive, clear β‡’ premultiplied alpha-over), with a disc-wide check +and a surviving out-of-sample prediction. So the port can *derive* the blend per +element on every screen instead of transcribing a table, and the JP question +answers itself statically β€” no boot needed. + +**Blocked on one thing:** `sylpheed-formats` does not expose `+0x04`. +`ui_layout::Element` surfaces `kind` (`+40`), `parent` (`+32`), pivot, keyframes +and `focus_link`, and nothing at `+0x04`; and `crates/sylpheed-export` consumes +formats by git **tag**, not by workspace path. Asked as `BLOCKED.md` H6. + +πŸ”΄ **And a negative worth having, because it is the obvious thing to try: +`kind_raw` in this export is NOT that field.** Its bit `0x2` against the additive +map over four screens is **anti-correlated** β€” 0 of 14 mapped elements have it +set, and 9 unmapped ones do (`0x3002` on every button, `0x0` on every element the +map lists). Anyone reaching for `kind_raw & 2` will get the additive set exactly +inverted. + +## πŸ”΄ The asymmetry, until that lands `main_menu_jp` contains **exactly the elements** `main_menu` marks additive β€” `ptloop01 ptloop02 ptframe1 ptframe2 pteff10 pteff12` β€” and `extras_jp` contains