fix(search): make the trimmed-applied-term invariant structural and update the shell contract doc

Review follow-ups on the echo-guard widening:

- setSearchState now trims too, so URL-sourced terms (deep links with
  ?q=Bein%20, actor/discover prefills passing raw provider titles) cannot
  put an untrimmed term into appliedSearchQuery — previously that path
  failed the echo guard's equality check, snapped the box, and dispatched
  the portal search twice. Regression spec added.
- docs/architecture/workspace-shell.md item 8 updated: the applied-term
  echo is now always ignored, not only while input is still debouncing.
- The facade spec's router mock exposes a mutable navigation trigger so
  facade-level tests can exercise the popstate branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5 committed 2026-08-23 09:15:30 +02:00
1 parent 2af67f5dae
commit 935200c4b8
4 files changed
+48 -9

No files matched your search

+12 -6
View File
@@ -165,12 +165,18 @@ Search is shell-owned and route-aware:
8. The URL is authoritative for the search box only when it carries search
intent. `WorkspaceShellSearchSyncService` re-reads `q` on every
`NavigationEnd`, but an **app-initiated** navigation that stays on the same
page and carries the term already applied is ignored while input is still
debouncing — otherwise a page writing an unrelated query param (a downloads
filter chip, a refresh bump) or the router echoing back our own `q` would
cancel the pending debounce and reset the box, eating everything typed
since. Pages are free to write their own query params while the user types;
they must not assume the shell will re-apply the search afterwards.
page and carries the term already applied is always ignored — whether or
not a debounce is still pending. Otherwise a page writing an unrelated
query param (a downloads filter chip, a refresh bump) or the router echoing
back our own trimmed `q` would reset the box to the applied term, eating
everything typed since: the whole word while the first keystroke is still
debouncing, or a just-typed trailing space once the debounce has fired
("Bein " would snap to "Bein" and typing on would yield "BeinSports").
Applied terms are always stored trimmed (`applySearchQuery` and
`setSearchState` both trim), so the echoed `q` compares directly; the box
keeps exactly what the user typed. Pages are free to write their own query
params while the user types; they must not assume the shell will re-apply
the search afterwards.
9. Browser history overrides that guard. The exemption is keyed on
`Navigation.trigger === 'imperative'`, so back/forward always re-applies
what the history entry carries, even mid-typing.