Skip to content

ADR 0021: Suggested drafts never send

Status

Accepted

Context

Duckbill™ must generate draft replies for RESPOND threads (FR §29, Lifecycle §30, API DraftReply) without becoming a mail composer and without sending. MVP forbids autonomous replies and Outlook-replacement compose/send (04_MVP §3, §6). ENABLE_SEND is default-off (ADR 0003 / MAIL-70). MAIL-170 / MAIL-173 require GENERATE DRAFT, DO NOT SEND even if someone flips that flag without an explicit future ticket. AI must not call mail provider adapters. Graph tokens must not include Mail.Send.

Stream A shipped evaluatePolicy (ADR 0019). Stream B golden-set evaluation is ADR 0020. This decision covers DraftReply only. Stream B must call the policy gateway when present and fail closed on every send path if it is missing. A gateway ALLOW for user send when ENABLE_SEND is true is still not enough — there is no future ticket.

Decision

  1. DraftReply lives in @inbox-agent/application (suggested-drafts.ts). It persists a Suggested Draft only — never a Saved Draft and never a Sent Message. The user copies the text into an existing mail client.
  2. SendDraft, Send, and AI-initiated CreateDraft-to-provider always throw. ENABLE_SEND=true is not authorization. A future ticket is required before any send path can succeed.
  3. Call evaluatePolicy (ADR 0019) for DraftReply (action: draft) and SendDraft (action: send). If the gateway is missing, send and provider-draft mutations fail closed. A gateway that allows SendDraft still cannot send.
  4. The worker does not import or call provider adapters. Prompt-injection in mail bodies is untrusted text, not a command.
  5. Draft text is stored with AIInference (task = draft_reply) and an insert-only audit event when provenance repositories are provided.

Alternatives

Consequences

Triage can show a Suggested Draft. Adapter send counters stay at zero. Mail.Send remains a forbidden Graph scope. Classify, extract, golden-set (ADR 0020), and the sanitizer/SSRF work stay on other tickets. ADR 0019 remains the untrusted-input and policy-gateway decision.