From a22edacbc83f56e0f1cc1cabbb8f7c2d2c45f0cf Mon Sep 17 00:00:00 2001 From: flybrain Date: Wed, 23 Sep 2026 01:17:33 +0000 Subject: [PATCH] docs: macros.md 12.20, macros-wram.md section 11, the row 56 audit The survey whole, both arms of the trap hunt, the ROM run, and the residuals -- including the two this row does not work: neither arm reaches rung 11 from this checkpoint, and the watchdog's check 10 cannot see a loop of exactly four distinct macros. --- docs/design/macros-wram.md | 61 +++++++++++++- docs/design/macros.md | 72 +++++++++++++++- infra/docs/macros-traps.md | 166 +++++++++++++++++++++++++++++++++++++ 3 files changed, 297 insertions(+), 2 deletions(-) diff --git a/docs/design/macros-wram.md b/docs/design/macros-wram.md index 30443a2..2b97299 100644 --- a/docs/design/macros-wram.md +++ b/docs/design/macros-wram.md @@ -159,7 +159,7 @@ forces. A battle that is neither — text, an animation, the turn resolving — | a submenu | the weakest rule here, and the reason `Unknown` exists: `wListMenuID` is the bag or an elevator list, or the party list geometry outside a battle. A submenu none of those catch reads as `Unknown`, never as `Overworld`. | trace | | mart | `wTextBoxID` = `BUY_SELL_QUIT_MENU` (`$15`) for the BUY / SELL / QUIT choice, and `engine/events/pokemart.asm:17` is its only user in the game; the buy list is `wListMenuID` = `$02` and the sell list is the bag's own `$03`, recognised only while the mart's template is still the last one drawn. | trace | | PC | `wMiscFlags` bit 3, above. | trace | -| a two-option YES/NO box | **new 2026-09-22** (`docs/design/macros.md` section 12.12). `wFontLoaded` bit 0, plus the border `DisplayTwoOptionMenu` draws at (11, 6)-(19, 11), plus the shared cursor parked at `wTopMenuItemY` 8, `wTopMenuItemX` 12 with `wMaxMenuItem` 1 and `wMenuWatchedKeys` = A\|B. **Both halves are load-bearing**: the cursor bytes survive the box closing, so all forty-six frames of a Pokémon Center nurse's conversation carry that geometry while the box is drawn on exactly one of them. It does **not** answer "is a choice open" in general — Red places a two-option menu where the script asking for it says, and a prompt drawn elsewhere reads `false`. | ROM (the rung-10 Pokémon Center checkpoint, surveyed one raw A pulse at a time: `examples/scene_probe.rs`, `FLY_PROBE_CATCH=nurse`) | +| a two-option YES/NO box | **new 2026-09-22, generalised 2026-09-23** (`docs/design/macros.md` sections 12.12 and 12.20). `wFontLoaded` bit 0, plus a two-option cursor (`wMaxMenuItem` 1, `wMenuWatchedKeys` = A\|B), plus the border `DisplayTwoOptionMenu` drew **around the cursor it parked** -- see section 11. **Both halves are load-bearing**: the cursor bytes survive the box closing, so all forty-six frames of a nurse's conversation and all fifty-two of a gym guide's carry that geometry while the box is drawn on a handful of them. The border is no longer pinned to one rectangle, because Red places the menu where the script asking for it says and two of those places are surveyed. | ROM (the rung-10 Pokemon Center and the rung-10 Pewter Gym checkpoints, each surveyed one raw pulse at a time: `examples/scene_probe.rs`, `FLY_PROBE_CATCH=nurse` and `=dialog`) | ### Money and bag @@ -834,3 +834,62 @@ the HRAM joypad bytes, which is the measurement seeing its own held button. reported rather than guessed — `docs/design/ladder.md`'s rule. `ITEM` and `THROW BALL` are the two macros it costs. - **The party list, likewise**: `PartyMenuInit`'s geometry outlives its list. + +## 11. A two-option box is the one the cartridge drew (2026-09-23, `docs/design/macros.md` 12.20) + +Section 10 read one menu by the figure it draws; row 41 read the YES/NO box the same way but at a +**pinned** rectangle, (11, 6)-(19, 11), and named the limit in its own residual: Red places a +two-option menu where the script asking for it says, so a prompt drawn elsewhere read `false`. + +Row 56 is that residual, live: the Pewter Gym guide's "Let me take you to the top!" draws the same +menu at **(14, 7)-(19, 11)** with the shared cursor at row 8, column **15**. Over 260 surveyed +presses of his conversation the box was drawn on **10** frames and `yes_no_prompt` answered `false` +on **all 260** -- so the dialog pad was `NEXT, YES, NO` on a box that was a choice, and the whole of +12.12 (no `NEXT` on a prompt, the nurse's one bound answer, the reopened-prompt exclusion) was +inert wherever the box was not the centre's. + +### The accessor + +Nothing in the reviewed symbol list says "a choice is open" and nothing can be added by hand +(`gen_symbols.py` refuses a hand-written address, and the disassembly is not built on this box), so +the reading is the construction `text_box`'s `waiting` already makes -- a WRAM flag plus the figure +-- with the figure **found** rather than pinned: + +1. `wFontLoaded` bit 0, as for every text display; +2. the cursor is a two-option menu's: `wMaxMenuItem` 1 and `wMenuWatchedKeys` = A\|B; +3. the border's **left edge is one column to the left of `wTopMenuItemX`**, because + `DisplayTwoOptionMenu` writes the cursor into the box's first interior column. This holds in + both surveyed boxes -- left 11 with the cursor at 12, left 14 with the cursor at 15 -- and it is + the only geometric relation that does; +4. the **top** is looked up from the cursor's row for the border's own top-left corner, at most + three rows, because the nurse's box begins two rows above the first item and the guide's one: + one menu carries a caption line and the other does not; +5. and the rest of the figure is then read **whole** by `border_drawn` -- both verticals, both + horizontal runs and all four corners -- because a single frame tile id is an ordinary character. + +### The survey + +`examples/scene_probe.rs`, `FLY_PROBE_CATCH=dialog`, which walks a conversation one raw pulse at a +time and prints, per frame, the seam's reading beside **every complete `TextBoxBorder` on screen** +(`state::drawn_boxes`, a diagnostic that tries every rectangle rather than one). Two checkpoints, +400 frames: + +| checkpoint | a box drawn above the dialogue box | `wTextBoxID` = `TWO_OPTION_MENU` (`$14`) | the new reading | frames | +| --- | --- | --- | --- | ---: | +| rung-10 Pewter Gym | false | false | false | 250 | +| rung-10 Pewter Gym | **true** | **true** | **true** | 10 | +| rung-10 Pokémon Center | false | false | false | 136 | +| rung-10 Pokémon Center | **true** | **true** | **true** | 4 | + +The three agree exactly on all 400 frames. **`wTextBoxID` is recorded and not used**: it would be a +tighter reading still, and row 41's own note says the nurse's *plain* boxes read `$01` — which is +confirmed here — but no survey on this branch covers Red's other two-option menus, and a reading +this crate has not verified does not go in (`docs/design/ladder.md`). It is the named strengthening. + +### What it does not claim + +A frame whose two-option cursor bytes have outlived their box reads `false`, which is the whole +point of reading the figure; a border drawn somewhere the cursor is not parked is not the cursor's +box; and a menu of more than two options is not this menu. What the pad makes of a readable prompt +is `pokemon_red::macros::palette`'s business (`docs/design/macros.md` 12.12 and 12.20), not this +accessor's. diff --git a/docs/design/macros.md b/docs/design/macros.md index 1c98296..1479817 100644 --- a/docs/design/macros.md +++ b/docs/design/macros.md @@ -1304,6 +1304,76 @@ offered, and the two presses that leave a counter are. Nothing here changes which button the fly presses. The decoder, the reward catalog, the adapter version and the compatibility string are untouched. +### 12.20 A two-option box is the one the cartridge drew, and a `NO` inside a conversation declines nothing (2026-09-23, row 56) + +Live on the release box: map 54 (**Pewter Gym**), scene `dialog`, rank 10, **thirty-plus brain +minutes of zero progress** with the explore and wild-win counters frozen, **747 macro starts in ten +brain minutes**, and a mix of `YES` 64 / `NO` 62 / `NEXT` 59 / `TALK` 6 with **no walk macro dealt +at all**. The watchdog did not flag it: four distinct macros is exactly its threshold. Surveyed +from the live checkpoint with a new probe mode (`examples/scene_probe.rs`, +`FLY_PROBE_CATCH=dialog`), which walks the conversation one raw pulse at a time and prints, per +frame, what the seam makes of it beside **every complete `TextBoxBorder` the cartridge actually +drew**. `infra/docs/macros-traps.md` has the survey whole. + +The shape is **row 41's ring one town over**: the gym guide's conversation is fifty-two presses -- +"Hiya! I can tell you have what it takes to become a POKeMON champ!", "Let me take you to the +top!" with a YES/NO box, the type-matchup tutorial, "matches could be made easier!" -- the box +closes for a frame, and the next A press at the guide two tiles away opens the whole thing again. +Nothing in it changes the world. Two things kept the fly walking it, and both are readings rather +than pads. + +- **The box was drawn where this crate was not looking.** 12.12 read the border at + (11, 6)-(19, 11), because that is where a Pokemon Center's script puts it, and said so in its own + residual: "Red places a two-option menu where the script asking for it says, so a prompt drawn + elsewhere reads `false` and its dialog keeps the pad it has always had". The guide's box is at + **(14, 7)-(19, 11)** with the cursor at column 15. Over 260 surveyed presses the box was drawn on + **10 frames** and `yes_no_prompt` answered `false` on **all 260** -- so the pad was + `NEXT, YES, NO` on a frame that was a *choice*, which is 12.10's forbidden pair (an A press at a + two-option menu confirms the option the cursor is on, and that is what `YES` is), and the + reopened-prompt exclusion of 12.12 never armed, because it only judges an answer to a prompt this + crate can read. **The whole of 12.12 was inert in that gym.** +- **So the figure is found rather than pinned.** One fact about `DisplayTwoOptionMenu` rather than + about any one script: the cursor goes in the box's **first interior column**, so the border's + left edge is one column to the left of `wTopMenuItemX` -- true of both boxes surveyed. The *top* + obeys no such rule, because the nurse's box begins two rows above the first item and the guide's + one, so the top is found by looking up for the border's own corner and the figure is then read + **whole**, exactly as `waiting` and the move list are. `docs/design/macros-wram.md` section 11 + has the accessor. Measured over both checkpoints, 400 frames: the reading is true on the 14 + frames a two-option box is drawn and false on the other 386, and `wTextBoxID` = `TWO_OPTION_MENU` + agrees with it exactly -- which is recorded as a third reading and **not** put in the accessor, + because no survey here covers Red's other two-option menus. +- **And a `NO` pressed inside a conversation declines nothing.** 12.4's rule -- "the fly said no, so + whatever it said no to is still on offer" -- took the pending `TALK` off the moment any `NO` + finished. A `NO`'s B press advances a plain text box exactly as `NEXT`'s A does, about a third of + the fifty-two presses that walk the guide's ring are `NO`, so the talked ledger **never learned + the conversation had happened**: `TALK` was on the overworld pad every hold and was the ring's + own door. In the twenty-brain-minute reproduction `TALK` started **25** times on that one map. +- **Which of the two a `NO` was is decided where it can be seen: by whether the box closes on it.** + The decision moves to the frame the text goes away, which is where the talked entry is written + anyway, and the reading is the answer still standing there -- `pending_answer`, armed only by an + answer to a prompt this crate can read and alive for one hold (12.12). A declining `NO` still + standing when the box closes is a `NO` the box closed *on*, and the offer stands; anything else + is a conversation walked through to its end, and the person is retired. The nurse's own declined + heal is unchanged: it writes her into the ledger by 12.12's named inversion, one person wide. +- **What the pad does instead, which is the point.** With the guide retired, the gym's overworld pad + is `GO OBJECTIVE`, `GO OUT`, `GO FRONTIER`, `GO HEAL` -- the walks -- and the fly is out of the + room in 0.37 brain minutes against never in twenty. Nothing new is on any pad and nothing is + ranked: `TALK` goes off a person this run has already had the conversation with, which is the + ledger 9.2 added doing exactly what it was added for, and `NEXT` goes off a readable prompt, + which is 12.10. + +**What the harness holds.** Unit: a two-option box reads as a prompt at both surveyed geometries +and at neither without its border, its font flag or its two-option cursor; a box the cursor is not +parked in is not the cursor's box; the pads the two frames are dealt (`YES, NO` against +`NEXT, YES, NO`); a declined offer leaves the thing on offer; and a `NO` deeper inside a +conversation leaves the conversation counted and `TALK` off the pad. ROM-gated from the live +checkpoint: the fly leaves map 54, `NEXT` is on no pad while a readable prompt is open, and **no +readable prompt is answered more than four times for one person in a session**, against 439 +answers at one person in the twenty-minute reproduction. + +The decoder, the reward catalog, the adapter version, the roles and the compatibility string are +untouched. + ## 13. Shops and Pokémon Centers (the operator, 2026-09-17: "refactor the shop macros. make it a ## priority to visit the shop at least once per area; make shop macros item purchases. same ## for the Pokécenter. heal should be a macro.") @@ -1380,7 +1450,7 @@ observe is not a precondition, it is a guess. | Overworld, inside a mart or a centre, counter unfaced | GO SHOP or GO HEAL, TALK when facing the counter | new, and it is the one place a pad is deliberately *narrow*. The errand is paid on entering and never offered again, so a walk that leaves the building spends the one visit the area gets — measured: the fly reached the mart in 1.7 brain minutes and `GO OBJECTIVE` walked it straight back out over the doormat. While the counter is unfaced nothing on the pad leaves (row 34b) | | Overworld, inside a mart or a centre, counter faced | the indoor pad, plus HEAL in a centre | the suppression is released by facing the counter, by talking to it, or by a walk to it failing. Since **12.12** `TALK` is not on it at a *nurse* the party has no use for: her conversation is a service whose need the cartridge publishes, and a ring of text that ends where it began is section 12.2's trap | | Dialog, a plain text box | NEXT, YES, NO | A and B both advance a plain box, so all three are dealt for one — what it buys is the fly being able to answer *no*. Forty-five of the nurse's forty-six frames are this row (**12.12**) | -| Dialog, a readable YES/NO box | YES, NO — or **one of them** at a Pokémon Center's nurse | **new, 12.12.** `NEXT` is off it: an A press at a two-option menu confirms the option the cursor is on, which is what `YES` is, so the two are one press under two names (12.10). At the nurse's own prompt the bound answer is the one that changes something — `YES` with a hurt or statused party, `NO` with a full one. An answer whose prompt comes straight back is excluded for the blocked window, and the exclusion never empties the pad | +| Dialog, a readable YES/NO box (**the box the cartridge drew**, 12.20) | YES, NO — or **one of them** at a Pokémon Center's nurse | **new, 12.12.** `NEXT` is off it: an A press at a two-option menu confirms the option the cursor is on, which is what `YES` is, so the two are one press under two names (12.10). At the nurse's own prompt the bound answer is the one that changes something — `YES` with a hurt or statused party, `NO` with a full one. An answer whose prompt comes straight back is excluded for the blocked window, and the exclusion never empties the pad | | Menu (the start menu) | CLOSE, CONFIRM, BACK | unchanged as a *scene*, and since **12.11** nothing on any other pad opens it: the fly reaches it with the **raw** START button, which still reaches the cartridge in macros mode, and moves its cursor with the raw D-pad. A SAVE or a POKéDEX button would be a macro per start-menu entry and is not asked for -- which is precisely why `MENU` had nothing behind it | | Menu (the bag, an elevator, the party list outside a battle) | CLOSE, CONFIRM, BACK | unchanged | | Unknown (the Pokédex, the trainer card, OPTION, a naming screen, a mid-warp frame) | NEXT, **BACK** | **BACK added** (row 9): B is what leaves the first three, and A leaves none of them | diff --git a/infra/docs/macros-traps.md b/infra/docs/macros-traps.md index 9b5dd5b..84428cb 100644 --- a/infra/docs/macros-traps.md +++ b/infra/docs/macros-traps.md @@ -2365,3 +2365,169 @@ appending in the same place: `docs/design/macros.md` (row 50 keeps 12.18, row 55 **12.19**), this file (both rows, both residual pairs and both arm sections kept) and `examples/scene_probe.rs` (both survey modes kept, `accept` and `shop`). Every source file auto-merged. + +## 2026-09-23, row 56: the box the seam could not see, and a `NO` fifty presses deep + +### What was live + +Rank 10 (PEWTER CITY, next the BOULDER BADGE), the fly inside the **Pewter Gym**, map 54, scene +`dialog`, **thirty-plus brain minutes of zero progress** with the explore and wild-win counters +frozen. **747 macro starts in ten brain minutes**, mix `YES` 64 / `NO` 62 / `NEXT` 59 / `TALK` 6 +over the last 40 KB of events, the labels running +`NO, TALK, NO, YES, YES, NEXT, NO, YES, NO, YES, NEXT, YES, YES, NEXT, NO, NEXT, NO, YES, YES, NO, +NEXT` with `YES` completing back to back and **no walk macro dealt at all**. The watchdog did not +flag it: four distinct macros is exactly check 10's threshold. + +At the checkpoint the fly stands at (7, 11) facing **Up** at sprite slot 3, picture `$24` -- the +gym guide, the only sprite the ten-by-nine window can see; Brock is off screen at the top of a +10x14 room. Two warps at (4, 13) and (5, 13), the town outside. `objective` is map 54 with a +**Person** on it, which is rung 11, and `objective_goals` is the gym's own two doors, because the +Pewter Pokémon Center is an unpaid errand and an errand goes ahead of the rung (row 54). + +### The survey: the conversation, one raw pulse at a time + +A new probe mode, `FLY_PROBE_CATCH=dialog` in `examples/scene_probe.rs`. It walks the conversation +one pulse at a time and prints, per frame, what the seam makes of it -- scene, `open`, `waiting`, +`yes_no_prompt`, the cursor bytes, `wTextBoxID` -- beside **every complete `TextBoxBorder` the +cartridge actually drew** (`state::drawn_boxes`, which tries every rectangle rather than one), with +the pad the palette deals for that frame. `FLY_PROBE_ANSWER` picks the button a frame with a box on +it is answered with, so both arms of the choice can be walked. + +The ring, elided to the states that matter (260 presses, `#n` is the pulse): + +``` +#17 Dialog waiting=true yesno=false cursor=(8,15,0,1,0x03) textbox=0x01 boxes=[(0,12)-(19,17)] | Let me +#19 Dialog waiting=true yesno=false cursor=(8,15,0,1,0x03) textbox=0x14 boxes=[(14,7)-(19,11) (0,12)-(19,17)] | Let me take you | to the top! +#21 Dialog waiting=true yesno=false cursor=(8,15,1,1,0x03) textbox=0x01 boxes=[(0,12)-(19,17)] | It. a free | service! Let. +... the type-matchup tutorial, thirty more boxes +#194 Dialog ... | matches could be | made easier! +#195 Overworld open=false pad=["GO OBJECTIVE", "GO OUT", "GO FRONTIER", "GO HEAL", "TALK"] +#196 Dialog ... | Hiya! I can tell | you have what it ... and the whole thing again +``` + +Five things that settles. + +1. **It is row 41's ring one town over.** Fifty-two presses, the box closes for a frame, and the + next A press at the guide opens it again. Nothing in it changes the world. +2. **The YES/NO box is drawn at (14, 7)-(19, 11)**, not the (11, 6)-(19, 11) row 41 pinned, with + the shared cursor at row 8 column **15** rather than column 12. `yes_no_prompt` answered `false` + on **all 260 frames** while the box was drawn on **10**. +3. **So the pad was `NEXT, YES, NO` on a frame that was a choice** -- printed on the line, hold by + hold. `NEXT` and `YES` are one A press with two channel names, which is 12.10's forbidden pair, + and the reopened-prompt exclusion of 12.12 never armed, because it judges only an answer to a + prompt this crate can read. The whole of 12.12 was inert in that gym. +4. **Answering `NO` at his prompt changes nothing either.** The `b` arm gets "It's a free service! + Let's get happening!" and the same tutorial, the same length, the same ending. So the brief's + first reading -- a prompt that re-offers itself for ever -- is **out**: the prompt appears once + per lap, on two frames of fifty-two, and what repeats is the conversation. +5. **The cursor bytes are stale on all 260 frames** (`wTopMenuItemY` 8, `wTopMenuItemX` 15, + `wMaxMenuItem` 1, `wMenuWatchedKeys` `$03`) while the box is drawn on ten, exactly as at the + nurse's counter. Both halves of the reading are still needed; what changes is that the figure is + found rather than pinned. + +The same probe on the rung-10 **Pokémon Center** checkpoint, 140 presses, as the regression check: +the box at (11, 6)-(19, 11) on 4 frames, `yes_no_prompt` true on those 4 and false on the other +136, her pad `["NO"]` throughout -- 12.12 intact. + +| checkpoint | a box drawn above the dialogue box | `wTextBoxID` = `$14` | `yes_no_prompt` (after) | frames | +| --- | --- | --- | --- | ---: | +| Pewter Gym | false | false | false | 250 | +| Pewter Gym | **true** | **true** | **true** | 10 | +| Pokémon Center | false | false | false | 136 | +| Pokémon Center | **true** | **true** | **true** | 4 | + +Three readings, 400 frames, exact agreement. `wTextBoxID` is **recorded and not used**: it would be +tighter still, but no survey here covers Red's other two-option menus and a reading this crate has +not verified does not go in. + +### Why `TALK` fired twenty-five times and retired nothing + +`TALK`'s ledger entry is armed when the macro finishes and written when the box closes +(`MacroMachine::observe_frame`). Between those two moments sit the fifty-one other presses of the +ring -- and **every `NO` among them un-armed it**, by 12.4's rule that "the fly said no, so whatever +it said no to is still on offer". A `NO`'s B press advances a plain text box exactly as `NEXT`'s A +does; about a third of the ring's presses are `NO`; so the guide never entered the talked ledger, +`TALK` stayed on the overworld pad, and it was the ring's own door. Twenty-five laps in twenty brain +minutes. + +Which of the two a `NO` was is decided where it can be seen: **by whether the box closes on it.** + +| # | trap | trigger | test | fix, or why it is left | +| --- | --- | --- | --- | --- | +| 56 | a YES/NO box drawn anywhere but the Pokémon Center's corner reads as plain text, so the dialog pad deals `NEXT` beside `YES` -- two names for one A press -- and none of section 12.12 applies | any two-option menu Red's script puts somewhere else; measured at the Pewter Gym guide, whose box is at (14, 7)-(19, 11): 260 surveyed frames, box drawn on 10, `yes_no_prompt` false on all 260 | `a_yes_no_prompt_is_the_box_drawn_around_the_cursor_wherever_red_draws_it`, and the prompt/`NEXT` claims in `the_fly_leaves_the_pewter_gym_guides_ring_from_the_rung_ten_checkpoint` (ROM-gated) | **fixed**: the border is **found** around the cursor the game parked rather than pinned. `DisplayTwoOptionMenu` writes the cursor into the box's first interior column, so the left edge is one column to its left in both surveyed boxes; the top obeys no such rule (a caption line in one, none in the other) and is looked up; the figure is then read whole. `docs/design/macros-wram.md` section 11 | +| 56b | a `NO` pressed fifty boxes deep into a conversation un-arms the pending `TALK`, so a person whose conversation is longer than one box is never retired and `TALK` is the door back into the ring | any conversation with more than one box in which the fly's roll lands on `NO`; at the gym guide, 25 laps in twenty brain minutes | `a_no_pressed_inside_a_conversation_is_not_a_declined_offer`, `a_fly_that_declines_an_offer_has_not_talked_to_anything` | **fixed**: the decision moves to the frame the box closes, which is where the talked entry is written anyway. A declining `NO` still standing there -- `pending_answer`, armed only by an answer to a readable prompt and alive for one hold -- is a `NO` the box closed *on*, and the offer stands; anything else is a conversation walked through to its end | + +### The trap hunt, before and after + +Same seed, same checkpoint, twenty brain minutes each; base `main` at `2de2dce` against this branch. + +| measure | before | after | +| --- | ---: | ---: | +| distinct (map, tile) | **83** | **441** | +| windows flagged | 68 / 73 | 69 / 73 | +| macros started | 1,356 | 1,182 | +| `YES` / `NO` / `NEXT` on map `0x36` | **227 / 212 / 198** (637 presses at one conversation) | **21 / 21 / 17** (59) | +| `TALK` starts | **25** | **2** | +| frames in scene `dialog` | **32,496 of 71,673** | **2,849** | +| frames in scene `overworld` | 8,571 | **17,425** | +| frames in scene `battle` | 29,530 | **49,877** (longest 13,089) | +| `GO OBJECTIVE` starts | 19, mean reach 5.0 | **37**, mean reach 9.4, max net 27 | +| `GO FRONTIER` / `GO OUT` / `GO ITEM` | 18 / 15 / 8 | 35 / 19 / 15 | +| the run ends | in scene `dialog` on map 54, `prompt=false`, `cursor=(8,15,0,1,0x03)` | in scene `overworld` | +| rung reached | 10 | 10 | +| wall clock | 1,070 s | 1,262 s | + +**The before arm is the live loop with the real brain in it**: 637 of the run's presses are the +guide's conversation, the fly ends the run still standing in it, and the last line of the dump is +the guide's own stale cursor with `prompt=false` beside it -- the trap, printed. **The after arm +covers 5.3 times the ground** and spends four per cent of its frames in a text box instead of +forty-five. + +**The flagged-window count does not fall (68 → 69), and this says so rather than smoothing it.** +After the fix 49,877 of the 71,673 frames are battles the fly is actually fighting, with a single +13,089-frame one, and the hunt's rule -- fewer than four distinct tiles in two brain minutes -- +flags a fighting fly exactly as hard as a stuck one. That is `docs/design/macros.md` section 15's +own measurement and the fourth branch in a row to run into it. The merge is Fable's call. + +### The ROM-gated run, from the live checkpoint + +`the_fly_leaves_the_pewter_gym_guides_ring_from_the_rung_ten_checkpoint`, 240,000 frames +(67.0 brain minutes), the stub rotation: + +- the fly **leaves map 54 on frame 1,337** -- 0.37 brain minutes -- against never in twenty; +- **27 macros spent in the gym**: `NEXT` 9, `YES` 9, `NO` 8, `GO OBJECTIVE` 1. One lap of the ring + and out; +- **`TALK` starts by map `{2: 12, 58: 1}`** -- twelve different townspeople, and **none** back at + the guide; +- **the most-answered prompt is answered once.** `answers_by_person` over the whole run is + `{(2, Sprite(4)): 1, (52, nobody): 1, (54, Sprite(3)): 1}`, against a bound of four and 439 + answers at one person in the before arm; +- `NEXT` on **no** pad while a readable prompt is open, over 85 prompt frames. + +### Residuals, named rather than worked around + +- **Neither arm reaches rung 11, and the ROM test reports the rung rather than asserting it.** In + the ROM run the fly leaves the gym and then walks Pewter City and its buildings -- route + `[54, 2, 58, 2, 58, 2, 58, 2, 57, 2, 55, 2, 55, 2, 52, 2, 56, 2, 56, 2, 56, 2]`, `GO FRONTIER` + 501 of its 507 starts on map 2 -- and never goes back through the gym door. `GO OBJECTIVE` **is** + on the pad on maps `{2, 52, 54, 55, 56, 57, 58}`, so the road exists; what is not measured here is + why it is chosen five times in sixty-seven brain minutes while the town's frontier is chosen five + hundred. The objective's target is a *person* the fly has never faced and its first hop is a door + the reached ledger retires once it has been through, which is row 12.5's ground and row 54's, not + this row's. **It is the next brief.** +- **The watchdog cannot see this loop.** Check 10 flags on fewer than four distinct macros in the + window and this row is exactly four (`YES`, `NO`, `NEXT`, `TALK`); its dominance rule needs one + macro at 95% and the mix here is three ways even. Raising the threshold is a systems change with + its own false-positive cost and it is not made here. +- **`wTextBoxID` = `TWO_OPTION_MENU` agrees with the new reading on all 400 surveyed frames** and is + not in the accessor, for want of a survey of Red's other two-option menus. +- **The hunt's tile rule still cannot tell a long battle from a stall** -- fourth branch running. + +### Gates + +- `cargo test --workspace` with `FLY_ROM` and `FLY_DATASET` set: see the report. +- `cargo clippy --all-targets`: **0 warnings**. +- `infra/tests/lint.sh`: ALL CHECKS PASSED, de-PII guard included. +- `flysim --print-compatibility`: **648 bytes, sha256 `4929f340...9ebd9`** -- byte-identical to this + branch's base `2de2dce`. Decoder, reward catalog, adapter version and roles untouched. +