Let a reply carry its subject - #134
Conversation
ReplyMessagePayload had only content, so every reply draft the SDK saved
showed as "No subject" in HEY's Drafts: the server permits
message[subject] on POST /entries/{id}/replies.json but never derives
one, and NewEntryReply hands back the prefilled "Re: …" subject with
nowhere to send it back.
Add an optional subject to ReplyMessagePayload in the Smithy model,
regenerate, and take it in the CreateReply and CreateReplyDraft
wrappers. An empty subject stays off the wire, leaving today's requests
byte-identical. Content remains the caller's reply body alone — the
server appends the quoted original at delivery via auto_quoting, so the
prefill's quoted content must not be echoed back.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
Pull request overview
Adds reply subjects throughout the Smithy-to-Go pipeline so saved reply drafts retain their prefilled subject.
Changes:
- Models and serializes optional reply subjects.
- Updates reply service signatures and tests.
- Extends conformance coverage.
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 8 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
spec/hey.smithy |
Models and documents reply subjects. |
openapi.json |
Regenerates the OpenAPI schema. |
go/pkg/generated/client.gen.go |
Regenerates Go payload types. |
go/pkg/hey/entries.go |
Accepts subjects in reply wrappers. |
go/pkg/hey/services_test.go |
Tests reply subject serialization. |
go/pkg/hey/drafts_test.go |
Tests draft reply subjects. |
go/pkg/hey/account_scope_test.go |
Updates the reply call signature. |
conformance/tests/paths.json |
Adds subject wire assertions. |
conformance/runner/go/main.go |
Passes subjects in conformance requests. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eebd6024ee
ℹ️ 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".
CreateReply now takes the subject before the body, which the quick-start still called in the old shape, and which apidiff rightly reports as an incompatible change — deliberately so, this being a v0.x SDK whose point here is the new parameter. Teach the gate about deliberate breaks via .github/apidiff-accepted: exact apidiff output lines, matched whole, so an entry names the old signature and can never mask a later accidental change reported from the new baseline.
* Send a reply as the sender the prefill resolved CreateReply and CreateReplyDraft always filled acting_sender_id from DefaultSenderID — the account's default sender — ignoring the sender HEY resolved for the thread. The reply prefill (NewEntryReply) picks its sender by inspecting the entry's own to and from addresses, so on a shared or alternate address (a HEY for Work support address, an extension, a forwarded external account) the SDK delivered every reply as the account owner instead: the wrong identity, every time. Take an actingSenderID on both wrappers, sent as acting_sender_id untouched; zero keeps today's DefaultSenderID fallback. Pass the prefill's Sender.Id — NewReply already hands it back, present exactly when it differs from the acting user. No wire or spec change: acting_sender_id was always sent, it just carried the wrong value. A deliberate v0.x signature break, acknowledged in apidiff-accepted alongside the reply-subject one (#134). * Carry the chosen sender through the draft lifecycle; pass ids through untouched DraftContent takes an ActingSenderID, so a draft saved as an alternate identity survives UpdateDraft and SendDraft instead of being handed back to the account default — HEY revises a draft from the whole of what is sent, acting_sender_id included. Resolution moves onto the Client, where every DraftContent consumer and both reply wrappers share it, and only zero means the default now: any other id reaches the server as given, an invalid one being the server's to reject rather than the SDK's to silently rewrite. The conformance CreateReply case asserts the id on the wire, and the README example checks the prefill read before leaning on it. * Exercise the reply wrappers in conformance at the HEY layer The generated-client case copies the fixture id into the request itself, so it proves nothing about the wrappers this change touches. Dispatch CreateReply and CreateReplyDraft through executeHEYOperation and assert the chosen acting_sender_id, subject, content and recipients on the wire from the hand-written layer. The zero fallback stays a unit-test concern: it resolves DefaultSenderID via an identity fetch, and requestBody assertions read the first recorded request. * Cover the draft lifecycle's sender carriage in conformance CreateDraft, UpdateDraft and SendDraft dispatch through executeHEYOperation now, each asserting the chosen acting_sender_id on the wire along with the lifecycle's status semantics: drafted on save and revision, omitted on the delivering send. * Carry cc and bcc through the reply conformance dispatch; honest README reply The reply dispatch dropped any cc or bcc a case supplied; all three recipient kinds ride now. The quick-start reply stops swallowing a failed prefill read and addresses the reply with the recipients the prefill resolved rather than a hardcoded one.
ReplyMessagePayloadmodeled onlycontent, so a reply had no way to carry a subject. HEY never derives one: the server permitsmessage[subject]onPOST /entries/{id}/replies.json, andNewEntryReplyhands back the prefilled"Re: …"subject — but the model provided nowhere to send it back, so every reply draft the SDK saved showed as "No subject" in HEY's Drafts.Change
spec/hey.smithy: optionalsubjectonReplyMessagePayload, with wire-format docs;openapi.jsonandclient.gen.goregenerated.EntriesService.CreateReplyandCreateReplyDrafttakesubjectahead ofcontent, mirroringMessagesService.Create. An empty subject stays off the wire (omitempty), leaving today's requests byte-identical.auto_quotingdefaults on), so the prefill's quoted content must not be echoed back — now documented on the payload.Tests
CreateReplycase now round-tripsmessage.subject.make checkgreen (MVP gate, 168/168 conformance).Breaking signature change to the two wrappers; hey-cli follow-up passes the prefill subject through: basecamp/hey-cli#370 (pins this branch's commit; bumps to the tagged release once this merges).
Basecamp: A reply can't carry a subject — every reply draft saves as "No subject"
Fixes the subject leg of basecamp/hey-cli#341.
Summary by cubic
Lets a reply carry its subject, so reply drafts no longer save as "No subject" in HEY's Drafts.
ReplyMessagePayloadgains an optionalsubjectthat stays off the wire when empty; content remains the caller's reply body alone.Breaking change
CreateReplyandCreateReplyDraftnow takesubjectbeforecontent; hey-cli passes the prefilled "Re: …" subject (Carry the reply prefill's subject onto every reply hey-cli#370)..github/apidiff-accepted, and the README example is updated.Written for commit 44894c6. Summary will update on new commits.