web: boosted links read href FRESH at click time — fix stale nav after a morph swap

Reported: on blog.rose-ash.com, home --boosted nav--> a post --click "edit"--> lands on
/tags (a HOME footer link), not /<slug>/edit; subsequent navs stop updating.

Root cause: an innerHTML boost swap uses morph-children, which REUSES DOM nodes in place
(matched positionally when links have no id). The home footer's <a href="/tags"> element is
re-purposed as the post's <a href="/compose-demo/edit"> — its href attribute is rewritten,
but bind-client-route-click had captured the OLD href in its click closure, and the element's
is-processed? mark survived the morph (so boost-descendants skipped re-binding it). Clicking
the reused "edit" link fired the stale /tags closure.

Fix: bind-client-route-click now reads the href FRESH from the element at click time
(dom-get-attr link "href", falling back to the captured value) instead of trusting the
closure. A reused node then always follows its CURRENT href — robust to morph reuse without
needing to clear marks or remove listeners. Recompiled the web stack (.sxbc + manifest).

TEST-FIRST: lib/host/playwright/{boost-nav.spec.js, run-boost-nav-check.sh} reproduces the
exact flow (home -> boosted nav -> click edit -> assert URL is /compose-demo/edit, NOT /tags)
against an ephemeral server. Confirmed RED before the fix (landed on /tags), GREEN after. No
regressions: relate-picker 3/3 (incl. boosted-nav populate) + block-editor 1/1 still pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 05:55:46 +00:00
parent f804a71726
commit a511b21dd2
5 changed files with 108 additions and 4 deletions

View File

@@ -611,7 +611,15 @@
(not (event-modifier-key? e))
(prevent-default e)
(let
((boost-el (dom-query "[sx-boost]"))
(;; Read the href FRESH from the element at click time. A morph swap
;; (innerHTML) REUSES DOM nodes in place — an <a> from the previous page
;; can be re-purposed as a different link, its href rewritten but this
;; click closure (and its is-processed? mark, so boost-descendants skips
;; re-binding) left intact. Capturing href in the closure then navigated
;; to the STALE target (e.g. home's /tags for a swapped-in "edit" link).
;; Reading the live attribute makes a reused node follow its CURRENT href.
(href (or (dom-get-attr link "href") href))
(boost-el (dom-query "[sx-boost]"))
(target-sel
(if
boost-el