Files
Sylpheed/docs/agents/PLAYTEST-2026-09-02.md
MechaCat02 a71dea9b8d agents: the logo splashes are DONE -- the human cannot tell them from the game
"Looks good! Cannot notice any obvious difference from the actual game.
   Mark logos as done."  -- 2026-09-02

Not "the check passes": a person compared the port against the real game and
could not tell them apart. That is the oracle, and it is the strongest result
this port has produced. The sole-focus order is lifted; both agents return to
their milestones.

The fix was one word -- pose_at ASSIGNED the settle instant instead of clamping
to it, so every query returned the settled pose whatever the clock said. The
same line manufactured the false green: the capture harness shoots after two
frames, so it was photographing t~=2 units, which looked settled only because
everything looked settled. The 0.01 % agreement that closed H2 was measured
through the accident. One bug produced the defect AND the evidence of its
absence.

Verified here before it went to the human, by film rather than by claim:
motion 16.4 % -> 27.7 %, distinct luma states 26 -> 43, the publisher ramp 6
steps -> 13 in one continuous run, and the developer splash's interrupting
0.50 s freeze gone. The publisher trajectory rises to a peak and settles back --
the crossfade signature.

The port then closed a gap motion-census names in its own header ("a wrong ramp
that moves every frame passes here") with a shape check pre-registered from the
disc, measured off a film, on a non-overlapped strip, in ratios so the texture
divides out: rise:last declared 1.20, measured 1.20 exact.

Kept as the standing lesson, because it is the fourth instance: an instrument
that sits below the thing under test cannot see it fail. Ask of any new check
what it would still report if the feature were entirely absent.

Explicitly NOT claimed: P5's gate is "a human clicks through it" and nobody has
said the milestone is met. The briefs say so, and say not to record it on the
human's behalf.

The decoder's end-to-end pipeline work returns to normal priority rather than
being dropped -- it is what decides whether the port's 60 units/s matches the
game. The ramp is now right in SHAPE and unverified in DURATION.
2026-09-02 18:15:03 +02:00

194 lines
9.7 KiB
Markdown
Raw Permalink 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 did not animate. **Now fixed and signed off.**
## ✅ CLOSED THE SAME DAY — the human's verdict, which is the only gate that counts
> *"Looks good! Cannot notice any obvious difference from the actual game.
> Mark logos as done."* — 2026-09-02
**The logo splashes are DONE.** Not "the check passes" — a person compared the
port against the real game and could not tell them apart. That is the oracle,
and it is the strongest result this port has produced.
⚠️ **Both agents: the sole-focus block is lifted.** Return to your milestones.
What remains open from the play-tests is listed at the bottom of this page; none
of it is this.
### The root cause was one word
```diff
- t = settle_instant if settle_instant >= 0.0 else min(t, settle_units(element))
+ t = min(t, settle_instant) # the comment above it already said "stop at"
```
`pose_at` **assigned** the settle instant instead of clamping to it, so every
query returned the settled pose whatever the clock said.
🔴 **And the same line manufactured the false green.** The capture harness shoots
after two frames, so it was photographing t ≈ 2 units — which *looked* settled
only because everything looked settled. **The 0.01 % agreement that closed H2 was
measured through the accident.** One bug produced the defect and the evidence
that the defect was absent.
### Verified independently before it went to the human
Filmed a real boot at 0.05 s, before against after:
| | before | after |
|---|---|---|
| splash in motion | 1.30 s / 7.95 s (16.4 %) | **2.20 s / 7.95 s (27.7 %)** |
| distinct luma states | 26 | **43** |
| publisher ramp | 0.30 s, 6 steps | **0.65 s, 13 steps, one continuous run** |
| developer splash | two bursts split by a **0.50 s freeze** | **one continuous 0.90 s run** |
The publisher trajectory rises to a peak and settles back — the **crossfade
signature**: glow alone, then both, then sharp only. The developer splash's
interrupting freeze disappearing is the clearest single sign the clock now drives
the poses.
The port then closed a gap `motion-census` names in its own header — *"a wrong
ramp that moves every frame passes here"* — with a shape check pre-registered
from the disc, measured off a film, on a strip no other element overlaps, in
**ratios** so the texture divides out: middle:last declared 0.203 / measured
0.213, rise:last declared 1.20 / measured **1.20 exact**. It agrees with the
Decoder's independent measurement of the running game.
### 🔴 The lesson, which outlives the bug
Three instruments passed a frozen screen. Keep this: **an instrument that sits
below the thing under test cannot see it fail.** A frozen sweep drives the clock
by hand; a settled comparison is *defined* to pass on a frozen screen; an
achieved-fps counter counts frames drawn, not frames different. Ask of any new
check: *what would this still report if the feature were entirely absent?*
---
## The original report, kept for the record
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.
---
## What is STILL OPEN after the sign-off
The logos are done. These are not, and none of them blocks a milestone gate:
| | | owner |
|---|---|---|
| **H1** | Does a held direction **repeat** in the menus, and at what rate? One step per deflection stays authored. The 61 % arm threshold is decoded and adopted; the 0.11 hysteresis gap is still authored. | Decoder |
| **H3** | The `PRESS Ⓐ` plate. All four named causes are dead and the port measures fractionally **early**, so what the human saw is **unattributed** — deliberately not closed green. ⚠️ Worth re-asking now: the animation fix changed what the whole boot looks like, so the original observation may simply no longer reproduce. | both |
| **pipeline** | The end-to-end graphics account — disc → decode → the game's per-frame update → submitted draws → Canary's processing → the presented frame. Cut short by the sole-focus order and **still the right work**: it is what would let the port know whether its ramp *duration* matches the game rather than only its own declared keyframes. | Decoder |
**P5's gate is "a human clicks through it" and has NOT been claimed here.** The
human has signed off the *logos*, and separately confirmed that Ⓐ, the stick and
the submenus work. Nobody has said the milestone is met, and an agent must not
say it on their behalf.