# F1 — the menu repeats on a held direction: mechanism shipped, **rate deliberately not** **Status:** ✅ mechanism implemented and wired. 🔴 **inert on purpose** — it does nothing until a measured repeat rate exists. Written 2026-09-02 by the Port. ## What was reported Two statements from the human, both about the **real game**, a play-test apart: > *"Moving stick up/down and holding only moves one item. In game it actually > continues to move when holding up/down, just at a medium pace so player does > not need to move pad middle↔up/down, but also slow enough to see which item is > selected and move to target."* > *"Confirmed D-Pad does repeat when holding too."* ## The file predicted its own refutation `gamepad.gd` carried this, written when the latch was added: > *"Whether the real game repeats while a direction is held, and how fast, is > unknown… If the game does repeat, this is a difference a human will notice as > 'I have to flick it again', and the fix is a measured repeat interval — not a > guessed one."* That is exactly what happened, in the words it predicted. **So one-step-per-deflection is no longer the conservative reading — it is a known defect**, and keeping it is choosing a wrong behaviour over an approximate one. ## What was built | | | |---|---| | `Gamepad.held_direction()` | −1 / 0 / +1, polled from the **devices** | | `Gamepad.repeat_due(delta)` | one step or 0, per frame | | `Boot._menu_repeat(delta)` | calls it under the same guards a real press gets | ### Why it polls devices and not `Input.is_action_pressed` `ui_up`/`ui_down` are bound to the stick axis at **Godot's 0.50 action deadzone**, while this port steps at the game's measured **0.61** (`ENTER`). Polling the action would repeat throughout the 0.50–0.61 band — the exact band `ENTER` exists to exclude — so the repeat would contradict the threshold on the same stick, on the same frame. That is the input-map lesson from 2026-09-01 arriving in a new place: **assert the device, not the layer above it.** The stick reads from the latch `accepts()` already maintains, so the first step and the repeat cannot disagree about hysteresis; the d-pad reads `JOY_BUTTON_DPAD_UP/DOWN` directly, which the human's second report makes load-bearing rather than defensive. ### Why the guards are duplicated rather than shared `_menu_repeat` re-applies the same four conditions `_unhandled_input` applies — no movie playing, a menu exists, its stack is non-empty, no transition pending. A repeat that could fire during a movie or mid-transition would be a **second, subtly different input path**, and the first thing this port learned about input is that a second path is where the defect hides. ## 🔴 And the rate is not shipped An earlier draft of this change had `REPEAT_DELAY = 0.40` and `REPEAT_INTERVAL = 0.20`, with a paragraph explaining that they were authored. **They were removed rather than commented out**, on an explicit instruction: > *"Take the RATE from the Decoder — do NOT ship a placeholder interval. An > invented rate here is indistinguishable from a measured one later, and this is > the exact field where that already cost us."* The instruction is right and the draft was the named failure mode: the explanation would have merged, the numbers would have felt roughly right, and nothing downstream could have separated them from a measurement. `REPEAT_DELAY` is `-1.0`; `repeat_due()` returns 0 while `repeat_rate_known()` is false. **One thing about the rate IS measured, and it narrows the question.** The game digitises the left stick to four direction bits at 61 % deflection, so it cannot see deflection magnitude at all — the repeat it drives *cannot* be faster-the-harder-you-push. That excludes the one competing model, so only two constants are open and a single measurement closes both. ## ⚠️ Adopting the rate breaks a green check, for the right reason `tools/port/verify-input` asserts *"a held stick is ONE step, not six"*. That row passes today **because the feature is inert**, i.e. it asserts the absence of the repeat. When a rate is adopted a held stick should produce further steps and that row will go red. It is not wrong and it should not be deleted in a hurry: it was written for the 2026-09-01 jitter defect, so it will *look* like that bug returning. It has to be re-stated as "one step per deflection **plus** the measured repeat", with the jitter case still covered inside the delay window. ## What this does not claim * That the repeat feels right. It cannot — it does not run. * Any rate, or any bound on one. "Medium pace" is a direction, not a number, and it is not recorded anywhere as data. * That the d-pad and the stick repeat at the *same* rate. Both repeat; nobody has said they match, and the code currently assumes one rate for both.