Uh oh!
There was an error while loading. Please reload this page.
Resolve a bundle's thread out of app_bundle_url - #301
Merged
Conversation
A bundle posting's app_url names its sender's contact page, so the topic_id resolved out of it was always zero and hey box view --json served bundle rows with no way to read the thread they open. haystack's bundle_posting route points app_bundle_url at a topic exactly when the bundle holds one unseen thread — the thread the row opens in the web app. mail.TopicIDOf now tries app_url and falls back to app_bundle_url, so those rows carry a usable topic_id in the CLI's listings and the TUI alike. A bundle with several unseen threads (or none) still answers zero, and the skill now says what a bundle row is, that topic_id can be absent on one, and to reach the bundled mail through hey contacts unbundle rather than passing the box item id to hey threads. Surfaced by #157, which documented the workaround; resolving it in the CLI keeps the skill's contract (every posting names its thread where one exists) true instead.
Contributor
There was a problem hiding this comment.
Pull request overview
Resolves thread IDs for single-thread bundles via app_bundle_url, aligning CLI, TUI, and agent guidance.
Changes:
- Adds shared bundle-aware topic ID resolution.
- Tests URL precedence and bundle cases.
- Documents bundle behavior and recovery options.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
internal/mail/posting.go | Adds bundle-aware topic resolution. |
internal/mail/posting_test.go | Covers resolution scenarios. |
internal/cmd/sdk.go | Reuses the shared resolver. |
skills/hey/SKILL.md | Explains bundle IDs and alternatives. |
AGENTS.md | Updates architectural guidance. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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 freeto 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.
A bundle posting's
app_urlnames its sender's contact page, soresolvePostingTopicIDalways answered zero for one:hey box view --jsonserved bundle rows with notopic_id, and nothing said how to reach the thread such a row opens. The skill meanwhile documentedtopic_idon every posting, so an agent following it into a bundle either got a missing field with no guidance or substituted the box itemid— whichhey threadsanswers withnot_found.haystack's
bundle_postingroute (config/routes.rb) pointsapp_bundle_urlat a topic exactly when the bundle holds one unseen thread — the thread the row opens in the web app. Several unseen threads get a bundle view of their own; none gets the contact page. So the URL fallback is faithful to what clicking the row does.Code:
mail.TopicIDOfresolves a posting's thread out ofapp_url, falling back toapp_bundle_url.mail.NewPosting(the TUI's path) andresolvePostingTopicIDininternal/cmd(every--json/styled/markdown listing) both use it, so a one-unseen-thread bundle now carries a usabletopic_ideverywhere. A bundle with several unseen threads, or none, still answers zero.Skill: the Boxes section now says what a bundle row is (
nameis the•-joined subjects), thattopic_idcan be absent on one, that a present one opens as that thread, and that the remedy for an absent one ishey contacts unbundle <contact_id>(contact in the posting'scontacts) or the HEY app — never the box itemid. The Threads ID note points at it.AGENTS.md: the "a bundle's URL names a contact instead and answers zero" sentence now describes the fallback.
Supersedes the guidance half of #157, which documented this as a manual workaround for agents; resolving it in the CLI keeps the skill's contract true instead. Related: #156 remains the gap for bundles of several unseen threads — there is still no command that lists the topics inside one.
The two
internal/tuicalendar test failures (TestDayLabelsCoverTodosAndHabits,TestWeekDrawsTheHabitsKeptEachDay) pre-exist on main and are unrelated.Summary by cubic
Resolve a bundle posting’s thread from
app_bundle_urlwhenapp_urlpoints to a contact, so bundle rows expose a usabletopic_idwhere one exists. Previously we parsed onlyapp_url, so bundles always returnedtopic_id = 0andhey box view --jsonoffered no way to reach the thread the row opens.mail.TopicIDOf: returnsTopicIDIn(app_url)or falls back toTopicIDIn(app_bundle_url). A bundle with one unseen thread now emits its thread ID; bundles with several unseen threads or none still return 0.TopicIDOfinmail.NewPostingandinternal/cmd’sresolvePostingTopicID, so CLI listings (--json/styled/markdown) and the TUI agree.skills/hey/SKILL.mdandAGENTS.mdto explain bundle rows, thattopic_idcan be absent, and to usehey contacts unbundle <contact_id>rather than passing a box itemidtohey threads.Written for commit cf5936d. Summary will update on new commits.