Skip to content

fix(desktop): show edited head content in thread panel - #6887

Merged
salman1993 merged 4 commits into
block:mainfrom
salman1993:smohammed/fix-thread-head-stale-edit
Aug 26, 2026
Merged

fix(desktop): show edited head content in thread panel#6887
salman1993 merged 4 commits into
block:mainfrom
salman1993:smohammed/fix-thread-head-stale-edit

Conversation

@salman1993

@salman1993 salman1993 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Why

Fixes #6886. In the channel timeline an edited message shows its newest body, but opening its thread panel showed the stale pre-edit body until the async thread-aux backfill landed (and permanently if it raced or missed).

Root cause

desktop/src/features/messages/lib/independentThreadPanel.ts built events = [head, ...replyEvents], discarding the head's aux overlay events (edits kind:40003, deletions, reactions) already present in the channel window. formatTimelineMessages only applies an edit when the edit event sits in the same array as its target, so the thread head relied solely on withThreadAux to re-supply it.

What

  • Carry the head's non-content aux events (via !isTimelineContentEvent) from the channel window into the thread head's event array, deduped against replyEvents by id.
  • Thread head is now byte-identical to the main timeline the instant the thread opens, independent of the async aux fetch.
  • Restricted to non-content kinds so reply content rows never leak in.

Tests

  • Unit regression independentThreadPanel.test.mjs (6 cases): red pre-fix ("two PRs"), green post-fix ("these PRs").
  • e2e thread-head-stale-edit.spec.ts: delays thread-replies aux to force the channel-window-vs-thread-aux divergence, then opens the thread via the reply button and asserts the panel head shows the edited body. Verified red pre-fix (stale message-thread-panel head), green post-fix.
  • Full desktop unit suite: 5562 pass / 0 fail. biome + tsc --noEmit clean.

Origin: Buzz channel fix-reply-thread-old-view-bug (#464b982f-fc05-42c8-918f-409e1f05a1df).

The channel top-level timeline renders a message's newest (edited) body,
but opening its thread panel showed the stale pre-edit body until the
async thread-aux backfill landed (and permanently if it raced/missed).

buildIndependentThreadPanel extracted a single head content event by id
from the channel window and built events = [head, ...replyEvents],
discarding the head's aux overlay events (edits kind:40003, deletions,
reactions) that already sit beside it in that same window.
formatTimelineMessages only applies an edit when the edit event is in the
same array as its target, so the head relied solely on withThreadAux to
re-supply the edit — stale whenever that fetch lagged.

Carry the head's non-content aux events (via !isTimelineContentEvent) from
the channel window into the thread head's event array, deduped against
replyEvents by id. This makes the thread head byte-identical to the main
timeline the instant the thread opens, independent of the async aux fetch.
Restricted to non-content kinds so reply content rows never leak in.

Tests:
- Unit regression (independentThreadPanel.test.mjs): fails pre-fix
  ("two PRs"), passes post-fix ("these PRs").
- e2e (thread-head-stale-edit.spec.ts): delays thread-replies aux to force
  the channel-window-vs-thread-aux divergence; asserts the thread panel
  head shows edited content. Red pre-fix (stale head), green post-fix.
- Full desktop unit suite: 5562 pass / 0 fail. biome + tsc clean.

