port: adopt the game's 61% stick threshold, and find my verify-screen numbers were llvmpipe-specific

Two things, and both are about a hidden parameter nobody was recording.

1. THE STICK THRESHOLD IS DECODED NOW, and it replaces an authored value.

The Decoder measured that the game digitises the left stick to four direction
bits at 61 % deflection, so it never sees a velocity. Gamepad.ENTER moves
0.5 -> 0.61. The 0.5 was never a chosen value: it was a FLOOR, because Godot's
`ui_*` action deadzone is 0.50 and the latch must not arm below it. Between 0.50
and 0.61 Godot reports a direction the real game does not, and at 0.5 this port
stepped there.

The mechanism also corroborates the human's latch fix rather than merely
agreeing with it: a control that digitises to bits cannot express a rate, so
"one step per deflection" is what the hardware layer CAN produce.

⚠️ The 0.11 hysteresis gap stays AUTHORED -- nothing says the game has
hysteresis at all. And a human chose 0.5, so this changes feel: revert the one
constant if 0.61 reads as needing too much push.

🔴 AND THE CONTROL CAUGHT MY FIRST ATTEMPT AT ASSERTING IT. I added the new
device-level row as subject "latch", and `verify-input --control` failed
immediately with "a check did not invert -- it is not testing what it claims to
test". It was right: removing the latch does not remove the THRESHOLD, the
unlatched path tests `>= Gamepad.ENTER` too, so 0.55 counts 0 either way and the
row could never invert. It is a NEGATIVE, and its positive control is the 0.70
row on the same shape. Reclassified.

That also exposed a smaller thing: ok()'s negative branch HARDCODED "positive
control is the stick row", so a second negative would have borrowed someone
else's green line. It now takes the control's name, defaulting to the original
text so the d-pad row is unchanged.

 I never consumed the pad bit table they have just corrected -- checked by grep
over port/, authored/ and tools/port/, not remembered.

2. MY verify-screen NUMBERS WERE llvmpipe-SPECIFIC, and the prediction failed.

Pre-registered: both renderers blend in encoded 8-bit space, so the diffs should
be identical or within 1 level on the GPU. They are not -- every mean rose 3-35 %:
title 0.4431 -> 0.5936, main_menu 3.9363 -> 4.1449, extras 6.7422 -> 6.9757,
title_jp 2.7715 -> 2.9448, main_menu_jp 0.7885 -> 1.0157, extras_jp 0.6592 ->
0.8906, build_12/15 0.0368 -> 0.0454.

But the MAXIMA are unchanged -- 41, 97, 113, 233, 17 identical, 26 -> 27 on one
row. That is a rounding population growing, not content moving: two rasterisers
round the last bit of a blend differently while the elements that genuinely
differ do not move.

Survives: the additive diagnosis, because it rests on an ORDERING and the
ordering holds (9 elements > 5 > 0); the pgloading_loop5 localisation; the
build_00/01 agreement; the derived allowance, same four failing rows.

Does not, and is now labelled: the histogram (53 % within 1 level, 16 844 over
40); every absolute mean; and the RMSE-vs-capture pair 3151.96 / 3769.61 -- that
ORDERING claim is not re-derived on the GPU and is not claimed until it is.

The rule this earns: a renderer comparison carries its RASTERISER as a hidden
parameter. Nothing here recorded which one produced a diff, and for months there
was only one so it never mattered. Same discipline TEMPORAL-VERIFICATION already
demands for capture rate, applied to what rasterises rather than what clocks.

Not settled: H1's repeat half; H6's +0x04 exposure; the four red verify-screen
rows; whether the port is still nearer the capture than the reference on the GPU.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018AHUQvXGyNcKonSEWsgWcX
This commit is contained in:
Sylpheed port agent
2026-09-01 18:37:38 +00:00
parent ba3f490c67
commit 7c8e4a863a
4 changed files with 124 additions and 9 deletions

View File

