port: the focus ring had silently stopped, and the plate's period is now the disc's

BLOCKED said the record-layout change touches five things: pose_at,
settle_units, spin_period_units, exit_ramp_units and the plate. I checked two,
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: 120 - 0 = 120 units, the same
number the old rule produced, which is evidence the corrected layout is
self-consistent rather than merely different. Verified the way P5 verified it,
by bit-identity one period apart on the ring's own 60x60 box so the ptloop sweeps
cannot confound it: 0 at +120 units (twice), 8.61 at a quarter period, 8.88 at
half.

Three wrong instruments on the way, and the sequence is the lesson. A whole-frame
`max` saturates on one rotating edge (adjacent frames scored 131 with a mean of
0.022). A live --menu filmstrip jitters by up to a frame, which is ~3 degrees of
ring. And a whole-frame comparison is dominated by the sweeps, which move 480 px
over one ring period. `--focus=<id>` was added so a --screen run can draw a focus
record deterministically, which is what made the check reproducible.

THE PLATE'S PERIOD IS NOW 105, THE DISC'S OWN GROUP LENGTH, and it disagrees with
the measurement. The ambiguity the entry carried is gone -- it used to say the
cycle might restart at t=6 rather than 0 and that nothing separated them; the
group now runs t=0 to t=105, both at alpha 0, and there is one reading. But 105
units is 1.750 s, or 1.906 s scaled by the factor the ring shows between its
declared 120 and its measured 2.177 s -- about 17% below all four corpus timings
(2.12 / 2.19 / 2.34 / 2.31). The old 129 gave 2.34 s, at the top of the range,
which is why it looked right.

129 was the last timed keyframe plus exit_ramp_units, and that constant is
deleted. A period built on 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. Verified bit-identical 105 units apart, 0.83 at 30 units.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7FiFFFwbvG2uxdcEh8HyF
This commit is contained in:
Sylpheed port agent
2026-08-29 19:35:01 +00:00
parent 5dbc9aeac0
commit d57366a35f
4 changed files with 111 additions and 22 deletions

View File

@@ -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."
]
}
},

View File

@@ -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=<id>` 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.

View File

@@ -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=<element id>` 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=<id>`: 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] = []

View File

@@ -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