Skip to content

feat(desktop): surface buried agent-to-human replies to channel timeline#1057

Draft
wpfleger96 wants to merge 1 commit into
mainfrom
duncan/reply-surfacing
Draft

feat(desktop): surface buried agent-to-human replies to channel timeline#1057
wpfleger96 wants to merge 1 commit into
mainfrom
duncan/reply-surfacing

Conversation

@wpfleger96

Copy link
Copy Markdown
Collaborator

Summary

Buried agent-to-human replies from nested sub-threads now surface to the channel timeline as read-time pointer rows, inserted at the reply's own createdAt — where the reader's eye actually is. The real message is never moved or copied; clicking the pointer navigates down to the still-nested original and highlights it.

This is the read-side complement to the reply-routing tree: it makes mis-routed (buried) agent-to-human replies visible without altering routing or the underlying event graph.

Design

  • Pointer rows, not moves. surfaceReplies is a pure projection that identifies agent-to-human replies nested below the root and emits a synthetic pointer at each reply's own createdAt. The original message stays nested.
  • Merge by sort, not append. mergeSurfacedReplies concatenates pointers with the main timeline and sorts by (createdAt, id) on the real message id, so a pointer lands chronologically where the human is reading.
  • Separate component. SurfacedReplyRow reuses the thread-summary pill visual (rounded button, avatar, muted "replied" label) without forcing synthetic summary semantics through MessageThreadSummaryRow.
  • Navigate down. Clicking routes via goChannel(channelId, { messageId, threadRootId }) — opens the thread at its root, scrolls to and highlights the specific nested reply. threadRootId gates the root-event prefetch in ChannelRouteScreen so the open never dead-clicks when the thread root sits outside the loaded window. This is the same path search hits use.

Classification fail-safe

The human/agent classifier resolves unknown pubkeys to human (pubkey == null || !agentPubkeys?.has(pubkey)), so an unrecognized author under-surfaces rather than mis-surfaces — honoring the pure-core's documented contract end to end.

Quality

Both phases certified 9/9/9 on minimalism, elegance, and correctness across a two-pass review:

  • Phase 1 (pure core): cross-thread false-dedupe fixed via composite threadKey; empty-body and malformed-tag edge cases pinned; isRootLevel drift contract locked with a broadcast-branch test.
  • Phase 2 (render wiring): isHuman fail-safe restored end to end; threadRootId prefetch confirmed load-bearing for the distant-root navigate-down case.

just desktop-typecheck exit 0, just desktop-check clean (2 pre-existing unowned getMockProfile warnings), just desktop-test 734/734.

@wpfleger96

Copy link
Copy Markdown
Collaborator Author

Surfaced reply appears in timeline

Agent (alice) replies nested to a human's root message, mentioning the user — a pointer row surfaces in the channel timeline at the reply's own timestamp.

01-surfaced-reply-in-timeline

Multiple surfaced replies interleave chronologically

Two threads, each with an agent reply mentioning a human — both surface as pointer rows, sorted by createdAt among root messages.

02-multiple-surfaced-replies

Agent-to-agent reply does NOT surface

A nested agent reply that only mentions another agent — no pointer row appears. The surfacing filter correctly requires a human p-tag.

03-agent-to-agent-no-surface

Click navigates to nested message

Clicking a surfaced-reply pointer opens the thread panel and highlights the actual nested reply in context.

04-click-navigates-to-thread

wpfleger96 pushed a commit that referenced this pull request Jun 15, 2026
@wpfleger96 wpfleger96 force-pushed the duncan/reply-surfacing branch from 28322ec to 6682de8 Compare June 15, 2026 20:39
@wpfleger96 wpfleger96 marked this pull request as draft June 16, 2026 03:50
The channel timeline renders only root-level entries, making agent
replies to humans in threads invisible to the humans who read the
channel. Added a surfaceReplies projection that identifies nested
agent-to-human replies and a mergeSurfacedReplies helper that
interleaves pointer rows into the root timeline by (createdAt, id).

SurfacedReplyRow renders each pointer and navigates via the existing
goChannel({messageId, threadRootId}) mechanism — the same path search
hits use. De-dupe is strict body-match so an agent's unrelated earlier
root post cannot suppress a genuinely new nested reply. Agent-to-agent
replies are explicitly excluded.

Includes E2E screenshot spec covering the four key scenarios (surfaced
reply present, multiple replies interleaved, agent-to-agent suppressed,
click-to-navigate) and hardened settle() to race animation promises
against a 1s timeout so AbortError on cancelled animations can't tear
down the page.evaluate context mid-await.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 force-pushed the duncan/reply-surfacing branch from e4cb25c to d76caa9 Compare June 16, 2026 16:08
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.

1 participant