Files
Sylpheed/docs/agents/PLAYTEST-2026-09-01.md
MechaCat02 1ad519d3ba agents: the splashes exactly, and stop photographing a moving thing
A human played the port on real hardware for the first time (2026-09-01) and
found four things. Two were port defects, fixed. Two are open and are now both
agents' focus: the PRESS (A) plate arrives late, and the splash fade/blur is
weaker than the game's.

Their verdict on method is the reason this is a brief change and not a ticket:

  "the agents were essentially guessing and trying to copy what one would see,
   but while they did get close it still is not quite right"

Close-but-not-right is the signature of reproducing APPEARANCE instead of
deriving MECHANISM. So the Decoder's focus block asks, in order: is there a
post-process pass at all, what is it, where do its parameters come from -- and
only then what curve. Both routes, dynamic (GPU state, shader constants, render
targets; add logging to Canary, it is theirs read-write) and static (.pe, the
DB, the paks), with each fact labelled by which produced it.

TEMPORAL-VERIFICATION.md is the other half, and it generalises past the
splashes. We have been photographing the game at time t, and t is never the
same twice: emulator speed varies with host load, Canary presents at ~28.1 fps,
the capture path costs a variable 0.1-10.8 s, and a long-lived x11grab stream
degrades and then freezes. The register already carries FOUR refutations of
exactly this shape. The replacement rule: record a film, not a photograph;
align by CONTENT, not by clock, and report the lag as a measurement rather than
minimising it away; prefer ordering, counts, durations and shape over any value
at a wall-clock instant; anchor on an event; report achieved fps against
requested fps.

Also into both briefs: the input set. The port had no joypad binding for (A) or
(B) and nobody noticed for a whole milestone, because --script sends
InputEventAction, which BYPASSES the input map -- so every check asserted the
code below the map and nothing about the map. The Decoder is asked to DECODE
the full set the game reads rather than discover it by pressing buttons; the
Port is told input is verified at the device level or not at all.

And both briefs now point at the R1 register reclassification, because two of
the ten re-opened entries land on this focus: "the declared keyframe timeline
reproduces the captured splash" is 🟡 our-reader, and the rest() pair is open
in BOTH directions -- while the two splashes are the only screens that reach
that fallback.
2026-09-01 17:59:17 +02:00

107 lines
5.2 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-01 — a human, a real controller, the port
**The first time a person played this port on real hardware.** It found four
things. Two were fixed on the spot by the human; two are open and are the
**current focus of both agents**.
⚠️ This page is a record of observations, not a mission change. `PORT-MISSION.md`
and the loop briefs carry the objective.
## What was found
| # | finding | status |
|---|---|---|
| 1 | **Ⓐ and Ⓑ did nothing on the pad.** Could not skip the intro, could not open a submenu. | ✅ fixed by the human — `port/scripts/gamepad.gd` |
| 2 | **The left stick moved the cursor far too fast.** | ✅ fixed by the human — latched to one step per deflection |
| 3 | **The `PRESS Ⓐ` plate appears too late.** | 🔴 **OPEN** |
| 4 | **The splash fade/blur is wrong** — the game's is *more pronounced*. | 🔴 **OPEN** |
## 1 & 2 — why no check caught them, which matters more than the fixes
> **`--script` sends `InputEventAction`, which BYPASSES the input map.**
Every check the port had asserted the code *below* the input map and nothing
about the map itself. The map turned out to have **no joypad binding for
`ui_accept` or `ui_cancel` at all** — measured on Godot 4.7.2, not remembered,
because the remembered answer was wrong:
```
ui_accept key:Enter, key:Kp Enter, key:Space <- no joypad button at all
ui_cancel key:Escape <- no joypad button at all
ui_up key:Up, JOYBTN:11, JOYAXIS:1- <- d-pad AND left stick
ui_down key:Down, JOYBTN:12, JOYAXIS:1+
```
Four actions reached the pad and two did not. Ⓐ was dead for the whole of P5
while the unattended walk passed on every iteration.
The **same blind spot** hid finding 2: an `InputEventAction` is not an analog
axis, so nothing could observe that a stick held at deflection emits an event
per *jitter*, each reporting the action as pressed — one cursor step per jitter.
Now asserted by `tools/port/verify-input`, with a control that removes each
check's own subject. (Its first version inverted all nine assertions when only
two depended on the fix, and reported seven correct checks as broken. Three rows
now say plainly they are **not controllable** — they assert Godot's own bindings
— and one is a **negative carrying a positive control** rather than a faked
inversion.)
### The standing rule that follows
**Synthetic input is not a test of input.** Anything injected below the input map
is evidence about the code above it and nothing else. A test of input must go in
at the device level — `InputEventJoypadButton`, `InputEventJoypadMotion`,
`InputEventKey` — or must assert the map directly.
## 3 — the plate is late
The port raises the plate at `t=236`, **3.93 s** after the shared clock starts,
which it derives as `238 118 = 120 units = 2.000 s` after the title's build-in
ends. A human watching both says it is **late**.
This lands in a spot the corpus already knows is soft. All of the following are
live:
* `REFUTED.md`: *"a screen has SETTLED at its `rest.t`"* → ❌ — believing `rest.t`
had already put a port's plate **3.97 s late** once.
* `REFUTED.md`: the 2.13 s figure was *"a wall-clock reading stretched by Canary
presenting at ~28.1 fps"*, corrected to 120 units. **So the conversion between
units and seconds is load-bearing here and is exactly what
[`TEMPORAL-VERIFICATION.md`](TEMPORAL-VERIFICATION.md) says not to trust from a
wall clock.**
* The keyframe **time-unit shift** is unresolved (`ui-keyframe-time-unit.md`).
* 🔴 After the 2026-09-01 R1 pass, *"the declared keyframe timeline reproduces the
captured splash"* is **🟡 `⟨our-reader⟩`**, not ❌ — the record-layout fix
re-times a group's final pose and the entry was never re-derived under it.
**Candidate causes, none established:** the unit→seconds constant; the clock
origin (do both builds really start together?); `rest.t` again; the record layout.
Settle it by **ordering and counts**, not by a stopwatch.
## 4 — the splash fade/blur
The port applies **no blur at all**. It draws declared keyframe alphas. So
*"more pronounced in the game"* is consistent with a post-process the export does
not describe, a different ramp shape, or both — and nothing in the export can
distinguish those.
🔴 **And the two splashes are the ONLY screens that reach `rest()`'s
plateau-less fallback** — title, main menu and `EXTRAS` reach it zero times. So
finding 4 lands precisely where our resting-pose heuristic is least trustworthy,
and the R1 pass just re-opened that question **in both directions** (see the
`rest()` pair in `REFUTED.md`). That is not a coincidence to step around.
## The human's verdict on method
> *"It seems the agents were essentially guessing and trying to copy what one
> would see, but while they did get close it still is not quite right."*
Close-but-not-right is the signature of reproducing **appearance** instead of
deriving **mechanism**. A ramp tuned until it looks right is wrong in a way
nobody can name and has no reach to the next screen.
The instruction that follows: for the splashes, **find out what the game is
doing** — is there a post-process pass, how many, what shader, what blend, what
render targets, and where do its parameters come from — before proposing any
curve. See the Decoder's brief.