a10e95eb5f
Two blockers from the adversarial review. BLOCKER 2, a new wrong state this PR introduced: resolving `AppMode::Normal` per-effect reads the batch's OPENING layout (gotcha 23), so a batch that CROSSES — `:toggle-records-mode . enter-mode normal` — settled against the grid it started on and left `RecordsNormal` over a pivot grid. Base was correct there. The fix moves the resolution to one place, `App::settle_normal_mode` called after `rebuild_layout`, where there is no crossing case to get wrong. That is strictly simpler than what it replaces: `ChangeMode` is an ordinary `simple_effect!` again, `ReturnToNormalUnlessMoved` and `CloseWizard` write plain `Normal`, and the per-edit coverage question the review filed as finding 3 is gone because there is one edit. Removing its call site fails exactly three tests, workspace-wide. And the prose defended the staleness with an example that does not bear the weight: `:toggle-records-mode` works, but because `ToggleRecordsMode` and `ViewBack` overwrite the answer in both directions, so it exercises nothing. Replaced by a test that drives the crossing both ways. BLOCKER 1, a false claim in `context/`: "keys.rs emits no ChangeMode" is true and irrelevant — keys.rs holds only `BrowserKey`. The live browser key resolver is `improvise_protocol::resolve`, which emits `Command::ChangeMode(Normal)` in five places, and `reduce.rs` folds it with no resolution. The browser front end still has the sihr bug; it is LIVE, not latent, and it compounds because the client's only records-ness fact is the mode it just lost. Named honestly, with improvise-e5pf cited. Still not closeable here — a client holds a `GridCache`, not a `GridLayout`. Also the two corrections: the thpe test's doc comment no longer claims a discrimination it does not have, and the 2ud deferral notes that `registry.rs`'s `visible_cols: 8` is production, not a test helper. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH