Files
Sylpheed/docs/agents/PLAYTEST-2026-09-02.md
MechaCat02 d394c55cbb agents: the splash does not animate, and three instruments could not see it
A human on a GPU at ~140 fps: "the logos just switch, there is no animation at
all." Measured from a real boot with --film at 0.05 s, then per-frame change:

  splash moves        1.30 s of 7.95 s = 16.4 %
  publisher splash    0.30 s of motion, then 3.20 s FROZEN
  developer splash    0.35 s + 0.25 s, then 2.40 s FROZEN
  distinct luma states in 7.95 s   26

A 45-unit build-in cannot be drawn in 26 states, and a fade does not hold one
picture for 3.20 s. The frame counter says 24.8 fps achieved; both are true --
the port is DRAWING 25 times a second and CHANGING almost never.

🔴 Why every check passed, which matters more than the bug:

  frozen sweep     drives the clock BY HAND -- proves the renderer can draw
                   pose N, never that the poses are drawn in sequence
  settled compare  0.01 % against the capture -- a screen frozen 84 % of the
                   time matches a settled reference PERFECTLY, that is what
                   frozen means
  achieved fps     counts frames DRAWN -- the same pixels 25x/s scores
                   identically to animating

Every one measured throughput or a pose. None measured CHANGE. Same shape as
InputEventAction bypassing the input map: the instrument sat below the thing
that was broken, so the break could not appear in it.

tools/motion-census closes the class. It measures change and nothing else, and
its --selftest asserts it separates a fade (97.4 % moving) from a switch (2.6 %)
from a frozen film (0.0 %) -- a detector that cannot tell those apart would
report the same green line on all three.

Both briefs: this is the SOLE focus. The port reproduces before changing
anything and gates every fix on a film rather than a still. The decoder maps the
whole pipeline end to end -- disc bytes, the game's per-frame update (does it
interpolate between keyframes or hold?), what is submitted per frame, and what
Canary does to it before a capture records it -- delivered as a SERIES, not a
settled value.

The port should also record the refutation against itself: H2 reads ANSWERED on
the strength of the frozen sweep. The mechanism half stands, the blur is a baked
companion texture. The behaviour half does not.
2026-09-02 17:24:42 +02:00

115 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Play-test, 2026-09-02 — **the splash does not animate at all**
**This is the only thing either agent works on until it is fixed.**
The human, watching the port on real hardware, on a GPU, at ~140 fps:
> *"The port does no blur animation at all! I cannot discern if there is any
> animation at all. The logos just switch without the animation."*
## Measured, not paraphrased
Filmed from a **real boot** at 0.05 s (`--film`), then per-frame change measured
with [`tools/motion-census`](../../tools/motion-census):
| | |
|---|---|
| splash moves | **1.30 s of 7.95 s — 16.4 %** |
| publisher splash | 0.30 s of motion, then **3.20 s frozen** |
| developer splash | 0.35 s + 0.25 s, then **2.40 s frozen** |
| distinct luma states in 7.95 s | **26** |
A 45-unit build-in cannot be drawn in 26 states, and a fade does not hold one
picture for 3.20 s. **This is a switch with a flicker on either side.**
The frame counter says 24.8 fps achieved. Both are true: the port is *drawing*
25 times a second and *changing* almost never.
## 🔴 Why three instruments all said it was correct
This is the part that matters more than the bug, because it is the fourth time
this shape has cost a milestone.
| instrument | what it proved | what it could not see |
|---|---|---|
| **frozen sweep** (`--time=`, 3 units a step) | the renderer CAN draw pose *N* | whether the poses are ever drawn **in sequence, while running** |
| **settled comparison** (0.01 % against the capture) | the resting pose is right | a screen frozen 84 % of the time matches a settled reference **perfectly — that is what frozen means** |
| **achieved-fps counter** | frames are being drawn | drawing the **same pixels** 25×/s scores identically to animating |
> **Every one measured throughput or a pose. Not one measured CHANGE.**
That is why the port could write *"the companion quads are drawn, verified by a
frozen sweep"* and be simultaneously right and useless: the sweep drives the
clock by hand. It is the same defect as `InputEventAction` bypassing the input
map — **the instrument sat below the thing that was broken**, so the thing that
was broken could not appear in it.
`tools/motion-census` exists to close this class. It measures change and nothing
else, and its `--selftest` proves it separates a fade (97.4 % moving) from a
switch (2.6 %) from a frozen film (0.0 %) — because a detector that cannot tell
those apart would report the same green line on all three.
## What both agents do now
**Nothing else.** Not the clock rate, not the plate, not blend, not audio. This
first.
### Port
1. **Reproduce it** with `--film` + `motion-census` before changing anything, and
quote the numbers. If your run does not reproduce 16.4 %, say so — the
disagreement is then the finding.
2. **Find why the clock does not advance the poses.** Candidates, unranked and
none established: the keyframe interpolation returns the same pose for a
range of *t*; `rest()`/plateau logic snapping to an endpoint; the group clock
not integrating; interpolation between keyframes not happening at all
(nearest-keyframe rather than lerp); the screen advancing by *keyframe index*
instead of by time.
3. **Every fix is gated by a film**, never by a still. A change that improves a
settled frame and leaves the film at 16 % has not fixed this.
4. `motion-census` goes into `check-all`, so a future regression fails a check
instead of waiting for a human.
### Decoder — **map the whole graphics pipeline, end to end**
The human's instruction, in their words: *"get the whole graphics pipeline, from
the xex/pe + the disc files to the final screen displayed, and take Xenia Canary
processing into account too."*
So: one continuous account, each stage with evidence and each labelled by the
instrument that produced it —
```
disc bytes → RATC/T8aD decode → what the GAME CODE does per frame
→ the draw calls it submits → Canary's own processing
→ the presented frame
```
Specifically, and none of it inferable from a file alone:
* **The game's per-frame update.** Which code advances a UI group's clock, in
what units, and what it does *between* keyframes — does it interpolate, or
hold to the next key? That single question decides whether the port should
lerp at all. It is in the image; find the function.
* **What the game submits per frame during the splash** — the draw list frame by
frame, not one settled frame. If the alpha changes, it changes *somewhere*
observable: a vertex colour, a PS constant, a blend factor, a texture swap.
**Name which, with the per-frame series.**
* **What Canary does to it.** Present cadence, any resolve/scaling/gamma between
the guest's draw and the pixels a capture records. A capture is evidence about
*Canary's output*, and the difference between that and the guest's intent has
bitten this corpus before (the `kernel_display_gamma_type` entry).
⚠️ **Deliver a per-frame SERIES, not a settled value.** Follow
[`TEMPORAL-VERIFICATION.md`](TEMPORAL-VERIFICATION.md): film it, align by
content, report ordering/counts/durations. The port needs to know what the
alpha *trajectory* is, and a single frame cannot carry one.
## And this is a refutation the port should record against itself
`BLOCKED.md` H2 currently reads ✅ **ANSWERED**, on the strength of the frozen
sweep. The mechanism half stands — the blur is a baked companion texture, that
is decoded and correct. **The behaviour half does not**: the port draws those
quads and does not animate them, so *"the companions are drawn"* was true and
did not mean what the row used it to mean.