Second cycle in comparison group B-independent-navigation-source-v1, a naturally-motivated follow-up to LEARN-0014's ZN07 finding rather than a manufactured repetition. Traces the target-message lookup and anchor- navigation fetch continuation to their unconditional completion points in message_fetch.ts: neither participates in the opt-in current-list-identity guard used elsewhere in the same file, and no located test covers a stale-navigation scenario. No reproduction or defect claim; static source read only. CAND-0009/CAND-0010 evidence and gaps updated accordingly. Declined to reopen the paused local navigation fixture to force a third cycle in an existing group, per the prior agreed pause and WP-0006 T03's own rule against manufacturing cycles to hit a count. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: sonnet Assistant-Process: 290719@bnt-lap001 Assistant-Session: 55cbe504-1bfe-4932-b761-29016523c87b
94 lines
7.7 KiB
Markdown
94 lines
7.7 KiB
Markdown
# Zulip target-lookup and anchor-continuation guard trace — 2026-09-27
|
||
|
||
LEARN-0017; Codex author-model review. [Protocol](../evolution/cycles/2026-09-27-zulip-target-lookup.md)
|
||
committed as `884803c` before this inspection. Stream B, comparison group
|
||
B-independent-navigation-source-v1 — second cycle in this group, following
|
||
LEARN-0014. **Static source evidence only**: no application build, browser run,
|
||
upstream test execution or inference call.
|
||
|
||
Pinned Zulip revision `5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c`, deliberately
|
||
reused from the earlier navigation and acceptance/projection studies, not
|
||
claimed latest. [Source manifest](ZulipTargetLookupSources.json) records four
|
||
files, raw SHA256 and reviewed ranges. This is the same Zulip family already
|
||
known to this research through other paths, not a new independent family.
|
||
|
||
## Question
|
||
|
||
ZN07 (ZulipNavigationSourceStudy.md) located the auxiliary target-message
|
||
lookup and the anchor-navigation fetch continuation and noted they have "no
|
||
visible captured-current-list guard like ZN02," but did not trace their actual
|
||
callers or check whether an existing test covers a stale-navigation race for
|
||
either path. This cycle traces both call paths to their concrete completion
|
||
point and checks test coverage.
|
||
|
||
## Located mechanism
|
||
|
||
| Claim | Exact pinned locators | Observed code | Interpretation / limit |
|
||
| --- | --- | --- | --- |
|
||
| ZT01 — target-message lookup | [channel.get to /json/messages/:id](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_view.ts#L663-L692) | On success or error, the callback calls `show(raw_terms, {...opts, fetched_target_message: true})` unconditionally — there is no check that the user is still on the same narrow/hash that triggered the lookup. | A completed lookup re-enters the main `show` function regardless of intervening navigation. `show` itself resets UI state on entry, so a stale re-entry would still apply its own reset, but no code at this call site rejects a stale re-entry before doing so. |
|
||
| ZT02 — anchor-navigation continuation | [navigate_to_anchor_message / load_messages_around_anchor](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_view.ts#L1001-L1073) | The continuation closure (`select_anchor_using_data`) captures `message_lists.current` and a freshly constructed `msg_list_data` at call time, then, once the fetch completes, unconditionally calls `message_lists.update_current_message_list(...)` and `select_msg_id(...)`. | If the user navigates away while `load_messages_around_anchor` is in flight, the completion still installs a message list built from the pre-navigation `message_lists.current` as the new current list. Nothing in this path re-checks that the captured list is still current before installing it. |
|
||
| ZT03 — where the continuation is actually invoked | [process_result](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_fetch.ts#L157-L206) calls `opts.cont(data, opts)` unconditionally whenever `opts.cont !== undefined`. | `load_messages_around_anchor` never sets `opts.msg_list` (only `opts.msg_list_data`), so this call path does not go through the `opts.msg_list !== message_lists.current` guard that exists elsewhere (see ZT04). The continuation always fires once the request resolves. |
|
||
| ZT04 — the guard that *does* exist, and why it doesn't apply here | [load_messages error path](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_fetch.ts#L455-L464) rejects further processing when `opts.msg_list !== undefined && opts.msg_list !== message_lists.current && !opts.msg_list.is_combined_feed_view`; [get_messages_success](https://github.com/zulip/zulip/blob/5b06fa3d34f28fa01f459fbf3ba053e3c0858e9c/web/src/message_fetch.ts#L249-L259) has an analogous success-path check. | Both guards are conditioned on `opts.msg_list`, which the anchor-fetch caller never supplies. The mechanism ZN02 relied on (list-identity checking before continuing) exists in this file, but is opt-in per caller, not automatic for every fetch that carries a `cont` callback. |
|
||
|
||
## Test coverage
|
||
|
||
- `web/tests/message_fetch.test.cjs` is 40 lines total (already reviewed in
|
||
full at ZN08); it exercises `get_parameters_for_message_fetch_api`'s
|
||
date-anchor parameter handling only. No test in this file exercises
|
||
`opts.cont`, `process_result`, or a stale-`message_lists.current` scenario.
|
||
- `web/tests/message_view.test.cjs` mocks `message_fetch.load_messages_around_anchor`
|
||
directly (bypassing `message_fetch.ts` entirely) in its
|
||
`fast_track_current_msg_list_to_anchor` tests (L1106–L1244); the mock invokes
|
||
the callback synchronously with no intervening navigation. No occurrence of
|
||
`target_id` or `fetched_target_message` was found anywhere in this file, so
|
||
the target-message-lookup path (ZT01) has no located test coverage at all.
|
||
|
||
## Interpretation and boundary
|
||
|
||
This corroborates and sharpens ZN07 rather than revising it: the two flagged
|
||
continuations genuinely lack the kind of captured-list guard that protects
|
||
`message_view.ts`'s main narrow-fetch continuation (ZN02) and that
|
||
`message_fetch.ts` itself implements for callers that pass `opts.msg_list`
|
||
(ZT04). The absence is now traced to a concrete cause — `load_messages_around_anchor`
|
||
and the target-lookup `channel.get` simply don't participate in that
|
||
opt-in mechanism — rather than only observed as "no visible guard." Neither
|
||
path has any located automated test coverage for the stale-navigation case.
|
||
|
||
This is **not** a reproduced race, a defect report, or a claim about runtime
|
||
behavior: `show()` itself performs its own state reset on entry (see ZT01),
|
||
which may or may not make a stale re-entry externally observable as wrong
|
||
behavior — that requires actual execution, which is out of scope here. No
|
||
production dependency, upstream message, or bug report follows from a source
|
||
trace. Do not treat this as established misbehavior without reproduction.
|
||
|
||
## Pattern disposition: corroboration, no promotion
|
||
|
||
CAND-0009 (URL-addressable State) and CAND-0010 (Progressive Enhancement)
|
||
remain draft/hypothesis; no candidate, relation or maturity change. The
|
||
existing "Zulip auxiliary target/anchor callbacks... remain unverified" gap
|
||
is sharpened, not resolved: it is now specifically "no captured-current-list
|
||
guard exists on these two paths, and no test covers the gap," rather than a
|
||
general unknown. Independent full-contract use counts stay unassessed.
|
||
|
||
## Scope closure and follow-up
|
||
|
||
Four files, all already implicated by ZN07; no further callees were fetched.
|
||
Files were inspected at the manifest's ranges except the two full test files,
|
||
which were short enough to review completely. No upstream code or frozen
|
||
evaluation artifact changed. Tests were read, not executed.
|
||
|
||
[Effort receipt](../evolution/cycles/2026-09-27-zulip-target-lookup-effort.json)
|
||
records active source work; token/cost unknown. This is the second cycle in
|
||
comparison group B-independent-navigation-source-v1 (with LEARN-0014); still
|
||
short of the three comparable cycles WP-0006 T03 needs before a returns review
|
||
of this specific group, and the workplan's own rule against manufacturing
|
||
cycles to reach that count still applies — a third cycle here should only
|
||
happen if a genuinely useful next question arises, not to close the count.
|
||
|
||
WP-0003 T01 follow-up, if pursued: before any defect or race claim, a
|
||
preregistered reproduction would need to actually run Zulip's frontend code
|
||
(not just read it) with a controlled clock/interception to hold the target-lookup
|
||
or anchor-fetch response while installing a competing navigation — outside the
|
||
static-source protocol used here. T02 retains independent human/AT and offline-case
|
||
obligations. WP-0006 T03 retains review-date/comparable-window work; the local
|
||
authored navigation fixture remains paused.
|