← 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 →