diff --git a/authored/timing.json b/authored/timing.json index ad745426..7d54c884 100644 --- a/authored/timing.json +++ b/authored/timing.json @@ -62,32 +62,34 @@ ], "press_start/ptbtn00": { "record_element": "ptbtn00f", - "period_units": 129, + "period_units": 105, "kind": "chosen", "why": [ - "129 units = the element's own group under the port's existing timing model:", - "its last timed keyframe is t=105 and the final untimed keyframe is reached", - "`exit_ramp_units` (24) later. NO NEW CONSTANT is introduced -- 24 is the", - "same authored value every other element's exit already uses.", + "105 units = the element's own group, start to end, under the CORRECTED record", + "layout: its keyframes now run t=0 (alpha 0x00) through t=105 (alpha 0x00), a", + "closed cycle with every pose timed.", "", - "129 units is 2.150 s at 60 units/s, and 2.295 s at the ~28.1 fps that", - "emulator presents (56.2 units/s), against measurements of 2.12 / 2.19 /", - "2.34 / 2.31 s. It sits inside that spread at either rate.", + "IT WAS 129, AND THAT NUMBER IS NOW INDEFENSIBLE: it was the last timed", + "keyframe (105) plus `exit_ramp_units` (24), and exit_ramp_units has been", + "DELETED because the untimed final keyframe it stood for does not exist. A", + "period built from a deleted constant cannot stay.", "", - "IT IS A CHOICE, and here is the alternative the data CANNOT rule out: the", - "cycle could restart at the group's first keyframe (t=6) rather than at 0,", - "giving 123 units = 2.050 s / 2.189 s at emulator rate. That ALSO sits", - "inside the measured spread. Nothing available separates them, so t=0 is", - "taken because it is where every other group in this port starts -- which is", - "consistency, not evidence.", + "GOOD NEWS: the ambiguity this entry used to carry is GONE. It said the cycle", + "might restart at the group's first keyframe (t=6) rather than at 0, giving 123", + "units, and that nothing separated them. Under the corrected layout the group", + "starts at t=0 and ends at t=105, both at alpha 0. There is one reading.", "", - "A fifth reading exists and is NOT averaged in: the Decoder re-picked its", - "troughs and got 2.628 s, having previously reported 2.369 from the same", - "run. It withdrew the estimate as too weak to resolve better than ~7%, so it", - "is recorded here and not used.", + "🔴 BAD NEWS, AND IT IS NOT PAPERED OVER: 105 units DISAGREES WITH THE", + "MEASUREMENT. The corpus timed the pulse four times at 2.12 / 2.19 / 2.34 /", + "2.31 s. 105 units is 1.750 s at 60 units/s. Scaling by the same factor the", + "ring shows between its declared 120 units and its measured 2.177 s (x1.089)", + "gives 1.906 s -- still about 17% below every measured value. The old 129 gave", + "2.34 s, at the top of the range, which is why it looked right.", "", - "WHAT DELETES THIS ENTRY: a capture that times the pulse against a landmark", - "in the same frame, or a loop flag decoded on the disc." + "The port ships the DISC's number and says it is wrong rather than keeping a", + "number that fitted the measurement but was built on a constant that no longer", + "exists. WHAT WOULD RESOLVE IT: whether the group loops from its start at all,", + "or holds at alpha 0 between cycles. Raised with the Decoder 2026-08-29." ] } }, diff --git a/docs/port/DECISIONS.md b/docs/port/DECISIONS.md index 27e6d0b7..5e7bf857 100644 --- a/docs/port/DECISIONS.md +++ b/docs/port/DECISIONS.md @@ -4310,3 +4310,64 @@ answer at all, and the parked Japanese-locale capture does not need reviving on its account. `authored/rendering.json` now says that first and the undecidability second. **Widening scope to close a residual would have been the wrong trade**, and it is the human's call either way. + +## 🔴 The focus ring had silently stopped, and BLOCKED had listed it + +`docs/port/BLOCKED.md` said the record-layout change touches five things: +`pose_at`, `settle_units`, `spin_period_units`, `exit_ramp_units` **and the +plate**. I checked `pose_at` and deleted `exit_ramp_units`, reported that, and +**did not work the rest of the list.** + +`spin_period_units` required *"the first timed and the second untimed"*. Under +the corrected layout the ring reads `t=0 rot=0` and `t=120 rot=360` — **both +timed** — so the rule returned 0 and **the focus ring stopped spinning**. Nothing +reported it: a period of 0 is a legal *"this element does not spin"*. + +Rewritten to take the **span** between the two poses. On the ring that is +120 − 0 = **120 units, the same number the old rule produced**, which is a small +piece of evidence that the corrected layout is self-consistent rather than merely +different. + +**Verified the way P5 verified it — bit-identity one period apart**, on the ring's +own 60×60 box so the `ptloop` sweeps cannot confound it: + +| separation | mean difference | +|---|---| +| **+120 units (one period)** | **0** | +| +120 units again | **0** | +| +30 units (quarter) | 8.61 | +| +60 units (half) | 8.88 | + +⚠️ Getting there took three wrong instruments, and the sequence is the lesson. +A whole-frame `max` saturates on one rotating edge — adjacent frames scored 131 +while their mean was 0.022. A live `--menu` filmstrip jitters by up to a frame, +which is ~3° of ring, and its cadence cannot be pinned. And a whole-frame +comparison is dominated by the sweeps, which move 480 px over one ring period. +**`--focus=` was added so a `--screen` run can draw a focus record +deterministically**, which is what made the check reproducible at all. + +## The plate's period is now the disc's 105, and it disagrees with the measurement + +Under the corrected layout `ptbtn00f` runs **t=0 (alpha 0) → t=105 (alpha 0)** — a +closed cycle with every pose timed. + +✅ **The ambiguity this entry carried is gone.** It used to say the cycle might +restart at t=6 rather than 0, giving 123 units, and that nothing separated the +two. There is now one reading. + +🔴 **And the number is worse against the oracle, which is stated rather than +avoided.** 105 units is **1.750 s**; scaled by the factor the ring shows between +its declared 120 units and its measured 2.177 s (×1.089), **1.906 s** — about +**17 % below** every one of the corpus's four timings (2.12 / 2.19 / 2.34 / +2.31 s). The old 129 gave 2.34 s, at the top of that range, which is exactly why +it looked right. + +**129 was the last timed keyframe plus `exit_ramp_units`, and that constant is +deleted.** A period built from a constant that no longer exists cannot stay, even +though it fitted better. So the port ships the disc's number **and says it is +wrong**, rather than keeping a number that agreed with the measurement for a +reason that has evaporated. + +Verified: the plate is bit-identical 105 units apart (mean diff **0**) and differs +at 30 units (0.83). ❔ What would resolve the disagreement: whether the group +loops from its start at all, or holds at alpha 0 between cycles. Asked. diff --git a/port/scripts/boot.gd b/port/scripts/boot.gd index 4a6a3e0b..7ebb6a2c 100644 --- a/port/scripts/boot.gd +++ b/port/scripts/boot.gd @@ -128,6 +128,13 @@ func _ready() -> void: if args.has("script"): _script = args["script"].split(",", false) _skip_at = float(args.get("skip-at", "0")) + # `--focus=` draws a button's focus record in a `--screen` run, + # which otherwise focuses nothing. A diagnostic: the spinning ring is only + # drawn on a FOCUSED button, so without this the ring can only be observed in + # a live `--menu` run, where the film cadence jitters by up to a frame and a + # 3-degree angular wobble swamps the "identical one period apart" check that + # verified it at P5. + _force_focus = args.get("focus", "") # P5. `--play` boots first and hands over on the title; `--menu` starts on a # screen directly, which is what makes an unattended run cheap -- it does not # sit through 137 s of intro to press a d-pad. @@ -213,6 +220,7 @@ func _ready() -> void: view.looping_focus = _looping_for(name) view.draw_leaf_for = _draw_leaf_for + view.focused_id = _force_focus if not view.load_screen(export_tree, name): push_error(export_tree.error) get_tree().quit(2) @@ -282,6 +290,8 @@ var _script: PackedStringArray = PackedStringArray() var _shots := "" ## `--skip-at=SECONDS`: when to send a synthetic (A) during a movie, or 0. var _skip_at := 0.0 +## `--focus=`: draw this element's focus record in a `--screen` run. +var _force_focus := "" var _skip_sent := false var _script_started := false var _sequence: Array[Dictionary] = [] diff --git a/port/scripts/screen_view.gd b/port/scripts/screen_view.gd index 0e62e050..26980d67 100644 --- a/port/scripts/screen_view.gd +++ b/port/scripts/screen_view.gd @@ -281,14 +281,30 @@ static func spin_period_units(element: Dictionary) -> float: return 0.0 var a: Dictionary = frames[0] var b: Dictionary = frames[1] - if not a.has("t") or b.has("t"): + # 🔴 REWRITTEN for the corrected record layout, and it had SILENTLY STOPPED + # THE RING. The old rule required "the first timed and the second untimed", + # which was true when a group's data stopped short of its final time slot. + # Under the corrected layout every pose is timed -- the ring now reads + # `t=0 rot=0` then `t=120 rot=360` -- so `b.has("t")` was true, the rule + # returned 0, and the focus ring stopped spinning. Nothing reported it: a + # period of 0 is a legal "this element does not spin". + # + # `docs/port/BLOCKED.md` had listed `spin_period_units` among the five things + # the layout change touches. I checked `pose_at` and `exit_ramp_units` and + # did not work the list. + # + # The period is now the SPAN between the two poses rather than the first + # one's declared time. On the ring that is 120 - 0 = 120 units, the same + # number the old rule produced -- which is a small piece of evidence that the + # corrected layout is self-consistent rather than merely different. + if not a.has("t") or not b.has("t"): return 0.0 for key in ["pos", "scale", "tint_rgba", "fade_argb"]: if a.get(key) != b.get(key): return 0.0 if absf(float(b.get("rotation_deg", 0)) - float(a.get("rotation_deg", 0))) != 360.0: return 0.0 - var t := float(a["t"]) + var t := float(b["t"]) - float(a["t"]) return t if t > 0.0 else 0.0