[object Object]

← back to George Gmail

george: draft contract + duplicate detection at the SHARED layer — TK-11231

efdf4ea07961513e46965e95a065e58b781b1729 · 2026-09-10 08:03:41 -0700 · Steve Abrams

Red-team finding: the duplicate refusal shipped in george-mcp protects exactly
ONE client. Anything POSTing /api/drafts directly — cron scripts, other repos,
Kamatera, the ~20 caller scripts under DW-Agents — walked straight past it. A
single locked door on a building with no walls.

Now, for EVERY caller:
- POST /api/drafts returns status=awaiting_owner_send, delivered=false,
  recipient_has_not_received_this=true + the do-not-close-the-ticket warning.
  Free, non-breaking, and it reaches callers the MCP never touched.
- audit() records acknowledged_existing and duplicates_to_recipient, so it is
  now possible to LEARN that the escape hatch went reflexive. Previously there
  was no telemetry pointed at that trapdoor.
- Duplicate detection behind GEORGE_DRAFT_DEDUPE: off (default) | warn | enforce.

Default is off DELIBERATELY: the check costs one extra Gmail search per draft,
and the bulk callers here (sample-followup drafts many vendors in a burst) are
exactly the traffic that already exhausted the per-minute quota once. Turning
it on is a one-env-var decision for Steve, not a surprise I impose on a shared
bridge. 'warn' reports and never blocks; 'enforce' 409s and fails CLOSED on an
unverifiable check.

Verified on an ISOLATED instance (free port, live :9850 untouched): enforce
returns 409 naming the existing draft id; default-off leaves callers unchanged
while still returning the new contract. Probe draft deleted; counts back to
the 136/41 baseline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VTxE4MgnygQ9EY2rPZvtcK

Files touched

Diff

commit efdf4ea07961513e46965e95a065e58b781b1729
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 10 08:03:41 2026 -0700

    george: draft contract + duplicate detection at the SHARED layer — TK-11231
    
    Red-team finding: the duplicate refusal shipped in george-mcp protects exactly
    ONE client. Anything POSTing /api/drafts directly — cron scripts, other repos,
    Kamatera, the ~20 caller scripts under DW-Agents — walked straight past it. A
    single locked door on a building with no walls.
    
    Now, for EVERY caller:
    - POST /api/drafts returns status=awaiting_owner_send, delivered=false,
      recipient_has_not_received_this=true + the do-not-close-the-ticket warning.
      Free, non-breaking, and it reaches callers the MCP never touched.
    - audit() records acknowledged_existing and duplicates_to_recipient, so it is
      now possible to LEARN that the escape hatch went reflexive. Previously there
      was no telemetry pointed at that trapdoor.
    - Duplicate detection behind GEORGE_DRAFT_DEDUPE: off (default) | warn | enforce.
    
    Default is off DELIBERATELY: the check costs one extra Gmail search per draft,
    and the bulk callers here (sample-followup drafts many vendors in a burst) are
    exactly the traffic that already exhausted the per-minute quota once. Turning
    it on is a one-env-var decision for Steve, not a surprise I impose on a shared
    bridge. 'warn' reports and never blocks; 'enforce' 409s and fails CLOSED on an
    unverifiable check.
    
    Verified on an ISOLATED instance (free port, live :9850 untouched): enforce
    returns 409 naming the existing draft id; default-off leaves callers unchanged
    while still returning the new contract. Probe draft deleted; counts back to
    the 136/41 baseline.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01VTxE4MgnygQ9EY2rPZvtcK
---
 server.js | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/server.js b/server.js
