Reply to the last emailed message, not a note after it - #517
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Nil page handling can produce a misleading not-found result, and required help/API documentation remains incomplete.
Review effort: Balanced
Findings: 1
Open (3)
What changed in this PR
Updates reply and forward flows to target the latest replyable emailed entry, avoiding internal notes and share notices.
Changes:
- Adds centralized reply-target selection across CLI and TUI flows.
- Labels internal entries distinctly in terminal, Markdown, HTML, and TUI output.
- Adds documentation and regression coverage.
[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or rungh pr ready --undo.
Click "Ready for review" or rungh pr readyto reengage.
| File | Description |
|---|---|
AGENTS.md |
Documents reply-target and entry-label behavior. |
docs/cli.md |
Updates thread and reply documentation. |
skills/hey/SKILL.md |
Updates agent guidance. |
internal/mail/reply_target.go |
Implements replyable-entry selection and labels. |
internal/mail/reply_target_test.go |
Tests target selection and kinds. |
internal/mail/entry.go |
Preserves entry kinds for presentation. |
internal/cmd/thread_reply.go |
Uses the shared target resolver. |
internal/cmd/forward.go |
Forwards the latest emailed message. |
internal/cmd/reply.go |
Updates reply help text. |
internal/cmd/topic.go |
Labels internal entries across output formats. |
internal/cmd/html_test.go |
Updates HTML expectations. |
internal/cmd/thread_internal_entries_test.go |
Adds CLI regression tests. |
internal/tui/compose.go |
Applies reply-target selection in TUI flows. |
internal/tui/mail.go |
Labels internal entries in the TUI. |
internal/tui/compose_internal_entries_test.go |
Adds TUI regression tests. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
robzolkos
force-pushed
the
reply-skips-notes
branch
from
October 1, 2026 00:29
c1214fa to
bce0924
Compare
On HEY for Domains a thread also holds notes (kind "comment") and share notices (kind "access_notice"). Neither is emailed, but `hey reply`, `hey forward`, `hey compose --thread-id` and the TUI's reply and forward all answered the thread's last entry. When that was a note, the reply went to the teammate who wrote it under a bare "Re:", and a forward handed an outsider what the team wrote to itself. They now answer HEY's Topic#last_replyable_entry through mail.ReplyTarget: the newest message, announcement or sign-up message, found on the topic's own page or further down the entry index. A thread with none is refused. The text formats and the TUI's thread view head a note "Note by …" and a share notice "… shared this thread", each tagged as not emailed, instead of a From line; --html carries each entry's kind as data-kind. JSON is unchanged: it already carried kind.
A nil page from the entry index is a malformed response, as threadload treats it, not a thread without a message, so it no longer surfaces as not_found. hey compose --thread-id and hey forward say in their help that they answer the latest emailed message, and API-COVERAGE.md lists the entry index's new readers.
robzolkos
force-pushed
the
reply-skips-notes
branch
from
October 1, 2026 13:52
bce0924 to
a70d2e4
Compare
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.


On HEY for Domains a thread can hold internal notes (
kind: "comment") and share notices (kind: "access_notice", posted by "Share with…"). Neither is emailed. Buthey reply,hey forward,hey compose --thread-idand the TUI's reply and forward all answered the thread's last entry. When that was a note, the reply was addressed from the note — on a local HEY,hey reply <thread> --dry-runcame backTo: david@basecamp.com(the teammate who wrote the note) under a bareRe:instead of to the prospect. A forward was worse: it would quote the team's internal note to whoever it went to.HEY's web app answers
Topic#last_replyable_entry— the newest active entry whose entryable saysreplyable?, which onlyMessage,AnnouncementandSignUp::Messagedo. The forward and bulk-reply paths in HEY use the same entry.What changes
mail.ReplyTarget(internal/mail/reply_target.go) mirrors that: it picks the replyable entry with the highest id (HEY'sreverse_orderon an unordered relation is id-descending) from the topic's own page, and only when that page has none walksTopics().GetEntriesPagenewest first, bounded at 20 pages. A thread with nothing replyable is refused (not_found, "thread N has no emailed message to reply to") rather than answered from a note. An entry with nokindis treated as a message, as every entry was before HEY served one.hey reply(send, draft and--dry-run),hey compose --thread-id,hey forward, and the TUI'sloadReplyContextandloadForwardContext. The reply prefill (Entries().NewReply) and the forward draft are requested for that entry only.hey bulk-replywas already right: HEY resolves its entries server-side withlast_replyable_entry.--markdown, styled output,--htmland the TUI thread view head a note "Note by Priya Raman" and a share notice "Marcus Lee shared this thread", each tagged not emailed, instead of aFrom:line.--htmlalso carries each entry'skindasdata-kind. JSON is unchanged — it already carriedkind.hey reply/hey thread readhelp and agent notes,docs/cli.md,skills/hey/SKILL.md,AGENTS.md.Seen on a local HEY (dry run only)
Before:
After:
hey thread read --markdown, after:hey thread read --jsonis byte-for-byte the same before and after.One gap in HEY's JSON
A share notice's web view says who was added ("David added Andrea to this thread"), but neither the entry nor the message JSON carries those contacts (
Access::Notice#contacts_with_access), so the label names only who shared. Also, HEY'slast_replyable_entrylooks only at active entries, while the entry index serves spam entries too and does not say which are which; the CLI cannot exclude a spam-marked message inside an otherwise active thread.Independent of #383 (
hey thread comment), which touches some of the same docs; the doc edits here are kept small and separate so either rebases cleanly onto the other.Summary by cubic
Reply and forward now answer a thread's last emailed message — as HEY's web app does — instead of its last entry. A note or share notice posted after it used to be replied to (going to the teammate who wrote it) or forwarded out (leaking an internal note); a thread holding nothing else is refused.
mail.ReplyTargetpicks HEY's last replyable entry (message, announcement, or sign-up message) from the topic's own page, walking the entry index only when that page has none; a missing entry page is treated as a malformed response.--markdown, styled,--html, and the TUI thread view head notes ("Note by …") and share notices ("… shared this thread") as never emailed instead of with a From line; HTML carries each entry'skindasdata-kind. JSON is unchanged.Written for commit a70d2e4. Summary will update on new commits.