Repository navigation
Catch-up 4/4: email replies, docs, fixture cleanup - #55
Merged
Merged
Conversation
…ester A comment posted with emailToRequester also goes to the sender whose email opened the ticket, in their thread. The DO records who it went to on the comment (emailTo, emailStatus "sending") and queues an email-reply side effect carrying the intake alias, a "Re: <title>" subject and the ticket's Message-IDs. It accepts the option from a person in the app or from an api key an owner or admin allowed (ApiKey.canEmailRequesters, set with PATCH /api/api-keys/:id), never over MCP, and only while EMAIL_INTAKE_REPLY is "true". Unverified senders can be answered too: intake only files allowlisted senders, and the reply goes to that address, so someone forging it never receives it. markEmailReply moves a sending comment to sent or failed once. Migration core-0014-email-replies adds the comment columns, api_keys.can_email_requesters and email_messages.authenticated (backfilled from each message's email.ingest audit row). TaskDetail gains emailReplyTo and emailReplySubject so a client can preview the email. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
deliverEmailReply sends the comment as the body from EMAIL_FROM / EMAIL_FROM_NAME, with Reply-To on the intake address and In-Reply-To/References on the ticket's thread, falling back to no custom headers if the binding refuses them. Like every other send it only goes out when EMAIL_DRY_RUN is exactly "false". The consumer then reports "sent" (guarded, so a failed report never retries into a second email), and the dead-letter handler reports "failed". Threading and the header-fallback send move to email-thread.ts, shared with the intake acknowledgement. PATCH /api/api-keys/:id sets canEmailRequesters (owner/admin); the comment route returns 403 for the option from a key nobody allowed; MCP's comment tool doesn't offer it, and MCP comment views show which comments went to the requester. wrangler.example.jsonc documents EMAIL_INTAKE_REPLY, off by default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An emailed comment shows who it went to and where delivery stands: "Sending to", "Emailed to", or "Email to … failed". Sending is API-only; the comment box itself is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
API.md gains handoffs, email intake, backups, the emailToRequester option, PATCH /api/api-keys/:id and subtask notes; /api/health documents backup staleness. AGENTS_AND_MCP.md covers the three handoff tools and the trust model. AUTOMATIONS.md adds set_due_date. SELF_HOSTING.md walks through email intake setup (a dedicated domain, the catch-all rule, verifying the auth header before turning on acknowledgements, and replies) and the new backup and retention behaviour. The package READMEs pick up the job runner, pruning, intake and reply internals, and the root README lists the new features. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Test fixtures move from real registered domains to reserved ones (clinic.example, lab.example), a real social post in the title-link test becomes a placeholder, and a few comments and fixtures lose details that only made sense for one deployment (dated incident notes, a specific routing rule, references to an outside CLI). No behaviour changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CodeQL flagged /^(\s*(re|fwd?|aw)\s*:\s*)+/ in replySubject as polynomial on long runs of spaces, and the title came from the sender's subject. Same results with a simple anchored match in a loop, plus tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Last of four stacked PRs (on top of #54): email replies, the docs for everything in this catch-up, and a fixture cleanup.
Email replies (migration
core-0014-email-replies)On a task opened by email intake, a comment posted with
"emailToRequester": truealso goes to the sender whose email opened the ticket. The comment is the email, so the task records exactly what was sent.EMAIL_FROM, asRe: <title>, threaded on the sender's messages, with Reply-To on the intake address so their answer lands back on the task.sentafter the send, and a dead-lettered send marks itfailed. A failed status report never retries into a second email.PATCH /api/api-keys/:id({"canEmailRequesters": true}). MCP never can, andflow_comment_on_taskdoesn't offer the option.EMAIL_INTAKE_REPLY(off inwrangler.example.jsonc), and fail-closed onEMAIL_DRY_RUNlike every other send.GET /api/tasks/:idaddsemailReplyToandemailReplySubject, so a client can preview the exact email before sending.Docs
emailToRequester, the API-key PATCH, subtask notes, and backup staleness on/api/health.set_due_date.Cleanup
Test fixtures now use reserved domains (
clinic.example,lab.example) instead of real registered ones, a real social post in a test is now a placeholder, and a few comments lose details that only made sense for one deployment. No behaviour changes.Checks
pnpm typecheckclean.pnpm test: api 472, core 412, web 101, importer 63, all passing. Web build ok.🤖 Generated with Claude Code