[object Object]

← back to George Gmail

Fleet auto-responder: deliveredto query fix + --only-ids scoped-test flag

7d77cad42a25e2787a594ab46e73d5170872c740 · 2026-05-19 10:08:50 -0700 · Steve Abrams

Tested live on barwallpaper.com + grassclothwallcovering.com (2-domain test):
- Gmail does not match plus-tags via to:; switched candidate query to
  deliveredto: and rely on Delivered-To header parsing for domain recovery.
- Added --only-ids flag so a scoped test does not touch the inbox backlog
  of unrelated probe mail.
- Verified: reply sent from info@designerwallcoverings.com landed in the
  recipient INBOX (not Spam), correct per-domain sign-off, no loop, label
  + 7-day ledger dedup both idempotent on re-run.

Ledger (data/fleet-autoresponder-ledger.json) gitignored — runtime state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 7d77cad42a25e2787a594ab46e73d5170872c740
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue May 19 10:08:50 2026 -0700

    Fleet auto-responder: deliveredto query fix + --only-ids scoped-test flag
    
    Tested live on barwallpaper.com + grassclothwallcovering.com (2-domain test):
    - Gmail does not match plus-tags via to:; switched candidate query to
      deliveredto: and rely on Delivered-To header parsing for domain recovery.
    - Added --only-ids flag so a scoped test does not touch the inbox backlog
      of unrelated probe mail.
    - Verified: reply sent from info@designerwallcoverings.com landed in the
      recipient INBOX (not Spam), correct per-domain sign-off, no loop, label
      + 7-day ledger dedup both idempotent on re-run.
    
    Ledger (data/fleet-autoresponder-ledger.json) gitignored — runtime state.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
 .gitignore             |  2 ++
 fleet-autoresponder.js | 34 +++++++++++++++++++++++++++-------
 2 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7e6a9c3..46948d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,5 @@ dist/
 build/
 .next/
 *.bak
+# fleet-autoresponder runtime ledger (per-sender reply timestamps — not source)
+data/fleet-autoresponder-ledger.json
diff --git a/fleet-autoresponder.js b/fleet-autoresponder.js
index 2768a05..b060a9a 100644
--- a/fleet-autoresponder.js
+++ b/fleet-autoresponder.js
@@ -20,8 +20,10 @@
  *     is never double-replied. Idempotent on re-run.
  *
  * USAGE
- *   node fleet-autoresponder.js            # process inbox once, send replies
- *   node fleet-autoresponder.js --dry-run  # detect + log, send nothing
+ *   node fleet-autoresponder.js                  # process inbox once, send replies
+ *   node fleet-autoresponder.js --dry-run        # detect + log, send nothing
+ *   node fleet-autoresponder.js --only-ids a,b   # restrict to specific Gmail msg ids
+ *                                                # (used for the scoped 2-domain test)
  *
  * This script reuses George's OAuth credentials (DW-MCP/.env). It does NOT need
  * the George server running and makes no prod deploy.
@@ -36,6 +38,13 @@ const { google } = require('googleapis');
 // Config
 // ─────────────────────────────────────────────────────────────────────────────
 const DRY_RUN = process.argv.includes('--dry-run');
+// --only-ids a,b,c  → restrict processing to these exact Gmail message ids.
+// Used for the scoped 2-domain test so a backlog of probe mail is not touched.
+const ONLY_IDS = (() => {
+  const i = process.argv.indexOf('--only-ids');
+  if (i === -1 || !process.argv[i + 1]) return null;
+  return new Set(process.argv[i + 1].split(',').map(s => s.trim()).filter(Boolean));
+})();
 const PROCESSED_LABEL = 'fleet-autoreplied';
 const LEDGER_PATH = path.join(__dirname, 'data', 'fleet-autoresponder-ledger.json');
 const DEDUP_WINDOW_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
@@ -44,6 +53,9 @@ const SIGNATURE_NAME = 'Steve';
 const SENDER_FROM = 'Designer Wallcoverings <info@designerwallcoverings.com>';
 const SEND_ACCOUNT = 'info';      // George account that physically sends
 const READ_ACCOUNT = 'steve-office'; // George account whose inbox receives the forwards
+// How far back to scan for inquiries. Gmail's newer_than granularity is days;
+// override with FLEET_SCAN_WINDOW (e.g. "2h", "1d", "14d") for tighter scoping.
+const SCAN_WINDOW = process.env.FLEET_SCAN_WINDOW || '14d';
 
 // ─────────────────────────────────────────────────────────────────────────────
 // Fleet domain list — loaded from dw-domain-fleet/sites/*.json
@@ -301,12 +313,20 @@ async function main() {
 
   const ledger = loadLedger();
 
-  // Candidate inquiries: unprocessed inbox mail addressed to a steve+ plus-tag.
+  // Candidate inquiries: unprocessed inbox mail delivered to a steve+ plus-tag.
+  // Gmail matches the plus-tag against the Delivered-To header via deliveredto:,
+  // NOT the visible To: header (which is the customer-facing info@<domain>).
   // We deliberately scope to INBOX (excludes SPAM) and exclude already-labelled mail.
-  const q = `in:inbox -label:${PROCESSED_LABEL} to:steve+ newer_than:14d`;
-  const list = await officeGmail.users.messages.list({ userId: 'me', q, maxResults: 100 });
-  const ids = (list.data.messages || []).map(m => m.id);
-  log(`[fleet-autoresponder] ${ids.length} candidate message(s) matched query: ${q}`);
+  const q = `in:inbox -label:${PROCESSED_LABEL} deliveredto:steve+ newer_than:${SCAN_WINDOW}`;
+  let ids;
+  if (ONLY_IDS) {
+    ids = [...ONLY_IDS];
+    log(`[fleet-autoresponder] --only-ids set — processing exactly ${ids.length} message(s): ${ids.join(', ')}`);
+  } else {
+    const list = await officeGmail.users.messages.list({ userId: 'me', q, maxResults: 100 });
+    ids = (list.data.messages || []).map(m => m.id);
+    log(`[fleet-autoresponder] ${ids.length} candidate message(s) matched query: ${q}`);
+  }
 
   const results = { replied: [], skipped: [] };
 

← d135118 Add fleet auto-responder for the 44 dw-domain-fleet domains  ·  back to George Gmail  ·  Fleet auto-responder: multipart/alternative HTML+plain reply 13d4e3b →