index f1b0073..74bc9d1 100644
--- a/server.js
+++ b/server.js
@@ -1536,6 +1536,46 @@ app.post('/api/drafts', async (req, res) => {
     const { gmail: g, key: account } = resolveAccount(req);
     if (!g) return res.status(400).json({ error: `unknown account: ${account}` });
 
+    // ─── Duplicate detection at the SHARED layer (TK-11231) ───
+    // The MCP wrapper refuses duplicates, but that only protects ONE client: anything
+    // POSTing here directly (cron scripts, other repos, Kamatera) walks straight past it.
+    // MODE via GEORGE_DRAFT_DEDUPE: 'off' (default) | 'warn' (report) | 'enforce' (409).
+    // Default is OFF ON PURPOSE: this costs one extra Gmail search PER DRAFT, and the bulk
+    // callers here (sample-followup drafts many vendors in a burst) are exactly the traffic
+    // that already exhausted the per-minute quota once. Flip to warn/enforce deliberately.
+    const dedupeMode = process.env.GEORGE_DRAFT_DEDUPE || 'off';
+    let duplicatesToRecipient = null;
+    if (dedupeMode !== 'off' && to && !(req.body.acknowledge_existing)) {
+      try {
+        const found = await g.users.messages.list({
+          userId: 'me', q: `in:draft to:${to}`, maxResults: 10,
+        });
+        const hits = found.data.messages || [];
+        if (hits.length) {
+          duplicatesToRecipient = hits.map((m) => m.id);
+          if (dedupeMode === 'enforce') {
+            audit(account, 'draft-refused-duplicate', { to, subject, existing: duplicatesToRecipient });
+            return res.status(409).json({
+              error: `REFUSED — ${hits.length} unsent draft(s) already addressed to ${to}. ` +
+                     `Revise one (PUT /api/drafts/:id) instead of composing another, ` +
+                     `or resend with acknowledge_existing:true.`,
+              existing_draft_message_ids: duplicatesToRecipient,
+            });
+          }
+        }
+      } catch (e) {
+        // 'warn' is advisory, so a failed check must not block a draft. 'enforce' fails CLOSED:
+        // silently skipping the check is how a quota blip reopened the duplicate hole once.
+        if (dedupeMode === 'enforce') {
+          return res.status(503).json({
+            error: `REFUSED — could not verify duplicates for ${to} (${e.message}). ` +
+                   `Refusing rather than risk a duplicate; retry, or set acknowledge_existing:true.`,
+          });
+        }
+        duplicatesToRecipient = ['check-failed'];
+      }
+    }
+
     const source = inferSource(req.body, req);
     const taggedBody = withSourceFooter(body, source);
     const encoded = buildRawMessage({ to, cc, bcc, subject, body: taggedBody });
@@ -1559,8 +1599,23 @@ app.post('/api/drafts', async (req, res) => {
       }
     }
 
-    audit(account, 'draft', { to, subject, source, draftId, labelIds: appliedLabels });
-    res.json({ success: true, draftId, messageId, account, source, appliedLabels });
+    // Telemetry: without this there is no way to ever learn that the acknowledge_existing
+    // escape hatch went reflexive, or that duplicates are still being composed.
+    audit(account, 'draft', {
+      to, subject, source, draftId, labelIds: appliedLabels,
+      ...(req.body.acknowledge_existing ? { acknowledged_existing: true } : {}),
+      ...(duplicatesToRecipient ? { duplicates_to_recipient: duplicatesToRecipient } : {}),
+    });
+    res.json({
+      success: true, draftId, messageId, account, source, appliedLabels,
+      // A draft is NOT a delivered outcome — every caller sees this, not just the MCP.
+      status: 'awaiting_owner_send',
+      delivered: false,
+      recipient_has_not_received_this: true,
+      warning: 'Draft created — NOT SENT. Nobody has received this. Do not report the task ' +
+               'complete, close a ticket, or record "waiting on the other party".',
+      ...(duplicatesToRecipient ? { duplicate_warning: duplicatesToRecipient } : {}),
+    });
   } catch (e) {
     res.status(500).json({ error: e.message });
   }

← bf2fe4c george: add PUT /api/drafts/:id (drafts.update) — TK-11231  ·  back to George Gmail  ·  george: give the 30-day draft drainer a KEEP-LIST — TK-11231 0d2c8e2 →