Signed-off-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz>
@salman1993
salman1993 requested a review from a team as a code owner August 26, 2026 15:32

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2c207987ae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread desktop/src/features/messages/lib/independentThreadPanel.ts
Comment thread desktop/tests/e2e/thread-head-stale-edit.spec.ts
Address the P1 review comment on block#6887: headAuxEventsFromChannelWindow
copied only aux events (#e-referencing the head), but a kind:5/9005 that
deletes one of those edits/reactions references the OVERLAY's id, not the
head's — so it was dropped. formatTimelineMessages skips an edit only when
the edit's own id is in deletedEventIds, so the thread panel resurrected a
deleted edit/reaction that the main timeline correctly hides, until the
async thread-aux backfill landed (or permanently if it failed).

Copy channel-window deletions that target the selected head aux events too,
mirroring the closure the aux-backfill paths build
(mergeAuxEventsWithDeletionBackfill). The existing replyEvents dedup covers
the copied deletions as well.

Also address the screenshot-timing comment: call waitForAnimations(page)
before the capture in thread-head-stale-edit.spec.ts, as AGENTS.md requires
before every Playwright screenshot.

Tests:
- Two new red/green unit cases in independentThreadPanel.test.mjs (deleted
  edit and deleted reaction on the head). Verified red pre-fix (2 fail),
  green post-fix.
- Full desktop unit suite: 5564 pass / 0 fail. biome + tsc clean.
- e2e thread-head-stale-edit.spec.ts: green; screenshot confirms the thread
  head shows the edited body while thread aux is still loading.

Signed-off-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz>
@salman1993

Copy link
Copy Markdown
Contributor Author

Tested through the Buzz app (desktop Playwright harness) at head 28a00e9. Thread-aux delayed 4s, thread opened via the reply button — the thread head shows the edited 3-PR body immediately, matching the main timeline, while replies are still loading. Bug fixed.

thread head shows edited body

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Carl, an automated reviewer, commenting via Wes’s GitHub account.

P1 — Make the pre-backfill E2E proof deterministic. The test delays get_thread_replies by 4 seconds, but both assertions use Playwright’s 5-second auto-retry window. On the old implementation, the panel can initially show the stale body, then the delayed relay response (which includes the root edit) arrives inside that retry window and the positive assertion passes. Navigation/opening time consumes part of the four seconds, so load makes the false-green path more likely. Gate the mocked response until after checking the initial panel state, or otherwise assert synchronously/with a deadline guaranteed to precede release. The unit test pins the pure helper, but this E2E is the only proof that the real async UI opens with the channel-window overlay rather than healing later.

Production logic at 28a00e9a518b1299991b96ebff4fa08a7c4222c2 otherwise looks sound: it carries direct head aux, includes one-hop deletion closure, filters content rows, and deduplicates against relay-provided thread aux.

Address Wes/Carl's P1: the spec delayed get_thread_replies by 4s but both
assertions used Playwright's 5s auto-retry window. On the buggy code the
panel first shows the stale body, then the delayed relay response (which
carries the root edit) lands inside the retry window and heals it, so the
positive assertion can false-green. Navigation time eats into the 4s,
making the false-green path more likely under load. The delay is a timer
that self-heals; it does not prove the head opens with the channel-window
overlay rather than healing later.

Replace the timer with a manual gate. New `deferThreadReplies` mock option
holds every get_thread_replies response open until the test calls
`__BUZZ_E2E_RELEASE_THREAD_REPLIES__()` — mirroring the existing
deferLinkPreviewMetadata / RELEASE_GET_EVENT queue pattern. The thread-aux
backfill provably cannot land before the assertion, so:
- head-edited assertion runs while the fetch is verifiably held
  (`__BUZZ_E2E_THREAD_REPLIES_PENDING__` poll guards against the fetch
  never having been dispatched);
- release the gate and re-assert the head stays edited (dedup path).

Verified red/green: reverting the production headAux carry in
independentThreadPanel.ts makes the spec fail deterministically at the
head assertion (received the stale "these two PRs?"); with the fix it
passes. tsc clean; 8/8 independentThreadPanel unit cases pass.

Signed-off-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz>

@salman1993 salman1993 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed the P1 (make the pre-backfill E2E proof deterministic).

The 4s threadRepliesDelayMs was a self-healing timer inside Playwright's 5s auto-retry window, so the delayed aux backfill could land mid-assertion and false-green on the buggy code. Replaced it with a manual gate: new deferThreadReplies mock option holds every get_thread_replies response open until the test calls __BUZZ_E2E_RELEASE_THREAD_REPLIES__() (mirrors the existing deferLinkPreviewMetadata / RELEASE_GET_EVENT queue pattern). The thread-aux backfill provably cannot land before the head assertion runs.

The spec now:

  1. asserts the head shows the edited body while the fetch is verifiably held (a __BUZZ_E2E_THREAD_REPLIES_PENDING__ poll guards against the fetch never having been dispatched, so the assertion can't pass by the request being absent);
  2. releases the gate and re-asserts the head stays edited (dedup path).

Verified red/green: reverting the production headAux carry in independentThreadPanel.ts makes the spec fail deterministically at the head assertion (receives the stale "these two PRs?"); with the fix it passes. tsc clean; 8/8 independentThreadPanel unit cases green.

Pushed as d051350.

Updated by Leo 🤖

@salman1993
salman1993 requested a review from wesbillman August 26, 2026 20:06

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Carl, an automated reviewer, commenting via Wes’s GitHub account.

The former P1 is resolved: the manual deferThreadReplies gate now prevents the aux backfill from landing before the decisive assertion, and the pending-request check proves the gated fetch was actually dispatched.

Blocking CI/code issue — format the changed E2E file. Desktop Core fails in just desktop-check because Biome would reformat the page.evaluate call at desktop/tests/e2e/thread-head-stale-edit.spec.ts:160-166. Apply the formatter and push the result. This is mechanical, but the current head does not pass the repository’s required desktop check.

Non-blocking: the post-release assertions can run before the released response is consumed, so they do not independently prove the post-backfill transition. The pure-helper dedup test covers that merge contract, so this does not block the fix.

CI Desktop Core failed on a biome format error in the E2E spec: the
page.evaluate arrow was wrapped across too many lines. Apply
`biome format --write` (whitespace-only, no logic change). All six
PR-touched desktop files now pass `biome check`.

Signed-off-by: Leo <5faf251baee50ee6bcde338aef6acdd70bb3e60115664c2cd490d94a55dfc488@buzz.block.builderlab.xyz>
@salman1993
salman1993 enabled auto-merge (squash) August 26, 2026 20:50
@salman1993
salman1993 merged commit eb8e97c into block:main Aug 26, 2026
25 checks passed
wpfleger96 pushed a commit that referenced this pull request Aug 26, 2026
…arer-auth

* origin/main:
  fix(desktop): show edited head content in thread panel (#6887)
  fix(desktop-tooltip): increase surface contrast (#6897)
  Deduplicate ACP thread prompt context (#6706)
  Apply access policy when reusing channel agents (#6838)

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
salman1993 added a commit that referenced this pull request Aug 26, 2026
…cp-sessions

* origin/main:
  fix(desktop): accent-colored mention badges that count thread mentions (#6900)
  Add Buzz benchmark evaluation layers (#6823)
  fix(desktop): show edited head content in thread panel (#6887)
  fix(desktop-tooltip): increase surface contrast (#6897)
  Deduplicate ACP thread prompt context (#6706)
  Apply access policy when reusing channel agents (#6838)
  feat(sidebar): prioritize unread DMs in overflow navigation (#6842)
  feat(projects): add agent and CLI project-home support (#6590)
  feat(desktop): restore message quick reactions (#6892)
  Use paired tags for standing & per-turn context (#6701)
  fix(cli): preserve signatures in event reads (#6884)
  refactor(db): finish replaceable event store extraction (#6777)
  Fix Admin feedback filter overflow (#6825)

Signed-off-by: Salman Mohammed <smohammed@squareup.com>

# Conflicts:
#	crates/buzz-acp/src/pool.rs
brow added a commit that referenced this pull request Aug 26, 2026
…ifications-pr

* origin/main:
  Add gated security reviews (#6816)
  fix(desktop): accent-colored mention badges that count thread mentions (#6900)
  Add Buzz benchmark evaluation layers (#6823)
  fix(desktop): show edited head content in thread panel (#6887)
  fix(desktop-tooltip): increase surface contrast (#6897)
  Deduplicate ACP thread prompt context (#6706)
  Apply access policy when reusing channel agents (#6838)
  feat(sidebar): prioritize unread DMs in overflow navigation (#6842)
  feat(projects): add agent and CLI project-home support (#6590)
  feat(desktop): restore message quick reactions (#6892)
  Use paired tags for standing & per-turn context (#6701)
  fix(cli): preserve signatures in event reads (#6884)
  refactor(db): finish replaceable event store extraction (#6777)
  Fix Admin feedback filter overflow (#6825)
  fix(desktop): stop pulsing addressed agents on send (#6873)
  fix(desktop): prioritize sidebar channel status (#6861)
  feat(desktop): hyperlink selected composer text on link paste (#6684)

Signed-off-by: Tom Brow <tomb@block.xyz>
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.

Thread panel shows stale pre-edit body when timeline shows edited content

2 participants