@@ -104,7 +104,7 @@ method finding that applies to the Decoder's harness as much as this one:
| # | ask | why the port cannot answer it |
|---|---|---|
| **H1** | **Does a held direction REPEAT in the menus, and at what rate?** Initial delay and repeat interval, for the d-pad and for the left stick separately if they differ. | Behavioural. The port now does **one step per deflection**`authored`, and deliberately the conservative half: it cannot run away and it invents no rate. If the game repeats, a human will feel it as *"I have to flick it again"*. |
| **H1** | 🟢 **PARTLY ANSWERED 2026-09-01 and the port has adopted the half that landed.** The game **digitises the left stick to four direction bits at 61 % deflection** and never sees a velocity (`input-button-numbering-is-remapped.md`). `Gamepad.ENTER` moves **0.5 → 0.61**: 0.5 was a *floor* (Godot's `ui_*` action deadzone), 0.61 is the game's own threshold, and between them Godot reports a direction the real game does not. Asserted at the device level in `tools/port/verify-input`. **Still open: does a held direction REPEAT, and at what rate**one step per deflection remains authored, and the digitise-to-bits mechanism corroborates that a rate cannot come from this layer. | 🔴 **The 0.11 hysteresis gap stays AUTHORED** — nothing measured says the game has hysteresis at all. ⚠️ **A human chose 0.5 and 0.61 changes how the stick feels**; revert that one constant if it reads as needing too much push. ✅ And I never consumed the mislabelled pad bit table — checked by grep over `port/`, `authored/` and `tools/port/`, not remembered. |
| **H2** | ✅ **ANSWERED 2026-09-01, and the port's own premise was wrong.** The Decoder found **no post-process pass** on either splash (`auto/frame-blend-draw-path` `3cc1b51`). The blur is **in the asset**: every logo ships a pre-blurred copy ~21×20 px larger and concentric, drawn alone first and crossfaded out as the sharp logo fades in — which is the human's *"blurred to clear/sharp, in about a second"*, with no shader. | 🔴 **"The port applies no blur at all" was FALSE and this page said it.** The port draws all seven quads of the developer splash, the three companions included, and always has. Verified by a frozen sweep, 3 units a step: companions alone at u=315, all seven at u=1842, sharp only from u=45. Writing down what the renderer was *believed* to do instead of running it is what produced the claim. What remains is the **extent**, and it is the same unit question as H3 — asked there, once. See [`plate-arrival-halves.md`](plate-arrival-halves.md). |
| **H3** | ✅ **CLOSED 2026-09-01. ALL FOUR NAMED CAUSES ARE DEAD AND THE HUMAN'S OBSERVATION IS NOT — recorded that way deliberately rather than left green.** The rate is **56.8 units per guest second**, measured, control at 1.15 %, two elements agreeing at one clock (`units-per-second-measured.md`). 30 and 120 both excluded. At 56.8 the plate's t=236 lands at **4.15 s** against the port's 3.93 s — the port is fractionally **early**. | Eliminated in order: `rest.t` (the arrival is a declared keyframe), the clock origin (85/85 filmed frames share one clock), the anchor (answered t=160, and `clock: "shared"` survives it), the unit constant (56.8). 🔴 **`units = 2 × frames` is dead as a route** — the same animation takes 21 labels in one capture and 33 in another. ✅ Audited: this port never used it; `boot.gd` integrates `delta * units_per_second`, so the retirement cost a *justification* in `authored/timing.json` and not a behaviour, and that file's second leg (12 declared units against a 0.140.30 s black plateau = 4086 units/s) has no frames in the chain. **60 units/s is KEPT** — 56.8 is 5.6 % away against ~5 % quantisation and the Decoder did not ask for a move. ⚠️ Reach is the **title**; the splashes are a different `GamePart`. What the human saw is now unattributed — see [`plate-arrival-halves.md`](plate-arrival-halves.md) for the two remaining candidates, of which the strongest is that **Ⓐ was unbound on the play-test build so that human could not skip the 137 s intro**, and the run they judged is not the run any of these measurements describes. |

View File

@@ -255,3 +255,72 @@ express, and the allowance cannot go stale against the map it is computed from.
* That the port's additive set is complete. It covers three screens; nobody has
logged the register on the rest.
* Anything about the four undiagnosed rows.
---
# 🔴 Re-validated on the GPU, and my prediction failed: these numbers are rasteriser-specific
The human activated a hardware GPU on 2026-09-01. **Every number on this page
above was measured under `llvmpipe`**, so they needed re-deriving before anyone
builds on them.
## Pre-registered (R2)
> Both renderers blend in encoded 8-bit space, so if the port's drawing is
> rasteriser-independent the diffs should be **identical, or within 1 level**.
> Anything materially different means a published conclusion here was
> GPU-specific.
## It failed
| screen | llvmpipe mean | **GPU mean** | change | max (llvmpipe → GPU) |
|---|---|---|---|---|
| `title` | 0.4431 | **0.5936** | **+34 %** | 41 → 41 |
| `main_menu` | 3.9363 | **4.1449** | +5.3 % | 97 → 97 |
| `extras` | 6.7422 | **6.9757** | +3.5 % | 113 → 113 |
| `title_jp` | 2.7715 | **2.9448** | +6.3 % | 233 → 233 |
| `main_menu_jp` | 0.7885 | **1.0157** | **+29 %** | 26 → 27 |
| `extras_jp` | 0.6592 | **0.8906** | **+35 %** | 26 → 26 |
| `build_12` / `build_15` | 0.0368 | **0.0454** | +23 % | 17 → 17 |
**Every mean rose, by 335 %.** So the diffs are *not* rasteriser-independent
and the prediction was wrong.
## What survives, and what does not
🔴 **The maxima are unchanged** — 41, 97, 113, 233, 17 identical, and 26 → 27 on
one row. The large differences are exactly where they were.
That is the shape of a **rounding population growing, not content moving**:
llvmpipe and the NVIDIA rasteriser round the last bit of a blend differently, so
the ≤1-level tier grows while the elements that genuinely differ do not move at
all. Consistent with both still blending in encoded space — which the control on
this page established for Godot generally, not for one rasteriser.
**What survives:**
* the **additive diagnosis**, because it rests on an *ordering*, and the ordering
holds on the GPU: `extras` 6.98 > `main_menu` 4.14 > `extras_jp` 0.89 and
`main_menu_jp` 1.02 > `title` 0.59 — nine additive elements, then five, then
none;
* the **`pgloading_loop5` localisation**, which is a bounding box;
* the **`build_00`/`build_01` agree** result — still 0 pixels over the bar;
* the **derived allowance**: the failing set is the same four rows.
**What does not, and is now labelled:**
* the histogram (*53 % within 1 level, 16 844 over 40*) was llvmpipe-specific and
the ≤1 tier is larger on the GPU;
* every absolute **mean** quoted above this section;
* the **RMSE-vs-capture** pair (3151.96 / 3769.61) was llvmpipe. The *ordering*
claim — the port is nearer than the reference — has not been re-derived on the
GPU and is not claimed here until it is.
## The rule this earns
**A renderer comparison carries its rasteriser as a hidden parameter.** Nothing
in this corpus recorded which one produced a diff, and for eight months there was
only one so it never mattered. Any diff quoted from here on should say what drew
it — the same discipline `TEMPORAL-VERIFICATION.md` already demands for capture
rate, applied to the thing that rasterises rather than the thing that clocks.