02-sops

draft and watch email loop spec

2026-05-11·sop·status: approved-spec-pre-build·! medium

Draft-and-watch email loop — closed-loop spec (approved 2026-05-11)

Why this is in the vault

Founder approved this skill build via the new HQ click-back rail on 2026-05-11 13:20 ET, with a hard constraint: the draft-and-watch email pattern must close its loops through the existing RDCO surfaces (vault, Notion task board, HQ /decisions/). No opaque side channels. Every drafted email and every parsed reply leaves a vault-canonical trace. This doc locks the spec before the build starts so the implementation can't drift.

Source priority: founder explicitly named this as the #1 L5 unlock when reviewing the Vending-Bench research brief ([[06-reference/2026-05-01-vending-bench-research-brief.md]]). The reason it's L5-grade: lets Ray drive multi-step external relationships (intro asks, follow-ups, scheduling, project negotiation) at the founder's reputation without ever taking the send-button.

Hard constraints (from founder, 2026-05-11)

  1. Ray never autonomously sends external email. Founder retains the send-button. Period. Memory pointer: [[~/.claude/projects/-Users-ray/memory/feedback_no_autonomous_external_email]].
  2. Drafts surface on /decisions/<slug>.html, not in iMessage. The decision page IS the approval UX.
  3. Replies parsed by Ray, routed back through canonical surfaces:
    • Follow-up payload → the originating /decisions/<slug>.html (or a new linked decision page if the reply requires a new founder call).
    • Ray-async work spawned by the reply → new Notion Task Board entry.
    • Substance worth retaining → vault note in 03-contacts/ (relationship-shifting context) or 05-meetings/ (multi-turn coordination).
  4. No drift to opaque side channels. Every state transition is auditable from the vault.

The loop

  Trigger (Ray-side):
    • /curiosity surfaces a contact to reach
    • /sync-contacts finds a relationship cold for >Nd
    • /check-board hits a task whose unblock requires an external ask
    • Founder explicitly asks Ray to draft a message

  ↓

  Stage 1 — Draft
    Ray composes draft using vault context on the recipient
    (prior threads, relationship notes, ongoing project state).
    Writes to ~/rdco-hq/public/decisions/<YYYY-MM-DD>-email-<recipient-slug>.html

    Page renders:
      • To / Subject / Body (preview)
      • Rationale block: why this email, what reply Ray expects, what
        action the reply unlocks
      • 4-option click-back rail:
          [ SEND-AS-IS ]  → fires Gmail MCP send + arms watch
          [ EDIT-IN-GMAIL ] → opens Gmail draft URL on iPhone; founder
                              edits + sends; Ray detects via watch
          [ KILL ]        → archive draft, no send, no watch
          [ DEFER until ] → re-surface decision page on chosen date

  ↓

  Stage 2 — Send (founder-gated)
    Founder taps SEND-AS-IS → iMessage payload returns to Ray →
    Ray calls Gmail MCP send_message → records {message_id, thread_id,
    recipient, sent_at, originating_decision_slug} in
    ~/.claude/state/email-watch-ledger.json

    OR

    Founder taps EDIT-IN-GMAIL → opens Gmail compose URL with pre-filled
    To/Subject/Body → founder edits + sends manually → Ray detects via
    polling watch (Stage 3) using From=founder + recipient match.

  ↓

  Stage 3 — Watch
    Cron-fired /watch-email-replies skill (every 30m during waking
    hours, every 2h overnight) queries Gmail for new messages in
    threads where Ray is the originating party. For each new reply:
      1. Fetch thread, isolate the new reply body
      2. Classify reply intent (yes/no/info-request/scheduling/decline/etc)
      3. Route per the matrix below

  ↓

  Stage 4 — Route the reply
    A. Founder-decision-needed (reply requires judgment):
        → write a new /decisions/<slug>-followup.html linked to the
          originating decision page. Append to _decisions.json.
        → iMessage the founder a tight summary + decision-page link.
    B. Ray-async-work-spawned (reply unblocks something Ray can run):
        → create Notion Task Board entry with the unblocked work.
        → execute next /check-board cycle.
    C. Substantive-context-only (reply is informational, no action):
        → append to a vault note in 03-contacts/<recipient>.md OR
          05-meetings/<date>-<topic>.md.
        → no founder ping; surfaces in next /morning-prep if relevant.
    D. Cold or hostile reply:
        → archive originating decision page status = decided / outcome
          = "declined" / link the cold reply.
        → iMessage the founder ("X declined; no further action") and stop.

  ↓

  Stage 5 — Close the loop
    Originating decision page status flips: open → decided (or shipped)
    once the reply is processed AND any spawned follow-up either
    completes or has its own open decision page.

What Ray needs to build

New skills

  1. /draft-email at ~/.claude/skills/draft-email/SKILL.md — composes a draft + writes the decision page + drafts the Gmail compose URL. Does NOT send.
  2. /watch-email-replies at ~/.claude/skills/watch-email-replies/SKILL.md — cron-fired polling against the email-watch ledger. Routes replies per Stage 4 matrix.

New state files

  1. ~/.claude/state/email-watch-ledger.json — every sent-from-loop email tracked as {message_id, thread_id, recipient, originating_decision_slug, status: awaiting | replied | timed-out, sent_at, last_polled, expected_reply_by}.

New cron entries

  1. */30 6-23 * * */watch-email-replies (every 30m during waking hours)
  2. 0 0,2,4 * * */watch-email-replies (sparse overnight)

Skill changes downstream

  1. /check-board skill should be aware: tasks whose unblock requires external email get routed through /draft-email instead of being marked Blocked-waiting-on-founder. Add a step in /check-board step 4: "if blocker is 'external party A's input needed' → spawn /draft-email to:A subject:... body-context:...".
  2. /sync-contacts skill could surface 7+d cold relationships as /draft-email candidates (founder approval gated).

Decision-page template extension

  1. The existing /decisions/<slug>.html template needs an email-draft variant that renders:
    • Recipient + Subject + Body preview
    • Rationale block
    • 4-option rail with email-specific actions (SEND-AS-IS, EDIT-IN-GMAIL, KILL, DEFER)
    • SEND-AS-IS button wires up the canonical click-back URL with DECISION:<slug>:SEND_AS_IS || (no additional context)
    • EDIT-IN-GMAIL button is a direct https://mail.google.com/mail/?view=cm&to=...&su=...&body=... link
    • KILL fires DECISION:<slug>:KILL || <optional reason>
    • DEFER fires DECISION:<slug>:DEFER || until:<date>

Open questions to resolve during build

Acceptance criteria (founder review)

Related