port: fit-trajectory -- position carries the clock, alpha carries almost nothing

Answering the human's 'what is holding you up': two agents spent three exchanges
on ALPHA while the answer sat in a POSITION series neither of us compared to
anything.

The arithmetic that should have been step one: the Decoder's quoted sweep centre
-1.690 read as NDC is -441.6 px, and the declared position at leaf t=0 is -439.5
px. A 2 px agreement on a 2160 px travel -- so the game's sweep begins
travelling at leaf t~0, the same as the port.

That CONTRADICTS my own earlier framing on this page, which blamed ~135 units on
the leaf clock. If the game's leaf clock also starts at 0 then F6 is a
visibility question, not a clock question. Flagged rather than rewritten: it
rests on two numbers relayed in a message, which is what should be read from the
repo instead.

The tool: solves x(frame) ~= declared(t0 + rate*frame) for both parameters and
reports the RESIDUAL, which says whether the model was right at all. Selftest
runs both directions -- recovers a known clock to 0.09 px and rejects a
wrong-shape series at 81.9 px against a 20 px bar. In check-all.
This commit is contained in:
Sylpheed port agent
2026-09-02 18:34:06 +00:00
parent 88d7656f50
commit 359f0d8bff
3 changed files with 205 additions and 0 deletions

View File

@@ -193,3 +193,60 @@ been burned before — and the port would be changing a renderer rule on it.
`screen_view.gd` keeps its current behaviour until the row is identified by
travel direction. **This is not scepticism about the conclusion**, which the
port's own flagged limit predicted would fall this way; it is about which row.
---
# 📌 What was actually holding this up: we both picked the wrong observable
The human asked what was blocking us and whether the approach needed revising.
**It did, and the diagnosis is short: two agents spent three exchanges on ALPHA
while the answer sat in a POSITION series neither of us compared to anything.**
## The arithmetic that should have been step one
The Decoder's capture reports the sweep's x-centre running `−1.690 → +0.500`.
Read as NDC (`x_ndc = 2·x_px/W − 1`, **an assumption, stated as one**):
| | ndc | centre px | implied leaf t |
|---|---|---|---|
| first sample | −1.690 | −441.6 | **−0.5** |
| last sample | +0.500 | +960.0 | 349.9 |
| **declared at leaf t=0** | **−1.687** | **−439.5** | — |
**A 2 px agreement on a 2 160 px travel.** So the game's sweep begins travelling
at leaf t≈0, from off-screen left — **the same as the port.**
🔴 **That contradicts my own earlier framing on this page**, which attributed
~135 units of the earliness to "the leaf clock starting at title t=0 with no
offset". If the game's leaf clock also starts at 0, that is not a defect and F6
is a **visibility** question — alpha, or draw order, or something not yet named —
rather than a clock question. I am flagging it rather than rewriting the section:
this rests on two numbers relayed in a message, which is exactly the thing that
should be read from the repository instead.
## Why alpha was the wrong tool, stated generally
| | alpha | position |
|---|---|---|
| dynamic range | 8 bits, quantised | **2 160 px** |
| shape | non-monotone, ramps and holds | **monotone** |
| failure mode that bit us | a 14-sample tail out of 1754 looks like signal | a wrong shape raises the residual |
| yields the clock? | no | **origin AND rate together** |
**When something moves, its position carries the clock and its alpha carries
almost nothing.** Neither of us reached for a trajectory comparison because
neither of us had one.
## So: `tools/port/fit-trajectory`
Solves `x_measured(frame) ≈ declared(t0 + rate·frame)` for the pair, and reports
the **residual**, which is the part that matters: it says whether the model was
right at all, where a value-at-an-instant never can.
Its `--selftest` runs both directions — recovers a known clock from a synthesised
series to 0.09 px, and **rejects** a wrong-shape series at 81.9 px against a 20 px
bar — because a fit that cannot fail is a curve-fitter, not a measurement. Wired
into `check-all`.
⚠️ It fits a **constant** rate. A stalling guest clock or uneven capture drops
raise the residual rather than being absorbed, which is deliberate.