notedeck · crates/notedeck_chrome/src/chrome.rs · fdc8f8ff1954

The Routed Entry Is the Switch

Clicking an inline Headway card from Dave used to push two entries onto the chrome's global history, so getting back to Dave took two back presses — the first one only reached the Headway board. This commit, the last of five, is the one that changes what the user sees: the chrome now asks Headway for the card's route first and pushes that as the app switch.

entries pushed per open: 1 (was 2) back presses to Dave: 1 (was 2) +106 / −20 lines, one file commit 5 of 5 · headway:notedeck/obey-margin-scale

1. What the click does to the history

Step through one click and one back press. The left column is the old headway arm (switch_to_headway() + headway.open()); the right column is the new open_headway_note. The outlined entry is the top of the stack, which is what gets rendered.

Before

After

The extra entry comes from set_active. It pushes an untyped Rc<()> app-switch entry, which Headway's render_nav downcasts to None and draws as the board root. Headway's own before/after diff then reads Board → Card and reconcile_nav pushes a second one.

2. Where the new code sits

Three small pieces in chrome.rs, each with one job:

chrome_handle_app_action is_headway_note arm open_headway_note headway_slot() open_note_in_app -> bool open_note_route Headway mints token push_app_route ONE route_to set_active(slot) fallback: board root None

push_app_route(slot, token) is split out from open_note_in_app so the one-entry invariant can be unit-tested with no AppContext: the new test note_route_push_adds_exactly_one_entry checks that it goes from [app0, app1] to [app0, app1, app2-route] and that one back press lands on app1. It's the counterpart to the existing drained_active_push_is_tagged_with_the_active_app, which shows the switch-then-push shape that caused the bug.

Why Headway doesn't then push a second entry itself: render_nav sets before = Card(card) from the token, and process_pending_open selects the same card. So before == after and reconcile_nav returns None. The headway-side test for this landed in commit 4 (chrome_nav_loop_cross_app_open_pushes_one_entry).

What was left alone

The Dave and Notebook arms still use switch_to_* + open(). Neither app has a route type yet, so each open lands only the one app-switch entry and neither has this bug. Once they get a route type they can call open_note_in_app too (Dave: headway:notedeck/any-bronze-illness). get_headway_app and switch_to_headway had only this one caller, so they were removed.

How it can still go wrong

Headway already active. Clicking a headway reference from inside Headway now also pushes a routed entry, since push_app_route has no same-app guard. That should be right, because opening a card is a new route. The old path gave the same result through Headway's own reconcile push, but the timing differs, and this path isn't exercised by any test.

The fallback is nearly unreachable. is_headway_note has already found the note in ndb, and resolve_open_target reads only that note. So open_note_route returns None only for a note of a headway kind that event::parse rejects (a malformed event). The old pending open() retry couldn't resolve that note either. Either way the user lands on Headway's board root.

Not verified

No manual GUI run. This session had no display, so the card's manual check didn't happen: click a card chip from Dave, confirm one back press returns, and repeat for a card on a non-active board and for a board reference. The chrome glue (chrome_handle_app_action → open_headway_note) has no test. Only its two halves are covered, by the chrome unit test and the headway nav-loop test from commit 4.