recover: the OPTIONS menu work from the deleted auto/port-p6-audio
The nine files touched by the OPTIONS commits of 2026-09-03 (77f1d18,fda417a,3efe1cc,80042cb,4c24e06,a921c1e,41f1331,6b4b1df,edf8979), taken as of0148cb8, the tip of auto/port-p6-audio. The branch was deleted from the server on 2026-09-17 during the consolidation cleanup; issue #6 asks for this work as a reviewable PR, so it is recovered here before the commits are garbage collected. This is a review slice, not a self-consistent tree: the OPTIONS work and the F5/F6 work interleaved in the original history and cannot be separated by file, so each file carries whatever else had changed in it by 2026-09-04, and files it depends on are absent. The complete state is recover/port-f5-f6. Refs #6. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
597
docs/port/f6-what-starts-the-sweep.md
Normal file
597
docs/port/f6-what-starts-the-sweep.md
Normal file
@@ -0,0 +1,597 @@
|
||||
# F6 unit a — what the port currently uses to start the title sweep
|
||||
|
||||
**Status:** ✅ answered. **No behaviour changed** — this unit exists so that when
|
||||
the Decoder says *when* the glow should start, the edit is one line.
|
||||
Port at `937f055`, 2026-09-02.
|
||||
|
||||
## The answer, in one line
|
||||
|
||||
`port/scripts/screen_view.gd:684`
|
||||
|
||||
```gdscript
|
||||
var t := leaf_time_units if leaf_time_units >= 0.0 else time_units
|
||||
```
|
||||
|
||||
**That is the whole start mechanism, and it is not a start mechanism.**
|
||||
`leaf_time_units` is set in exactly one place — `boot.gd:359`, the `--leaf-time`
|
||||
diagnostic flag — and is `-1.0` on every real boot. So the travelling glow runs
|
||||
on `view.time_units`, the title screen's own clock, which `_advance` sets to
|
||||
`0.0` when the title is raised. **Zero offset, no gate.**
|
||||
|
||||
## 🔴 And the obvious gate does not exist
|
||||
|
||||
The natural reading — mine, before checking — is that the parents gate it:
|
||||
`ptloop01`/`ptloop02` declare `0:0 70:0 100:255 238:255 250:0`, invisible until
|
||||
t=70. **That is not what happens**, because what reaches the screen is the LEAF,
|
||||
and `screen_view.gd` records as decoded that *"the leaf runs on its OWN timeline
|
||||
and the parent's alpha is NOT multiplied in"*. The parent ramp gates nothing.
|
||||
|
||||
The leaves' own declarations:
|
||||
|
||||
| leaf | alpha | x position |
|
||||
|---|---|---|
|
||||
| `pteff03` | **`0:255`** 150:128 540:255 600:255 | −639 → −39 (t=150) → 1521 (t=540) |
|
||||
| `pteff03a` | 0:0 150:128 630:255 720:255 | 1721 → 1111 (t=150) → −839 (t=630) |
|
||||
|
||||
**`pteff03` is at full alpha from title t=0** and is travelling from t=0. It
|
||||
clears the left edge (sprite is 399 wide) at around t≈60 and is well inside the
|
||||
frame by t=150.
|
||||
|
||||
The plate arrives at **t=214–236**. So the port starts the sweep roughly
|
||||
**150+ units ≈ 2.5 s early** — which is the size and the direction of what the
|
||||
human reported.
|
||||
|
||||
## Corroborated on a film, not only read
|
||||
|
||||
Filmed a real boot at 0.05 s and measured frame-to-frame change in the title art
|
||||
band `1280x420+0+90`, which **excludes the plate's own rectangle** (y 550–600) so
|
||||
the plate cannot be what registers:
|
||||
|
||||
```
|
||||
view_units 22 54 69 86 118 134 214 341 406
|
||||
delta 31.8 39.9 30.8 9.4 12.6 0.2 0.2 0.1 0.4
|
||||
```
|
||||
|
||||
Motion is heavy through the build-in and the band is quiet by t≈134 — consistent
|
||||
with `pteff03` having already crossed the measured band and with the coarse
|
||||
resize washing a thin glow out. **The film neither adds to nor contradicts the
|
||||
declaration; the declaration is the evidence here.**
|
||||
|
||||
## Where the sweep actually is, computed from the leaf's own translation
|
||||
|
||||
The port positions the leaf **from the leaf's own clock** — it does not draw the
|
||||
parent's pose and ignore the translation. Sprite 399 wide on a 1280 screen:
|
||||
|
||||
| t | 0 | **61** | 70 | 100 | 150 | **236** | 250 |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| `pteff03` x | −639 | **−395** | −359 | −239 | −39 | **305** | 361 |
|
||||
| `pteff03a` x | 1721 | 1477 | 1436 | 1314 | 1111 | 762 | 705 |
|
||||
|
||||
**`pteff03` enters the frame at t=61 and is mid-screen at t=305 when the plate
|
||||
reaches full alpha at t=236** — visible and travelling for ~175 units ≈ 2.9 s
|
||||
before the plate. `pteff03a` enters much later.
|
||||
|
||||
## 🔴 The open question in this file sits exactly inside F6's window
|
||||
|
||||
`screen_view.gd` flags its own limit on the leaf-vs-parent alpha decode:
|
||||
|
||||
> *"Every observation behind this has parent alpha 0, so 'the leaf wins' and 'the
|
||||
> parent is ignored because it draws nothing' are NOT separated. **A capture
|
||||
> during t=100…238 would separate them.**"*
|
||||
|
||||
The parent is non-zero exactly on `t=100…238`, and the plate arrives at 236. So
|
||||
that unresolved ambiguity is **the same interval F6 is about**, and it is
|
||||
load-bearing for the first stretch: the parent ramps 70→100, so applying it would
|
||||
hide the sweep until t=70 and dim it to t=100, while the port shows it at full
|
||||
alpha from t=61.
|
||||
|
||||
That accounts for ~40 units of the earliness. **It does not account for the other
|
||||
~175**, which is the leaf clock starting at title t=0 with no offset.
|
||||
|
||||
📌 **One capture in `t=100…238` would settle both** — F6's start time and the
|
||||
leaf/parent alpha rule — rather than two.
|
||||
|
||||
## What changes when the answer lands
|
||||
|
||||
A start time is an **offset**, and `leaf_time_units` is an absolute override —
|
||||
they are not the same field. The one-line edit at 684 becomes a subtraction, fed
|
||||
by one authored value. Nothing else moves.
|
||||
|
||||
## What this does NOT do
|
||||
|
||||
* **It does not choose a start time.** That is the Decoder's, and this unit was
|
||||
scoped to exclude it deliberately.
|
||||
* It does not touch the glow. A boot looks exactly as it did.
|
||||
* It says nothing about whether the *speed* or the *path* is right — only when it
|
||||
begins.
|
||||
|
||||
---
|
||||
|
||||
# 🔴 Unit b, HELD: the parent-alpha refutation may be right, but its identification step cannot carry it
|
||||
|
||||
**Status:** ⏸️ **the renderer is NOT changed.** The Decoder's
|
||||
`f6-unit2-parent-alpha-multiplies.md` refutes `screen_view.gd`'s *"the parent's
|
||||
alpha is NOT multiplied in"* using a bound. The bound's shape is sound and its
|
||||
premise checks out against this export. **The step that assigns the measurement
|
||||
to an element does not.**
|
||||
|
||||
## The premise holds
|
||||
|
||||
`pteff03`'s leaf declares `0:255 150:128 540:255 600:255` — **minimum 128**,
|
||||
confirmed off `export/screens/title/title.json`. A drawn alpha below 128 cannot
|
||||
come from that leaf alone. That part is right.
|
||||
|
||||
## 🔴 But the two strips are the SAME SIZE, so size cannot say which is which
|
||||
|
||||
The identification is stated as *"by size against the corpus's independently
|
||||
measured AABB height of 1134 px"*. Measured off this export:
|
||||
|
||||
| sprite | dimensions | leaf alpha range | travel |
|
||||
|---|---|---|---|
|
||||
| `pteff03` | **399 × 180** | **128 … 255** | left → right (−639 → 1521) |
|
||||
| `pteff03a` | **399 × 180** | **0 … 255** | right → left (1721 → −839) |
|
||||
|
||||
**They are byte-identical in size**, which is consistent with the two reported
|
||||
rows measuring `1.38 × 3.15` and `1.39 × 3.15` — a 0.7 % difference. Size
|
||||
separates the sweeps from everything else on the screen; it cannot separate them
|
||||
from **each other**, and that is the distinction the argument needs.
|
||||
|
||||
## Why it matters — the assignment flips the conclusion
|
||||
|
||||
The quoted row that reaches **8** is the one the argument leans on. But
|
||||
`pteff03a`'s leaf alpha floors at **0**, not 128, and ramps `0 → 128` across
|
||||
t=0…150. Values of 8, 24, 33, 50 … are exactly what **that leaf alone** produces.
|
||||
So if the 8-row is `pteff03a`, the bound is satisfied with no parent at all.
|
||||
|
||||
⚠️ **And the conclusion may still be correct via the OTHER row.** The row
|
||||
reported as `16 41 67 91 116 128 129 130 131` contains values below 128 *and* a
|
||||
dense cluster at 128–131 — the signature of `pteff03`'s floor. If that row is
|
||||
`pteff03`, then 16 < 128 refutes no-multiply exactly as claimed. **The finding
|
||||
may be right and the cited row wrong.**
|
||||
|
||||
## The discriminator is free and already in their capture
|
||||
|
||||
The two leaves travel in **opposite directions**: `pteff03` left→right,
|
||||
`pteff03a` right→left, separated by ~1 000 px for most of their run. One frame
|
||||
pair settles it. Nothing needs re-capturing.
|
||||
|
||||
## ✅ Resolved: the discriminator worked, and MY proposed repair was wrong
|
||||
|
||||
The Decoder ran the travel-direction check on the capture they already had:
|
||||
|
||||
```
|
||||
1.38x3.15 n=1140 x centre -1.690 -> +0.500 LEFT->RIGHT
|
||||
1.39x3.15 n= 614 x centre -1.685 -> +0.495 LEFT->RIGHT
|
||||
```
|
||||
|
||||
**Both rows travel the same way, so both are `pteff03`** — the 0.7 % size
|
||||
difference is per-frame rounding splitting **one** element across two keys.
|
||||
`pteff03a` is not in the capture at all.
|
||||
|
||||
🔴 **So my "the finding may still be correct via the OTHER row" does not apply —
|
||||
there is no other row.** The identification was broken a third way that neither
|
||||
of us named: not two elements confused for each other, but one element counted
|
||||
twice. I was right that size could not carry the assignment and wrong about what
|
||||
the assignment actually was.
|
||||
|
||||
⚠️ And the part I could not see from here, which is the larger one: the quoted
|
||||
`8 24 33 50 58 …` were the nine **lowest distinct** values, not a series — a
|
||||
tail, presented in a shape that reads as a ramp. **14 sub-floor samples out of
|
||||
1754.** The bound survives in shape and collapses in weight: one sample below 128
|
||||
is still unexplainable by a leaf flooring at 128, but 14 of 1754 is where a
|
||||
grouping slip or a partial frame would show up.
|
||||
|
||||
**Status: 🟡 not refuted, not established.** The next unit is a re-read of data
|
||||
already on disk — the same capture as a *series* across t=70…100, where a
|
||||
multiplying parent must ramp on nearly every frame rather than in 14 stragglers.
|
||||
That unit is the Decoder's and has not been run.
|
||||
|
||||
## What the port does about it: nothing yet
|
||||
|
||||
Adopting a decode whose element assignment is unresolved is how this project has
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
# ❌ WITHDRAWN — Unit c: "the port draws a sweep the game does not"
|
||||
|
||||
> 🔴 **This whole section is refuted, and the port was right.** `pteff03a` **is**
|
||||
> drawn by the game. The two strips are batched into a **single additive draw of
|
||||
> eight vertices — two quads** — and the Decoder's log reader took the first
|
||||
> vertex match per draw line and discarded the rest, so every analysis saw quad A
|
||||
> and never quad B. No new capture was needed; `pteff03a` was in the same logs
|
||||
> that were read as declaring it absent
|
||||
> (`docs/re/f6-unit11-pteff03a-IS-drawn.md`). Measured on both sides: the strips
|
||||
> travel in opposite directions with a size ratio of 1.301 against the declared
|
||||
> 800/600 = 1.333.
|
||||
>
|
||||
> ✅ **Nothing in the port changed on the strength of it.** I proposed gating
|
||||
> `pteff03a` and held, because absence in one capture read by one probe is a lead
|
||||
> and not a finding, and because the check I asked for was a human's look rather
|
||||
> than another measurement. That hold is the only reason this cost nothing.
|
||||
>
|
||||
> ⚠️ **And the absence claim cited the port as evidence against itself** — "the
|
||||
> port draws it, the game does not" — so a defect was inferred in my renderer
|
||||
> from a gap in a reader. Kept in place rather than deleted: the reasoning below
|
||||
> is sound given its premise, and the premise is exactly the kind that looks like
|
||||
> data.
|
||||
|
||||
## The original section, kept for its shape
|
||||
|
||||
## First, the correction: my refutation was right in outcome and WRONG in its reason
|
||||
|
||||
I challenged the Decoder's by-size identification on the ground that *"both
|
||||
sweep sprites are 399×180, so size cannot separate them"*. **That was wrong.** I
|
||||
compared the source PNGs and never looked at the leaf declarations:
|
||||
|
||||
| leaf | sprite | declared scale | **drawn height** |
|
||||
|---|---|---|---|
|
||||
| `pteff03` | 399×180 | `[100, 600]` | **1080 px** |
|
||||
| `pteff03a` | 399×180 | `[100, 800]` | **1440 px** |
|
||||
|
||||
The *drawn* quads differ by a third, which is exactly the 3.15 vs 3.62 NDC the
|
||||
Decoder was separating by. **Size distinguishes them fine.** The hold was still
|
||||
correct and the check I asked for still found a real defect — but it found a
|
||||
different one (one element double-counted, and a set presented as a series), and
|
||||
my stated reason did not survive. Recorded because a right answer reached by a
|
||||
wrong argument is the kind that gets cited later for the wrong reason.
|
||||
|
||||
## And it makes the real finding sharper
|
||||
|
||||
Because size *does* separate them, the Decoder's line — *"`pteff03a` does **not**
|
||||
appear in this capture at all"* — is well-evidenced rather than incidental. They
|
||||
looked for a distinct size and found nothing.
|
||||
|
||||
**The port draws it.** Asked directly, at three instants:
|
||||
|
||||
```
|
||||
t=120 drew 9: ptbase2, pteff03, pteff03a, pteff04, ...
|
||||
t=180 drew 10: ptbase2, pteff03, pteff03a, pteff04, ...
|
||||
t=240 drew 10: ptbase2, pteff03, pteff03a, pteff04, ...
|
||||
```
|
||||
|
||||
`pteff03a` is on screen in the port from t≈108 (it crosses x=1280 there) until
|
||||
t≈521, travelling **right-to-left** at 800 % vertical scale while `pteff03` runs
|
||||
left-to-right at 600 %. The capture covers that window and contains only
|
||||
`pteff03`.
|
||||
|
||||
> ~~**So the port appears to render a second light sweep, larger and travelling
|
||||
> the opposite way, that the game does not draw during the title build-in.**~~
|
||||
> ❌ **False.** The game draws both, batched into one eight-vertex draw.
|
||||
|
||||
⚠️ **Absence in one capture is not absence in the game**, and this is one
|
||||
capture, read by one probe, identified by size. It is a lead, not a finding. But
|
||||
it is the first thing in F6 that is *visible*, *port-side*, and *checkable by a
|
||||
person in seconds* — which is what this whole exchange has been missing.
|
||||
|
||||
## What did NOT work, recorded so nobody repeats it
|
||||
|
||||
I tried to isolate the two sweeps visually by differencing title frames at
|
||||
several `--time` values. **It failed and the output is not evidence**: at those
|
||||
instants the whole title is still animating — logo, effects, copyright — so the
|
||||
difference is the entire screen rather than the sweeps. Frame-differencing
|
||||
isolates motion only when everything else is still, and during a build-in nothing
|
||||
is.
|
||||
|
||||
## The unit, and it is one question for a person
|
||||
|
||||
> **On the real game's title screen, is there ONE light streak sweeping across,
|
||||
> or TWO travelling in opposite directions?**
|
||||
|
||||
Pass for the port as it stands: two. If the game shows one, `pteff03a` is drawn
|
||||
here and should not be — and an extra glow arriving at t≈108 is a very good
|
||||
candidate for *"the glow starts too early"*.
|
||||
|
||||
**Not covered:** the start time of `pteff03` itself, which is still open; and the
|
||||
parent-alpha question, still 🟡.
|
||||
|
||||
---
|
||||
|
||||
# ❌ Unit d — the "variant link" explanation, raised and killed in one pass
|
||||
|
||||
The Decoder's second candidate for why the game submits `ptloop01` and not
|
||||
`ptloop02` was *"a focus/variant link means only one of the pair is ever
|
||||
active"*. **That is answerable from the export, and the answer is no.**
|
||||
|
||||
## What looked like a smoking gun
|
||||
|
||||
`ptloop01` carries **`opt_link = "ptloop02.rat"`**, `ptloop02` carries none, and
|
||||
it is the only linked element on the title screen. The field is exported straight
|
||||
from `el.focus_link` (`crates/sylpheed-export/src/screen.rs:622`), and
|
||||
**`port/scripts/` never reads it.** An ignored variant link would have explained
|
||||
the extra sweep exactly.
|
||||
|
||||
## ❌ And it is not a variant link
|
||||
|
||||
Surveying `opt_link` across the whole export splits it into two populations:
|
||||
|
||||
| target | example | is the target also a top-level element? |
|
||||
|---|---|---|
|
||||
| `*f.rat` | `ptbtn00 → ptbtn00f` | **no** — variant only |
|
||||
| everything else | `ptloop01 → ptloop02` | **yes** — both are drawn |
|
||||
|
||||
And the second population **chains across unrelated element kinds**. On
|
||||
`main_menu`:
|
||||
|
||||
```
|
||||
index 3 ptloop01 -> ptloop02.rat
|
||||
index 4 ptloop02 -> ptbtn01.rat
|
||||
index 10 ptbtn01 -> ptbtn01f.rat
|
||||
```
|
||||
|
||||
**A light sweep points at a button.** A variant selector cannot do that, so
|
||||
`opt_link` is a chain pointer that happens to land on the focus variant when the
|
||||
element is a button — which is why it was exported under the name `focus_link`.
|
||||
|
||||
> So the field does not select between `ptloop01` and `ptloop02`, and the port
|
||||
> ignoring it is not what draws the extra sweep. **Candidate eliminated.**
|
||||
|
||||
## The smaller finding that survives
|
||||
|
||||
**`focus_link` is carrying two different things** and the exporter names it after
|
||||
only one of them. The `*f` population is a variant; the rest is a chain. Nothing
|
||||
depends on this today — the port reads neither — but the name asserts a meaning
|
||||
the data does not support, and the next person to reach for it will reach for the
|
||||
wrong one. Worth renaming when something actually needs it; not worth a
|
||||
re-export on its own.
|
||||
|
||||
## Where that leaves F6
|
||||
|
||||
The lead is unchanged and unexplained: **the port draws `pteff03a`, the game's
|
||||
capture never does** — now confirmed by an exhaustive scan of every tall quad in
|
||||
1..2499 rather than a filtered subset. One of the two candidate causes is now
|
||||
eliminated from the export side, which leaves the Decoder's first: a zero-alpha
|
||||
skip suppressing the opening frames. ⚠️ That one does not obviously survive
|
||||
either — it would explain `pteff03a`'s *opening* frames, not its whole run, and
|
||||
its leaf reaches α=128 well inside the captured window.
|
||||
|
||||
**Nothing is deleted and the renderer is unchanged**, pending one five-second
|
||||
human look: one streak, or two?
|
||||
|
||||
---
|
||||
|
||||
# Unit e — the port draws exactly TWO travelling lights, and the human reports more
|
||||
|
||||
The human, watching the real game: *"I think multiple, possible more than two…
|
||||
The lights move on blue lines looking like PCB board lines. And frankly I cannot
|
||||
tell if the game renders a light per line or uses a light that is shown around
|
||||
multiple, close lines."*
|
||||
|
||||
That is a different question from the one both agents had been asking, and it is
|
||||
worth having the port's own number first.
|
||||
|
||||
## Census of every element on the title that travels
|
||||
|
||||
| element | x travel | note |
|
||||
|---|---|---|
|
||||
| `ptlogo1` / `ptlogo2` (×3 instances) | 300 px | the **logo** sliding in, t=34…251 — not a light |
|
||||
| **`pteff03`** (leaf of `ptloop01`) | **2 160 px** | left → right |
|
||||
| **`pteff03a`** (leaf of `ptloop02`) | **2 560 px** | right → left |
|
||||
|
||||
Every other title element — `pteff00`, `pteff01`, `pteff02`, `pteff04`,
|
||||
`ptlogo_back2eff` and `…eff1…5`, `ptlogoall_eff`, `ptlogoall_eff2`,
|
||||
`ptcopyright`, `ptbase2` — **declares no positional travel at all.** They fade in
|
||||
and out in place.
|
||||
|
||||
> **The port renders exactly two moving lights.** The human describes multiple,
|
||||
> possibly more than two, running along individual PCB traces.
|
||||
|
||||
## What that reframes
|
||||
|
||||
Both agents had been asking *when* the sweep starts. If the game's effect is a
|
||||
population of small lights on separate traces and the port's is two full-height
|
||||
streaks crossing the screen, then **the port may have the wrong effect
|
||||
altogether**, and "starts too early" is what a wrong effect looks like to someone
|
||||
who is not reading keyframes.
|
||||
|
||||
⚠️ **And it puts a limit on the capture result.** The Decoder's scan that found
|
||||
`pteff03a` absent covered every quad **taller than 1.2 NDC**. Small per-trace
|
||||
lights are far below that, so that scan cannot count them — it is exhaustive over
|
||||
full-height streaks and silent about the population in question. `pteff03a`'s
|
||||
absence stands (it would be 3.62 NDC); *"only one travelling quad exists"* does
|
||||
not generalise beyond tall quads.
|
||||
|
||||
## ⚠️ A limit of this census
|
||||
|
||||
It reads **declared** keyframes. An element with a single keyframe shows as
|
||||
"visible 0…0" here and is in fact held and drawn — `ptbase2`, the background, is
|
||||
the obvious case. So the visibility column understates; **the travel column is
|
||||
the load-bearing one**, and travel is what a moving light needs.
|
||||
|
||||
It also cannot see motion that is not positional — a scrolling UV, a texture
|
||||
animation, or a shader would move light along a trace while declaring no travel
|
||||
at all. **Nothing in this export declares such a thing**, but the port would not
|
||||
know if the game did it that way, and that is now a live possibility rather than
|
||||
a remote one.
|
||||
|
||||
## Not covered
|
||||
|
||||
Whether the game's lights are one-per-trace or one glow spanning several — the
|
||||
human says they cannot tell, and it is the Decoder's screenshots to settle.
|
||||
|
||||
|
||||
---
|
||||
|
||||
# 📌 What the withdrawal is worth, since the port lost nothing
|
||||
|
||||
Three of my own claims rested on `pteff03a` being absent and all three fall with
|
||||
it: that the port renders a sweep the game does not, that this was "the first
|
||||
thing in F6 that is visible and port-side", and — in a report to the human — that
|
||||
"the port draws two, the game's capture has one." **The port draws two and so
|
||||
does the game.** The census on this page stands unchanged; what changed is that
|
||||
it now agrees with the capture rather than contradicting it.
|
||||
|
||||
**The one thing that made this free was refusing to act on it.** The evidence was
|
||||
an exhaustive scan, from an agent with the oracle, corroborated by a mechanism
|
||||
and by two candidate causes. It was still an *absence*, measured once, by one
|
||||
reader — and the check I asked for was a human's look, not another measurement.
|
||||
|
||||
⚠️ **An absence is a claim about an instrument, not about the world.** A count of
|
||||
zero says only that nothing got through the reader. Every positive result on that
|
||||
same capture — the alpha decomposition, the press-vs-control comparisons, the
|
||||
pulse ratio — is untouched, because those compare like with like on the same
|
||||
quad. Only the absence compared a count against zero, and that is precisely where
|
||||
a truncating reader is fatal.
|
||||
|
||||
📌 The Decoder notes this is the third time this corpus has been bitten by an
|
||||
under-reading dump, and that `REFUTED.md` already recorded a draw carrying two
|
||||
rotated parallelograms — **the general fact was written down before the reader
|
||||
contradicted it.** Their cheap check is worth repeating here because it applies to
|
||||
anything the port ever reads: *read one raw record in full before trusting any
|
||||
count derived from it.* The batch size was printed on every one of those lines.
|
||||
|
||||
## 📌 And the same error recurred, which makes it a pattern rather than a slip
|
||||
|
||||
The withdrawn alpha bound on this page failed because nine values quoted as a
|
||||
series were `sorted(set(...))[:9]` — the lowest distinct values, a tail wearing
|
||||
the shape of a trajectory. The Decoder has since found the same thing in a second
|
||||
finding: an implied-parent range quoted as 254.0–256.9 turned out to be *the rows
|
||||
they had printed*, every twentieth frame, standing in for a population whose real
|
||||
first-cycle spread was 250.9–260.5.
|
||||
|
||||
**Twice, and both times the output looked fine.** That is the tell: a summary
|
||||
drawn from a subset does not look like an error, it looks like a result. The
|
||||
conclusion survived on both occasions, so nothing here needs undoing — but a
|
||||
conclusion surviving is not evidence the number under it was sound, and this port
|
||||
has now inherited two numbers that were not.
|
||||
|
||||
⚠️ **Neither was reachable by reasoning**, which is the part worth keeping. In
|
||||
both cases the argument was valid and the *inputs to the summary* were wrong. No
|
||||
amount of re-reading the claim finds that; only re-running it does. It is the
|
||||
argument for re-running over re-checking, and it is why the two findings flagged
|
||||
as unverified above were re-run rather than defended.
|
||||
|
||||
---
|
||||
|
||||
# ❌ A refutation aimed at this renderer, measured and NOT landed
|
||||
|
||||
The Decoder raised it and could not test it from their side: *"if your renderer
|
||||
runs both leaves on a single rate, the two strips stay locked together and drift
|
||||
from the game by ~118 units per cycle, growing without bound."* The two leaves
|
||||
declare **600** and **720** unit loops.
|
||||
|
||||
**Pre-registered, then measured on a real boot** via `--probe-leaf`. At a raw leaf
|
||||
clock of 4873:
|
||||
|
||||
| leaf | span | measured `leaf_t` | `fposmod(4873, span)` |
|
||||
|---|---|---|---|
|
||||
| `pteff03` | 600 | **72.6** | 73 |
|
||||
| `pteff03a` | 720 | **552.6** | 553 |
|
||||
|
||||
The port takes each leaf's span from **its own keyframes** — `span = max(k.t)`
|
||||
over `fe.keyframes` — so the two were never locked. **17 748 probe samples, title
|
||||
clock reaching 9 745**, i.e. the sweep is still looping 162 seconds in.
|
||||
|
||||
## 🔴 Two false alarms of my own on the way there, both from the same mistake
|
||||
|
||||
1. **I used `--time` to ask a question about running behaviour.** It sets
|
||||
`frozen`, which by design bypasses the `holding` clamp, so the title read as
|
||||
*empty* past t=250 and I nearly reported the whole title vanishing. On a real
|
||||
boot it does not: `settle_window` is `[160, 236, 198]`, the elements clamp to
|
||||
t=198, and a filmed frame at `view_units 6733` shows the complete title.
|
||||
2. **I read a probe stopping as the feature stopping.** Two runs ended at
|
||||
u≈236 and I took that as the sweep dying at settle. It was the run ending —
|
||||
without `--film` the boot exits sooner. With a film attached the same probe
|
||||
reaches 9 745.
|
||||
|
||||
📌 Both are the frozen-sweep lesson wearing new clothes: *the diagnostic that
|
||||
pins the clock cannot answer a question about the clock running*, and *an
|
||||
instrument going quiet is not the subject going quiet*. The second is the same
|
||||
shape as the Decoder's own absence-of-a-quad bug — a count of zero says only that
|
||||
nothing reached the reader.
|
||||
|
||||
---
|
||||
|
||||
# ✅ Out-of-sample: what the port ships was in the passing half
|
||||
|
||||
The Decoder pre-registered six predictions and tested them on a fresh boot that
|
||||
had no hand in deriving them. **Three failed.** Audited here against what this
|
||||
port actually authors, and the answer is **nothing to change**:
|
||||
|
||||
| their prediction | fresh boot | does the port carry it? |
|
||||
|---|---|---|
|
||||
| leaf period ratio 1.200 | 1.1753 ✅ | **yes** — this is `rate = 0.5` |
|
||||
| strip size ratio 1.333 | 1.3009 ✅ | yes, as element identity |
|
||||
| pulse / sweep loop 0.100 | 0.0963 ✅ | yes, `looping_focus_records` 120 |
|
||||
| pulse amplitude ≤3 levels | 8.73 🔴 | no |
|
||||
| `ptcopyright` ramp ratio 0.733 | 0.550 🔴 | no |
|
||||
| sweep leads plate 0.138–0.141 | **0.0996** 🔴 | **no** — grepped, absent |
|
||||
|
||||
`authored/rendering.json` `leaf_clock` is `{start_units: null, rate: 0.5}` and
|
||||
nothing else. No separation constant exists in `authored/`, `tools/port/` or
|
||||
`port/scripts/`.
|
||||
|
||||
📌 **That split is not luck and is worth naming.** Everything the port adopted is
|
||||
either **declared on the disc** (the parent gate, the 120-unit pulse loop, the
|
||||
600/720 leaf periods) or **corroborated by three independent legs** (the rate).
|
||||
Every failed prediction is a figure derived from *relationships between elements
|
||||
measured in a capture* — the class with no declared counterpart, which
|
||||
`check-authored-vs-declared` says out loud it cannot arbitrate. The rule "adopt
|
||||
what the disc declares, or what three unrelated things agree on" selected exactly
|
||||
the surviving half without anyone knowing which half that would be.
|
||||
|
||||
⚠️ And the Decoder reports that `check_labels.py` — offered last iteration as the
|
||||
mechanism for capture-only labels — **fails its first independent test**: two of
|
||||
its four checks fire on a third capture, having been validated on the two that
|
||||
produced the labels. An instrument validated on its own training data. Nothing
|
||||
here depends on it, but it is not a mechanism this port should lean on either.
|
||||
70
docs/port/menu-destinations-are-one-line-away.md
Normal file
70
docs/port/menu-destinations-are-one-line-away.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Four of five main-menu destinations are blocked on ONE hardcoded archive
|
||||
|
||||
**Status:** ✅ feasibility established, nothing changed yet. 2026-09-03.
|
||||
|
||||
## The gap, in player terms
|
||||
|
||||
| button | destination | today |
|
||||
|---|---|---|
|
||||
| NEW GAME | `DLG_SELECT_DIFFICULTY` → SELECT DATA → video | **jumps straight to the video** |
|
||||
| LOAD GAME | `GP_SAVE_LOAD` | **dead** |
|
||||
| TUTORIAL | — | **dead** |
|
||||
| OPTIONS | `GP_OPTIONS` | **dead** |
|
||||
| EXTRAS | `extras` | works |
|
||||
|
||||
All four are recorded in `authored/flow.json` as **measured destinations** —
|
||||
somebody drove the real game to them. They are `blocked` for one structural
|
||||
reason, stated there: *"not a GP_TITLE build, so there is no screen file to go
|
||||
to."*
|
||||
|
||||
## The cause is one line
|
||||
|
||||
`crates/sylpheed-export/src/main.rs` hardcodes `let archive = "dat/GP_TITLE.pak"`.
|
||||
|
||||
## And the reader already works on the rest
|
||||
|
||||
`examples/probe_archives.rs` runs the **existing** `ui_layout::is_build` over
|
||||
every `.pak` on the disc. It decodes nothing new:
|
||||
|
||||
| archive | entries | builds |
|
||||
|---|---|---|
|
||||
| `GP_OPTIONS` | 26 | **14** |
|
||||
| `GP_SAVE_LOAD` | 108 | **18** |
|
||||
| `GP_DIALOG` | 140 | **105** |
|
||||
| `GP_TUTORIAL` | 2 | **2** |
|
||||
| `GP_TITLE` | 16 | 12 |
|
||||
|
||||
**24 archives contain UI screen builds. The exporter reads one.**
|
||||
|
||||
> So this is not blocked on the Decoder and needs no new format work. It is an
|
||||
> exporter scope limit, and the exporter is the port's.
|
||||
|
||||
## Why this is worth doing before the queued items
|
||||
|
||||
Measured against *"if this is wrong, what does a player experience?"* — the
|
||||
filter this port adopted after spending two rounds on a plate pulse that turned
|
||||
out not to be a defect:
|
||||
|
||||
* **four dead menu entries** and a missing difficulty screen: a player hits them
|
||||
immediately and three of them do nothing at all;
|
||||
* the audio mix (F2): a player notices, but the menu still works;
|
||||
* the repeat rate (F1) and the title track (F3): both blocked on measurement.
|
||||
|
||||
## ⚠️ What this does NOT establish
|
||||
|
||||
* **That the screens will render.** `is_build` says the record parses as a build,
|
||||
not that its sprites resolve, its names are known, or its layout is complete.
|
||||
`GP_HANGAR_ARSENAL` reports 390 builds and is squarely gameplay, out of scope.
|
||||
* **Which entry is the difficulty dialog.** `GP_DIALOG` has 105 builds and none
|
||||
of them is named yet; `DLG_SELECT_DIFFICULTY` is a name from the flow, not an
|
||||
entry index.
|
||||
* **That more screens are free.** Every screen the export gains is a screen
|
||||
`check-all`'s comparisons iterate over, and screen names are authored per
|
||||
archive+entry — unnamed screens need a naming decision, not just a loop bound.
|
||||
|
||||
## Next unit
|
||||
|
||||
Widen the exporter to **one** further archive — `GP_OPTIONS`, the smallest at 26
|
||||
entries — as data rather than a second hardcoded constant, and see what actually
|
||||
comes out. Not all four at once: 139 new screens arriving together would make any
|
||||
regression unattributable.
|
||||
118
docs/port/options-screens.md
Normal file
118
docs/port/options-screens.md
Normal file
@@ -0,0 +1,118 @@
|
||||
# The OPTIONS menu tree exists, renders, and is named
|
||||
|
||||
**2026-09-03.** `GP_OPTIONS` joined `export_archives` and produced 14 screen
|
||||
builds. All 14 render; all 14 are now named.
|
||||
|
||||
## What they are
|
||||
|
||||
| entry | name | English | | entry | name |
|
||||
|---|---|---|---|---|---|
|
||||
| 19 | **`options`** | **the root** — GAME / CONTROL / SOUND / SCREEN SETTINGS, BACK | | 21 | `options_jp` |
|
||||
| 16 | `game_settings` | Auto-Save, View Point, Radio Log, Subtitles | | 18 | `game_settings_jp` |
|
||||
| 4 | `control_settings` | Control Type, Throttle, sensitivities, Vibration | | 8 | `control_settings_jp` |
|
||||
| 3 | `sound_settings` | Music / Movie / Voice / SFX Volume | | 5 | `sound_settings_jp` |
|
||||
| 6 | `screen_settings` | Gamma Correction, R/G/B, NEXT PAGE | | 9 | `screen_settings_jp` |
|
||||
| 7 | `screen_settings_page2` | White / Black Level Adjust, PREVIOUS PAGE | | 10 | `screen_settings_page2_jp` |
|
||||
| 20 | `control_customize` | per-action key remapping | | 22 | `control_customize_jp` |
|
||||
|
||||
A clean EN/JP pair for every screen, which is itself a check: 14 builds, 7
|
||||
pairs, no leftovers.
|
||||
|
||||
## How they were identified, and why that is stronger than usual here
|
||||
|
||||
**By the text the screen renders about itself.** Each was exported, drawn by the
|
||||
port at rest, and read: the titles and row labels are legible.
|
||||
|
||||
📌 That matters because this project has been bitten three times by
|
||||
identification via **position, size or ordinal** — the sweep strips confused by
|
||||
size, the plate identified by screen position, `ptcopyright` mistaken for the
|
||||
plate. A screen that renders the words `SOUND SETTINGS` above four volume rows is
|
||||
not that kind of inference.
|
||||
|
||||
⚠️ **What it still does not establish:** which screen the *game* navigates to
|
||||
from which. The tree above is read off content, so `control_customize` being
|
||||
"reached from CONTROL SETTINGS" is a reading of its own legend
|
||||
(`Ⓨ : Customize` on `control_settings`), not a measured transition. Wiring
|
||||
anything beyond `main_menu → options` needs the real navigation.
|
||||
|
||||
## Not yet done
|
||||
|
||||
* **Nothing is reachable yet.** `main_menu` `ptbtn04` still has `goto: null`.
|
||||
* **`po_pad_slider1` has no sprite** in the export and reports NOT DRAWN.
|
||||
* **All 14 are `NEVER COMPARED`** by `verify-screen` — reported, not asserted;
|
||||
both its allowance and the reference renderer were calibrated on `GP_TITLE`.
|
||||
* The screens are static: no navigation, no focus movement, no value editing.
|
||||
|
||||
---
|
||||
|
||||
# ✅ OPTIONS is reachable — and navigation inside it is blocked on a kind
|
||||
|
||||
`main_menu` `ptbtn04` now has `goto: "options"`. Walked with the menu harness:
|
||||
main_menu → ⬇⬇⬇ → Ⓐ → the OPTIONS root renders. Ⓑ backs out.
|
||||
|
||||
## ✅ RESOLVED — the rows move. `0x3003` is `0x3002` with the parent bit set
|
||||
|
||||
The Decoder decoded it disc-wide: **bit 0 of `kind` is the PARENT FLAG**, and it
|
||||
carries no role information. Over every `.pak` in `dat/`, `kind & 1` agrees with
|
||||
"has a parent" on **15 493 elements with zero disagreements**
|
||||
(`docs/re/ui-kind-bit0-is-has-parent.md`). The OPTIONS rows are parented; the
|
||||
main-menu buttons are not. Same record class.
|
||||
|
||||
So the detector now matches `0x3002 | 0x3003` — **two values listed, not a
|
||||
mask**. `kind & 0xFFFE == 0x3002` would also catch `0x73002`/`0x73003`, 160
|
||||
elements whose high bits nobody has decoded, silently and on screens neither
|
||||
agent has seen.
|
||||
|
||||
**Impact measured before re-exporting, not after:** exactly two screens gain
|
||||
buttons — `options` and `options_jp`, five rows each. No existing screen changes.
|
||||
|
||||
Verified by walking it: `main_menu` → ⬇⬇⬇ → Ⓐ → OPTIONS, then ⬇⬇ moves
|
||||
`po_menu_btn2` → `po_menu_btn3` with the focus ring rendering on the highlighted
|
||||
row.
|
||||
|
||||
📌 **The port was right to wait.** The rejected rule — "carries a focus record ⇒
|
||||
menu item" — would have reached the same answer here by a second inference from
|
||||
structure, and would have reclassified elements on screens nobody had looked at.
|
||||
The field cost one question and needed no inference at all.
|
||||
|
||||
## The original section, kept for the shape of the block
|
||||
|
||||
The exporter's button detector is `kind == 0x3002 && !focused`. The OPTIONS rows
|
||||
are **`kind_raw = 0x3003`**, so `role` comes out `unknown`, the export's
|
||||
`buttons[]` is empty, and up/down move nothing.
|
||||
|
||||
| screen | element | kind | focus record | in `buttons[]` |
|
||||
|---|---|---|---|---|
|
||||
| `main_menu` | `ptbtn01` | `0x3002` | yes | yes |
|
||||
| `extras` | `ptbtn11` | `0x3002` | yes | yes |
|
||||
| **`options`** | **`po_menu_btn1`** | **`0x3003`** | **yes** | **no** |
|
||||
|
||||
**What `0x3003` means is not the port's to decide**, so the rule was not widened
|
||||
here. The circumstantial case is strong — five rows, each carrying a focus
|
||||
record, on a screen whose own text lists five options — and *circumstantial* is
|
||||
precisely the standard that has cost this project three separate retractions.
|
||||
Asked of the Decoder.
|
||||
|
||||
⚠️ A tempting alternative rule is "an element with a focus record is a menu item",
|
||||
which fits both screens. It is still an inference about semantics from structure,
|
||||
and it would silently reclassify elements on every screen in the export. Not
|
||||
taken.
|
||||
|
||||
## The workflow cost this exposed, worth knowing before repeating it
|
||||
|
||||
**A screen name is authored data, but it only reaches the port through a full
|
||||
re-export** — which re-transcodes both movies. Renaming one screen costs the
|
||||
whole tree. Not worth fixing today; worth knowing before anyone plans a naming
|
||||
pass.
|
||||
|
||||
## 🔴 And a genuinely dangerous mistake, recorded because it nearly cost the session
|
||||
|
||||
Killing a background check with `pkill -f "check-all"` matched **the container's
|
||||
own entrypoint**, whose command line contains the loop prompt — and that prompt
|
||||
mentions `check-all`. `pgrep` duly reported the process as still running after it
|
||||
had stopped, and a `pkill -9` on that pattern could have killed the session
|
||||
itself.
|
||||
|
||||
**Match a process by its actual `comm`, or list with `ps` and check, before
|
||||
sending a signal.** A pattern that appears in your own instructions is not a
|
||||
pattern that identifies a process.
|
||||
Reference in New Issue
Block a user