diff --git a/docs/design/AutoStory-1717-review-drafts.md b/docs/design/AutoStory-1717-review-drafts.md
new file mode 100644
index 0000000000..fb821023af
--- /dev/null
+++ b/docs/design/AutoStory-1717-review-drafts.md
@@ -0,0 +1,70 @@
+# Review comment drafts for PokemonAutomation/Arduino-Source#1717 (BDSP AutoStory)
+
+**Not for merge, and not posted.** These are ready-to-paste drafts for the
+user, who decides whether to post them, and when. Each one stands on its own
+and makes no request beyond the fix. None of them proposes shared code or
+asks the BDSP team to restructure anything.
+
+**Stale:** checked against PR head `65094d4cd` (2026-10-25); the head is now `45a206f0d`
+(2026-10-29). Recheck every draft before posting. Lessons Part 1 (§1 to §7) holds the newer
+findings for Butters.
+
+Checked against PR head `65094d4cd` (2026-10-25). Before posting, check that
+the head has not moved (`gh pr view 1717 -R PokemonAutomation/Arduino-Source
+--json headRefOid`), and recheck the line numbers if it has. Paths are under
+`SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/`.
+
+Each draft is meant as an inline comment on the file and line given in its
+heading. If posted as a plain PR comment instead, add the path.
+
+---
+
+### 1. `PokemonBDSP_AutoStoryTools.cpp:245-246` (the retry with no save)
+
+> When `save` is false and the retry scope isn't `SELECTED_RANGE`, a failed
+> attempt logs "Reloading from backup save before retry..." and calls
+> `reload_backup_save`, which is still an empty TODO (lines 188-197). The
+> next attempt then starts from wherever the failure left the player. Until
+> the backup reload exists, resetting from HOME here as well (the `save`
+> branch just above) would at least retry from a known state.
+
+### 2. `StorySegments/PokemonBDSP_AutoStory_Segment_08.cpp:116` (segment end)
+
+> `AutoStory_Segment_08::run_segment` stops after 0821, but the registry
+> (`PokemonBDSP_AutoStory.cpp:181-185`) continues to 0826. In segment mode,
+> 0822-0826 (the house west of Sunyshore through the eighth gym) never run.
+
+### 3. `PokemonBDSP_AutoStory.cpp:155-156` (checkpoint list)
+
+> The checkpoint list goes from 0716 to 0718, so 0717 can't be selected as a
+> start or end checkpoint in advanced mode.
+
+### 4. `StorySegments/PokemonBDSP_AutoStory_Segment_07.cpp:214` (trainer id)
+
+> This branch matches `"route_215_skier_edward"`, but the only caller
+> (`:773`) passes `"route_216_skier_edward"`, the id that `handle_battle`
+> (`:242`) also uses. The 8.1 s push back after that battle never runs.
+
+### 5. `StorySegments/PokemonBDSP_AutoStory_Segment_04.cpp:31` and `Segment_05.cpp:30` (badge texts)
+
+> In BDSP the third badge is Veilstone (Maylene) and the fifth is Hearthome
+> (Fantina). Segment 04's start text says "Third Gym Badge acquired
+> (Hearthome City)" and Segment 05's end text says "Fifth Gym Badge acquired
+> (Veilstone City)", so the two city names are swapped.
+
+### 6. `Utils/PokemonBDSP_AutoStory_Battle.cpp:208, 698` (unused handlers), optional
+
+> `handle_battle_with_outside_help` and `handle_expert_battle` have no
+> callers in the PR. Each of segments 04-09 uses its own `handle_battle`,
+> keyed by trainer-id strings. Is the plan to switch the segments over to
+> these?
+
+Draft 6 is a question rather than a bug. Leave it out if the thread is
+already long.
+
+---
+
+Left out on purpose (design opinions, not defects): the shared run-flow
+core, the tile-path executor, moving the landmark alignment loop into shared
+code, and the retry-limit differences from SV. Raise them only after the FRLG
+pilot is running and only if they are asked about. See AutoStory.md §6.
diff --git a/docs/design/AutoStory-Design.md b/docs/design/AutoStory-Design.md
new file mode 100644
index 0000000000..7989acfd9a
--- /dev/null
+++ b/docs/design/AutoStory-Design.md
@@ -0,0 +1,235 @@
+# AutoStory: design of the closed-loop pieces
+
+Companion to [AutoStory-Lessons.md](AutoStory-Lessons.md): that doc lists the failures, this one
+describes the pieces built in response and how they fit together. Status as of 2026-10-28
+(evening): 00.04, from the catch to Dawn in Jubilife, runs unattended on console B (r79);
+00.05 was stepped through by hand and rewritten on a Jubilife grid (not yet run). Each section
+says what is built, where, and what is still planned. Next work:
+[AutoStory-Segment01-Plan.md](AutoStory-Segment01-Plan.md), then
+[AutoStory-Segment00-Finish.md](AutoStory-Segment00-Finish.md).
+
+The common idea: **never act on an assumption the screen could have checked.** Each piece
+reads the screen, takes one step, and reads again. Durations are caps, not plans.
+
+```
+ story segment (00.04 after-catch, 01 Roark, 02 Floaroma ...)
+ | | |
+ scripted screens overworld walking battles
+ (intro, menus) map -> plan -> execute fight / escape / heal
+ | | |
+ ScreenMachine TileGrid + MapSet battle helpers
+ + StepTrace planner, executor (starter-agnostic)
+ \_______________________|________________________/
+ |
+ walk_until / screen reads
+ (dialog, black screen, battle menu, screen change)
+```
+
+## 1. Screen machine (built)
+
+`CommonTools/ScreenMachine/{ScreenMachine,StepTrace}` (branch `lab/console`, merged into
+`bdsp/new-game-intro-v2`). Used by the BDSP new-game intro.
+
+- A routine is a list of `ScreenState`s: a matcher, one step to take on that screen, the
+ screens the step may lead to, and visit limits. States are tried in order; the last one
+ matches anything ("other").
+- Loop: wait for inputs to finish, read the screen (a state counts only when two frames
+ 150 ms apart agree), check, act. Stop after a terminal state acts.
+- Checks between reads turn silent drift into an early, explained failure:
+ - the new screen must be one the previous state lists in `next`;
+ - no state may be seen more than `max_visits` times, or `max_in_a_row` in a row;
+ - eight steps in a row that change nothing on screen is a dead end;
+ - an overall timeout.
+- `StepTrace` logs every recognised screen change with a numbered screenshot and every input,
+ and a failure message ends with the last few screens ("dialog -> keyboard -> dialog").
+ This is what makes a failed run explainable from one folder.
+
+Why not a fixed script: a dropped A press or an extra dialog page shifts every later press of
+a script. The machine does the right thing for whatever screen is up, so an extra page is just
+one more "dialog" visit.
+
+Use it for menu-like stretches with a small set of recognisable screens. Overworld walking is
+not a good fit (the screen is always "overworld"); it uses the walk loop below.
+
+## 2. Walking by the screen (built)
+
+`PokemonBDSP_AutoStory_Walk.{h,cpp}`.
+
+`walk_until(x, y, limit)` holds the stick in 400 ms chunks and returns why it stopped:
+
+| Stop | Read from | Meaning |
+|---|---|---|
+| ARRIVED | caller's matcher | a landmark is on screen |
+| DIALOG | short dialog box | an NPC or a trainer stopped us |
+| TRANSITION | black screen with no battle after it | door, building, new area |
+| BLOCKED | screen change < 10 (RMSD at 192x108) for two chunks | walking into a wall |
+| LIMIT | time | none of the above |
+
+Wild battles on the way (white flash, black screen then battle UI, battle menu) are escaped
+and the walk resumes; they are counted, not returned.
+`walk_to_talk` walks in 400 ms steps and presses A after each until a dialog opens, for NPCs
+in tall grass, where the grass sway hides BLOCKED. `walk_expecting` fails with a screenshot if
+the stop is not the expected one.
+
+The caller decides what a stop means (lesson 4): a wall leg accepts BLOCKED or LIMIT; a trainer
+leg accepts anything but TRANSITION and then waits for the dialog.
+
+Known limits: 400 ms granularity is about 1.5 tiles; BLOCKED does not register in tall grass.
+The probe (section 6) samples every ~100 ms instead.
+
+## 3. Map: tile grid and map set (built, being extended)
+
+`Pokemon/Navigation/Pokemon_TileGrid.{h,cpp}`, `Pokemon_MapSet.{h,cpp}`; data in
+`PokemonBDSP/Programs/AutoStory/Data/` (Route 201, Sandgem Town, Route 202, `story_maps()`).
+
+**Source.** BDSP keeps DP's overworld layouts, so each grid is traced from Bulbapedia's DP map
+(16 px tiles). Fetched maps and the tracing scripts stay out of the repo; only the ASCII grid is
+committed, with a comment naming the source image.
+
+**Encoding.** One ASCII row per tile row, so a grid reads like the map and diffs like text:
+
+```
+# blocked (trees, walls, ground items) . walkable " tall grass (encounters)
+v ledge, south only, hop over N NPC/trainer S sign
+D door ? off the map / exit
+```
+
+Named tiles (catch spot, Potion giver's talk tile, exits) and trainers with facing and sight
+range sit next to the grid.
+
+**Joining maps.** `MapSet` places each grid at an offset on one world grid, so a plan can cross
+from Route 201 into Sandgem and on to Route 202. Overlaps are allowed only where both maps are
+blocked; a conflict throws at start-up (and in the unit tests). `describe()` turns a world tile
+back into "Route 201 (45,3)" for logs.
+
+## 4. Planner (built)
+
+`plan_tile_route(grid, from, to, costs)` returns straight legs (direction + tiles). It is a
+cheapest-path search over (tile, direction) with costs per tile, per grass tile, per leg, and
+a large extra for a leg that **stops in the open** rather than against a wall.
+
+Why the wall preference: a leg that ends at a wall lands on the same tile however long the stick
+is held past it (lesson 1). A leg that ends in the open depends on timing; the planner uses one
+only when nothing else reaches the goal, and the executor treats it as the leg to localise
+after.
+
+Also built: `walk_to_wall` (where a straight walk stops, and whether it is held there),
+`sight_tiles` / `legs_crossing_sight` (which legs walk into a trainer's view; reported, used to
+expect a battle, not avoided, since we want the experience before Roark), `trace_legs` (the
+tiles a plan visits, for tests and logs).
+
+## 5. Executor, localisation and calibration (built for 00.04)
+
+Built in `Utils/PokemonBDSP_AutoStory_RouteWalk` (`walk_route`), with `plan_robust_route` in
+the planner. What it does differs from the plan below in these ways:
+
+- **Plans for uncertainty.** The robust planner tracks every tile we may be on (at most 5
+ after the start). An open leg of n tiles may end 1 + n/5 tiles off; wall legs bring the
+ possible tiles back together. Offline tests check each plan the segment uses.
+- **Turning is its own action.** A tap facing another way only turns, so an open leg turns
+ first (`route_turn`). Measured speeds: 160 ms per tile across, 240 ms up or down; an open
+ leg holds (n - 1/2) tiles' worth.
+- **Things the map lacks.** A walk that stops early waits twice for the way to clear, then
+ presses A (a trainer who saw us from the side, or someone standing in the way), fights
+ if a battle follows, marks the tile blocked, and plans again.
+- **Map changes** are checked against the location banner (OCR).
+- **Every leg** saves a screenshot under `Screenshots/AutoStory/route/`, and the overlay and
+ log show the checkpoint's steps, the current one and the next.
+
+The original plan:
+
+The piece that turns a plan into stick input:
+
+- **Leg to walk.** Each leg becomes one `walk_until` with an *expected* stop:
+ - ends at a wall: hold for `tiles x ms_per_tile` plus a margin; BLOCKED or LIMIT both mean
+ "at the wall";
+ - ends in the open: hold `tiles x ms_per_tile`, then localise;
+ - enters a trainer's sight: expect DIALOG, run the trainer battle, then re-plan from where
+ the trainer stopped us;
+ - ends at a door or an exit: expect TRANSITION.
+- **Localisation.** We know where we are when:
+ - a wall stopped us (the stop tile is known from the grid);
+ - the route banner appeared (OCR of "Route 201" etc. on entering a map);
+ - a scripted scene started (it fixes a tile).
+ After any surprise (wrong stop, extra battle), walk to the nearest walls on two axes to
+ re-anchor, then re-plan from there. Re-planning beats replaying.
+- **Calibration.** `ms_per_tile` (walking, ~267 ms in DP) and the start-up delay are measured
+ on the console by probing known wall distances, not guessed. Running shoes would be a second
+ speed; we walk.
+
+**Routes first, towns only when trusted.** Routes are open ground between tree walls, where the
+DP layout carries over and walls make good anchors. Towns changed more in the remake (buildings,
+signs, roofs), so a town grid is used for planning only once the console check (section 6)
+confirms it. Until then a town is crossed with screen-driven legs: walk to a wall or fence,
+then to the exit, ending on the route banner (OCR) or the transition. Each map carries a
+confidence flag that the executor checks before planning across it.
+
+This replaces Butters' recorded legs in 00.04 (catch -> Potion giver -> Jubilife) first, then
+segments 01 and 02.
+
+## 6. Verifying the map on the console (next)
+
+Before the executor trusts a grid, check it against the game:
+
+1. `bdsp_probe
` Lab Console command: hold the stick, sample the screen every
+ ~100 ms, report the time moved before the stop.
+2. From the post-catch save, probe the walls in all four directions; compare with the grid's
+ predicted tile counts to pin the start tile and fit `ms_per_tile`.
+3. Repeat at each corner of the planned route (Route 202, Sandgem, Route 201 to the Potion
+ giver), with a screenshot at each stop and the route banner at map joins.
+4. Output: predicted vs measured tiles per probe; grid fixes where they disagree.
+
+Probes run on open ground, since grass sway hides the stop.
+
+Doubts to settle first (from tracing the DP images): Route 202's row offset against Sandgem
+(the images overlap by only 8 px, so it may be off by one); Sandgem's lab sign at row 6 or 7
+and which roof rows block; Route 201's tree line sits about 3 px off the grass grid; trainer
+sight range (4 tiles is a guess).
+
+## 7. Battles and healing (built)
+
+- **Move choice:** each turn the Fight menu's move names are read by OCR, with the threshold
+ set per button (the one under the cursor is light blue). The strongest known attack is
+ used, so Water Gun once Piplup learns it, whatever the starter. If no attack can be read,
+ the last move that is not a known status move is used: the starter's own-type attack.
+- **Training:** before the Route 202 trainers, three wild battles in the catch grass. The
+ run walks up and down between the tree walls north and south of the grass, never in short
+ sideways steps (lesson 7).
+- **Healing:** 00.04 first gets 10 Potions from the Poké Mart lady on Route 201, saves on
+ Route 202, then heals from the Bag. That means full after training, and a Potion after any
+ trainer that leaves the lead under half (`use_potion_first_pokemon(…, heal_below)`). The
+ Pokémon Center routine (lining up with the lit doorway, the nurse) is kept for later.
+- Items are found by OCR of their names (Potion), not by pixel templates.
+- Wild battles while walking are run from.
+- Planned: the opponent's type in the move choice (Roark), and a lead that faints (the
+ "can't battle" party screen).
+
+## 8. Stepping through new ground (Lab Console)
+
+A Lab Console program on the console takes command files: walk until stopped
+(`bdsp_walk x y ms`), presses, stick holds, detector reads and screenshots. New ground is
+stepped through in batches of 5 to 10 commands (15 to 30 s), each ending with a screenshot,
+and each stop and dialogue is written down before the automated version is written. This is
+how the Poké Mart lady's tile, her dialogue timing, and Barry's position in the Trainers'
+School were found, after whole-run attempts had failed on each.
+
+## 9. Evidence and repeatability (built)
+
+- The clock is set (2026-01-01 00:00:50) at the start of each attempt, because some landmark
+ matchers depend on lighting.
+- A failure saves its screenshot under `Screenshots/AutoStory/failures/` before the retry
+ resets the game; each save prompt is saved under `Screenshots/AutoStory/saves/` (play
+ time, badges).
+- Runs stop at the first failure and close the game (lesson 3); retries are for real
+ randomness, not for deterministic failures.
+
+## Open questions
+
+- Is BDSP really 1:1 with DP everywhere along the route? Section 6 answers this for the
+ first three maps; buildings and towns changed most in the remake.
+- Can tall grass get a stop signal (e.g. the player sprite's position instead of the whole
+ view)? That would remove the special case in section 2.
+- How much of the planner and map set is shared with FRLG (same 16 px tiles, same ledges)?
+ Answered 2026-10-29 in [AutoStory-Navigation-Stack.md](AutoStory-Navigation-Stack.md): the
+ planner and walker are shared through a per-game step model and mover; the tile legend and
+ `TileGrid` stay BDSP's. The BDSP MVP branch is `bdsp/floaroma-mvp`.
diff --git a/docs/design/AutoStory-Lessons.md b/docs/design/AutoStory-Lessons.md
new file mode 100644
index 0000000000..4ae623d20c
--- /dev/null
+++ b/docs/design/AutoStory-Lessons.md
@@ -0,0 +1,266 @@
+# AutoStory: what broke on a real console, and the rule each break taught
+
+Each entry is a failure seen on a Switch while running BDSP AutoStory (Developer-Butters'
+U#1717 plus our intro work, October 2026), the pattern behind it, and the rule we now follow.
+Runs are the lab builds r14 to r109 on console B. The patterns repeat, so the rules matter
+more than the individual fixes.
+
+The lessons fall into two parts. **Part 1 is for Butters**: what his recorded segments in
+#1717 (PokemonAutomation/Arduino-Source#1717) need to survive another console, another
+starter or another day, with the general rules behind each fix. **Part 2 is for us**: our
+own code, our lab and our process. The rules in Part 1 apply to our code too.
+
+
+# Part 1. For Butters: his segments on another console
+
+## 1. Timed movement drifts; stop on what the screen shows
+
+| Run | What happened |
+|---|---|
+| r27 | "Route 201 align east 2300ms" walked into the trees; the next dialogue never came. |
+| r31 | Two timed grass walks ended a tile short; Tristan never saw us. |
+| r33, r39 | Five timed legs to the Potion giver ended one grass row off; A faced nobody. |
+| r35 | Walking 5 s into Jubilife ran past the spot where Dawn's scene starts. |
+
+Error from each timed leg adds up, and the same leg lands in different places from run to run.
+
+**Rule:** a duration is only a cap. A walk ends on a screen event: a wall (the view stops
+scrolling), a dialogue, a black transition, a battle, or a landmark. Between two anchors, use
+as few timed legs as possible. Walking to a wall is the cheapest anchor: hold long enough to
+reach it from anywhere the previous legs could have left us, and the wall puts us in the same
+place every time. (`walk_until`, `walk_to_talk` in `PokemonBDSP_AutoStory_Walk`.)
+
+## 2. Checks tied to one capture, one starter or one time of day
+
+| Run | What happened |
+|---|---|
+| r27 | The Potion bag search matched pixels against the author's capture. Console B is warmer (blank list 235 blue vs 253) and softer, so the Potion in row 1 failed the match. |
+| r28 | The Tristan fight required "Ember" in move slot 3: Chimchar only. We had Piplup. |
+| r28 | Grass-exit landmarks scored 0.41 and 0.60 after the console clock had drifted 3.5 h from the time the references were taken at. |
+| r34, r36 | "Blocked" was read from screen change; in tall grass the sway keeps the screen changing. |
+
+**Rule:** if the screenshot plainly shows the thing a check looks for, the check is too
+narrow. Fix the check, don't gather more runs. Prefer signals that survive other capture
+cards and other playthroughs: OCR of names (items, moves, route banners), layout facts,
+colour-normalised comparison. Never assume the starter: pick moves from what the Fight menu
+shows. When an image check depends on lighting, set the clock right before it, including on
+retries.
+
+## 3. A retry is not a fix
+
+Butters' checkpoint retry reloads the save and runs the same steps again. A deterministic
+failure (wrong starter, wrong row, a narrow check) fails all 20 attempts. r27 spent three
+attempts on the Potion check, and every r28 to r39 failure retried until we stopped it.
+
+**Rule:** stop at the first failure, go HOME and close the game, find the cause. Save the
+screen before the retry resets it (`Screenshots/AutoStory/failures/`), and save the save prompt
+at each checkpoint (`Screenshots/AutoStory/saves/`: play time, badges, party).
+
+## 4. Know what a stop means before reacting to it
+
+| Run | What happened |
+|---|---|
+| r30 | Walking toward Tristan read as "blocked": he had spotted us and frozen the player while he walked over. |
+| r32 | Natalie spotted us, but her walk kept the screen changing, so the walk ran out its time instead. |
+
+**Rule:** for a trainer or a scene, the walk can end in any way short of a transition; the
+dialogue decides. Walk, then wait for the dialogue with its own timeout.
+
+## 5. Butters' 00.04 and 00.05, checked against the game
+
+What his recorded version does, what went wrong with it on console B, and what replaced it. Our
+changes are on the fork branch
+[`bdsp/new-game-intro-v2`](https://github.com/dayfine/Arduino-Source/commits/bdsp/new-game-intro-v2),
+on top of his segment 00 ([`bdsp/1717-seg00`](https://github.com/dayfine/Arduino-Source/compare/503a28fc6...bdsp/1717-seg00)).
+
+| His version | What happened | Now | Our commits |
+|---|---|---|---|
+| Every leg is a stick hold of a recorded length (00.05 alone is ~150 lines of them). | Drift, as in section 1. In r79 the 00.05 legs reached the Trainers' School, then "west 200 ms, A" faced an empty tile: Barry stands up and to the right, at the blackboard. | 00.04 walks map plans whose legs end at walls (design doc, sections 3 to 5). 00.05 is being stepped through (section 10 below). | [`1598fc2c7`](https://github.com/dayfine/Arduino-Source/commit/1598fc2c74e7cccf6356251bf35e21fdbcac5006), [`0a5311175`](https://github.com/dayfine/Arduino-Source/commit/0a5311175ec72feb0fcb326a38d7e2dc7b1ec673) |
+| Tristan is fought with move slot 3 (Ember). | Chimchar only; we had Piplup (r28). | The Fight menu is read by OCR and the strongest move picked. | [`c16ee0262`](https://github.com/dayfine/Arduino-Source/commit/c16ee0262772a5519e489fb9ad360d263d07852c), [`97df88c43`](https://github.com/dayfine/Arduino-Source/commit/97df88c439845600a02d0114478bb66d4b158ad5) |
+| No training, no healing before the Route 202 trainers. | A Lv5 Piplup with Pound fainted to Logan's Shinx (r72). | Three wild battles first (Piplup learns Water Gun), then Potions. | [`08a9fb7e0`](https://github.com/dayfine/Arduino-Source/commit/08a9fb7e01b6be5ee042708b89e32ca2149dca69), [`a95359311`](https://github.com/dayfine/Arduino-Source/commit/a953593111dba3d56b10cf995eb7e9c86455018d) |
+| The Potion is found in the Bag by a pixel match. | Console B's colours differ; the match failed (r27). | OCR of the item name. | [`50c9d955d`](https://github.com/dayfine/Arduino-Source/commit/50c9d955d44d767d58d45834dc7e8d3a1b0e6c65) |
+| The Route 201 Poké Mart lady is reached by timed legs. | Missed her by a tile (r33, r39); so did our first planned route, which had her above the row (r77). | Walk west along her grass's top row until she stops us: she stands at (43,3), and we stop at (44,3) facing her. One A per line, 1.5 s apart. | [`99715d5d6`](https://github.com/dayfine/Arduino-Source/commit/99715d5d6d3e2c373980b68123363a2b64ae1022), [`f823453e9`](https://github.com/dayfine/Arduino-Source/commit/f823453e9cea8966d20752c4da5f48f54e634d76) |
+| Barry in the Trainers' School: "west 200 ms, A", then mash B. | Faced nobody (r79). | From the school door: one tile west, north until Barry stops us, then A per line. He gives the Town Map. | (not yet coded) |
+| Leaving 00.04: walk north 5 s into Jubilife. | Ran past Dawn's spot (r35); with our route, walked into the wall beside the stairs (r78). | Walk north; when the wall stops us and no dialogue follows, step a tile west and try again. Next: put the stairs in the map. | [`3cef78b77`](https://github.com/dayfine/Arduino-Source/commit/3cef78b771877675c83356133d8917eb0de681c9) |
+| The Pokétch president (Phase 23): timed legs, then wait for his dialogue. | He stands in the road and talks when we face him; pressing A to advance re-opened his talk each time it ended, so his first line came back again and again. | Talk once with A, then advance with B, which never starts a talk. | (stepped through; not yet coded) |
+| Three coupon clowns: recorded legs, then press toward each and A until a dialogue opens. | The recorded legs ended a row off each clown; walking past a clown on the row above or below never talks. | Each clown stands still: below the Poké Mart's planters (from the south, facing north; Coupon 1), at the TV station door (from the east, facing west; Coupon 3), beside the Pokétch Company sign (from the south, facing north; Coupon 2). The default "Yes" answers both quiz questions; close "obtained" with A, the rest with B. | (stepped through; not yet coded) |
+| Pokétch from the president: a landmark match, then align and talk. | — (not reached) | He is back at his first spot. Stand level with him on his east side, face west, A, then B. "Please press the R Button" means the Pokétch is ours. | (stepped through; not yet coded) |
+| Into the Jubilife Pokémon Center, to the PC. | — (not reached) | The door opening is half a tile east of where the frame stops a walk north. Inside: north to the counter, about 4 tiles east, then the tile below the PC, facing north. | (stepped through; not yet coded) |
+| Checkpoint retry reloads and repeats the same steps. | Deterministic failures retried up to 20 times. | Lab runs stop at the first failure (section 3). | lab scripts |
+
+His route did one thing right that we dropped for a while: the Poké Mart lady on Route 201
+gives **10 Potions**. With them, healing needs no trip to the Pokémon Center: heal to full
+after training, and after each trainer read the HP bar in the Bag and use a Potion under half.
+The run now gets the Potions first and saves on Route 202, (30,19), so later attempts skip her.
+
+## 6. Butters' segment 01, checked against what we found
+
+| His version | What we found | Rule |
+|---|---|---|
+| Oreburgh city, mine and gym are timed stick holds and d-pad tap counts. | The same drift as section 5; d-pad taps are one tile each only when the player already faces that way. | Walls and the map, not counts. |
+| Roark in the mine: A once, then up to 30 s for his first box, B to advance. | Right to wait long and to advance with B. But A does nothing until Barry has been talked to at the gym door, which his route gets from the tour scene's mashed B. | Make the Barry talk an explicit step. |
+| Gym: up 3, left 4, up 5, right 8, up 11, left 5, up 4, then A at Roark. | That path goes round both gym trainers; straight up meets both. | Worth keeping as a plan for the gym grid; the trainers' experience helps an under-levelled team, though. |
+| "Barry 1 battle logic can be used here since the moves don't matter this early." | Moves matter against Roark: Pound does almost nothing to rock types, and the first two tries were lost. | Pick Water Gun by name; heal or switch before Cranidos. |
+
+## 7. Butters' route on console B, 2026-10-29 (r107 to r109)
+
+Two failures in today's runs came from #1717's own code rather than ours:
+
+| Run | What happened | Rule / fix | Commit |
+|---|---|---|---|
+| r108 | 00.01 "Failed to enter Date Change screen". | The clock pin walks the System Settings menu by position; with "Synchronize Clock via Internet" on, the date row is disabled and the walk lands on Time Zone. Turned the sync off on console B; dropping the pin is queued. | — |
+| r108b | 00.04: A beside Mom started nothing. Two d-pad taps north from the door should have put us level with her; one did not move. | Same as section 1, now in the house: counted taps lose tiles. The door fixes the column, so face east and press A one row at a time going north until she answers. | `985de47c1` |
+
+# Part 2. For us: our code, our lab, our process
+
+## 8. Our own process
+
+- Look at the screen, then the code, before rerunning. Every run since r28 that failed in a new
+ way was explained by one screenshot.
+- Put new code in its final module first; no copy-then-move PRs of our own code.
+- Keep line endings as they are (CRLF files stay CRLF); compare `git diff --stat` with
+ `--ignore-cr-at-eol`.
+- When a program is stuck or failing, stop it and close the game at once; don't let retries run.
+
+## 9. Our own mistakes in this round
+
+- **An optional step kept for two hours.** We spent ~15 runs making the Route 201 detour
+ work before asking whether the story needs it. Rule: before automating an inherited
+ step, write down why the story needs it; after three different failures at one stage,
+ question the stage, not the symptom.
+- **Claims not checked against the screen.** We concluded "the save already has Potions"
+ from log lines of runs that had, in fact, reached the lady; r76's Bag screenshot showed an
+ empty Medicine pocket. Rule: an inventory or position claim needs a screenshot of it.
+- **Map errors, each found from one screenshot.** Route 202's east tree wall is at column 31
+ in rows 15 to 17, not 33 (r75 lost its place by three rows); the lady is in the grass row,
+ not above it; Jubilife's south edge is a wall with stairs, not open ground. Rule: when a
+ wall leg stops somewhere unexpected, compare its screenshot with the grid before changing
+ any timing, and add the measured stop as a wall-hit test. ([`a95359311`](https://github.com/dayfine/Arduino-Source/commit/a953593111dba3d56b10cf995eb7e9c86455018d), [`f823453e9`](https://github.com/dayfine/Arduino-Source/commit/f823453e9cea8966d20752c4da5f48f54e634d76), [`3cef78b77`](https://github.com/dayfine/Arduino-Source/commit/3cef78b771877675c83356133d8917eb0de681c9))
+- **Movement is not tile-locked.** A short tap moves about 0.77 of a tile. A left/right
+ wiggle for encounters rocked across one tile edge and found no battle in three minutes
+ (r74). Walk wall to wall instead: the catch grass's rows sit between tree walls. ([`68279ca9b`](https://github.com/dayfine/Arduino-Source/commit/68279ca9ba631a5012207ad664892a2bdf88e760))
+- **One fixed OCR cut for every button.** Move names under the cursor are dark text on a
+ light blue button, and status moves sit lower (no "Effective" line); a fixed cut and box
+ read "" for Water Gun and Growl (r73). Threshold halfway between each button's background
+ and its darkest pixel, and keep the tallest text band. ([`97df88c43`](https://github.com/dayfine/Arduino-Source/commit/97df88c439845600a02d0114478bb66d4b158ad5))
+- **Reading positions from roofs and heads.** In the tilted 3D view, a roof shifts against the
+ ground as the camera follows us, and a sprite's head rises into the row above its feet. Fixed
+ clowns looked like they wandered, and we pressed A diagonally next to the president, which
+ talks to nobody. Rule: judge rows and columns by feet and ground features (planters, curbs,
+ plaza edges), and stand level with an NPC before talking.
+- **Dead reckoning in a town without a map.** In Jubilife, open-street "walk until stopped" legs
+ ran far past what was meant, and buildings hid the player; we lost our place twice. Bulbapedia's
+ DP map of Jubilife matches the BDSP layout (school, Center, Mart, TV station, Pokétch Company,
+ fountain park all where DP has them), so towns get a tile grid too, checked against the
+ console, with the story NPCs on it. *Later (r82):* the grid's walls in the open plaza are not
+ where the game has them, so it is not good enough to plan on there; see section 11.
+- **Not saying what we are doing.** The overlay and the log now print each checkpoint's steps,
+ the current one and the next, and every status report starts with the current problem.
+ ([`3cd874125`](https://github.com/dayfine/Arduino-Source/commit/3cd8741250dc03f1c166e15cfc9563f5ac36add9))
+
+## 10. Step through new ground before automating it
+
+After hours of whole-run attempts at the lady, five minutes of stepping found her: the Lab
+Console takes 5 to 10 commands at a time (walk until stopped, a few presses, a screenshot),
+then waits while we look. Where the map or the scene is unknown (a new town, a building,
+an NPC to talk to), step through it first, write down each stop and dialogue, and only then
+write the automated version from what was seen. 00.05 was done this way on 2026-10-28: Barry and the Town Map, the president, all three
+coupon clowns, the Pokétch and the Center PC, about 60 batches, ending in a save at the PC
+(segment 0 complete).
+
+## 11. Segment 01 to Oreburgh City (2026-10-28, r81 to r90)
+
+01.01 (Jubilife Center to Barry) and 01.02 (Route 203, Oreburgh Gate, Oreburgh City) were
+stepped through by hand once, written from that ([`8dda984d5`](https://github.com/dayfine/Arduino-Source/commit/8dda984d5ca88a1d59b605a3312711162777bba7)), and then run on console B. Ten
+runs, each stopped at its first failure, and one fix per failure:
+
+| Run | What happened | Rule / fix | Commit |
+|---|---|---|---|
+| r81 | West from the PC hit the end of the counter; the walk south then stopped at the stools. | The hand run had gone south first without our noticing. Write down every move of a hand run, including the ones that "did nothing". | [`da01cc799`](https://github.com/dayfine/Arduino-Source/commit/da01cc799ba92eb5723493d943414c881a4a36dc) |
+| r82 | A grid leg to a wall the Jubilife map shows walked on ~19 tiles and ended at the Trainers' School. | Jubilife's plaza grid is wrong in its open areas. There, use the holds measured on the console, and end each leg with a search (walk east; at a wall, try the next row). | [`c4883f3cb`](https://github.com/dayfine/Arduino-Source/commit/c4883f3cb2e7c1ea18f1a34a1dd0f82b89d1b2f4) |
+| r83 | Barry stopped us, but "only talked" was decided 0.6 s into the pause before his battle. | A trainer's talk and battle have a quiet gap with no text box: wait 4 s of overworld before calling it a talk, and for a story battle never call it one. | [`85ef91790`](https://github.com/dayfine/Arduino-Source/commit/85ef91790d74c0b6b2a9caa88c9890fa07886dfe) |
+| r84 | Stuck for 4 min choosing Water Gun at 0 PP; the game refused it each turn. The user spotted the PP risk at the same time. | Read each move's PP by OCR (text = whatever differs from the button's own color, since PP text turns yellow to red); skip 0-PP moves; fail a battle still at its menu after 25 turns. | [`a5f91acde`](https://github.com/dayfine/Arduino-Source/commit/a5f91acdeff362da2e502e33b56e4c5aaf5ddd6a), [`9721801e3`](https://github.com/dayfine/Arduino-Source/commit/9721801e3f258cd49bae506434253df55528648e) |
+| r85 | Barry started at 66 % with Water Gun 12/25: the nurse had not healed. Her Yes/No is on her third line, and the B after two A presses answered No. The in-battle Potion (15 % → 93 %) worked. | Count an NPC's lines before choosing how many A presses; an extra A on a later line is harmless, a B on a question is not. The run now also saves after Barry (01.01 split at him). | [`b2236ba72`](https://github.com/dayfine/Arduino-Source/commit/b2236ba72f48e09f237a2ea07b82da802c2efba9) |
+| r85 | After the Hiker, a "Hidden Moves" tutorial card held the screen; it is no dialogue box, so the walk read it as a wall. | Tutorial cards close with B; press B after a scene that teaches something. | [`a9863ad8b`](https://github.com/dayfine/Arduino-Source/commit/a9863ad8b73b7941d2b0070025b2415b9913f3a3) |
+| r86 | The nurse healed, but `finish_dialogue` ended during the healing jingle; her "Thank you for waiting" then stopped the walk out. | A scene with an animation in the middle needs a second wait for its last lines. | [`00c8fcbf7`](https://github.com/dayfine/Arduino-Source/commit/00c8fcbf7c1f88680ebf54a98e42001918a122a2) |
+| r87 | Route 203: only one trainer stopped us, so "after two fights the next wall is the cliff" fired at the stairs between stretches. | Who stops us depends on our row, so trainer counts say nothing about where we are. | [`5345260db`](https://github.com/dayfine/Arduino-Source/commit/5345260dbb113e90cb0174a594561b3d3281bef7) |
+| r88 | Reached the north-east corner by the second sign, but the row search counted blocked steps north as moves and never tried the cave's row. | The user's rule: at a wall, go north to the top wall (that pins the row), then try rows south of it one at a time. The cave is two rows below the corner. | [`22998e853`](https://github.com/dayfine/Arduino-Source/commit/22998e853caa8b8529165a33b0c5135511a869a5) |
+| r89 | Got into the Gate on the first row, but the cave's fade did not read as a transition; the Hiker's talk was then taken for a trainer's, and his card stayed up. | On Route 203 only trainers start a talk, so a talk with no battle means the Hiker inside the Gate. | [`3cb2e2f78`](https://github.com/dayfine/Arduino-Source/commit/3cb2e2f78631648b132824997e9f8c757cefd657) |
+| r90 | **01.02 passed**: trainer, Hiker, Curtis, Diana, the man at Oreburgh's entrance. | | |
+
+Also from these runs:
+
+- **01.02 ends too early.** The man at Oreburgh's entrance walks us to the Pokémon Center,
+ where Barry shows up ("Huh? There's someone there."). `finish_dialogue` returned while the
+ scene was walking, between lines. The next checkpoint must finish that scene first. The user
+ saved by hand there after r90.
+- **The lab script's Stop click can miss.** r87's load step left the Lab Console running, so
+ AutoStory was refused as "console busy". `run_from_save.sh` now retries Stop until the
+ console reads free.
+- **Overlay text was cut off** at the log box's width; AutoStory now wraps its overlay lines.
+ ([`344a050ce`](https://github.com/dayfine/Arduino-Source/commit/344a050cecc08a61a0f5b528c2c301493c2095e9))
+- **What held up:** the in-battle HP read and Potion, the PP read (29, 40, 12 → 11 in Barry's
+ battle), the walk out of the Center to the girls below the door, and Barry from the measured
+ holds plus row search.
+
+## 12. Oreburgh Mine, Roark's gym and on to Floaroma (2026-10-28/29, r91 to r106, then by hand)
+
+01.03 (city → mine → Roark) was written from the parsed mine map (`devtools/maps/levels.py`)
+and run r91 to r106. After that the user asked to fight the gym as we were, gaining
+experience by losing, and to reach Floaroma by hand; the console now holds a save in the
+Floaroma Pokémon Center (Badge 1, play time 4:23, Piplup Lv 15, Starly Lv 13).
+
+| Run | What happened | Rule / fix | Commit |
+|---|---|---|---|
+| r91–r93 | Timed holds put us a column off the mine's stairs, sometimes left, sometimes right. | Anchor on walls the map shows (the conveyor pillars, the fence below the Mart); never count on a hold's length for a column. | `1a77905e4` |
+| r95, r96 | The walk south stopped early: the hard-hat worker by the fence wanders onto the path. | A stop that a wall cannot explain may be a wandering NPC: wait and walk again before trusting it. | `1a77905e4` |
+| r96 | One column off, the walk ends on the sign above the mine's stairs. | The user: being stuck is fine if we can rescue ourselves. Step one tile east off the sign's column; the stairs are open on both sides. | `b3928e6fc` |
+| r97 | Blocked legs left us a row high, probing Roark's own row with his rock in between. | End a search on a wall that pins the row (the bottom wall) before probing. | `f84c3ce3c`, `2f8be46f9` |
+| r98 | Right below Roark; A started his camera zoom but the first line came after 2 s. | Story NPCs zoom the camera before the first line: wait up to 8 s. | `f59923b67` |
+| r99, r103 | The route back walked through the corridor's top into the hall ("unexpected transition"); later the hall and the corridor turned out to be one walk. | End the planned route on a tile a wall pins (the corner at (14,11)) and let one walk do the transitions. | `a414e333a`, `f59a16e51` |
+| r101, r104 | Below Roark, A did nothing, by hand too. | **Roark only answers after Barry has sent us** ("go down to the mine and ask him"), so talk to Barry at the gym door first. Confirmed by hand: after that talk, Roark answered the first A. | `a65a02b84` |
+| r105, r106 | "Passed", but the nudge before A had stepped onto the rock beside Roark and the rock's one line ("It's a rugged rock…") was taken for his. | A dialogue opening is not proof: check the story effect. Movement is by whole tiles (30, 40 and 60 ms holds all move one tile; 20 ms only turns), so there are no half-tile nudges. 01.03 now steps onto Roark's tile after the talk and fails if he is still there. | `a65a02b84` |
+
+By hand, after 01.03:
+
+- **The gym.** Walking straight up the middle meets both gym trainers (one at the entrance,
+ one on the second tier). Roark (Geodude 12, Onix 12, Cranidos 14) beat Piplup Lv 10 and 14
+ twice; the third try at Lv 14 with a fixed move choice won and Piplup reached 15. A whiteout
+ costs some money and keeps experience, so losing and retrying is a cheap way to level.
+- **Battle loop on the Lab Console** (`~/BDSPLab/fight_lc.sh`, with a colour check for the
+ four battle buttons in `~/BDSPLab/battlemenu`): Battle, A, then the move; the move list
+ needs ~2 s to open, or the Down presses are lost and Pound is used. A double battle needs a
+ third A to pick the target. Move-learn prompts need "Keep old moves" (Down, A); B alone
+ loops on them.
+- **Screenshots lag.** After long walks a `shot` can show a frame several seconds old, which
+ made us think the player was stuck. Wait ~3 s before a `shot` that decides the next move.
+- **Route 204** (Jubilife → Floaroma): Jubilife's north road starts ~10 tiles west of where
+ Route 203 enters; Rowan, Dawn and two Galactic grunts stop us there for a tag battle with
+ Dawn. The Ravaged Path needs Rock Smash on its rocks (A, Yes, Yes); its north exit is the
+ lit opening a tile west of the corridor's east end, *south* of the entrance chamber, not
+ across the water. North of it: up the stairs, then a row down and east along the sand,
+ north through the grass to the flower beds, round the twins (a double battle), and north
+ between the beds into Floaroma. The Center is a few tiles east of where we arrive.
+
+## 13. Floaroma MVP from a new game, and the lab around it (2026-10-29, r107 to r109)
+
+The MVP branch (`bdsp/floaroma-mvp`, fork #68) runs segments 00 and 01 from a new game on
+the shared navigation core. Half of today's failures were in the lab, not in the game.
+
+| Run | What happened | Rule / fix | Commit |
+|---|---|---|---|
+| r107 | Every program refused to start: "requires video feedback". The camera opened without error but delivered no frames. | Launch lab apps through LaunchServices (`open -n`), never by running the binary from a shell: the app then inherits the shell's (the Claude app's) camera permission, which has none. `stage_launch.sh` now checks for video before anything else. | lab scripts |
+| r107 | The MVP build stopped at run time on a missing template ("Unable to open file"). | The worktree's `Packages` linked to upstream's resource set (686 files); BDSP AutoStory needs `~/BDSPLab/packages` (1,467). The build copies `Resources` only when it is missing, so delete it after relinking. | r108 deployment |
+| r109 | Mom answered after 1 step; then entering Sandgem the check read the banner as "andgemltown" and failed. | A check tied to an exact OCR string is too narrow (section 2): the place was right. Match banner names loosely, with a test that a different town still fails. | pending |
+| r109a | The run never started: the Start click landed where the button was before the window grew to 1554×1488. `stage_run.sh` would then have clicked Start/Stop "to stop the wrong program" and started an unwatched run. | Never toggle Start/Stop blindly: stop only when the log says RUNNING. Better, don't click at all: the lab app gets a control folder (`LabControl/start`, `stop`, `state`). Clicks also take focus from the user's work. | `lab/control-folder` |
+
+What this round adds to the design:
+
+- **Confirm where we are, not just where we planned to be.** The planner plans in tiles and
+ the walker counts them, but nothing reads the position back; every drift so far (sections
+ 1, 5, 11, 12 and the Mom row in section 7) was found only when the next action failed. After each
+ leg, confirm with something the game shows (a wall reached, a landmark, a dialogue
+ answered, the facing) and replan from the confirmed tile when it differs.
+- **Overworld screens belong in the screen machine.** The new-game intro runs on
+ `ScreenMachine`; walking, dialogue and battle interrupts should be states of the same loop,
+ so a dropped input shows up as an unexpected screen instead of a later timeout.
diff --git a/docs/design/AutoStory-Navigation-Stack.md b/docs/design/AutoStory-Navigation-Stack.md
new file mode 100644
index 0000000000..2493bbc126
--- /dev/null
+++ b/docs/design/AutoStory-Navigation-Stack.md
@@ -0,0 +1,159 @@
+# AutoStory: shared navigation core and the two MVP branches
+
+Status: plan, 2026-10-29. Replaces the T1/T2 sketch in [AutoStory.md](AutoStory.md) §5.1 and §7.7
+and answers the open question in [AutoStory-Design.md](AutoStory-Design.md) ("how much of the
+planner and map set is shared with FRLG"). Evidence: the BDSP lab branch
+`bdsp/new-game-intro-v2` (00.04 runs unattended; 01.05 to Floaroma written) and the FRLG lab
+branch `frlg/overworld-nav` (walked Nugget Bridge to Mewtwo on console B, 2026-10-29).
+
+## 1. What the two games built, side by side
+
+| Concern | BDSP (`Pokemon/Navigation`, AutoStory utils) | FRLG (`PokemonFRLG/Overworld`) |
+|---|---|---|
+| Direction, tile | `GridDirection`, `TilePos` | `MapDirection`, `MapPoint` (same thing again) |
+| Map | `TileGrid`, hand-traced from DP maps: blocked, walk, grass, ledge (south), NPC, sign, door, unknown | `OverworldMap`, generated from pokefirered: walls, water, one-way tiles, ledges, doors / cave mouths / ladders / exit mats, elevation, objects with flags and wander boxes |
+| Several maps | `MapSet`: grids placed on one world grid; no warps | maps joined by warps and edge connections with offsets |
+| Planner | `plan_tile_route` (legs, extra for stopping in the open); `plan_robust_route` (a set of possible tiles, open legs a tile off) | search over (map, tile, elevation, surfing, facing, mid-leg), extra for stopping in the open, warps, Surf |
+| Walking | `walk_route`: stick holds, 160 ms per tile across, 240 ms up/down; tracks possible tiles; re-plans after a trainer | `walk_overworld_route`: D-pad holds, 267 ms per tile (133 surfing); warps; Surf |
+| Interruptions | hard-wired: trainer fight callback, fail on a wild battle in an open leg, dialog, transition | `OverworldInterrupt` list: battle flash (about 300 ms after the step), battle screen, message / Repel wore off (uses a Max Repel) |
+| Facing | tracked; a tap only turns a player facing another way | tracked, and read from the player sprite |
+| Trainers | sight lines on the grid | none yet (the decomp has them) |
+
+Written twice: the direction and tile types, legs that end at a wall, the extra cost for
+stopping in the open, open leg timing ((n - 1/2) tiles of hold), the turn tap, and what to do
+when something stops a leg. Neither planner can serve the other game, because each is bound to
+its own map class. That is the layering problem to fix: the shared code should call into the
+game once per step, not own the tile legend.
+
+## 2. Layers
+
+**Shared, `Pokemon/Navigation/` (console-free):**
+
+- `Pokemon_Grid.h`: `Direction`, `TilePos`.
+- `Pokemon_LegPlanner.h`: the cheapest legs from a start to a goal, templated on a step model.
+ The game supplies `press(state, direction)`: the next state, whether the step ends the leg
+ (warp, starting or leaving Surf), and the facing after it; and `held_at(state, direction)`:
+ whether holding further keeps the player on this tile (a wall, not a person or a map edge).
+ The planner searches, merges steps into legs, marks legs that end at a wall, and charges for
+ turns, leg ends in the open, grass, warps. FRLG's `press()` and BDSP's `TileGrid::step()` both
+ fit as they are.
+- `Pokemon_RobustPlanner.h`: planning over a set of possible tiles (BDSP's
+ `plan_robust_route`), on the same step model. Only BDSP needs it (stick walking drifts); FRLG
+ walks exactly.
+- `MapSet` stays: one way for a game to build a step model (maps placed side by side). FRLG's
+ warp graph is the other.
+
+**Shared, `Pokemon/Navigation/Pokemon_LegWalker.h` (templated on the controller context, like
+`run_until`, so `Pokemon/` stays free of `NintendoSwitch/`):**
+
+- `LegMover`, per game: how to turn (tap), how to hold a direction (D-pad or stick), the time
+ per tile for a leg (direction, surfing, biking).
+- `LegInterrupt`, per game: a watcher, how long after the step it fires, how to clear it.
+- `walk_leg()`, shared: hold the leg while watching every interrupt; on one, clear it, then
+ hold a wall leg again, or work out how far an open leg went from when it was seen. Reports
+ tiles walked and whether that is exact.
+
+**Per game:** the map data and its generator, the step rules, timings, detectors, interrupt
+handlers (trainer, wild battle, message, Repel), the facing reader, landmarks.
+
+## 3. State: what is on screen, and where the player is
+
+Two different things, kept apart:
+
+- **Screen state**: which screen is up (overworld, dialogue, battle, menu, bag, black). The
+ shared mechanism is the screen machine and step trace (`CommonTools/ScreenMachine`, already
+ game-neutral); the per-game part is the detectors. Menus and scripted scenes run on the
+ screen machine. While walking, the same detectors are the leg walker's interrupts, so the
+ walker does not need a screen machine of its own. Missing: an FRLG overworld detector
+ (overworld vs anything else without opening a menu), planned as D1.
+- **Overworld state**: where the player is and what the world looks like. The pose is shared
+ vocabulary: map, tile, facing, movement mode (walking, surfing, biking), plus how sure we are
+ (one tile, or a set of possible tiles). The world state is per game but follows one pattern
+ in both: maps are built from data with flags applied (FRLG event flags hiding objects; BDSP
+ defeated trainers and smashed rocks), and the planner reads the result. The walker updates
+ the pose after each leg and after an interrupt; the facing reader and landmarks (BDSP) or
+ wall anchors (both) correct it.
+
+Rule: the screen is read, the pose is tracked. A pose is never inferred from elapsed time
+alone except for an open leg, and then it is marked inexact until a wall or a landmark fixes
+it.
+
+**Status 2026-10-29: the pose is planned but not yet corrected.** The planner plans in tiles and
+the walker counts them; nothing reads the pose back after a leg, so a dropped tap is found
+only when the next action fails (00.04 r108b: two taps to Mom's row moved one tile; see
+Lessons §7). Next, in the core: after each leg, confirm the pose with an anchor the game shows
+(wall, landmark, dialogue answered, facing) and replan from the confirmed tile. FRLG's
+overworld screens (overworld, dialogue, battle interrupt) should also become screen-machine
+states, as BDSP's intro already is.
+
+Review follow-ups on the core (2026-10-29, fork #66/#67): the single-method `LegMover` becomes a
+`MoveOnlyFunction` (fork `common/move-only-function`); `ScreenState`'s `matches`/`act` move
+from `std::function` to `MoveOnlyFunction` too; `plan_legs` is split under 50 lines; FRLG's
+`OverworldMap` (20 public methods) becomes plain data plus free functions.
+
+## 4. Branches and PRs
+
+```
+upstream main
+ └─ nav/core shared: grid types, leg planner, leg walker, screen machine
+ ├─ frlg/brock-mvp FRLG: maps + step model, D-pad mover, interrupts, facing,
+ │ Brock route (new game -> Boulder Badge)
+ └─ bdsp/floaroma-mvp BDSP: TileGrid/MapSet on the planner, stick mover, interrupts,
+ AutoStory 00 and 01 to Floaroma, nothing else
+```
+
+`nav/core` (one fork PR per item, stacked):
+
+| ID | Contents | Tests |
+|---|---|---|
+| N1 | `Pokemon_Grid.h`, `Pokemon_LegPlanner.h` | a small made-up game: walls, a warp, a one-way tile, grass, open vs wall legs |
+| N2 | `Pokemon_LegWalker.h` | scripted mover and interrupts: wall leg re-walked, open leg estimate, too many interrupts |
+| N3 | `CommonTools/ScreenMachine` + `StepTrace` (from the BDSP lab) | none yet: the checks between reads need tests with small fake frames |
+
+`frlg/brock-mvp` (on N1–N3): the FRLG overworld code moves onto the core types in its final
+place (no move PRs of our own code later), then the Brock route. Includes the bag fix
+(fork #65) and the facing reader. The Mewtwo route stays in the lab branch as the live test of
+the walker. Brock route work follows §7.4 segments 00 and 01 of [AutoStory.md](AutoStory.md).
+
+`bdsp/floaroma-mvp` (on N1–N3): only what AutoStory 00 and 01 need to reach Floaroma.
+
+- In: the BDSP maps (Route 201 to Route 204 north, Jubilife, Oreburgh Mine, Ravaged Path) as a
+ step model, `walk_route` on the leg walker, the trainer and wild battle interrupts, the
+ AutoStory program with segments 00 and 01, the detectors and readers they use, the battle,
+ dialogue, Pokémon Center and new-game utilities, the system keyboard detector (in
+ `NintendoSwitch/Inference/`, its final home), their tests.
+- Out: the old Lab Console and BDSP Lab Console commands (the Command File Runner, fork #58,
+ replaces them in the lab), the lighting test program, the unused `walk_steps` /
+ `walk_until_*` stubs, anything segment 02+.
+- Risk: moving `walk_route` onto the shared walker changes code that ran unattended. The
+ branch is not done until 00.04 runs on a console again and 01.05 reaches Floaroma.
+
+Fork drafts (2026-10-29): `nav/core` #66, `frlg/brock-mvp` #67, `bdsp/floaroma-mvp` #68 (on
+#1717 at `45a206f0d`, branch `base/bdsp-1717`; their segments 02 to 09 kept but not in the
+checkpoint list, since they start where #1717's own 01 ends).
+
+Follow-ups queued for `bdsp/floaroma-mvp` (2026-10-29), in order:
+
+1. **Drop the clock pin.** Segment 00 sets the Switch clock (2026-01-01 00:00:50) because #1717's
+ landmark matchers were captured at that lighting. It is used by 00.01's timed walk from the
+ bedroom, the starter-grass walk, the Sandgem junction alignment and finding the Poketch man in
+ Jubilife; segment 01 and the tile-grid walks don't need it. Replace the two landmark
+ alignments with tile-grid walks anchored on walls (as 00.04 does) and the 00.01 timed moves
+ with screen-driven walks, then remove `set_time_fast`. It also breaks runs on its own: with
+ "Synchronize Clock via Internet" on, the date row is disabled and the menu walk lands on Time
+ Zone (r108, 00.01, six failed attempts).
+2. **Move `walk_route` onto the shared leg walker** (N2), then run 00.04 and 01 on the console.
+3. **Trim the BDSP resources** to what segments 00 and 01 load (the AutoStory folder is 789 files,
+ 101 MB, mostly #1717's item icons and later-segment landmarks).
+
+Order: N1, then the FRLG child up to its planner tests (it already passes on the core idea),
+then N2 with the FRLG walker on it (re-run the Mewtwo route), then the BDSP child. N3 can go
+any time. The lab branches keep everything else and merge these branches in.
+
+## 5. Checks before each branch is called done
+
+- N1–N3: unit tests; no game code depends on anything not in the core.
+- FRLG child: route tests; the Mewtwo route walked again on the console with the shared walker.
+- BDSP child: offline plan tests for every segment plan; 00.04 and 01 run on the console.
+- For both: `git diff --stat` against the lab branch shows only dropped lab code, no lost
+ game logic.
diff --git a/docs/design/AutoStory-Proposal.md b/docs/design/AutoStory-Proposal.md
new file mode 100644
index 0000000000..d823ad849e
--- /dev/null
+++ b/docs/design/AutoStory-Proposal.md
@@ -0,0 +1,72 @@
+# FireRed AutoStory: proposal
+
+Status: draft, 2026-10-25. The full internal plan, with the evidence and line
+references, is [AutoStory.md](AutoStory.md).
+
+## Problem
+
+FRLG on `main` has most of the low-level pieces an AutoStory needs: dialog,
+battle and menu detectors, fly, heal, a tested wild-battle exit, and a
+frame-exact tile walker inside the Elite Four farmer. Nothing plays the story,
+and users have asked for it
+([ComputerControl#259](https://github.com/PokemonAutomation/ComputerControl/issues/259)).
+A Poke-Controller script outside this project already plays FireRed to the
+Hall of Fame with the starter alone, so the route is known to be automatable.
+
+## What I'm building first: a pilot to Brock
+
+A FireRed AutoStory from a new game to the Boulder Badge (about 12
+checkpoints), solo with no trades. It changes no existing program: the
+game-neutral parts (the run flow with checkpoints, retries from the last
+save, start/end selection and stats; the tile-path walker; a closed-loop
+screen driver) go into new shared folders from the start, with FRLG as
+their only user, so nothing has to be moved later.
+
+About 10 PRs, each 150–500 lines, each doing one thing:
+
+1. Tile-path executor with unit tests, in a new `Pokemon/Navigation/`.
+2. Elite Four farmer moved onto it, with no behaviour change.
+3. A closed-loop screen driver (each input follows a screen read), then the new-game screens and naming keyboard on it.
+4. Program shell: options, run flow, checkpoint list.
+5. **New game → starter → rival battle. This is the first version users can run.**
+6. Overworld detector.
+7. Trainer-spotted and item-pickup watchers.
+8. Resuming a walk after a battle or dialog interrupts it.
+9. Trainer battle handler with unit tests.
+10. **Parcel → Pokédex → Viridian Forest → Brock. This completes the pilot.**
+
+Detectors come one per PR, with screenshot tests in CommandLineTests. Paths
+are planned offline from the pret/pokefirered decompilation (trainer
+positions and sight lines) and checked in as reviewed data.
+
+## What I'd like reviewed first
+
+PRs 1–2: the tile executor's result and resume types, and moving `walk_path`
+out of `PokemonFRLG_EliteFourFarmer.cpp`. They touch existing FRLG code, and
+the rest of the pilot builds on them.
+
+## The cost, honestly
+
+Mysticial has said that SV AutoStory took over a year. Most of that time
+went into hardware hours per checkpoint: writing a checkpoint, then tuning
+it and running it again on a console until it is reliable. Shared code and
+decomp-planned paths do not shorten that. So the pilot is there to measure
+this cost before committing to the rest of the game (about 100 checkpoints
+to the Champion).
+
+## Go/no-go gate
+
+The pilot passes if **every pilot checkpoint (new game → Boulder Badge,
+about 12) succeeds 3 times in a row starting from its own save**, and this
+takes **at most 3 Switch hours per checkpoint on average**, counting both
+development and validation. If the pilot misses either target, I stop at
+Brock. The pilot stays a working program, and I write up what it cost. If
+it passes, the projection is roughly 300 hardware hours to the Champion, and
+I'll post the measured numbers before continuing.
+
+## Later, only if the pilot is used
+
+- The remaining segments to the Champion, one PR per segment.
+- With FRLG running on the shared run flow, I'd offer to migrate SV
+ AutoStory onto it (pure moves with unit tests first) and offer the same to
+ BDSP. None of this is a precondition for the FRLG work.
diff --git a/docs/design/AutoStory-Segment00-Finish.md b/docs/design/AutoStory-Segment00-Finish.md
new file mode 100644
index 0000000000..7c10810e9d
--- /dev/null
+++ b/docs/design/AutoStory-Segment00-Finish.md
@@ -0,0 +1,42 @@
+# AutoStory segment 00: what is left (scheduled after segment 01's first pass)
+
+**Superseded 2026-10-29:** segment 00 now runs on the MVP branch `bdsp/floaroma-mvp`
+(fork #68, on #1717 and the shared core); 00.00 to 00.03 passed unattended from a new game
+(r108b) and 00.04 got past Mom (r109). Current results are in
+[AutoStory-Lessons.md](AutoStory-Lessons.md) §7 and §13. Kept for the history below.
+
+Status 2026-10-28. Code: fork branch `bdsp/new-game-intro-v2`, worktree
+`~/Projects/Arduino-Source-bdsp`, latest commit 6dbcc8e10 (00.05 on a Jubilife grid).
+Testing: the `bdsp-autostory-test` skill.
+
+## Where it stands
+
+| Checkpoint | State |
+|---|---|
+| 00.00 to 00.03 (new game, intro, starter, Pokédex) | run unattended earlier (new-game intro on the screen machine) |
+| 00.04 (Potions, training, Route 202, Dawn) | ran unattended from the Route 202 Potion save (r79) |
+| 00.05 (Jubilife: Barry, president, clowns, Pokétch, Center PC) | done by hand on the console, then rewritten on a tile grid (6dbcc8e10); **not yet run** |
+
+The only save on console B is the end of segment 00 (at the Jubilife Center PC). There is no
+00.05-start save any more.
+
+## Why it is deferred
+
+Testing 00.05 for real needs a new game through 00.00 to 00.04, which overwrites the
+end-of-00 save that segment 01 starts from. Segment 01 is stepped through first from that save;
+segment 00 is finished afterwards.
+
+## Plan
+
+1. **Dry run in town (no new game).** From the current save the NPCs are still there and still
+ talk. Walk the 00.05 legs with the Lab Console or a diagnostic entry: school door, president
+ (44,26), the three clowns (Mart clown at (54,23) is unverified), the Center door (51,30).
+ Fix the grid where the console disagrees. Story-specific dialogue lengths differ after the
+ story, so only walking and talk-starting are checked here.
+2. **Shorten the zig-zag plans.** The school-to-president plan is about 30 legs because the
+ plazas have few walls to end legs on. Either accept open legs with localisation after them,
+ or add known stopping features (planters, curbs) to the grid as walls.
+3. **New game, full segment 00, twice** (needs the user's OK at that time: it overwrites the
+ save). Each run must end with "Completed: 00.05" and a save at the PC; keep both runs' logs
+ and the save screenshots.
+4. Push the branch and update the fork PR's description (2 to 5 sentences).
diff --git a/docs/design/AutoStory-Segment01-Plan.md b/docs/design/AutoStory-Segment01-Plan.md
new file mode 100644
index 0000000000..a74407d901
--- /dev/null
+++ b/docs/design/AutoStory-Segment01-Plan.md
@@ -0,0 +1,109 @@
+# AutoStory segment 01 plan: Jubilife to the Coal Badge
+
+**Done 2026-10-29:** 01.01 to 01.03 were built from this plan and run on the console
+(r81 to r106; Lessons §11 and §12), and Floaroma was reached by hand. The MVP branch
+`bdsp/floaroma-mvp` (fork #68) carries the code. Kept as the plan of record.
+
+Status 2026-10-28: research done, nothing built yet. Companion to
+[AutoStory-Design.md](AutoStory-Design.md) (the pieces) and
+[AutoStory-Lessons.md](AutoStory-Lessons.md) (what went wrong in segment 00).
+Testing is done by an agent with the `bdsp-autostory-test` skill
+(`~/.claude/skills/bdsp-autostory-test/SKILL.md`).
+
+## 1. Starting point
+
+- **Save on console B:** end of segment 00, at the Jubilife Pokémon Center PC (play time 1:53,
+ "Last saved 01/01/2026 01:00"). This is 01.01's start, so segment 01 can be stepped through
+ and tested **without a new game**.
+- **Party:** Piplup alone, about Lv 10 to 11 after the 00.04 training and three Route 202
+ trainers, knowing Water Gun. Bag: the rest of the 10 Potions, Poké Balls, no money worth
+ speaking of beyond the trainer prize money.
+- **Help mode:** `HELP_MODE = no_outside_help`. Butters' segment 01 assumes the starter was
+ swapped for a **level-100 Pokémon from HOME** at that PC (his 01.01 start text: "Received
+ Starter back from Pokemon Home. Max leveled pokemon"). With that, every battle is one
+ attack. Without it we must win with a Lv 10 to 15 Piplup, so training, healing and move
+ choice matter here, unlike in his version.
+
+## 2. The story, step by step
+
+| # | Where | What happens | Ends when |
+|---|---|---|---|
+| 1 | Jubilife Center | (optional) heal at the nurse; buy Potions at the Mart next door if money allows | back on the Center's front step |
+| 2 | Jubilife east exit to Route 203 | Barry stops us at the route entrance: **rival battle**, Starly Lv 7 then Turtwig Lv 9 (we chose Piplup) | Barry's dialogue ends |
+| 3 | Route 203 | trainers in sight along the way: Youngster **Dallas** (Kricketot 7), Youngster **Sebastian** (Machop 7). Off-path: Youngster Michael (Bidoof 5, Zubat 5), Lass Kaitlin (Bidoof, Budew, Starly 4), Lass Madeline (Starly 5, Bidoof 5) | the Oreburgh Gate door (cave entrance, east end) |
+| 4 | Oreburgh Gate 1F | a Hiker gives **HM06 Rock Smash** (dialogue only). Camper **Curtis** (Shinx 7), Picnicker **Diana** (Budew 5, Psyduck 5). Wild: Geodude 4 to 8, Zubat 6 | white-screen exit east into Oreburgh City (end of 01.01) |
+| 5 | Oreburgh City | a man stops us at the entrance and takes us on a short "tour" (dialogue); Roark is not in the gym yet | mine entrance (south of the city) |
+| 6 | Oreburgh Mine 1F, B1F | Worker **Colin** (Geodude 7, Onix 7), Worker **Mason** (Machop 9); Roark is at the far end of B1F, talks, smashes a rock, sends us to the gym. Wild: Geodude 5 to 10, Zubat 7 to 8, Onix 8 to 9. Items: X Defense 1F, Potion and Escape Rope B1F | back outside (walk or Escape Rope), then gym door (end of 01.02) |
+| 7 | Oreburgh Gym | tiered floor with stairs (BDSP layout, not DP's); one or two gym trainers that bridges let us skip. **Roark**: Geodude 12 (Stealth Rock, Defense Curl, Rollout), Onix 12 (Stealth Rock, Rock Throw, Bind), Cranidos 14 (Headbutt, Bulldoze, Leer; Mold Breaker) | Coal Badge, standing at Roark (end of 01.03) |
+
+Sources: Serebii's BDSP pages for Route 203, Oreburgh Gate, Oreburgh Mine and the gym list
+(Bulbapedia's text pages are behind a Cloudflare check; its image archive is not). Trainer
+lists for the gym and the exact Roark location in the mine are still to confirm on the console.
+
+## 3. Battles: what Piplup is up against
+
+Our move picker uses the strongest known attack (Water Gun) every turn. Where that is wrong:
+
+| Fight | Problem | Plan |
+|---|---|---|
+| Barry's Turtwig Lv 9 | Grass resists Water: Water Gun with STAB ≈ 30 power, Pound 40 | add type effectiveness to the move picker (step 3 below); Potions under half HP |
+| Roark's Geodude / Onix | Rock/Ground, Water is ×4 | Water Gun is right |
+| Roark's Cranidos Lv 14 | Headbutt hurts; Mold Breaker irrelevant | Water Gun ×2; reach Lv 14 to 15 first; Potion under half |
+| Wild battles | run, as in 00.04 | unchanged |
+
+**Levels.** Aim for Piplup Lv 14 before Roark. The trainers on the way (Barry, Dallas,
+Sebastian, Curtis, Diana, Colin, Mason) give roughly 3 to 4 levels; if short, train on
+Geodude in the mine (Water Gun one-shots them, good experience).
+
+**Losing.** A whiteout returns us to the last Center with half the money. Detect it (black
+screen into a Center, or the "can't battle" party screen) and fail the attempt with a clear
+message; do not continue blindly.
+
+## 4. Maps and grids
+
+Following segment 00's rule: plan routes offline on tile grids traced from Bulbapedia's DP maps,
+and put NPCs/trainers found on the console onto the grid.
+
+| Map | Source image | State |
+|---|---|---|
+| Route 203 | `Sinnoh_Route_203_DP.png` (976×368, three stitched panels; check that the panel joins sit on the 16 px grid) | downloaded, to trace |
+| Oreburgh Gate 1F | filename not found yet (`Oreburgh_Gate_DP.png` etc. 404) | find the file name, or trace from console screenshots |
+| Oreburgh City | not found yet | same |
+| Oreburgh Mine 1F, B1F | `Oreburgh_Mine_B1F_DP.png` (400×396); 1F not found | B1F downloaded |
+| Oreburgh Gym | BDSP layout differs from DP; `Oreburgh_Gym_BDSP.png` is one screenshot | trace from console screenshots |
+
+Archive URL: `https://archives.bulbagarden.net/media/upload///`, h = md5 of the
+file name. Downloads go to the session scratchpad, not the repo; only the ASCII grid is
+committed.
+
+Butters' segment 01 (fork branch `bdsp/autostory-1717`, `Segment_01.cpp`, 2111 lines) is
+mostly timed stick holds (`pbf_move_left_joystick(…, 2694ms)`) plus picture matching inside
+the mine (crate and rock patches compared at shifted positions). Useful as a record of the
+order of events and dialogue counts, not as code to keep: the same timed-leg style failed in
+00.04 and 00.05 (Lessons §6).
+
+## 5. Work order
+
+1. **Trace Route 203** into `Data/PokemonBDSP_AutoStory_Route203.{h,cpp}`, joined to Jubilife's
+ east exit in a `MapSet`. Offline plan tests for Center → Barry → Gate.
+2. **Step through 01.01 on the console** from the current save with the Lab Console
+ (batches of 5 to 10 commands with a screenshot each): Barry's trigger tile, trainers'
+ positions and sight, the Gate's Hiker, Curtis and Diana, the white exit. Record each stop.
+3. **Type-aware move choice**: read the opponent's species name (OCR of the battle text box),
+ look up its types, and score moves by power × STAB × effectiveness. Unit tests on saved
+ battle screenshots.
+4. **Write 01.01** on the grid with `walk_route`, `talk_to_npc`, `finish_dialogue` and the
+ trainer handling from 00.04. Save at the Oreburgh Center when it ends.
+5. **Gate, city and mine grids** (from the images or console screenshots), then step through
+ 01.02, then write it. Decide walk-out vs Escape Rope after seeing the mine.
+6. **Gym**: step through, write 01.03 with a heal at the Oreburgh Center first.
+7. **Test** each checkpoint twice from its start save with the testing skill; then the whole
+ segment once from the end-of-00 save.
+
+## 6. Open questions
+
+- Is Barry's trigger a tile (walk-in) or a line across the route? Step 2 answers it.
+- Do Route 203's trainers stop us on the shortest path, or can the plan go around the optional
+ ones? We want the experience, so the plan should walk into Dallas and Sebastian on purpose.
+- Money: enough for Potions after Barry (prize money) or not? Read it in the Mart.
+- Where exactly is Roark in B1F, and does the scene move us?
diff --git a/docs/design/AutoStory.md b/docs/design/AutoStory.md
new file mode 100644
index 0000000000..d14601e42e
--- /dev/null
+++ b/docs/design/AutoStory.md
@@ -0,0 +1,887 @@
+# AutoStory: FRLG pilot first, shared run flow and 2D movement later
+
+Status: internal plan, revised 2026-10-25 after review. The upstream-facing
+one-page summary is [AutoStory-Proposal.md](AutoStory-Proposal.md). The
+review comment drafts for the BDSP PR are in
+[AutoStory-1717-review-drafts.md](AutoStory-1717-review-drafts.md). Neither
+file has been posted.
+Sources: SV AutoStory on `main`; BDSP WIP
+[PokemonAutomation/Arduino-Source#1717](https://github.com/PokemonAutomation/Arduino-Source/pull/1717)
+at `65094d4cd`; SwSh WIP PokemonAutomation/Arduino-Source#1207 at `32d506c27`;
+FRLG code on `main`; the [pret/pokefirered](https://github.com/pret/pokefirered)
+decompilation; the [52poke walkthrough](https://wiki.52poke.com/wiki/%E9%99%84%E5%BD%95:%E5%AF%B6%E5%8F%AF%E5%A4%A2_%E7%81%AB%E7%B4%85%EF%BC%8F%E8%91%89%E7%B6%A0%E6%B5%81%E7%A8%8B).
+Paths: `SV/` = `SerialPrograms/Source/VideoGames/PokemonSV/Programs/AutoStory/`,
+`BDSP/` = `SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/` (PR ref),
+`FRLG/` = `SerialPrograms/Source/VideoGames/PokemonFRLG/`.
+
+## 0. Summary
+
+**Problem.** AutoStory programs (play a game's story from a new save,
+unattended) are being written one game at a time. SV has one on `main`
+(22.4k lines). A team is building BDSP
+(PokemonAutomation/Arduino-Source#1717, 32.7k lines, WIP), SwSh has an
+early draft (PokemonAutomation/Arduino-Source#1207), and we want one for
+FireRed. Each copies or reinvents the same two things:
+
+- **The run flow:** an ordered list of checkpoints, each starting from a
+ save, retried from that save when it fails, with start/end selection,
+ stats and notifications.
+- **Walking on a grid** (FRLG and BDSP): following a path, and getting back
+ on it after a trainer, a wild Pokémon or a dialog interrupts the walk.
+
+**Situation.**
+
+- BDSP copied SV's run flow almost name for name, and the copies have
+ already drifted: different retry limits (SV allows 100 but runs 102, BDSP
+ 20), error reporting and day skip only in SV, BDSP's backup reload still a
+ TODO. SwSh does not use the framework at all. None of them has unit tests
+ of the flow.
+- Neither SV, BDSP nor the existing FRLG code can tell which step of a path
+ it was on when something interrupted it. BDSP's later segments push the
+ player back by hard-coded amounts per trainer, keyed by strings; one of
+ those pushes never runs because the names do not match. Its earlier
+ segments have a good image-based landmark alignment loop that the rest of
+ the PR does not use.
+- FRLG on `main` has most of the low-level pieces (detectors, fly, heal,
+ a tested wild-battle exit, a frame-exact tile walker inside the Elite Four
+ farmer) but nothing that plays the story.
+- The pokefirered decompilation gives exact positions and sight ranges of
+ every trainer, so FRLG paths can be planned offline instead of tuned by
+ hand.
+- It has been done before outside this project: a Poke-Controller script
+ (`FRLG_Story_Auto.py`, [junari's blog](https://junari.hatenablog.com/entry/2024/08/02/234339))
+ plays FRLG to the Hall of Fame with the starter alone in about 12–14
+ hours, on a GameCube Game Boy Player or a Switch, resetting to the last
+ save on failure. Users asked for the same here
+ ([ComputerControl#259](https://github.com/PokemonAutomation/ComputerControl/issues/259));
+ Mysticial's reply there is that an AutoStory takes far more effort than it
+ looks (SV took over a year).
+
+**What that year was, honestly.** Most of it was hardware hours per
+checkpoint: writing a checkpoint, then tuning it and rerunning it on a
+console until it is reliable. Shared infrastructure does not shorten that.
+Decomp-planned paths might reduce the tuning, but that is unproven. What we
+can do is measure the cost per checkpoint early and stop if it is too high
+(the gate in §7.7).
+
+**Order (revised 2026-10-25): own something before proposing changes to
+anyone else's code.** Revised again 2026-10-27: new code goes in its final
+home from its first PR (§0.1). Adding new files to a shared module changes no
+one else's code, so "FRLG files only" bought nothing, and "write it in FRLG,
+move it out later" was a plan for move-only PRs of our own code.
+
+1. **Phase A, the pilot: about 10 PRs, touching only new files and FRLG.**
+ Build a FireRed AutoStory from a new game to Brock. The run flow (S0)
+ and the tile-path executor (T1) are written in `Pokemon/AutoStory/` and
+ `Pokemon/Navigation/` from the start, used only by FRLG; SV and BDSP are
+ not touched. The Elite Four farmer moves onto the tile path (T2). Users
+ can run something at PR 5 (new game → rival battle); the pilot is
+ complete at PR 10 (Boulder Badge).
+2. **Gate F3**, a public commitment in the proposal: every pilot checkpoint
+ passes 3 times in a row from its save, at an average of at most 3 Switch
+ hours per checkpoint, counting development and validation. Otherwise we
+ stop at Brock.
+3. **Phase B:** the remaining segments to the Champion, one PR per segment.
+4. **Phase C, any time and independent of A and B:** the BDSP bugs found
+ while reading #1717 become standalone review comments with no request
+ attached. They are drafted in the sibling file and not posted; the user
+ decides.
+5. **Phase D, only after the Brock pilot runs and people use it:** "FRLG
+ runs on the shared core; I'll migrate SV and offer it to BDSP." Nothing
+ of ours moves: phase D changes SV's code to use the shared core (R1–R5),
+ and BDSP gets an offer.
+
+Requirements unchanged (§9): FireRed, to the Champion, solo with no trades,
+shortcuts as options, the team worked out backwards from what the route
+needs.
+
+**What reviewers should expect:** about 10 PRs to the pilot, most 150–500
+lines, in the order of §7.7, then a go/no-go decision with measured
+numbers. The likely reviewers are the FRLG authors in §8, plus Mysticial
+for the new shared folders.
+
+The rest of the doc gives the code layout (§0.1), the evidence (§2, §4),
+the interfaces (§3, §5), the BDSP findings (§6), the FireRed plan and PR
+sequence (§7), and who wrote the FRLG code we build on (§8).
+
+### 0.1 Code layout: reusable pieces and their homes (2026-10-27)
+
+Four AutoStory efforts (SV on `main`, BDSP #1717, SwSh #1207, our FRLG
+plan) and our BBQ and BDSP lab work keep rebuilding the same pieces. SwSh
+#1207 is timed input throughout (mash A for 30 s, hold the stick for 20 s,
+fixed menu walks), as BDSP's segment 00 was; running BDSP's intro on a
+console showed why that breaks (dropped inputs, menus opened by a stray A,
+loops that never end). The pieces below are game-neutral and get their
+final home in the PR that first adds them, even while one game uses them.
+
+| Piece | What it is | Home | First PR |
+|---|---|---|---|
+| Screen machine | Closed loop over known screens: each state has a detector, one step, the screens it may lead to, a visit limit and an in-a-row limit; a screen counts once two frames agree; fails on an unexpected screen, a stall (8 steps with no change) or a timeout. Replaces mash-for-N-seconds. | `CommonTools/` (templated on the controller context, like `run_until`) | BDSP new-game intro (lab), then FRLG D4 |
+| Step trace | Logs each recognized screen and action, saves a screenshot when the screen changes, and fails with the last screens attached. | `CommonTools/` | same |
+| Failure policy | On a failed operation: keep the error screenshot, go HOME, close the game, rethrow, so every run ends in a known state. | `NintendoSwitch/Programs/` (next to `GameEntry`) | same |
+| List selection | Read the selected row, move until the target row, confirm, verify the result. Readers are per game (BDSP name list, FRLG start menu, SV menus). | routine in `CommonTools/`; readers per game | same |
+| System keyboard | Detector, cursor finder, `type_on_system_keyboard`. | `NintendoSwitch/Inference/` | lab Lab Console PR |
+| Lab Console | Probe and debug runner (command files, per-game detector registry, `detect name image` offline). | `NintendoSwitch/Programs/LabConsole/` | lab Lab Console PR |
+| Run flow | Checkpoint list, save, retry from save, start/end range, stats (§3). | `Pokemon/AutoStory/` | fork #43–#46 / S0 |
+| Tile path | Path data, interrupt and resume, landmark alignment (§5). | `Pokemon/Navigation/` (console-free) | T1 |
+| Decision tests | Game steps injected as fakes (`WildBattleExitActions`, the BBQ Tera tests). | a test file per program, from its first PR | every program PR |
+
+Per game, deliberately not shared: detectors and templates, timings and
+speeds, battle UI, menus, new-game screens, path data.
+
+Rules that follow (also in the PR workflow): no "copy now, move later"
+step in any plan; tests go into the program's own test file in the PR that
+adds them; a seam PR sets the interface later PRs need, and behaviour PRs
+change policy, not signatures.
+
+## 1. Design from first principles
+
+Written before reading SV or BDSP; the rest of the doc checks it against them.
+
+1. **Checkpoint = story between two saves.** Precondition and postcondition
+ are observable states ("standing in Pewter Center, party healed"). On any
+ failure: reset to the last save and retry, up to a limit.
+2. **Program = ordered checkpoint list.** User picks start and end; the save
+ file is the resume state.
+3. **Checkpoints are short compositions of a small step vocabulary:** walk a
+ path, interact, clear dialog to a stop state, battle, menu choice, shop,
+ heal, fly, save. Each step names the screen it ends on and waits for it
+ with a timeout.
+4. **Movement is the game-specific layer.** Tile games (FRLG, BDSP d-pad) can
+ walk by count; 3D (SV) aligns a camera to markers. They should not share
+ code.
+5. **One battle handler** with policies (trainer: chosen move; wild: flee or
+ repel; prompts: evolution, move learn, faint switch; blackout: fail the
+ checkpoint).
+6. **Game actions behind interfaces** so the runner, the path executor and
+ battle decisions get unit tests with scripted fakes, like
+ `WildBattleExitActions` (`FRLG/PokemonFRLG_Navigation.h:88-127`).
+
+SV and BDSP match 1–2 closely, partially do 3 (helpers, not a vocabulary),
+and do 5–6 weakly (no tests of the flow; BDSP's smarter battle code is
+unused). 4 is where BDSP split into two styles.
+
+## 2. Run flow: what exists
+
+| Element | SV (`main`) | BDSP #1717 | Verdict |
+|---|---|---|---|
+| Segment interface | `name/start_text/end_text/run_segment(env, ctx, AutoStoryOptions, AutoStoryStats&)` `SV/PokemonSV_AutoStoryTools.h:96-107` | identical `BDSP/PokemonBDSP_AutoStoryTools.h:126-138` | same |
+| Checkpoint interface | same + `checkpoint_text()` `SVT.h:109-123` | no `checkpoint_text()` `BT.h:140-152` | same |
+| Registries | function-local statics, public `ALL_AUTO_STORY_SEGMENT_LIST()` `SV/PokemonSV_AutoStory.cpp:146`, `..._CHECKPOINT_LIST()` `:321` | file-static `ALL_SEGMENTS()/ALL_CHECKPOINTS()` `BA.cpp:51,245` | renamed |
+| Select databases | 6 near-duplicate `make_*_database` `SVA.cpp:152-198,326-372` | one `section_database(bool,bool)` `BA.cpp:257-283` | BDSP's is better |
+| Index math | `.index() + (LAST_TUTORIAL+1)` `SVA.cpp:1102-1168` | `stoul(slug())` `BA.cpp:735-750` | same result |
+| Tutorial split | hard-coded 9 / 20 `SVA.cpp:93-94` | segment 0, 6 checkpoints `BA.cpp:256` | same idea |
+| start > end | `UserSetupError` `SVA.cpp:1304-1313` | none (loop clamps) | BDSP missing |
+| Options | `{language, starter_choice, notif_status_update&, notif_error_recoverable&}` `SVT.h:89-94` | `{language, starter, notif_status_update&, help_mode, outside_help, save_mode, retry_scope}` `BT.h:111-119` | per game |
+| Stats | Checkpoint / Segment / Reset `SVT.h:27-40` | same + non-atomic `m_first_run` `BT.h:50` | same keys |
+| Save | `save_game_from_overworld` via `checkpoint_save` `SVT.cpp:1180` | verified save, 3 retries, blind fallback; FAKE diagnostic mode `BT.cpp:75-185` | per game |
+| Reset | `reset_game(info, console, ctx)` | `reset_game_from_home(...)`, throws on false `BT.cpp:236-243` | per game |
+| Day skip on retry | yes, if enabled `SVT.cpp:1443-1446` | no | SV only |
+| Retry limit | `i > 100` → 102 attempts (off by one) `SVT.cpp:1436,1471` | 20, rethrows `BT.cpp:210,230` | diverged |
+| Error report | at attempt 10, telemetry `SVT.cpp:1457-1469` | none | SV only |
+| Post-success check | `enter_menu_from_overworld` (retry if not overworld) `SVT.cpp:1453` | none | SV only |
+| Tutorial loop | separate copy `SVT.cpp:1491-1547` | — | SV only |
+| `save=false` path | — | the retry loop calls `reload_backup_save` (`BT.cpp:245-246`), an empty TODO (`:188-197`), and reruns the action without resetting | bug |
+| Dev harness | `test_checkpoints` / `test_code` `SVA.cpp:901-1080,1196-1291` | none | SV only |
+| End of program | `GO_HOME_WHEN_DONE`, start-up checks | neither; language hard-coded English `BA.cpp:701` | BDSP missing |
+
+Coupling worth knowing before moving anything:
+
+- SV's segment list is used outside AutoStory:
+ `ClaimMysteryGift::run_autostory_until_pokeportal_unlocked` runs segments
+ 0..6 (`SV/../Farming/PokemonSV_ClaimMysteryGift.cpp:371-382`). Keep the
+ list and `AutoStoryOptions` exported.
+- `ML/Inference/ML_YOLONavigation.cpp:20,87` includes SV AutoStoryTools: a
+ layering inversion to leave alone in this work, but not to copy.
+- Segments construct their checkpoints inline
+ (`AutoStory_Checkpoint_04().run_checkpoint(...)`), so checkpoint order is
+ written twice: in each segment and in the registry. In BDSP this already
+ went wrong twice (§6).
+- Code-health limits (fork #41: functions ≤50 lines, nesting ≤4, ≤7 params)
+ are exceeded by: SV constructor ~397 lines, `test_checkpoints` 180 lines /
+ 8 params / nesting 5, `test_code` 96, `on_config_value_changed` 87,
+ `program` 82, both retry loops (64 and 57 lines, 8 and 7 params), and every
+ `checkpoint_NN` (8 params). BDSP: constructor 282 (four copies of the
+ 14-move list), `program` 74, `save_game` 71, retry loop 51 / 8 params. Pure
+ moves keep these as legacy; anything new should not add more.
+
+## 3. Run flow: shared core
+
+The shared core is written in `Pokemon/AutoStory/` in the shape below
+(hooks, a policy struct, fake-hook unit tests) by the first PR that needs
+it: fork #46 already does this for SV, and S0 adds FRLG's hooks on top of
+it. FRLG is its first user only if S0 lands before #46; either way the code
+is written once, in its final place (§0.1). SV changes only in phase D,
+BDSP not at all.
+
+### 3.1 What is shared, what is per game
+
+Shared, in a new `SerialPrograms/Source/Pokemon/AutoStory/` (that module has
+no `NintendoSwitch/` includes today; keep it that way):
+
+- `run_checkpoint_with_retries(hooks, policy, checkpoint_text, action)`: the
+ loop only.
+- `CheckpointRetryPolicy{max_attempts, report_at_attempt, ...}`.
+- `AutoStoryStats` (same three keys; persisted stats depend on the strings).
+- `make_index_database(list, begin, end)` and `to_global_range(...)`: the
+ select/slug/index math.
+- Optionally `AutoStorySegmentT` / `CheckpointT<...>` so a
+ game declares `using AutoStory_Segment = ...`.
+
+Per game: Options struct, save/reset/day-skip, post-success check, the
+start-up checks, the registries, and every checkpoint.
+
+```cpp
+// Pokemon/AutoStory/Pokemon_AutoStoryRetry.h
+namespace PokemonAutomation{
+namespace Pokemon{
+
+struct CheckpointRetryPolicy{
+ size_t max_attempts; // SV 100, BDSP 20
+ bool fatal_when_greater; // SV: i > max (keeps today's off-by-one)
+ size_t report_at_attempt; // SV 10; SIZE_MAX = never
+};
+
+class CheckpointHooks{
+public:
+ virtual ~CheckpointHooks() = default;
+ virtual void log(const std::string& message) = 0;
+ virtual void save_checkpoint() = 0; // save, m_checkpoint++, status notification
+ virtual void before_attempt(size_t attempt) = 0; // SV: day skip + save on retries
+ virtual void after_success() = 0; // SV: confirm overworld; else no-op
+ virtual void report_recoverable(const std::string& message) = 0;
+ [[noreturn]] virtual void fail(const OperationFailedException& e, size_t max_attempts) = 0;
+ virtual void reset_for_retry() = 0; // reset, m_reset++, update stats
+};
+
+void run_checkpoint_with_retries(
+ CheckpointHooks& hooks,
+ const CheckpointRetryPolicy& policy,
+ const std::string& checkpoint_text,
+ const std::function& action
+);
+
+}
+}
+```
+
+SV keeps `checkpoint_reattempt_loop(...)` and `..._tutorial(...)` with their
+current signatures as thin wrappers that build `SVCheckpointHooks` /
+`SVTutorialCheckpointHooks`, so none of the ~102 call sites in 40 segment
+files change.
+
+### 3.2 Behaviours the move must keep (SV)
+
+- Save on attempt 0 happens inside the `try`; if the save throws, later
+ attempts do not save again.
+- `i > max_attempts` fires the fatal error (102 attempts run). Fix later in
+ its own behaviour-change PR, not in the move.
+- Recoverable report exactly once at attempt 10.
+- `wait_for_all_requests()` before the action and before the reset.
+- `after_success` throwing (the overworld check) counts as a failed attempt.
+- Each loop's own fatal message text.
+
+### 3.3 Phase D PR sequence (after the pilot is in use; SV only, no behaviour change)
+
+P1–P5 here are R1–R5 in §7.7.
+
+| # | PR | What moves | Risk | Review |
+|---|---|---|---|---|
+| P1 | Shared select databases | 6 `make_*_database` → `make_index_database`; same slugs/labels | low | `--color-moved` |
+| P2 | Range math | `get_*_index` arithmetic → pure `to_global_range` + tests | low | tests |
+| P3 | Shared stats | `AutoStoryStats` → `Pokemon/AutoStory`; alias in `SVT.h` | low (keys) | pure move |
+| P4 | Retry core | hooks + `run_checkpoint_with_retries`; SV loops become adapters; fake-hook tests | medium | loop body moved, adapters new |
+| P5 | Dev harness out of the program class | `test_checkpoints` / `test_code` → `PokemonSV_AutoStoryDevTests.cpp`; delete the dead 106-line comment block | low | `--color-moved` |
+| P6 (opt.) | Templated segment/checkpoint base | aliases in SV; no segment file changes | low | small |
+
+Tests enabled by P2/P4 (fake hooks recording steps as strings, registered
+through a new `Pokemon::add_tests_AutoStory` in
+`ComputerPrograms/UnitTestRunner.cpp`, or in `PokemonSV_Tests.cpp` first):
+first-try success sequence; save only on attempt 0 (also when the save
+throws); day skip only on retries; report exactly once at 10; SV fatal at 101
+vs BDSP rethrow after 20; reset exception propagates; non-`OperationFailed`
+exceptions are not caught; `after_success` failure retries; range math for
+SV (10/21 offsets) and BDSP (1/6); slugs equal global indices.
+
+Separate behaviour-change PRs after the move, each titled as such: fix the
+off-by-one; the BDSP-style single `section_database` if P1 did not already
+get there; registry-driven segments (a segment = a range of the checkpoint
+registry, removing the duplicated order).
+
+## 4. 2D movement: what exists
+
+### 4.1 Primitives
+
+| | FRLG (`main`) | BDSP #1717 |
+|---|---|---|
+| Grid walker | `WalkSegment{DpadPosition, steps}`, `walk_path` in `FRLG/Programs/Farming/PokemonFRLG_EliteFourFarmer.cpp:395-449`: decomp timings `FRAME_MS=16.74`, `TURN_FRAMES=8`, `STEP_FRAMES=16`; one continuous hold, direction switched mid last step (±8 frames tolerance) | `repeat_dpad` `BDSP/PokemonBDSP_AutoStoryTools.cpp:327-343`: one tap per tile, an extra tap when turning; 211 uses |
+| Elsewhere | millisecond holds: `StarterRng.cpp:224-230,413-449`, `PickupFarmer.cpp:142-167`, `EvTrainer.cpp:206-277`, `BlindNavigation.cpp:414-477` (RNG timing budgets), 33 ms turn taps in `grass_spin` `Navigation.cpp:1571-1592` | joystick holds dominate (Seg03: 328 holds, 0 `repeat_dpad`); tile counts only in comments (`1600ms // 8`, `8100ms // 27++ (snow)`) |
+| Facing | passed in by caller; not detected | `DpadState::last_dir` |
+| Doors | `walk_through_door` (:451): path + 300 ms under `BlackScreenOverWatcher`; `check_room` floor colour | `BlackScreenOverWatcher` everywhere; `walk_until_doorway` `Segment_01.cpp:85-187` (black screen then battle menu = wild battle, else doorway) |
+| Arrival | the transition only | transition, or landmark match (00–03) |
+| Executor stubs | — | `Utils/PokemonBDSP_AutoStory_Navigation.cpp:19-52`: `walk_steps`, `walk_until_dialog`, `walk_until_battle` are TODO |
+
+FRLG screen geometry: `GAME_BOX` (`FRLG/PokemonFRLG_Settings.cpp:110-115`)
+is exactly 6.5× of 240×160 at 1080p; one tile = 104 px; the view is 15×10
+tiles with the player in the centre tile.
+
+### 4.2 Interruptions
+
+- **FRLG:** no walking code watches for trainers, wild battles, NPC dialog or
+ items (the E4 paths are safe only because the League has none).
+ `exit_wild_battle` (`Navigation.cpp:576-660`) is a good, tested state
+ machine for leaving a battle, but nothing feeds position back into a path.
+- **BDSP:** trainer spotting is watched with `MarkDetector` ("!"), then
+ battle, then `move_back_after_unexpected_battle(trainer_id)`, a per-segment
+ if-chain of hard-coded pushes (Seg04:188, Seg05:185, Seg06:138, Seg07:203,
+ Seg08:134, Seg09:66). `handle_unexpected_battle` (`Utils/..._Battle.cpp:73-100`)
+ re-runs the whole action after a battle instead of resuming.
+ `recalibrate_after_battle` and `handle_unexpected_battle` are never called
+ by segments. Wild battles are mostly avoided with Repel; Repel expiry is a
+ `ShortDialogWatcher` interrupt (Seg07:687-698).
+- **Neither** reports "which step and tile was I on", handles item pickups or
+ generic NPC dialog during a walk, or re-localizes other than by hard-coded
+ pushes or a reset.
+
+### 4.3 Landmark alignment (BDSP 00–03)
+
+`match_landmarks` (`Utils/PokemonBDSP_OverworldMatchers.cpp:46-126`):
+downscale to 960×540 grey (optional local-contrast or blue-fraction
+features), `TM_CCOEFF_NORMED` per landmark in a search box, margin over the
+best competitor outside an exclusion radius, multiple landmarks must agree
+within ~1/64 × 1/54 of the screen; fine pass then a coarse pass that can
+never declare "at target". Lighting variants per hour count as one landmark
+(`match_lighting_pair` :309-340), with a lighting sweep test program.
+
+Control loop (`align_to_route204_floaroma_turn`, `Segment_02.cpp:406-463`):
+snapshot → if dialog or battle, return interrupted → match → 3 consecutive
+at-target hits with 150 ms waits = done → else one-axis joystick nudge of 40
+or 100 ms, 250 ms settle → "blocked" if dx moved < 1/384 over 3 moves, switch
+axis → throw after 12 frames lost → give up after 60.
+
+This is the strongest movement code in the PR. It also fits FRLG better than
+BDSP: no lighting changes, exact pixels, and an offset in game-box fractions
+converts directly to whole tiles (`round(dx·15)`, `round(dy·10)`), so FRLG
+alignment becomes "measure → emit a `{dir, n}` path → re-measure" instead of
+nudging. Sample only while standing still (the view scrolls during a step);
+avoid animated tiles; Flash-dark caves are the exception.
+
+## 5. 2D movement: shared design
+
+**Superseded 2026-10-29** by [AutoStory-Navigation-Stack.md](AutoStory-Navigation-Stack.md): a
+shared core (`nav/core`: grid types, a leg planner over a per-game step model, a leg walker
+with per-game movers and interrupts, the screen machine) with two children, `frlg/brock-mvp`
+and `bdsp/floaroma-mvp`. The sketch below is kept for its reasoning (§5.2 still holds).
+
+### 5.1 Pieces
+
+Console-free. It lives in `SerialPrograms/Source/Pokemon/Navigation/`
+from T1 on (precedent: `Pokemon/Pokemon_AdvRng.h` is shared by FRLG and
+RSE), with FRLG's adapter in the FRLG module. BDSP can adopt it later
+without anything moving.
+
+```cpp
+namespace PokemonAutomation{
+namespace Pokemon{
+
+enum class TileDirection{ UP, DOWN, LEFT, RIGHT };
+
+struct TileStep{
+ TileDirection direction;
+ uint16_t tiles;
+ bool ends_in_warp = false; // door/stairs: expect a fade at the end
+ std::optional trainer_stops_at; // sight line triggers after this many tiles
+};
+using TilePath = std::vector;
+
+enum class WalkInterrupt{ NONE, WILD_BATTLE, TRAINER_BATTLE, DIALOG, ITEM, EARLY_WARP, NO_WARP, BLOCKED };
+
+struct StepOutcome{
+ WalkInterrupt reason = WalkInterrupt::NONE;
+ std::chrono::milliseconds walked{0}; // input start -> detection frame time
+};
+
+struct PathRunResult{
+ bool completed = false;
+ WalkInterrupt last_reason = WalkInterrupt::NONE;
+ size_t step = 0; // where it stopped
+ uint16_t tiles_done_in_step = 0;
+ bool tiles_exact = false;
+ size_t interrupts = 0;
+};
+
+class TileMovementActions{
+public:
+ virtual ~TileMovementActions() = default;
+ virtual StepOutcome walk(std::optional facing, const TileStep& step) = 0;
+ virtual uint16_t estimate_tiles(const TileStep& step, std::chrono::milliseconds walked) const = 0;
+ virtual bool resolve(WalkInterrupt reason) = 0; // battle/dialog handled, back in overworld
+ virtual std::optional relocalize(const TileStep& step) = 0; // landmark fix, if any
+};
+
+class InterruptPolicy{
+public:
+ enum class Decision{ RESUME, RESTART_PATH, ABORT };
+ virtual ~InterruptPolicy() = default;
+ virtual Decision on_interrupt(const PathRunResult& state) = 0;
+};
+
+PathRunResult run_tile_path(
+ Logger& logger,
+ TileMovementActions& actions,
+ InterruptPolicy& policy,
+ std::optional facing,
+ const TilePath& path,
+ size_t max_interrupts = 10
+);
+
+// Alignment: BDSP nudges the joystick, FRLG emits whole tiles.
+struct LandmarkFix{ bool located = false, at_target = false, coarse = false; double dx = 0, dy = 0; };
+class AlignActions{
+public:
+ virtual ~AlignActions() = default;
+ virtual std::optional observe() = 0; // nullopt = interrupted
+ virtual void move_toward(double dx, double dy) = 0;
+ virtual void settle() = 0;
+};
+struct AlignConfig{ size_t max_attempts = 60, confirmations = 3, max_missing = 12; double blocked_epsilon = 1.0/384; };
+enum class AlignResult{ ALIGNED, INTERRUPTED, LOST, DID_NOT_CONVERGE };
+AlignResult align_to_landmark(Logger& logger, AlignActions& actions, const AlignConfig& config);
+
+}
+}
+```
+
+Per game (adapters in the game module, so dependencies point
+VideoGames → Pokemon): frame timings and speeds (walk, run, bike, surf,
+snow), how a step is sent (FRLG one continuous d-pad hold; BDSP d-pad taps),
+detectors (black screen box, battle start, dialog, "!" mark, item jingle),
+landmark matchers and templates, field moves, battle resolution. Do **not**
+share timing constants: two engines are not evidence for a common model.
+
+### 5.2 Getting back on the path after an interrupt
+
+Options for "how many tiles of the interrupted step were done":
+
+- **A. Time estimate** `floor((t_detect − t_start − turn) / step)`, using the
+ detecting frame's timestamp and starting the clock only after
+ `wait_for_all_requests()`. ±1 tile from emulator/capture latency.
+- **B. Path data.** A trainer's sight line triggers on a fixed tile, so the
+ step can declare `trainer_stops_at`. Exact. This is what BDSP's hard-coded
+ pushes encode, without a schema, and keyed by strings: `Segment_07.cpp:214`
+ matches `"route_215_skier_edward"` but the only caller (`:773`) passes
+ `"route_216_skier_edward"`, so the 8.1 s push never runs.
+- **C. Landmark fix** (§4.3). Exact where templates exist.
+- **D. Bonk anchor.** Walk into a known wall, counter or door until blocked
+ or warped; overshoot is harmless. Already how Pokécenter entry works.
+
+Recommendation: B when the step declares it; otherwise A, marked inexact, and
+end with C or D before anything that needs an exact tile (doors beside walls,
+interacting, the next trainer). Never chain two inexact estimates.
+
+### 5.3 Tests (no console)
+
+Following `ScriptedWildBattleExit` (`FRLG/PokemonFRLG_Tests.cpp:46-166`):
+executor bookkeeping (step/tile indices, splitting the remainder after an
+interrupt, early warp vs missing warp, `max_interrupts`, RESUME vs
+RESTART_PATH vs ABORT); FRLG hold-duration maths (turn cost, mid-step switch)
+if computed by a pure function; `estimate_tiles` boundaries; the alignment
+control law fed scripted `LandmarkFix` sequences (converging, blocked, lost
+13 frames, interrupted). Matchers themselves: screenshot tests in
+CommandLineTests, one PR per detector (Mysticial's request on #1717).
+
+### 5.4 Risks
+
+- NSO GBA runs ~59.73 Hz inside a 60 Hz output; long holds drift. Split long
+ paths at anchors.
+- Input and capture latency hurt option A; dropped 33–40 ms taps (use ≥80 ms
+ or continuous holds).
+- BDSP joystick moves are not grid-locked: snap back to the grid (d-pad or a
+ wall bump) after any joystick alignment.
+- Variable speed tiles and forced movement (snow, bike, ledges = 2 tiles,
+ spin tiles, currents), wandering NPCs blocking or pushing.
+- Battle intro and door fade both look like a black screen: check for a
+ battle menu after the fade (BDSP `walk_until_doorway` does; FRLG must).
+
+## 6. BDSP #1717: findings (phase C)
+
+**Revised 2026-10-25.** We do not own anything yet, so we don't ask the BDSP
+team to restructure their WIP, adopt a shared core, or reorder their PRs.
+The defects below become standalone review comments with no request
+attached, drafted in
+[AutoStory-1717-review-drafts.md](AutoStory-1717-review-drafts.md) and
+re-checked at `65094d4cd`. The user decides whether and when to post them.
+Design opinions stay in this internal doc until phase D, and come up
+earlier only if someone asks.
+
+They asked for feedback on "the SV-derived architecture, checkpoint and
+recovery contracts, BDSP helper/detector boundaries, resource distribution,
+and the order in which to extract focused PRs".
+
+**Defects (drafted as comments 1–5):**
+
+- `reload_backup_save` is an empty TODO (`BT.cpp:188-197`), but the retry
+ loop calls it when `save` is false (`:245-246`) and then reruns the action
+ without any reset.
+- Checkpoint order is written twice (segment + registry) and has drifted:
+ - Segment 08's `run_segment` stops at 0821 (`Segment_08.cpp:116`), while
+ the registry goes on to 0826 (`BA.cpp:181-185`), so 0822–0826 (Sunyshore
+ and the 8th gym) never run in segment mode.
+ - 0717 is missing from the checkpoint list (`BA.cpp:155-156` goes 0716 →
+ 0718).
+- `Segment_07.cpp:214` matches `"route_215_skier_edward"`, but the only
+ caller (`:773`) passes `"route_216_skier_edward"`, so the 8.1 s push never
+ runs.
+- Badge texts swapped: Segment 04 (`:31`) says "Third Gym Badge acquired
+ (Hearthome City)", Segment 05 (`:30`) "Fifth Gym Badge acquired (Veilstone
+ City)"; Veilstone is the 3rd, Hearthome the 5th.
+
+**Question (draft 6, optional):** `handle_battle` is copied into each of
+segments 04–09, keyed by trainer-id strings, while `Battle.cpp`'s
+`handle_expert_battle` (`:698`) and `handle_battle_with_outside_help`
+(`:208`) have no callers.
+
+**Held back (design opinions, for phase D or if asked):**
+
+- A shared run-flow core (§3) instead of per-game copies; their
+ `section_database` is cleaner than SV's six copies and would be the model.
+- No start > end check, no recoverable-error report, no `GO_HOME_WHEN_DONE`,
+ language fixed to English (`BA.cpp:701`), diagnostic options marked
+ "remove before production" (`BA.h:42-46`). These are normal for a WIP
+ and not worth a comment.
+- Movement: path data with `trainer_stops_at` (§5.2) instead of
+ per-segment `move_back_after_unexpected_battle` chains; the landmark loop
+ (segments 00–03) as a shared alignment routine (§5.1).
+- Changes to shared `DialogDetector` boxes affect every BDSP program and
+ need screenshot tests; Mysticial has already asked for detectors first,
+ one PR each.
+- Templates live only in their Packages fork, and per-hour lighting variants
+ multiply them.
+
+## 7. FRLG AutoStory plan
+
+### 7.0 Prior art: junari's FRLG script
+
+From the [blog post](https://junari.hatenablog.com/entry/2024/08/02/234339)
+(Poke-Controller MODIFIED, Python; we have not read its code):
+
+- **Scope and time:** new game to the Network Machine, Silph Co., the
+ ending or the Hall of Fame (selectable stop point); about 12–14 hours to
+ the end.
+- **Team:** the starter alone fights every trainer. Squirtle is fastest,
+ then Charmander, then Bulbasaur. Moves come from TMs and tutors: Squirtle
+ Mega Kick + Water Pulse; Charmander Mega Kick, Dig, Dragon Claw; Bulbasaur
+ Return, Earthquake. No transfers from another save. This confirms the
+ solo rule (§9) is workable and shrinks the requirements plan (§7.6) to HM
+ users, TMs and tutors, and money.
+- **Recovery:** on a lost battle or a loop running too long, reset and
+ restart from the nearest save.
+- **Options:** start/stop number, version, hardware, language, gender,
+ player/rival names, starter, fossil, Dojo Pokémon, and a "Nugget farm"
+ mode (lose to the Nugget Bridge Rocket repeatedly, 1.5–2 min each), the
+ same trick as our `NuggetBridgeFarmer`.
+- **Setup it requires:** delete the save from the title screen (Y + B + Up
+ on Switch), 720p output, screen size 100%.
+- **Images:** none shipped; it crops reference images at run time on the
+ first run and reuses them.
+
+Its terms forbid redistribution and claiming authorship, so it is a
+reference for the route, team and options, not a source of code. Its demo
+videos are useful for checking routes.
+
+### 7.1 Reuse
+
+Already on `main` (`FRLG/`):
+
+- Detectors: dialogs, battle menu/dialogs/learn/level-up/out-of-PP, party
+ and summary, start menu, bag, load menu (untested on a new game),
+ selection arrows, faint watchers, fly map, E4 rooms, Hall of Fame.
+- Navigation: `fly_from_kanto_map` (13 towns), `heal_at_pokecenter`,
+ `enter_leave_pokecenter`, `use_teleport_from_overworld`,
+ `open_party_menu_from_overworld`, `save_game_to_overworld`, `soft_reset`.
+- Battle: tested `exit_wild_battle`; `spam_first_move`;
+ `EliteFourFarmer::run_battle` (OCR the opponent, move plan per opponent,
+ out-of-PP) as the model for a trainer battle handler.
+- Story fragments: StarterRng's `walk_to_rival_battle`,
+ `walk_to_route1_from_lab`, `auto_battle_rival`; SidHelper's
+ `finish_intro_animations`.
+- **Money:** `NuggetBridgeFarmer` already loops the Nugget Bridge event
+ (loses the battle on purpose, ~72 s per Nugget by its "100 ≈ 2 hours"
+ default, Nugget sells for 5,000, so ≈250k/hour). Wrap it as an optional
+ "money top-up" checkpoint after Nugget Bridge (or any time later, by flying
+ to Cerulean), with a target amount.
+- `ItemDuplication` (Retro Mail glitch; needs Farfetch'd from the Vermilion
+ trade): optional way to duplicate Rare Candies instead of grinding. Keep
+ it out of the default route; offer it as a config.
+
+### 7.2 Missing pieces (infrastructure, ~3–5k lines)
+
+1. The run flow (§3), shared core in `Pokemon/AutoStory/`; FRLG hooks
+ (save via start menu, reset via `soft_reset` + load menu), options,
+ registries in the FRLG module.
+2. Tile path (§5) in `Pokemon/Navigation/`, the FRLG adapter in the FRLG
+ module, moving `walk_path` and the constants out of
+ `EliteFourFarmer.cpp` (thewhitewolfking's code, not ours); add run
+ (8 frames/tile), bike (6), surf (8) per decomp; ledge steps.
+3. Overworld detector (overworld vs dialog vs battle vs menu without opening
+ the start menu) and facing detection (player sprite in the centre tile).
+4. Walk interrupts: trainer "!" or approach, wild battle (black screen +
+ battle menu), item pickup jingle/dialog, NPC dialog; resume via §5.2.
+5. Trainer battle handler: move choice (per-checkpoint plan first, type
+ table later), "will you switch?" (avoid by Battle Style = Set), next
+ Pokémon after a faint, move learn (skip or replace per config),
+ evolution (allow/cancel per config), whiteout → fail the checkpoint.
+6. New game: intro, naming keyboard (preset names avoid it), options menu
+ (text Fast, animations Off, style Set), load menu on a fresh save. Built
+ on the screen machine and step trace (§0.1), as the BDSP intro is in the
+ lab: every input follows a screen read, no mash-for-N-seconds.
+7. Field moves: teach HM from bag, use Cut/Surf/Strength from the party
+ menu, Fly (exists), Bike on/off; puzzles below.
+8. Shop and PC: buy list per Mart, deposit/withdraw for HM carriers.
+9. Catch routine and the requirements planner (§7.6).
+
+English only (decision 5), so text detectors and OCR need one language.
+
+### 7.3 Route data
+
+The decomp (`data/maps/