frontend-patterns/research/reviews/ZulipNavigationSourceStudy.md

94 lines
11 KiB
Markdown
Raw Normal View History

# Zulip navigation/search source contrast — 2026-09-27
LEARN-0014; Codex author-model review. [Protocol](../evolution/cycles/2026-09-27-zulip-navigation.md)
committed as `f3ac569` before source inspection. Stream B, group
B-independent-navigation-source-v1. **Static source evidence only**: no application
build, browser run, upstream test execution or inference call.
Pinned Zulip revision `5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c`, deliberately reused
from the earlier recovery review, not claimed latest. [Source manifest](ZulipNavigationSources.json)
records twelve files, raw SHA256 and reviewed line ranges. This is one independently
developed application family, already known to this research through different paths;
it is not twelve independent uses or an independent review of our conclusions.
## Located claims
| Claim | Exact pinned locators | Observed code | Interpretation / limit |
| --- | --- | --- | --- |
| ZN01 | [hash reconstruction](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/hashchange.ts#L171-L235), [history metadata](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/browser_history.ts#L168-L195), [save guard](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/narrow_history.ts#L10-L63) | Hash terms select a message view; history state supplies message pointer/offset. Saving selection checks URL/filter agreement first. | Addressability and within-session continuation are separate contracts; these paths require the client. No no-script or direct-load runtime claim. |
| ZN02 | [fetch continuation](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_view.ts#L830-L878), [selection guard](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_view.ts#L1413-L1481), [current-list lifecycle](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_lists.ts#L24-L64) | Main narrow-fetch continuation returns when its captured message list is no longer current; selected-message rendering also checks list identity. | Partial support for protecting current selection/hash effects without requiring a global navigation counter. Not a proof for all callbacks. |
| ZN03 | [success routing](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_fetch.ts#L213-L280), [processing](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_fetch.ts#L159-L210), [preserved view policy](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_list.ts#L152-L199), [list updates](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_list.ts#L269-L326) | For a noncurrent, nonpreserved list, success switches to data-only processing instead of discarding the response. Preserved combined-feed views are an explicit exception. Other message/sidebar processing still occurs. | Leaving a view does not make its data useless. Cache/data applicability differs from permission to affect active navigation. Not a claim that inactive results have zero UI effects. |
| ZN04 | [error and connection feedback](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_fetch.ts#L418-L508) | Success and HTTP 400 close shared connection feedback before the noncurrent-list error guard. Errors for noncurrent, non-combined lists return before further processing/retry; other errors have backoff/retry handling. | Feedback has scopes too. Global connection evidence and view-specific errors are different; helper internals and runtime correctness were not audited. |
| ZN05 | [query dispatch](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/search.ts#L60-L95), [suggestion callback](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/search.ts#L151-L212), [suggestion construction](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/search_suggestion.ts#L1530-L1636), [IME/Enter handling](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/search.ts#L290-L335) | Committed search passes parsed terms to an injected callback and blurs search; the inspected suggestion provider synchronously returns a list. Enter/IME handling distinguishes composing or selecting a suggestion from submitting search. | Do not treat every keystroke as a remote query race. Callback wiring and all imported suggestion-helper internals are outside this twelve-file slice. |
| ZN06 | [search continuation](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/search.ts#L396-L437), [close focused search](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/search.ts#L470-L483), [editing focus restore](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_list_view.ts#L345-L392) | Search entry focuses search; exit may keep a narrow, redirect or focus the app. Editing focus capture records message/selection; restoration checks selected-message identity, then schedules focus work. | Non-modal task continuation differs from return to a modal invoker. A pre-timeout identity check is not proof of completion-time focus ownership or AT behavior. |
| ZN07 | [target-message lookup](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_view.ts#L662-L699), [anchor navigation](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_view.ts#L1002-L1074) | An auxiliary target lookup re-enters show from success/error callbacks. The anchor fetch continuation creates/uses the then-current list through a closure. These branches have no visible captured-current-list guard like ZN02. | Concrete unverified paths prevent a whole-app ownership guarantee. No reproduced race, exploit or vendor bug claim; unseen transport/caller behavior may constrain execution. |
| ZN08 | [preserved-list reuse](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_view.ts#L175-L194), [filter source](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/narrow_state.ts#L14-L67), [inspected upstream test](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/tests/message_fetch.test.cjs#L1-L40) | A preserved combined-feed list may be reused; narrow state derives from the current view. The inspected test covers date-anchor parameter handling. | List identity is not equivalent to a monotonically increasing navigation generation. This test file supplies no executed or inspected competing-navigation coverage; the rest of the suite was not reviewed. |
## Bounded trace and counterexample to overgeneralization
The hash path parses route terms and calls message_view.show. That function constructs
or restores a list, updates the hash when required and makes the list current. A needed
message fetch captures that list. When it completes, data/fetch state can still be
processed, while the main view continuation checks current-list identity before changing
selected-view state. See ZN01–ZN03. This is a trace across located functions, not a
running-session observation. Search dispatch itself is located, but the initializer
that supplies its injected navigation callback was not included; do not infer its
complete wiring from the naming alone.
The meaningful contrast is **applicability per effect**:
| Effect | Located decision / remaining uncertainty |
| --- | --- |
| Populate cached message data | Can remain useful after leaving a view; cache consistency/retention internals not audited |
| Update a preserved rendered list | Explicit conditional combined-feed exception; not a universal discard rule |
| Change selected view/hash/message position | Main continuation and selection paths check current list identity |
| Update shared connection feedback | Some updates precede view-specific error applicability checks |
| Move input focus | Search-task policy and editing selection context are distinct; timer completion still untested |
Our earlier generation guard is one implementation of a selected-view policy. It must
not become a requirement to drop an entire response or every side effect whenever a
newer navigation exists. Nor does Zulip's list identity establish that all effects are
safe: ZN07 and deferred focus in ZN06 are explicit limits requiring additional evidence.
A preserved object can become current again; object identity and latest-intent generation
encode different policies. Freshness, authorization and data consistency are not proven
by either identity mechanism.
## Pattern disposition: revision without promotion
Revise URL-addressable State to make client-fragment reconstruction an explicit option,
with its required client/runtime contract, separate from server-visible path/query
resolution. Hash-based addressability does not establish progressive enhancement.
Refine Progressive Enhancement's asynchronous-navigation obligation to decide result
applicability per effect: cache data, current selection, global versus local feedback,
and focus. Zulip is a static implementation contrast for that obligation, **not a
verified no-script application or a full known use of Progressive Enhancement**.
Focus Return gains only a non-modal scope contrast, not an additional complete modal use.
This is one consequential revision, with corroborating identity guards and limiting
branches recorded together. CAND-0009/0010/0016 remain draft/hypothesis; independent
full-contract use counts stay unassessed. Independent implementation provenance is
stronger than another authored fixture variant, but the review remains author-model.
No new canonical identity, relation, pattern, M1/M2 acceptance or usefulness finding.
## Scope closure and follow-up
The twelve-file cap is reached; no further callees were fetched. Files were inspected
at the manifest's ranges, not exhaustively audited. The raw GitHub URLs supplied the
source after the browsing tool could not open the commit page; retrieval hashes are
preserved. No upstream code or frozen evaluation artifact changed. A source test was
read, not executed. The earlier htmx fixture remains paused.
[Effort receipt](../evolution/cycles/2026-09-27-zulip-navigation-effort.json) records
active source work versus repository-check execution, with final administration
excluded and token/cost unknown. This source-review protocol is a separate comparison
group; four distinct prospective protocols still do not provide a diminishing-returns
window. Full language usefulness and independent human/AT review remain unresolved.
WP-0003 T01 follow-up: before trusting ZN07 or delayed focus, preregister a narrowly
scoped upstream call-path/test study of the target lookup and anchor continuation;
include callback wiring/transport context, preserve negative findings, and do not
report a bug until reproduced or otherwise established. Broader evidence collection
can proceed without expanding our local adapter. T02 retains independent review and
the offline case branch. WP-0006 T03 retains comparable-window/review-date work.