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.
107 lines
5.2 KiB
Markdown
107 lines
5.2 KiB
Markdown
# 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.
|