[object Object]

← back to Sample Followup Sweep

sample-followup: record 3 manually-sent 10-day letters + gated FM-stamp script (TK-12109)

05177f265f922b1a8a095cbffe45346d7d967cda · 2026-09-23 15:47:37 -0700 · Steve Abrams

Steve sent FABR/WAT/GRD 10-day letters manually from info@ (verified in Sent).
recently-contacted.json += the 3 recipients (14-day anti-dup now reflects it).
Added scripts/tk12109-stamp-letter-sent.mjs (+ --rollback) to stamp FM
Date Sample Request Letter Sent=09/23/2026 on the 3 outstanding records
(541857/541215/541860) with a restore-map — gated to Steve (classifier blocks
the FM write). BAR held (not sent), REID arrived, MDCKEN no-chase — not stamped.

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

Files touched

Diff

commit 05177f265f922b1a8a095cbffe45346d7d967cda
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 23 15:47:37 2026 -0700

    sample-followup: record 3 manually-sent 10-day letters + gated FM-stamp script (TK-12109)
    
    Steve sent FABR/WAT/GRD 10-day letters manually from info@ (verified in Sent).
    recently-contacted.json += the 3 recipients (14-day anti-dup now reflects it).
    Added scripts/tk12109-stamp-letter-sent.mjs (+ --rollback) to stamp FM
    Date Sample Request Letter Sent=09/23/2026 on the 3 outstanding records
    (541857/541215/541860) with a restore-map — gated to Steve (classifier blocks
    the FM write). BAR held (not sent), REID arrived, MDCKEN no-chase — not stamped.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 data/recently-contacted.json          |  5 +++-
 scripts/tk12109-stamp-letter-sent.mjs | 52 +++++++++++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/data/recently-contacted.json b/data/recently-contacted.json
index a2bf542..d8e070a 100644
--- a/data/recently-contacted.json
+++ b/data/recently-contacted.json
@@ -24,5 +24,8 @@
   "sample@mdcwall.com": "2026-08-20",
   "samples@koroseal.com": "2026-08-20",
   "sshortino@koroseal.com": "2026-08-20",
-  "therese@egg-and-dart.com": "2026-08-20"
+  "therese@egg-and-dart.com": "2026-08-20",
+  "customers@fabricut.com": "2026-09-23",
+  "sophie@watts1874.co.uk": "2026-09-23",
+  "orders@graduatecollection.co.uk": "2026-09-23"
 }
\ No newline at end of file
diff --git a/scripts/tk12109-stamp-letter-sent.mjs b/scripts/tk12109-stamp-letter-sent.mjs
new file mode 100644
index 0000000..2a50b83
--- /dev/null
+++ b/scripts/tk12109-stamp-letter-sent.mjs
@@ -0,0 +1,52 @@
+#!/usr/bin/env node
+// TK-12109 — stamp FileMaker `Date Sample Request Letter Sent` = 09/23/2026 on the 3 outstanding
+// records whose 10-day follow-up letters Steve SENT MANUALLY from info@ on 2026-09-23 (verified in
+// the Sent folder: FABR customers@fabricut.com, WAT sophie@watts1874.co.uk, GRD orders@graduatecollection.co.uk).
+// BAR was NOT sent (held), REID is arrived, MDCKEN is no-chase — none of those are stamped.
+//
+// This is a canonical FileMaker WRITE (the sweep's own FIELD_2ND_DATE stamp) and is blocked by the
+// Claude auto-mode classifier, so it is handed to Steve to run:
+//     node ~/Projects/sample-followup-sweep/scripts/tk12109-stamp-letter-sent.mjs            # stamp + write restore-map
+//     node ~/Projects/sample-followup-sweep/scripts/tk12109-stamp-letter-sent.mjs --rollback # undo (set fields back to '')
+// Blast radius = 3 rows. Reversible via the restore-map (data/tk12109-stamp-restore-map.json).
+import { readFileSync, writeFileSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path';
+const cfg = JSON.parse(readFileSync(join(homedir(), '.claude.json'), 'utf8'));
+const fenv = cfg?.mcpServers?.filemaker?.env || {};
+for (const k of ['FM_CLOUD_HOST', 'FM_CLARIS_EMAIL', 'FM_CLARIS_PASSWORD']) process.env[k] = fenv[k];
+process.env.FM_READONLY = '0';
+const fm = await import('/Users/macstudio3/Projects/filemaker-mcp/src/fm-client.js');
+const DB = 'WALLPAPER', LAYOUT = 'Report for old memo samples', FIELD = 'Date Sample Request Letter Sent', TODAY = '09/23/2026';
+const MAP = join(homedir(), 'Projects/sample-followup-sweep/data/tk12109-stamp-restore-map.json');
+const targets = [
+  { sku: 'DWFC230158', vendor: 'FABR/Fabricut', recordId: '541857' },
+  { sku: 'DWC324296', vendor: 'WAT/Watts 1874', recordId: '541215' },
+  { sku: 'GRD8806', vendor: 'GRD/Graduate Collection', recordId: '541860' },
+];
+const ROLLBACK = process.argv.includes('--rollback');
+
+async function readField(sku, recordId) {
+  const r = await fm.findRecords(DB, LAYOUT, { 'combo sku': sku }, { limit: 20 }).catch(() => ({ records: [] }));
+  const rec = (r.records || []).find((x) => String(x.recordId) === recordId);
+  return rec ? (rec.fieldData[FIELD] || '') : '(record not found)';
+}
+
+if (ROLLBACK) {
+  const map = JSON.parse(readFileSync(MAP, 'utf8'));
+  for (const rec of map.records) {
+    await fm.updateRecord(DB, LAYOUT, rec.recordId, { [FIELD]: rec.oldValue }, { dryRun: false });
+    const back = await readField(rec.sku, rec.recordId);
+    console.log(`  ROLLBACK ${rec.vendor} ${rec.sku} rec ${rec.recordId}: -> ${JSON.stringify(back)} ${back === rec.oldValue ? '✓' : '✗'}`);
+  }
+  console.log('rollback complete.');
+} else {
+  const restore = [];
+  for (const t of targets) {
+    const oldVal = await readField(t.sku, t.recordId);
+    restore.push({ sku: t.sku, vendor: t.vendor, recordId: t.recordId, field: FIELD, oldValue: oldVal });
+    await fm.updateRecord(DB, LAYOUT, t.recordId, { [FIELD]: TODAY }, { dryRun: false });
+    const newVal = await readField(t.sku, t.recordId);
+    console.log(`  ${t.vendor} ${t.sku} rec ${t.recordId}: old=${JSON.stringify(oldVal)} -> new=${JSON.stringify(newVal)} ${newVal === TODAY ? '✓ VERIFIED' : '✗ MISMATCH'}`);
+  }
+  writeFileSync(MAP, JSON.stringify({ ts: new Date().toISOString(), ticket: 'TK-12109', db: DB, layout: LAYOUT, field: FIELD, stampedTo: TODAY, records: restore }, null, 2));
+  console.log('\nrestore-map:', MAP, '\nrollback:  node scripts/tk12109-stamp-letter-sent.mjs --rollback');
+}

← 6c9b709 sample-followup: draft the 5 verified-owed 10-day SKUs + CON  ·  back to Sample Followup Sweep  ·  auto-data-snapshot: 2026-09-23T15:52:37 (3 data files) — dat ab598e5 →