Repository navigation
fix(messages): pick the latest message with the shared ordering - #2403
Merged
Merged
Conversation
The newest-message lookup behind Up-to-edit and react-to-latest compared whole-second createdAt and kept the first row on a tie, so in a same-second burst it picked an earlier message. Use compareMessages, the rendered order (send ms, then id). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Cheng Peng <cheng@block.xyz> Co-authored-by: Cursor <cursoragent@cursor.com>
Follow the mounted-test conventions in docs/contributing.md: render with reactStrictMode and find the list by its region role instead of a test id. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com> Signed-off-by: Cheng Peng <cheng@block.xyz>
cheng-block
marked this pull request as ready for review
November 4, 2026 23:05
This was referenced Nov 4, 2026
joahg
approved these changes
Nov 5, 2026
joahg
left a comment
Member
There was a problem hiding this comment.
🤖 compareMessages matches the timeline order and the same-second test covers the tie cases. Merge state is BLOCKED, presumably on required review/checks.
wesbillman
reviewed
Nov 5, 2026
wesbillman
left a comment
Collaborator
There was a problem hiding this comment.
🤖 Carl, an automated reviewer, commenting via Wes’s GitHub account.
No blockers. newest() now takes the maximum by compareMessages, so it agrees with the rendered timeline order for same-second bursts. A row without createdAtMs falls back to seconds × 1000, then id. Thread tree order doesn't affect the result, and membership notices are still skipped.
Validation: Court review of the source at this head plus green CI. No local runs.
wesbillman
approved these changes
Nov 5, 2026
wesbillman
pushed a commit
that referenced
this pull request
Nov 5, 2026
…2402) Scope: let ⇧Esc reach Mark all as read while focus is in the Channel settings panel. Non-goals: other Escape handlers and any change to the shortcut itself. Reuses the guard from the sibling panels. One file, 2 production lines. ## What was wrong With focus inside a channel's settings panel, ⇧Esc (Mark all as read, from #2170) closed the panel and marked nothing read. ## Why On `main`, the panel's `onKeyDown` in `src/bundled/channels/ChannelSettingsPanel.tsx:101` closes on any `Escape` from inside the panel and calls `event.stopPropagation()`. Since #2506 it skips events that are already handled or come from a portalled layer, but it still has no `!event.shiftKey` check. The sibling panels got that check in #2170 (`src/features/panels/PanelCard.tsx:32`, `src/bundled/channels/useChannelPanels.tsx:152`). Because the panel stops the event, the window shortcut dispatcher (`src/features/shortcuts/service.ts`, `src/app/shell/MarkAllReadShortcut.tsx`) never sees ⇧Esc. The fix adds the same `!event.shiftKey` guard and comment the siblings use, next to the #2506 checks. Plain Esc still closes the panel. ## How verified - Extended the existing Escape test in `src/bundled/channels/ChannelSettingsPanel.test.tsx`, "closes with Escape or its close button without swallowing other keys": ⇧Esc does not call `close` and reaches a `window` keydown listener; plain Esc still closes and does not reach it. It fails without the fix and passes with it. - `bin/pnpm vitest run src/bundled/channels/ChannelSettingsPanel.test.tsx`: 28 passed after merging `main`, including #2506's handled and portalled Escape test. - `bin/just iterate`: passed. ## In-app check Build: this PR at `4b286248b`, with #2403, #2404 and #2405 at their heads then, merged locally onto `main` at `cb8c971e9`. Run as the macOS desktop app with `BUZZ_DEV_VIEWER= just desktop`. The branch was later merged with `main` twice to resolve conflicts with #1624 (test file only) and #2506 (the same Escape handler). The production behavior of this fix did not change: ⇧Esc still passes through and plain Esc from a focused control in the panel still closes it. #2506 only adds its own skips for handled and portalled events. With the mouse alone, focus never lands inside the panel in the WebKit app, so these steps need macOS Keyboard navigation (System Settings > Keyboard) or Tab focus. 1. Open Channel actions > View channel details. Press Tab until a control in the panel shows a focus ring. Esc closes the panel. 2. Reopen the panel and Tab in again. ⇧Esc keeps the panel open and shows "Mark everything as read?". Where focus goes after opening the panel from the menu is a separate issue. buzz-review-completed --------- Signed-off-by: Cheng Peng <cheng@block.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
wesbillman
pushed a commit
that referenced
this pull request
Nov 5, 2026
…b-minimum window (#2405) Scope: fix how the main window restores its saved size after the monitor it was saved on is unplugged and the remaining monitor has another scale. Non-goals: storing the scale in the state file (tauri-plugin-window-state stays the only writer), plugin version changes, onboarding geometry. Production diff is 34 added and 10 removed lines in `src-tauri/src/window_state.rs`, plus tests. The case with several monitors left needs more logic and is handled in a follow-up PR that builds on this one. It and the remaining limitations are listed under Known gaps. ## What was wrong Quit Buzz with the window on a 1x external monitor, unplug it, and relaunch on a 2x laptop. The window comes back at half size, at or below the 480x400 minimum. The plugin then saves that small size, so later launches keep it. ## Why The plugin saves size in physical pixels. A 602x470 window saved at 1x is 301x235 points at 2x, below `minWidth`/`minHeight` in `tauri.conf.json`. macOS does not clamp a programmatic resize to the minimum (`tao` `platform_impl/macos/util/async.rs:88-101`), so it can open below it; a later user resize is clamped to it. - When no monitor contains the saved rect, `reachable_client` (`src-tauri/src/window_state.rs:215-220` on `main`) moves the window onto the primary monitor but keeps the physical size. - When the external sat to the right of the laptop, the saved rect can still fall inside the laptop's physical rect (3024 px wide at 2x), so it counts as reachable and is restored as saved. - In the fallback case, the planned rect is in the fallback monitor's physical pixels, but tao's macOS and GTK setters convert physical values with the window's current scale (`tao` 0.37.2 `platform_impl/macos/window.rs:702-731`, `platform_impl/linux/window.rs:472-510`). When Buzz starts on a monitor with another scale, the window lands at the wrong size and place. Separately, `restore_geometry_from` resized before it moved (`window_state.rs:158-159` on `main`). On Windows, moving to a monitor with another DPI rescales the window (`tao` `platform_impl/windows/event_loop.rs`, `WM_DPICHANGED`), which undoes a resize sent first. The plugin's own `restore_state` moves first. ## What changed - `restore_request` gets the fallback monitor's scale and the main window config, and stays pure. If the saved size is below the configured minimum at that scale, it was saved at a lower scale, and it gets the configured default size (1200x800) instead, still capped to the work area and horizontally centered at its top. The minimum is converted to pixels with the rounding tao uses. - On Windows tao applies the minimum to Buzz's undecorated frame, and the shadow insets come off the saved client (`tao` `platform_impl/windows/event_loop.rs:1783-1810`, `:2124-2130`), so a window at the minimum is saved below it (464x391 at 100%). The check there compares with that client: the rounded minimum minus the insets `calculate_insets_for_dpi` computes (`windows/util.rs:419-447`) with default metrics (4 px size frame plus 4 px padded border, scaled by DPI) and the Windows 11 top inset. - Windows 10 has no top inset, so its client is taller and still passes (a Windows 10 save up to that one pixel short of its own client also passes). Anything below the Windows 11 client is replaced, since tao does not clamp a programmatic resize on Windows either (it answers `WM_WINDOWPOSCHANGING` without `DefWindowProc`, `windows/event_loop.rs:1145`). - A sub-minimum size is replaced when the saved rect is unreachable, and also when only one monitor is left, since that monitor's scale is known even if the rect still falls inside it. With more monitors attached, a reachable rect is kept as saved. - A relocated window is sent in the fallback monitor's logical units on macOS and Linux. Windows keeps physical values, since its monitor and window positions are true physical pixels. - `set_position` now runs before `set_size`. ## Known gaps Handled in a follow-up PR that builds on this one (opens after this one merges): - Two or more monitors left after unplugging, with the saved rect still inside one of them. Example: a 2x laptop and a second 2x monitor remain after a 1x external is unplugged. The rect stays reachable, so this PR keeps it as saved and it opens at half size, at or below the minimum. The follow-up keeps a rect only if a monitor under its header has a scale at which the saved size meets the minimum, and still keeps a header that spans a 1x and a 2x monitor. Not fixed here or in the follow-up: - When the saved monitor is gone, its scale is unknown, so only a size that would fall below the minimum is replaced. A window saved at 960x800 pixels or larger on a 1x monitor is still at least the minimum at 2x and opens at half its points; the default 1200x800 opens at 600x400 points. A 2x window resized to 600x400 points saves the same numbers, so the two cannot be told apart. - Exact geometry when the saved monitor is still attached, the saved rect is reachable, and its scale differs from the monitor Buzz starts on. On macOS each monitor's physical rect is its point rect times its own scale, so rects overlap when scales differ. A 1x external to the right of a 2x laptop overlaps the laptop's 0 to 3024 px range, and a saved rect there has two valid readings, one per monitor. Monitor rects alone cannot tell which one was meant without the scale at save time, which only the plugin could store. - A Windows machine with a wider window border than the default (user-wide `SM_CXSIZEFRAME` / `SM_CXPADDEDBORDER`) saves a smaller client at the minimum, so a window dragged to the minimum there is reset to the default size. Reading tao's `get_frame_thickness` would cover it; this PR assumes the default. ## How verified - New tests: - `saved_size_is_kept_with_monitors_attached_and_recovered_on_a_lone_monitor` - `fallback_uses_default_size_when_saved_size_is_below_minimum_at_its_scale` - `minimum_allows_windows_shadow_insets_but_a_lower_scale_size_is_replaced`: Windows minimum clients at 100%, 125% and 200% are kept only on Windows, and one pixel under them is replaced everywhere. A 1x save of 900x750 on a 2x laptop is replaced everywhere. At 110 and 100 DPI the rounded minimum and the Windows client are checked on both sides. A minimum saved one step lower, including 225% to 250%, is replaced everywhere. - Existing `restore_request` tests now also check whether the window was relocated and at which scale. - Removing the lone-monitor rule, the default size, the fallback scale in the result, the rounding, either Windows inset, or the top inset each makes at least one of them fail. - The Windows branch was also run on macOS by forcing `cfg!(windows)` to true; CI runs only the host's branch. - `bin/cargo test -p Buzz --lib --locked window_state -- --skip onboarding_snapshot_is_kept_until_next_launch_restore`: 15 passed. That skipped test segfaults on macOS on unmodified `main` too. It calls `ns_window()` through `MockRuntime`. - `bin/cargo clippy -p Buzz --lib --tests --locked -- -D warnings`, `bin/cargo fmt --all --check` and `git diff --check` pass. - MockRuntime geometry setters are no-ops, so neither the call order nor the logical-unit conversion has automated coverage. The in-app check below covers the macOS fallback path. The Windows inset values follow tao's code and the default metrics but were not measured on a Windows machine. ## In-app check Build: this PR at `94bc460e5` (later commits only rerun CI and merge `main`; no code change), #2402, #2403 and #2404, merged locally onto `main` at `cb8c971e9`. Run as the macOS desktop app with `BUZZ_DEV_VIEWER= just desktop` on a 2x laptop with two 1x external monitors. Window bounds were read from the window server (`CGWindowListCopyWindowInfo`), not by eye. - Setup: I quit with the window on a 1x external. The saved state was 1201x714 pixels, below the 800 pixel minimum height at 2x. Then I unplugged both externals, leaving only the 2x laptop (1728x1117 points). - First relaunch: Buzz used the one-shot onboarding snapshot instead (`onboarding-window-restore.json` is used once, then deleted). That snapshot was about 1720x1410 pixels at 1x, so the window opened at 860x705 points. That is above the minimum at half the points, the first "Not fixed here or in the follow-up" case under Known gaps. - I quit, put back the state saved on the 1x monitor, and relaunched. The window opened at 1200x800 points at (264, 33), horizontally centered under the menu bar. buzz-review-completed --------- Signed-off-by: Cheng Peng <cheng@block.xyz> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
wesbillman
pushed a commit
that referenced
this pull request
Nov 5, 2026
…menu (#2526) Scope: after Channel actions > View channel details, leave focus on the Channel Settings tab so Escape closes it. Non-goals: the settings panel's own Escape handling (#2402), the shared menu, and the `finalFocus` helper. Reuses the menu's existing `finalFocus` ref, which Move channel > Create new already sets. One file, 12 production lines added and 1 removed. ## What was wrong In the macOS app, opening View channel details from the Channel actions (…) menu with the mouse left focus on the … button. Escape then ran the Escape mark-read shortcut instead of closing the settings tab. Escape closed the tab only after Tab moved focus into the panel. ## Why The menu item only called `openDetails()` (`src/bundled/channels/ChannelHeaderMenu.tsx:88` on `main`). The menu's `finalFocus` then returns focus to the trigger when the popup unmounts after its exit animation. `ChannelsPage` (`src/bundled/channels/ChannelsPage.tsx:1756`) and `PanelWorkspace` (`src/features/panels/PanelWorkspace.tsx:298`) focus the settings tab first. But `useFinalFocusUnlessMoved` counts focus on the page body or in the closing menu as "not moved" (`src/shared/design-system/ui/finalFocus.ts:59-65`). In WebKit the tab can lose focus to the body before the menu unmounts, so the trigger wins. With focus in the main pane header, the side pane's Escape handler (`PanelWorkspace.tsx:376`) never sees the key. `docs/channels.md:412-414` says View channel details opens or focuses the Channel Settings tab. The fix sets `finalFocus.current` for that item to the settings tab (`[data-panel-workspace] [data-tab-value="settings"]`, the selector `ChannelsPage` already uses). If no such tab exists, it falls back to the trigger as before. The shared "moved" check is unchanged, so focus the person moved elsewhere still stays put. ## How verified - New test in `src/bundled/channels/ChannelHeaderMenu.test.tsx`, "hands focus to the opened settings tab when the menu finishes closing after focus drops to the body". It mounts the real menu and a `PanelWorkspace` with a closable settings tab, holds the menu's exit with a `getAnimations` stub, clicks the item with `userEvent`, and blurs to the body before the menu unmounts. Then it asserts the tab has focus and Escape calls `close`. It fails on `main` with focus on the Channel actions button and passes with the fix. The path where focus stays on the tab is already covered by `tests/browser/channel-settings.spec.mjs` and `channel-header-menu.spec.mjs` in Chromium and WebKit. - `bin/pnpm vitest run` on `ChannelHeaderMenu`, `PanelWorkspace`, `ChannelSettingsPanel`, and the five `ChannelsPage*` test files: 144 passed. - `bin/just iterate`: passed on the first commit. The second commit changes only the test. - jsdom and the Playwright click do not reproduce the WebKit focus drop, so the test forces it with the blur. - Hook exception, with my OK: I pushed with `LEFTHOOK_EXCLUDE=types-and-related-unit-tests`, so that one pre-push job was skipped and the other pre-push jobs ran. On the current head that job's type check passed and its full Vitest run failed only two `ChannelMembersDialog.test.tsx` tests (2 failed, 17351 passed). Those two fail the same way at `40401e4d8` without this change, both locally and in `main` CI (`JavaScript (4/4)`). ## In-app check Found in hand testing on the macOS app, and verified there with this fix. Build: this PR at `75fef69f8` (later commits only rerun CI and merge `main`; no code change) with #2402, #2403, #2404 and #2405, merged locally onto `main` at `cb8c971e9`. Run as the macOS desktop app with `BUZZ_DEV_VIEWER= just desktop`. 1. In a regular channel, open Channel actions (…) > View channel details with the mouse. 2. Press Esc right away, without clicking anything else or pressing Tab. 3. The Channel Settings tab closes. Before the fix, it stayed open and Esc marked the conversation as read. buzz-review-completed --------- Signed-off-by: Cheng Peng <cheng@block.xyz> Co-authored-by: Cursor <cursoragent@cursor.com>
wpfleger96
added a commit
that referenced
this pull request
Nov 5, 2026
…-service * origin/main: (29 commits) fix(agents): treat what an agent reads as information, and give answering rules only to mentions (#2456) feat: add up arrow message editing preference (#2659) fix(channels): move focus to channel settings opened from the header menu (#2526) fix(window): restore position before size and never fall back to a sub-minimum window (#2405) fix(messages): pick the latest message with the shared ordering (#2403) fix(channels): let shift+escape through the channel settings panel (#2402) fix(builderlab): clarify signed-out agent settings (#2602) feat(builderlab): preselect the catalog's recommended model (#2552) feat(host-process): set BUZZ_VERSION for every spawned process (#2674) fix(agents): finish Delete for a synced agent with no config (#2692) ci(flake-check): select related tests within a 9-minute lane budget (#2607) feat(macos): show the closed window again when Buzz becomes active (#2658) fix(channels): show channel actions as soon as the menu opens (#2657) fix(huddles): confirm before starting a huddle from the header (#2687) perf(composer): reuse per-keystroke projections, snapshots and reference scans (#2590) perf(agents): skip the activity snapshot compare for unrelated channel traffic (#2586) test(solo): wait for the live header before opening a joined viewer's access (#2599) fix(messages): show who reacted in channels where you can't react (#2601) feat(agents): clarify remote execution across desktop (#2600) perf(relay): size paged history with the cached per-event sizes (#2597) ... # Conflicts: # src-tauri/src/host_process/tests.rs
wesbillman
pushed a commit
that referenced
this pull request
Nov 5, 2026
Scope checkpoint: end bare `http(s)://` and `buzz://` links at fullwidth and CJK punctuation in rendered messages and in `messageLinkParts`, keep CJK letters inside URLs, and leave explicit `<url>` and `[label](url)` links unchanged. Non-goals: links without a scheme (`www.`), Markdown link syntax, link previews. Files: `message-link-parts.ts`, `MessageMarkdown.tsx`, their tests, and one line in `docs/buzz-help.md`. Budget: about 30 production lines (actual +30/−1). ## What was wrong A bare link followed by Chinese or Japanese punctuation took the punctuation and any text after it into the link. `https://github.com/block/buzz-app。` opened `…/buzz-app%E3%80%82` (404). `看这个 https://example.com/spec,大家看一下` linked the whole rest of the sentence. `(https://example.com)` opened the host `example.com)`, because the URL parser folds `)` to `)`. ## Why Two places find bare URLs, and both end them only at whitespace: - Rendered messages get bare links from `remark-gfm`. Its `findUrl` transform (`mdast-util-gfm-autolink-literal` `lib/index.js:151`) reads up to the next space and trims only ASCII punctuation (`splitUrl`, line 241). `messageLinkParts` never sees these URLs; it only gets the text around them. - `messageLinkParts` (`src/features/messages/message-link-parts.ts:98`) drives the composer link decorations, chat cards and clipboard. Its bare URL pattern is `` [^\s<>"`]+ ``, so the same input fails there too. ## How verified - `message-link-parts.ts:98`: the bare URL pattern now also stops at fullwidth and CJK punctuation (`,。、.!?;:"'()[]{}「」『』《》【】〈〉〔〕〖〗〝〞〟。「」、…~〜`) and curly quotes (`“”‘’`), which Chinese text uses as its quotes. CJK letters are still allowed, so `https://zh.wikipedia.org/wiki/中文` stays whole. - In the host name (before any `:`, `/`, `?` or `#`), `。.。` followed by an ASCII host label continue the link (unless another `://` follows that label), because the URL parser reads them as `.`: `https://github。com/block/buzz-app` still opens github.com, as on `main`. After the host name, including after a port, they end it. `\s` already covers the ideographic space. Bracket pairs need no extra trimming, because the match stops before them. - `MessageMarkdown.tsx:342`: a small remark step runs after `remark-gfm`. When `messageLinkParts` splits a literal autolink differently, the step replaces it with the parts `messageLinkParts` finds, so `https://a.com,https://b.com` becomes two links with the `,` between them as text. Links whose source is `<url>` or `[url](url)` keep their written destination. - `docs/buzz-help.md`: one bullet says where a bare http(s) or buzz link ends and that `<url>` keeps those characters. - New tests: `message-link-parts.test.ts` "ends a bare URL at fullwidth punctuation but keeps CJK letters" (18 cases, plus one that `https://a.com。https://b.com` gives two links), and `MessageMarkdown.test.tsx` "ends a bare link at fullwidth punctuation" (9 cases, checks the rendered `href`). 23 of the 28 fail on `main`. The other 5 guard IRIs, `<url>` and hosts written with `。`. - `bin/pnpm vitest run src/features/messages/message-link-parts.test.ts src/features/messages/MessageMarkdown.test.tsx`: 175 passed after the latest merge with `main`. - `bin/pnpm vitest run src/features/messages src/features/conversation src/features/relay src/bundled/links src/bundled/relay-notes`: 5574 passed, 4 skipped. - `bin/just iterate` (format, lint, typecheck, build): passed. - Known limits: GFM reads Markdown link syntax glued to a bare URL (`https://a.com,[label](https://b.com)`) as part of that URL, so the brackets still show, as they do on `main`. A bare URL that really contains one of these characters, such as a typographic `’` in a title, now ends there; `<url>` keeps it whole. A host followed by `。` and an English word (`https://example.com。OK`) still reads the word as part of the host, as on `main`. ## In-app check Build: this PR at `2dd13819d`, the SHA I tested by hand (the branch was later merged with `main` to resolve conflicts in `docs/buzz-help.md`; no behavior change), with #2402, #2403 and #2405, merged locally onto `main` at `cb8c971e9`. Run as the macOS desktop app with `BUZZ_DEV_VIEWER= just desktop`. I sent these nine lines as one message in a Solo chat. The link on each line covered exactly this text: | # | Line | Linked text | | --- | --- | --- | | 1 | `https://github.com/block/buzz-app。` | `https://github.com/block/buzz-app` | | 2 | `看这个 https://example.com/spec,大家看一下` | `https://example.com/spec` | | 3 | `https://a.com,https://b.com` | `https://a.com` and `https://b.com`, two links | | 4 | `https://zh.wikipedia.org/wiki/中文` | the whole URL | | 5 | `(https://example.com)` | `https://example.com` | | 6 | `<https://example.com/a。b>` | the whole URL | | 7 | `https://github。com/block/buzz-app` | the whole URL | | 8 | `http://localhost:3000。OK` | `http://localhost:3000` | | 9 | `https://github。com:/block/buzz-app` | the whole URL | Clicking lines 1 and 7 opened github.com/block/buzz-app, with no `%E3%80%82` in the address. ## Screenshots The same message in the installed 0.7.8 release and in the build above. In 0.7.8, lines 1, 2, 3 and 5 take the punctuation into the link, and line 8 is not linked at all. | Before (0.7.8) | After (this PR) | | --- | --- | | <img width="360" alt="Before, Buzz 0.7.8: the nine test lines, with punctuation inside links on lines 1, 2, 3 and 5 and line 8 not linked" src="https://github.com/user-attachments/assets/064dd7ff-cde4-476e-887c-7310288254d2" /> | <img width="360" alt="After, this PR: the nine test lines, with each link covering the text listed in the table above" src="https://github.com/user-attachments/assets/9569922c-3365-4df4-97ae-b469faab0637" /> | buzz-review-completed --------- Signed-off-by: Cheng Peng <cheng@block.xyz> Co-authored-by: Cursor <cursoragent@cursor.com>
baxen
added a commit
that referenced
this pull request
Nov 6, 2026
* origin/main: (36 commits) perf(mentions): rank only the shown rows and skip empty-query Space work (#2298) fix(messages): stop bare links at fullwidth and CJK punctuation (#2404) fix(agents): treat what an agent reads as information, and give answering rules only to mentions (#2456) feat: add up arrow message editing preference (#2659) fix(channels): move focus to channel settings opened from the header menu (#2526) fix(window): restore position before size and never fall back to a sub-minimum window (#2405) fix(messages): pick the latest message with the shared ordering (#2403) fix(channels): let shift+escape through the channel settings panel (#2402) fix(builderlab): clarify signed-out agent settings (#2602) feat(builderlab): preselect the catalog's recommended model (#2552) feat(host-process): set BUZZ_VERSION for every spawned process (#2674) fix(agents): finish Delete for a synced agent with no config (#2692) ci(flake-check): select related tests within a 9-minute lane budget (#2607) feat(macos): show the closed window again when Buzz becomes active (#2658) fix(channels): show channel actions as soon as the menu opens (#2657) fix(huddles): confirm before starting a huddle from the header (#2687) perf(composer): reuse per-keystroke projections, snapshots and reference scans (#2590) perf(agents): skip the activity snapshot compare for unrelated channel traffic (#2586) test(solo): wait for the live header before opening a joined viewer's access (#2599) fix(messages): show who reacted in channels where you can't react (#2601) ... # Conflicts: # docs/buzz-help.md
nathan-thillairajah
added a commit
that referenced
this pull request
Nov 6, 2026
…page * origin/main: (49 commits) fix(browser): recheck the FI session when the app viewer exchange returns 401 (#2678) fix(members): find verified people who have no profile (#2554) fix(dev): clear React's dev render measures so long sessions do not grow (#2495) Move app navigation to a dock and unify conversation headers (#2626) perf(mentions): rank only the shown rows and skip empty-query Space work (#2298) fix(messages): stop bare links at fullwidth and CJK punctuation (#2404) fix(agents): treat what an agent reads as information, and give answering rules only to mentions (#2456) feat: add up arrow message editing preference (#2659) fix(channels): move focus to channel settings opened from the header menu (#2526) fix(window): restore position before size and never fall back to a sub-minimum window (#2405) fix(messages): pick the latest message with the shared ordering (#2403) fix(channels): let shift+escape through the channel settings panel (#2402) fix(builderlab): clarify signed-out agent settings (#2602) feat(builderlab): preselect the catalog's recommended model (#2552) feat(host-process): set BUZZ_VERSION for every spawned process (#2674) fix(agents): finish Delete for a synced agent with no config (#2692) ci(flake-check): select related tests within a 9-minute lane budget (#2607) feat(macos): show the closed window again when Buzz becomes active (#2658) fix(channels): show channel actions as soon as the menu opens (#2657) fix(huddles): confirm before starting a huddle from the header (#2687) ... Signed-off-by: Nathan Thillairajah <nthillairajah@squareup.com> # Conflicts: # docs/browser.md # src-tauri/src/browser/builderlab.rs
nathan-thillairajah
added a commit
that referenced
this pull request
Nov 6, 2026
…dylan/browser-unavailable-page * origin/dylan/browser-denied-page: (49 commits) fix(browser): recheck the FI session when the app viewer exchange returns 401 (#2678) fix(members): find verified people who have no profile (#2554) fix(dev): clear React's dev render measures so long sessions do not grow (#2495) Move app navigation to a dock and unify conversation headers (#2626) perf(mentions): rank only the shown rows and skip empty-query Space work (#2298) fix(messages): stop bare links at fullwidth and CJK punctuation (#2404) fix(agents): treat what an agent reads as information, and give answering rules only to mentions (#2456) feat: add up arrow message editing preference (#2659) fix(channels): move focus to channel settings opened from the header menu (#2526) fix(window): restore position before size and never fall back to a sub-minimum window (#2405) fix(messages): pick the latest message with the shared ordering (#2403) fix(channels): let shift+escape through the channel settings panel (#2402) fix(builderlab): clarify signed-out agent settings (#2602) feat(builderlab): preselect the catalog's recommended model (#2552) feat(host-process): set BUZZ_VERSION for every spawned process (#2674) fix(agents): finish Delete for a synced agent with no config (#2692) ci(flake-check): select related tests within a 9-minute lane budget (#2607) feat(macos): show the closed window again when Buzz becomes active (#2658) fix(channels): show channel actions as soon as the menu opens (#2657) fix(huddles): confirm before starting a huddle from the header (#2687) ... Signed-off-by: Nathan Thillairajah <nthillairajah@squareup.com> # Conflicts: # src-tauri/src/browser/builderlab.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scope: make the "newest message" lookup behind ↑ (select the newest message) and react to latest use the rendered message order. Non-goals: the same whole-second pattern in
ThreadPanel.tsxandunread.ts, which touches the unread contract and needs its own PR. ReusescompareMessages; one file, 2 production lines.What was wrong
When several messages land in the same second (agents do this a lot), ↑ in an empty composer and react to latest (⌘⇧) select an earlier message instead of the newest one.
Why
newest()insrc/features/messages/use-message-selection.ts:29comparedrow.createdAt > newest.createdAt.createdAtis whole seconds and the comparison is strict, so on a tie it kept the first row. The timeline renders rows bycompareMessages(src/features/relay/message-order.ts:17-24: send ms, then event id), so the first row of a same-second burst is the oldest. Thread rows render in tree order, so taking the last row is not a fix there.The fix calls
compareMessages(row, newest) > 0in the same reduce.ChannelMessagealready carriesid,createdAtandcreatedAtMs. No API change.How verified
src/features/messages/use-message-selection.test.tsxmounts the hook with React Testing Library in StrictMode and callsselectNewestMessage. It covers a same-second burst ordered by send ms, a same-second tie broken by id, and an ordinary cross-second case that skips membership notices.main(Expected: "a",Received: "z") and passes with the fix.pnpm vitest run src/features/messages/use-message-selection.test.tsx src/features/messages/message-selection.test.ts src/app/message-shortcuts.test.ts: 14 passed.just iterate(format, lint, typecheck, frontend build): passed.In-app check
mainatcb8c971e9. macOS desktop, started withBUZZ_DEV_VIEWER= just desktop.use-message-selection.test.tsx, which fails onmain.buzz-review-completed