← back to Sample Followup Sweep
TK-11409: sent-poller stops pre-filling the 2nd-request slot on a first stamp
d0bc2fd2da5e1c227a0ba767c570a9b0ffac00be · 2026-09-10 12:33:07 -0700 · Steve Abrams
Last hole in this ticket. watch-sent-stamp.mjs wrote BOTH date fields whenever it
detected a real send, but this path only ever makes a FIRST stamp -- records already
carrying FIELD_SENT are excluded via the 'already' set. So it filled the 2nd-request
field on the first chase, and pass 2 then skipped those records forever because the
field was non-empty, silently defeating the tracking Steve asked for today.
Steve's 8/25 'stamp BOTH' was correct when written: the 2nd-request field had no
separate meaning then, so writing it alongside was the only way to populate it.
TK-11409 gave it a distinct meaning, which is what makes the dual-write wrong now.
This serves that intent rather than reversing it, and matches how fmpro.mjs pass 1
was aligned.
Also corrected two stale comments and a log line that reported '2ndReq=<date>' on a
write that was never a 2nd request.
Dry run clean (FM_READONLY=1, no stampable follow-ups in window, nothing written).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Files touched
M scripts/watch-sent-stamp.mjs
Diff
commit d0bc2fd2da5e1c227a0ba767c570a9b0ffac00be
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Thu Sep 10 12:33:07 2026 -0700
TK-11409: sent-poller stops pre-filling the 2nd-request slot on a first stamp
Last hole in this ticket. watch-sent-stamp.mjs wrote BOTH date fields whenever it
detected a real send, but this path only ever makes a FIRST stamp -- records already
carrying FIELD_SENT are excluded via the 'already' set. So it filled the 2nd-request
field on the first chase, and pass 2 then skipped those records forever because the
field was non-empty, silently defeating the tracking Steve asked for today.
Steve's 8/25 'stamp BOTH' was correct when written: the 2nd-request field had no
separate meaning then, so writing it alongside was the only way to populate it.
TK-11409 gave it a distinct meaning, which is what makes the dual-write wrong now.
This serves that intent rather than reversing it, and matches how fmpro.mjs pass 1
was aligned.
Also corrected two stale comments and a log line that reported '2ndReq=<date>' on a
write that was never a 2nd request.
Dry run clean (FM_READONLY=1, no stampable follow-ups in window, nothing written).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---
scripts/watch-sent-stamp.mjs | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/scripts/watch-sent-stamp.mjs b/scripts/watch-sent-stamp.mjs
index 95e0deb..8b8383b 100644
--- a/scripts/watch-sent-stamp.mjs
+++ b/scripts/watch-sent-stamp.mjs
@@ -1,6 +1,6 @@
#!/usr/bin/env node
// FAST Sent-poller (Steve 8/20): watch info@ Sent for sample follow-ups that ACTUALLY LEFT, and
-// stamp FileMaker "2nd Request" (+ the proven "Date Email Sent to Vendor after 10 Days") on that
+// stamp FileMaker "Date Email Sent to Vendor after 10 Days" (the chase date) on that
// vendor's outstanding memos. Drafting never stamps — only a real send does. Idempotent via a ledger.
// node scripts/watch-sent-stamp.mjs # one pass
// node scripts/watch-sent-stamp.mjs --loop # poll every POLL_SECS (default 45s)
@@ -9,8 +9,8 @@ const ROOT = join(homedir(), 'Projects/sample-followup-sweep');
const LEDGER = join(ROOT, 'data/stamp-ledger.json');
const DB = 'WALLPAPER', LAYOUT = 'Report for old memo samples';
const FIELD_SENT = 'Date Email Sent to Vendor after 10 Days';
-// Steve 8/25: stamp the 2nd-request DATE on a real send. No dedicated "2nd request date" field is
-// exposed to the Data API (only text "Dear Vendor 2nd Request"); this real date field is the chosen target.
+// The 2nd-request date field. TK-11409: this poller only ever makes a FIRST stamp, so it no longer
+// writes this field — pass 2 of fmpro.mjs owns it. Kept here for reference//future pass-2 support.
const FIELD_2ND_DATE = 'Date Sample Request Letter Sent';
const POLL_SECS = Number(process.env.POLL_SECS || 30);
// Steve 8/25: match on the ACCOUNT # in the subject ("…(Acct NNNNN)") — works for EVERY vendor with
@@ -91,8 +91,16 @@ async function stampVids(vids, bodyText, win, today) {
if (!bodyNorm.includes(norm(mfr))) continue; // only items PRINTED IN the sent letter
done.add(id);
if (DRY_RUN) { stamped.push(mfr + ' [dry]'); continue; }
- // Steve 8/25: stamp BOTH the dedup guard (Date Email Sent…) AND the 2nd-request date.
- const res = await fm.updateRecord(DB, LAYOUT, r.recordId, { [FIELD_SENT]: today, [FIELD_2ND_DATE]: today }, { dryRun: false }).catch((e) => ({ err: e.message }));
+ // Steve 8/25 asked for BOTH fields here, and at the time that was right: the 2nd-request
+ // field had no separate meaning, so writing it alongside was the only way to populate it.
+ // TK-11409 (Steve, 2026-09-10) gave it a real meaning — "a 2nd request was sent" — and this
+ // block only ever runs on a FIRST stamp (records already carrying FIELD_SENT are excluded
+ // above via the `already` set). So writing both here pre-fills the 2nd-request slot on the
+ // first chase, and pass 2 then skips the record forever because that field is non-empty —
+ // silently defeating the very tracking Steve asked for. Writing only the chase date keeps
+ // the slot free for an actual 2nd request. This SERVES the 8/25 intent rather than reversing
+ // it; fmpro.mjs pass 1 was aligned the same way.
+ const res = await fm.updateRecord(DB, LAYOUT, r.recordId, { [FIELD_SENT]: today }, { dryRun: false }).catch((e) => ({ err: e.message }));
if (res.committed) stamped.push(mfr);
else console.warn(` ⚠ FM stamp not committed for ${mfr} (recordId ${r.recordId})${res.err ? ': ' + res.err : ''}`);
}
@@ -117,7 +125,7 @@ async function pass() {
const stamped = await stampVids(vids, full.body || '', win, today);
if (!DRY_RUN) seen[m.id] = { vids, stamped: stamped.length, patterns: stamped, to: m.to || '', at: new Date().toISOString() };
acted += stamped.length;
- console.log(` ${DRY_RUN ? '·' : '✓'} SENT → ${m.to || ''} [${vids.join('/')}] → ${DRY_RUN ? 'WOULD stamp' : 'stamped'} ${stamped.length} memo(s) [${stamped.join(' + ')}] 2ndReq=${today}`);
+ console.log(` ${DRY_RUN ? '·' : '✓'} SENT → ${m.to || ''} [${vids.join('/')}] → ${DRY_RUN ? 'WOULD stamp' : 'stamped'} ${stamped.length} memo(s) [${stamped.join(' + ')}] chaseDate=${today}`);
}
if (!DRY_RUN) writeFileSync(LEDGER, JSON.stringify(seen, null, 2));
if (!acted) console.log(` (no ${DRY_RUN ? 'stampable' : 'new sent'} follow-ups) — ${new Date().toLocaleTimeString()}`);
← 74e43c2 TK-11409: the static contact list expires instead of blockin
·
back to Sample Followup Sweep
·
auto-data-snapshot: 2026-09-10T13:02:09 (1 data files) — dat 843b2df →