feat(poll): cast votes on native Messages polls#148
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 1, 2026, 1:15 AM ET / 05:15 UTC. Summary Reproducibility: not applicable. for a feature PR. The relevant verification is live macOS Messages delivery plus source review; the PR body now includes redacted CLI output, decode readback, and chat.db rows for delivered vote messages. Review metrics: 3 noteworthy metrics.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the additive vote surface after maintainer review of the private bridge construction, with focused CLI/RPC/store tests or documented validation for option resolution and a clear proof trail for macOS Messages delivery. Do we have a high-confidence way to reproduce the issue? Not applicable for a feature PR. The relevant verification is live macOS Messages delivery plus source review; the PR body now includes redacted CLI output, decode readback, and chat.db rows for delivered vote messages. Is this the best way to solve the issue? Likely yes, if maintainers want this feature in core. The patch is additive, preserves current poll-send behavior, returns option metadata through RPC, and follows existing poll/reaction bridge patterns; the main remaining decision is accepting the private native send surface and its validation burden. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 6a20d3a42cad. Label changesLabel changes:
Label justifications:
Evidence reviewedWhat I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
Adds `imsg poll vote` plus RPC `poll.vote` and the `send-poll-vote` bridge action, so the CLI can cast a vote on an existing native Messages poll. The vote is an associated message (associatedMessageType 4000, bare poll GUID) carrying the Polls balloon payload. Key detail: macOS 26 exposes no IMMessage initializer that carries a balloon payload AND an associated message together, so the vote is built with the associated-message initializer (the path reactions use, which persists the association) and the balloonBundleID/payloadData are then stamped onto the message's backing item. Option selection resolves a 1-based index or option text to the stable optionIdentifier via a new `MessageStore.pollOptions(guid:)`, and the resolved option label is returned so callers can suppress a redundant text reply that merely restates the vote.
|
Addressed the review findings and added real behavior proof to the PR body:
|
|
Maintainer review and fixups complete on Improvements:
Proof:
Known proof gap: this maintainer Mac has SIP enabled, and the online MacBook has Remote Login disabled, so I could not repeat the injected Messages send locally. CI plus the contributor's real final-path delivery proof cover the merge decision. |
feat(poll): cast votes on native Messages polls
What & why
imsgcould create and read native Apple Messages polls but not vote on them.This adds
imsg poll vote, thepoll.voteRPC method, and thesend-poll-votebridge action. Additive only — existing
poll send/poll.sendbehavior andits response are unchanged.
How it works
A vote is an associated message (
associatedMessageType 4000, bare poll GUID)that also carries the Polls balloon payload. macOS 26 exposes no IMMessage
initializer that carries a balloon payload AND an associated message together, so
the vote is built with the associated-message initializer (the path reactions use,
which persists the association) and
balloonBundleID/payloadDataare then stampedonto the message's backing item — the one place they survive
[chat sendMessage:].Option selection resolves a 1-based
--option-index, an--optiontext(case-insensitive), or a direct
--option-idUUID to the stable optionIdentifiervia a new
MessageStore.pollOptions(guid:), which also validates the poll existsand the option belongs to it. The CLI and the RPC response return the resolved
option id/text so callers can suppress a redundant reply that restates the vote.
Both paths normalize a
p:<part>/<guid>reference down to the bare poll GUID.Surface added
imsg poll vote --chat <c> --poll <guid> (--option-index N | --option TEXT | --option-id UUID)poll.vote/messages.poll.vote(response includesoption_id,option_text,poll_guid)send-poll-voteMessageStore.pollOptions(guid:)Evidence — real behavior proof (macOS 26.4.1, redacted)
Live CLI vote from this branch's build (GUIDs truncated, chat/handles omitted):
Decode readback of the sent vote:
Multiple live votes, from
chat.db(GUIDs truncated, handles omitted). Each vote isan associated message (type 4000) whose
assocGUID is the originating poll's GUID,delivered with no error:
swift build— Build complete.associated_message_type=4000linkedto the poll,
is_delivered=1 error=0, and decode back to the selected option.Addressed from ClawSweeper/Codex review
handleSendPoll; this PR no longer changespoll sendbehavior (additive only).poll.votenow returnsoption_id/option_text/poll_guid.unknown poll or an
option_idnot belonging to it, matching the CLI.Codex review (independent, gpt-5.4)
Reviewed to READY over 3 rounds (non-bare GUID,
--option-idvalidation, RPCvalidation all resolved). The private-API "associated init then stamp balloon
fields" construction was validated as sound.