← Back to brief

reference external draft sop

memory · reference_external_draft_sop.md

External-recipient draft SOP

Hardwired 2026-05-11 PM after a full-day session that burned through 8+ failed draft attempts to ship one email to the Devine prospects. Every failure shape in that session is now structurally blocked by one of the 6 gates below. An agent that follows this SOP in order cannot reproduce any of today's failures.

The six gates

Each gate produces or consumes a cert file. The Claude Code PreToolUse hook at ~/.claude/hooks/gmail_draft_audit_gate.py blocks create_draft unless ~/Library/Application Support/SkyRun/draft_audit_pass.json is current AND was produced by claim_audit_passer.py with ALL gates satisfied.

#GateHelper scriptCert / output
1Claim audit — every factual claim about a 3rd party has a primary source cited THIS turnclaim_audit_passer.py (--claims-file)inline in audit pass
2Latest-inbound certreplyToMessageId is the recipient's actual most-recent messagelatest_inbound_check.py<inbound_cert>.json
3No-stacking cert (MCP-authoritative) — Gmail MCP list_drafts shows zero existing drafts in this thread/recipientgmail_draft_purge.py (consumes Gmail MCP JSON)<nostack_cert>.json, verification_path=gmail_mcp_list_drafts, schema_version=2
4Voice check — zero AI tells (section labels, performative phrases, consultant-speak, marketing copy)voice_check.py (invoked inside audit-passer)inline verdict
5Commitment audit — every forward commitment in body has a verified deliverable backing, OR body has zero forward commitmentscommitment_audit.pyinline detection + optional <commitments_backing>.json
6Body persistence verify (POST-create) — after create_draft returns success, Gmail MCP get_thread FULL_CONTENT confirms body actually persisted (not "loading empty")draft_body_verify.py<body_verify_cert>.json

The full workflow


STEP 1 — Compose body
  Write the body text to disk at /tmp/<deal>_body.txt
  Body must contain ZERO forward commitments unless each has a verified
  deliverable file/data/capability that exists right now.

STEP 2 — Identify every factual claim
Write claims JSON to /tmp/<deal>_claims.json with each claim mapped to
a primary source citation (gmail:, transcript:, hs:, sot:, track:,
keydata:, calendar:, sms:, walkthrough:, operator_directive:,
public_record:, sla_capability:, or memory_file:+verified).

STEP 3 — Latest-inbound cert
Call Gmail MCP get_thread on the candidate thread to verify the
most-recent inbound message ID. Run:
latest_inbound_check.py
--recipient <email>
--expected-msg-id <msg_id>
--out /tmp/<deal>_inbound_cert.json

STEP 4 — No-stacking cert (MCP-AUTHORITATIVE — NOT UI-based)
Call Gmail MCP list_drafts with query "to:<recipient>".
Persist the JSON response to /tmp/<deal>_mcp_drafts.json.
If the response shows ANY existing drafts: operator deletes them manually
via Gmail UI, then re-fetch list_drafts and re-persist.
Then run:
gmail_draft_purge.py
--recipient <email>
--thread-id <thread_id>
--gmail-mcp-drafts-json /tmp/<deal>_mcp_drafts.json
--out /tmp/<deal>_nostack_cert.json

STEP 5 — Full 5-gate audit
Run:
claim_audit_passer.py
--body-file /tmp/<deal>_body.txt
--claims-file /tmp/<deal>_claims.json
--to <to_email> --cc <cc_email>
--reply-to-message-id <msg_id>
--latest-inbound-cert-file /tmp/<deal>_inbound_cert.json
--no-stacking-cert-file /tmp/<deal>_nostack_cert.json
--no-forward-commitments (if body has zero forward commits)
OR --commitments-backing-file /tmp/<deal>_backing.json
Audit-passer writes draft_audit_pass.json valid for 180s.

STEP 6 — create_draft within 180-second pass window
Hook checks pass file; if valid, allows create_draft.
Returns Gmail draft ID.

STEP 7 — POST-create body persistence verification
Call Gmail MCP get_thread (FULL_CONTENT) on the thread.
Persist response to /tmp/<deal>_thread_postcreate.json (<120s).
Run:
draft_body_verify.py
--expected-body-file /tmp/<deal>_body.txt
--thread-json-file /tmp/<deal>_thread_postcreate.json
--draft-id <draft_id>
--out /tmp/<deal>_body_verify_cert.json
Three possible statuses:
verified — plaintextBody matches; safe to send
lagging — plaintextBody not yet indexed but snippet shows match;
operator visually verifies in Gmail UI
empty — body did NOT persist; delete draft, retry or pivot to
manual-paste path

STEP 8 — Operator opens Gmail UI, eyeballs, sends
Never assert "the draft is in Gmail and complete" until operator
visually confirms.

Anti-patterns this SOP closes (today's failures, indexed)

FailureWhere it was caught (or would have been)
Fabricated SkyRun-partner-firm claim ("happy to make the intro")Gate 1 claim audit — no primary source for the claim
replyToMessageId pointing at stale threadGate 2 latest-inbound cert — refuses pass unless msg_id matches verified latest
Two drafts in same thread (V5 + V6 stacking)Gate 3 no-stacking cert (MCP path) — refuses pass if Gmail MCP shows any existing drafts
Phantom stacking that fooled UI-rowcount certGate 3 schema v2 — requires verification_path: gmail_mcp_list_drafts, rejects UI-based or skip-purge certs
"On peak:" / "the lever in" / "writing flattens" AI tellsGate 4 voice_check — phrase blacklist + section-label structural patterns
"I'd rather pull our verified 90-day median before the call"Gate 5 commitment audit — refuses pass with forward commit not backed by deliverable
V7 went out empty (body did not persist)Gate 6 body verify — refuses send unless body verified or operator-acknowledged lagging
"$103,713 × midpoint compounding = $128K" (invented methodology)R-31 (no invented methodology in chat to operator)
"I can't easily pull Dec-Jan market data" (fabricated blocker)R-30 (KeyData full access) + R-17 (no fabricated capability blockers)

What to do when a gate refuses

What this SOP does NOT cover

Maintenance

- claim_audit_passer.py (5-gate orchestrator) - latest_inbound_check.py - gmail_draft_purge.py (v2, MCP-authoritative) - voice_check.py - commitment_audit.py - draft_body_verify.py

Cross-references