diff --git a/authored/flow.json b/authored/flow.json index 98c8b50a..ea2835ec 100644 --- a/authored/flow.json +++ b/authored/flow.json @@ -378,7 +378,19 @@ "measuring history -- which is precisely what made the caveat necessary while", "persistence here was unknown.", "", - "โš ๏ธ WHAT IS STILL AMBIGUOUS, and it matters if another screen is ever authored:", + "โš ๏ธ WHAT IS STILL AMBIGUOUS -- and the condition below is ALREADY MET:", + "", + "๐Ÿ”ด CORRECTED 2026-08-31. This read \"it matters IF another screen is ever", + "authored\" whose opening item is not its first. Such a screen exists and is", + "recorded IN THIS FILE: DIFFICULTY, under `main_menu/buttons/ptbtn01`, is", + "EASY/NORMAL/HARD/BACK and opens on NORMAL -- the SECOND of four. Measured:", + "driven with no d-pad, unchanged for 90 s, matching the committed capture at", + "r=+0.999 (Decoder, docs/re/captures/newgame-path/newgame-difficulty.png).", + "", + "So \"a screen opens on its first item\" is REFUTED as a general description of", + "this game. On EXTRAS, TUTORIAL and OPTIONS the named item and the top item", + "coincide BY ACCIDENT. A top-item rule would be wrong on DIFFICULTY.", + "", "nobody can separate \"resets to MISSION SELECT\" from \"resets to the TOP ITEM\".", "They coincide here -- ptbtn11 is both. The port's value is correct under either", "reading, and the REASON is not established.", diff --git a/docs/port/BLOCKED.md b/docs/port/BLOCKED.md index c2aca35c..a9b1d7fd 100644 --- a/docs/port/BLOCKED.md +++ b/docs/port/BLOCKED.md @@ -147,6 +147,12 @@ HANDOFF. | ~~P1โ€“P7 โ€” the keyframe record layout~~ | ~~adopt the corrected pose/time pairing~~ | โ€” | โœ… **ADOPTED 2026-08-29 by pinning `formats-pin-2026-08-29c`.** This row was wrong twice: it said the change *"cannot be taken yet"* and that it *"reaches the port only when that branch lands on `main`"*. **It arrives when the tag is pinned**, which is what MISSION ยง2's tagging rule exists for. โš ๏ธ And the knob I tested first, `SYLPHEED_KF_TIME_SHIFT`, is a **retired partial fix** that left pose 0 untimed โ€” the real correction is the tagged crate's default, with the old reading behind `SYLPHEED_KF_TIME_LEGACY=1`. **The blast radius was far smaller than this row predicted**: under the correction *every pose is timed* (866 keyframes, 0 untimed), so `pose_at`'s synthetic-exit branch became dead code rather than wrong code and nothing needed re-deriving. Oracle: `publisher_logo` 1.00 %โ†’**0.75 %**, `developer_logos` 0.39 %โ†’**0.33 %**, `extras`' differing region collapsing from 736ร—525 to **398ร—295 at the sweep position**. ๐Ÿ”ด Open cost: `sylpheed-cli` builds from the workspace crate, so `verify-screen` compares two decoder eras until the tag reaches `main`. Revert to the path dependency then. | | ~~P7 / naming โ€” the four unnamed builds~~ | ~~which locale and variant is each of entries 0, 1, 12, 15?~~ | โ€” | โœ… **answered 2026-08-29** (`docs/re/ui-title-build-map.md`): all four are the loading screen, two variants โ€” plain (7 elements) and dressed (10) โ€” decoded from their own `pgloading_*` element names. โš ๏ธ **Not adopted as names yet, for two reasons the RE agent gave and one the port found.** Theirs: the executable names exactly two, and *which* bundle takes which name is ๐ŸŸก undecided, so `LOADING`/`LOADING2` must not go in an asset path; and locale is ๐ŸŸก โ€” the English member of a pair is the one in the first half of `GP_TITLE.p00`, 8/8 structurally but only 3/3 where a capture can check, and the three pairs that matter are the three no capture can check. Mine: **the message gives the bundles as "0/1 and 10/11", which is the `is_build` ordinal, and `authored/screen_names.json` is keyed by PAK ENTRY** โ€” in entry space 10 and 11 are `palogo_sqex` and `palogo_gamearts`, the splashes. See the refutation section in `DECISIONS.md`. | +## Half-answered, 2026-08-31 โ€” derived from HANDOFF (today's `DIFFICULTY` delivery) + +| Milestone | Needs | HANDOFF | State | +|---|---|---|---| +| P5 โ€” "resets to the named item" vs "resets to the top item" | **move the cursor in `DIFFICULTY`, leave, re-enter** | today | ๐ŸŸก **HALF ANSWERED, and the half that landed was in my own file.** *"A screen opens on its first item"* is **refuted**: `DIFFICULTY` is EASY/NORMAL/HARD/BACK and opens on **NORMAL, the second of four** โ€” measured, no d-pad, unchanged 90 s, r=+0.999 against the committed capture. ๐Ÿ”ด `authored/flow.json` has recorded *"opening on NORMAL"* since `eef45ec` (2026-08-29) โ€” **I wrote the counter-example I then spent iterations asking for**, and framed the ambiguity as conditional in the same file. โ” Still open is the **reset** half, which needs the cursor moved inside `DIFFICULTY` and the screen re-entered; its forward path crashes the guest at `SELECT DATA`, so the run must go back rather than on. โš ๏ธ No authored value moves โ€” `DIFFICULTY` is not a `GP_TITLE` build; `EXTRAS` keeps `ptbtn11`, correct under either reading. | + ## Open on my own tooling, 2026-08-31 โ€” derived from HANDOFF `d38adcf` | Milestone | Needs | HANDOFF | State | diff --git a/docs/port/DECISIONS.md b/docs/port/DECISIONS.md index a3713c11..a280c8cc 100644 --- a/docs/port/DECISIONS.md +++ b/docs/port/DECISIONS.md @@ -9,7 +9,7 @@ dies, which is what this file is for. -269 sections. Search this before re-deriving anything. +270 sections. Search this before re-deriving anything. * [P0 โ€” the exporter, 2026-08-28](#p0--the-exporter-2026-08-28) * [P1 โ€” Godot draws the screen, 2026-08-28](#p1--godot-draws-the-screen-2026-08-28) @@ -280,6 +280,7 @@ dies, which is what this file is for. * [The register check had no executable control, and an empty register passed forever](#the-register-check-had-no-executable-control-and-an-empty-register-passed-forever) * [Two harness gaps closed, and one of them was mine done by hand](#two-harness-gaps-closed-and-one-of-them-was-mine-done-by-hand) * [All four submenus reset, and I am not promoting it to a rule](#all-four-submenus-reset-and-i-am-not-promoting-it-to-a-rule) +* [๐Ÿ”ด The counter-example I kept asking for was in a file I wrote](#the-counter-example-i-kept-asking-for-was-in-a-file-i-wrote) ## P0 โ€” the exporter, 2026-08-28 @@ -13744,3 +13745,49 @@ selection and 01 is still first. **NEW GAME is untested.** ๐Ÿ“Œ Both agents now hold the same outstanding item โ€” controls without harness self-tests โ€” and neither list is empty. Mine is down to `verify-transcode-fidelity`. + +## ๐Ÿ”ด The counter-example I kept asking for was in a file I wrote + +For several iterations I have said the MISSION-SELECT-versus-top-item ambiguity +would be decided by *"a screen whose opening item is not its first"*, and that no +such screen was known. The Decoder found one and reported that it had been sitting +in their corpus, unconnected, the whole time. + +**It is in mine too, and I authored it.** `authored/flow.json`, under +`main_menu/buttons/ptbtn01`, has read since **2026-08-29** (`eef45ec`): + +> *"MEASURED destination (EASY/NORMAL/HARD/BACK, **opening on NORMAL**, then +> SELECT DATA)"* + +**DIFFICULTY opens on the second of four items.** So *"a screen opens on its first +item"* is **refuted as a general description of this game** โ€” and on `EXTRAS`, +`TUTORIAL` and `OPTIONS` the named item and the top item coincide **by accident**. + +๐Ÿ“Œ Worse than an index failing to amplify: **my `extras/initial_focus_why` framed +the ambiguity as conditional โ€” *"it matters IF another screen is ever authored"* โ€” +in the same file that already recorded such a screen.** Future tense over a fact +twelve keys away. Corrected to name DIFFICULTY concretely. + +### What it changes in the port, and what it does not + +* โœ… `MenuFlow.initial_focus`'s `buttons[0]` fallback is now documented as **a + repair for broken data, not a default** โ€” and that is measured rather than + fastidious. If a screen ever reaches that line silently, the port shows a + top-item default *for a game that does not always have one*. +* โš ๏ธ **No authored value moves.** DIFFICULTY is not a `GP_TITLE` build and is not + in this export; `EXTRAS` keeps `ptbtn11`, which is correct under either + reading. Walk re-run to confirm: unchanged. +* โ” **It still does not settle my question**, which is about *reset*, not + *opening*. That needs the cursor moved inside DIFFICULTY, left, and re-entered + โ€” and DIFFICULTY's forward path crashes the guest at `SELECT DATA`, so the run + has to go back rather than on. Theirs to run. + +๐Ÿ“Œ **And no checker either of us has built would have caught this.** Every +instrument in this project verifies that a *claim* matches a *value*. Nothing +detects that an answer already written down is not being connected to the +question it answers โ€” mine included, and mine had both halves in one file. + +โš ๏ธ It also makes the previous iteration's restraint look better than it did: +declining to promote *"4/4 submenus reset"* to a rule was argued from the +principle that a generalisation should not pre-decide the next screen. **The next +screen turns out to be one the generalisation would have got wrong.** diff --git a/port/scripts/menu_flow.gd b/port/scripts/menu_flow.gd index aa3bf34b..aafb5391 100644 --- a/port/scripts/menu_flow.gd +++ b/port/scripts/menu_flow.gd @@ -65,6 +65,17 @@ func focus() -> String: ## the first button rather than to nothing, and SAY SO: a menu that opens with ## no focus looks like a rendering bug, and this is the one place that mistake ## would hide. +## +## ๐Ÿ”ด THE FALLBACK IS A REPAIR, NOT A DEFAULT, and since 2026-08-31 that is +## measured rather than fastidious. `DIFFICULTY` -- EASY/NORMAL/HARD/BACK -- +## opens on **NORMAL, the second of four**, so "a screen opens on its first item" +## is refuted as a description of this game. On `EXTRAS`, `TUTORIAL` and +## `OPTIONS` the named item and the top item coincide by accident. +## +## So `buttons[0]` here is what to draw when the DATA IS BROKEN, and it warns +## precisely because it is not a claim about the game. If a screen ever reaches +## this line silently, the port will be showing a top-item default for a game +## that does not always have one. func initial_focus(name: String, buttons: Array) -> String: if buttons.is_empty(): return ""