Skip to content

NEXUS-4328: Keep selected messages unread across navigation - #3026

Closed
kazemek21 wants to merge 1 commit into
mainfrom
kylez_NEXUS-4328_preserve-selected-unread
Closed

kazemek21 wants to merge 1 commit into
mainfrom
kylez_NEXUS-4328_preserve-selected-unread

Conversation

@kazemek21

@kazemek21 kazemek21 commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR

The channel message menu's Mark unread action kept a selected message unread only during the current channel visit. This keeps the selected message and its loaded reply subtree available to existing unread selectors after leaving and reopening the channel during the current window session.

Why

Leaving a channel is not a read of the message selected in its menu. The current-window overlay already marks those exact messages, but the host dropped it on channel leave, so Unreads and other selectors could no longer find the mark.

What changed

  • Keep the existing exact selected-message and loaded-subtree overlay across channel leave; unrelated newer messages remain ordinary unread.
  • Retire only the covered selected marks after a successful explicit read in the same window, including Mark all read. Loaded deletion evidence, access retirement and session end also retire them; a host restart does not preserve them. The evicted-target deletion gap is noted below.

How

The existing in-memory overlay now retains each selected message's timestamp and thread scope across navigation and observation-cache eviction. Verified ancestry updates a provisional parent scope, including when ancestry arrives after eviction. A committed selection also survives cache eviction before its save returns; captured intent stays hidden while the save is pending and still honors admitted deletion and lifecycle fences. Only applicable verified deletion evidence retires a selection; the lookup of viewer-authored deletion references is not deletion authority. Explicit reads retire only the marks they cover after storage succeeds. This adds no persistence, schema, wire or sync-authority changes and no new public API.

Risk

This changes current-window message unread lifetime only. Native Buzz/Tauri behavior was not exercised by this worker, and the change does not update a running app.

Validation

  • At c2ba27dbb442e6741ef7f98cd97907fe353afe6b, all 213 focused unread tests passed. Three new fail-before cases prove that viewer-signed deletion references without target-author authority preserve committed and pending selections. Held-commit regressions cover navigation, overflow, admitted and forged deletion, plus pre-save invalidation. Overflow and admitted-deletion cases fail without their corresponding fixes. The suite also covers late nested ancestry before and after eviction. Mandatory precommit checks, touched-file Biome and git diff --check passed.
  • Existing cases cover navigation races, failed storage saves, explicit reads, Mark all read, observation eviction and scoped DM reads. No committed browser journeys were added or removed. A temporary synthetic capture journey passed on Chromium on Blox (one test, 4.4s test / 10.7s total); the scratch file is not part of this PR.
  • Current-head CI is pending. The prior Activity dock selector failures have a shared repair merged in #3144, now included by rebasing onto main. No selector fix was copied into this PR. An earlier WebKit timeout passed after a narrow retry; its cause remains unproven.
  • The host guard refused local typechecking due to machine load; hosted CI owns types and broad checks. The pre-push types-and-related-unit-tests, design-system and rust-clippy hooks were skipped because hosted CI runs those checks.
  • No native Buzz/Tauri host run was performed; packaged desktop navigation remains unproven. A host build/release is required before a running app contains the fix.

Screenshots

Synthetic Chromium app fixture at c2ba27dbb442e6741ef7f98cd97907fe353afe6b; the browser journey passed. This is browser-only evidence, not native Buzz/Tauri or installed-app verification.

Select Mark unread Navigate to Beta Return to Alpha: Mark read remains available
Message menu offers Mark unread Alpha remains bold while Beta is open Returned message menu offers Mark read

The harness also verified the selected message's forced-unread intent after returning. Menus were captured during their entrance transition.

Open review item

A selected message evicted before its author's deletion can return as forced unread when the original arrives again. Self-review identified this ordering; it is not fixed by the ancestry or committed-save corrections above. The proposed repair needs a narrowly scoped hook in the FOUNDATION relay session owner, which remains unchanged in this fix. This is an open correctness item, not a completed review disposition.

Bigger picture

This is a current-main host fix, separate from #427's relay-authoritative architecture, #2407/#2408's Inbox evidence work, #2401's viewer-thread Inbox membership, #2558's Inbox/navigation replacement, #2641's signed-peer read ordering, #2722's Activity and mention presentation, #2756's Option-click gesture, and #3009's seen-row receipt and background-entry behavior. It affects a running Buzz only after the normal host build and release that includes the merged change is delivered.

Fixes NEXUS-4328

Generated with Codex (gpt-6.1-sol) on behalf of kylez

@kazemek21
kazemek21 force-pushed the kylez_NEXUS-4328_preserve-selected-unread branch 4 times, most recently from bc66072 to 7ee6a39 Compare November 9, 2026 16:39
@kazemek21
kazemek21 marked this pull request as ready for review November 9, 2026 17:22
@kazemek21
kazemek21 requested a review from a team as a code owner November 9, 2026 17:22
@kazemek21
kazemek21 force-pushed the kylez_NEXUS-4328_preserve-selected-unread branch 3 times, most recently from 88bb937 to 67f1f90 Compare November 9, 2026 19:44

@gilgtm gilgtm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 No P0/P1 findings in the deep-dual review of 67f1f90cec714395bfe98e4cc95ed3d9c02bbfa4.

Posted by duel-review skill [driver: codex(high), mode: deep-dual, review synthesized by codex(high)]

@dr0uleau dr0uleau left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 two P2 findings in the selected unread lifecycle

Updated by Codex 🤖

Comment thread src/features/relay/unread.ts Outdated
// observation reset is not a read of the selected message.
for (const [channelId, ids] of forcedMessages) {
for (const id of ids.keys())
if (tombstones.has(id) || retracted.has(id)) ids.delete(id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 [P2] should we retain verified target evidence so deletion can retire an evicted selection? after the observation cache resets, accept() resolves deletion ownership only from incoming and events. it drops the author's deletion when the original is absent. session visibility can reject it earlier if its caches also lack the target. retracted covers only the viewer's deletions. when the original message returns, its retained mark forces it unread again. keep enough verified evidence to admit an authorized deletion and retire the selected mark, with the existing access and author checks intact

Updated by Codex 🤖

Comment thread src/features/relay/unread.ts Resolved
@dr0uleau
dr0uleau requested a review from a team November 9, 2026 22:23
@kazemek21
kazemek21 force-pushed the kylez_NEXUS-4328_preserve-selected-unread branch 2 times, most recently from 1f648d7 to 6f927d4 Compare November 9, 2026 23:53
@tellaho

tellaho commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

🤖 Could you add a screenshot or short video of the change to the PR? Design uses these for faster reviews.

Co-authored-by: Codex <codex@openai.com>
Signed-off-by: Kyle Zemek <kylez@squareup.com>
@kazemek21
kazemek21 force-pushed the kylez_NEXUS-4328_preserve-selected-unread branch from 6f927d4 to c2ba27d Compare November 10, 2026 00:15
@kazemek21

Copy link
Copy Markdown
Contributor Author

Added the requested screenshots to the PR description: Mark unread, navigate away, and return with Mark read still available. The synthetic Chromium journey passed at c2ba27dbb442e6741ef7f98cd97907fe353afe6b and verified the retained forced-unread intent. This is browser-only evidence, not native Buzz/Tauri or installed-app verification. The deletion-after-eviction review item remains open.

— Posted by Codex (gpt-6.1-sol) on behalf of kylez

@kazemek21 kazemek21 closed this Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants