← back to Sample Followup Sweep
sample-followup: --include-suppressed drafts the anti-dup-held vendors for review
db9ca32806de1161ba24cc50f3158d5ca2241695 · 2026-09-01 11:19:46 -0700 · Steve
Steve 9/01: when Drafts came up empty, the cause was the anti-duplicate guard
suppressing ~10 vendors the team already emailed a New Sample Request (last 14d).
--include-suppressed (DRAFT mode only) drafts them ANYWAY, flagging each with a
'[REVIEW — team emailed recently]' subject prefix so they're identifiable in the
Drafts list. Nothing sends — Steve's review stays the dedup. Ran once over the
10-60d backlog: 12 drafts created (11 flagged + Wolf Gordon).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M scripts/scheduled-run.mjs
Diff
commit db9ca32806de1161ba24cc50f3158d5ca2241695
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Sep 1 11:19:46 2026 -0700
sample-followup: --include-suppressed drafts the anti-dup-held vendors for review
Steve 9/01: when Drafts came up empty, the cause was the anti-duplicate guard
suppressing ~10 vendors the team already emailed a New Sample Request (last 14d).
--include-suppressed (DRAFT mode only) drafts them ANYWAY, flagging each with a
'[REVIEW — team emailed recently]' subject prefix so they're identifiable in the
Drafts list. Nothing sends — Steve's review stays the dedup. Ran once over the
10-60d backlog: 12 drafts created (11 flagged + Wolf Gordon).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
scripts/scheduled-run.mjs | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/scripts/scheduled-run.mjs b/scripts/scheduled-run.mjs
index 912587f..59297b8 100644
--- a/scripts/scheduled-run.mjs
+++ b/scripts/scheduled-run.mjs
@@ -33,6 +33,10 @@ const SEND = process.argv.includes('--send');
// Steve 8/20: DRAFT mode — create a Gmail draft per vendor in info@ Drafts for human review,
// instead of sending directly. The draft IS the human gate; Steve sends from Drafts.
const DRAFT = process.argv.includes('--draft');
+// Steve 9/01: draft the anti-dup-SUPPRESSED vendors ANYWAY (for review) instead of skipping them.
+// Each such draft's subject is flagged so Steve can see the team already emailed that vendor recently.
+// Nothing sends — the draft is still the human gate, Steve's review is the real dedup. DRAFT-mode only.
+const INCLUDE_SUPPRESSED = process.argv.includes('--include-suppressed') || process.env.INCLUDE_SUPPRESSED === '1';
const DB = 'WALLPAPER';
const LAYOUT = 'Report for old memo samples';
const FIELD_SENT = 'Date Email Sent to Vendor after 10 Days';
@@ -232,7 +236,10 @@ const run = async () => {
const c = cmap[vid];
if (!c || c.noChase) { skipped.push({ vid, n: rows.length, reason: c?.noChase ? 'no-chase vendor' : 'no sample email/account on file' }); continue; }
const recips = String((c.sample_email || c.main_email) || '').toLowerCase().split(/[,;]\s*/).map((s) => s.trim()).filter(Boolean);
- if (recips.length && recips.some((e) => RECENT.has(e))) { suppressed.push({ vid, name: c.name, to: (c.sample_email || c.main_email), n: rows.length, reason: 'already emailed since 8/15 (manual team send)' }); continue; }
+ const recentlyEmailed = recips.length > 0 && recips.some((e) => RECENT.has(e));
+ // Default: skip a recently-emailed vendor (anti-dup). With --include-suppressed we DRAFT it anyway,
+ // flagged, so Steve can review + decide per-vendor (nothing sends).
+ if (recentlyEmailed && !(INCLUDE_SUPPRESSED && DRAFT)) { suppressed.push({ vid, name: c.name, to: (c.sample_email || c.main_email), n: rows.length, reason: 'already emailed since 8/15 (manual team send)' }); continue; }
// In SEND mode, an unconfirmed (resolved/fallback) recipient is HELD — never fires. In DRAFT
// mode the draft itself IS the review, so everything is drafted for Steve to check + send.
if (c.needs_confirm && !DRAFT) { needsConfirm.push({ vid, name: c.name, to: (c.sample_email || c.main_email), n: rows.length, skus: rows.map((r) => r.sku), reason: 'main-email fallback — CONFIRM person vs recent emails before send' }); continue; }
@@ -240,10 +247,13 @@ const run = async () => {
const fresh = rows.filter((r) => !draftLedger[r.recordId]); // only newly-outstanding SKUs
if (!fresh.length) continue; // already drafted — no dup
const draft = compose({ name: c.name, account_number: c.account_number, sample_email: c.sample_email, main_email: c.main_email, ship_to: SHIP_TO }, fresh.map((r) => ({ mfr: r.mfr, sku: r.sku, requested: r.requested })));
- const res = await georgeDraft({ account: 'info', to: draft.to, subject: draft.subject, body: draft.html });
+ // Flag (subject-only, visible in the Drafts list) when the team already emailed this vendor. Strip
+ // the tag before sending — it's a review marker, not part of the vendor-facing letter.
+ const flagPrefix = recentlyEmailed ? '[REVIEW — team emailed recently] ' : '';
+ const res = await georgeDraft({ account: 'info', to: draft.to, subject: flagPrefix + draft.subject, body: draft.html });
if (res.ok) {
for (const r of fresh) draftLedger[r.recordId] = today; // dedup marker (NOT a FileMaker stamp)
- drafted.push({ vid, name: c.name, to: draft.to, n: fresh.length, draftId: res.id, confirm: !!c.needs_confirm });
+ drafted.push({ vid, name: c.name, to: draft.to, n: fresh.length, draftId: res.id, confirm: !!c.needs_confirm, flagged: recentlyEmailed });
} else { skipped.push({ vid, n: fresh.length, reason: 'George draft failed: ' + res.detail }); }
continue;
}
@@ -269,7 +279,7 @@ const run = async () => {
console.log(`Scheduled follow-up — ${mode} · window ${win} (>=${MIN_AGE_DAYS}d, ${bandDesc})\n`);
if (DRAFT) {
console.log(`Created ${drafted.length} draft(s) in info@ Drafts for review:`);
- drafted.forEach((s) => console.log(` ✎ ${s.name.padEnd(30)} [${s.vid}] → ${s.to} (${s.n} SKU${s.n > 1 ? 's' : ''})${s.confirm ? ' [CONFIRM recipient]' : ''}`));
+ drafted.forEach((s) => console.log(` ✎ ${s.name.padEnd(30)} [${s.vid}] → ${s.to} (${s.n} SKU${s.n > 1 ? 's' : ''})${s.confirm ? ' [CONFIRM recipient]' : ''}${s.flagged ? ' [⚠ team emailed recently — REVIEW]' : ''}`));
} else {
console.log(`Would ${SEND ? 'HAVE SENT' : 'send'} to ${sent.length} vendor(s):`);
sent.forEach((s) => console.log(` ${SEND ? '✓' : '·'} ${s.name.padEnd(30)} [${s.vid}] → ${s.to} (${s.n} SKU${s.n > 1 ? 's' : ''})`));
← 3d869fe auto-data-snapshot: 2026-09-01T10:56:56 (1 data files) — dat
·
back to Sample Followup Sweep
·
auto-data-snapshot: 2026-09-01T11:38:20 (3 data files) — dat 4dfe1c2 →