[object Object]

← back to Sample Followup Sweep

chore: sent-poller stamps per-pass date (midnight-safe for 24/7 loop), session-close quality gate

541e2afc18b0b8cbb822673077919383f76f4e92 · 2026-08-25 10:55:50 -0700 · Steve Abrams

Files touched

Diff

commit 541e2afc18b0b8cbb822673077919383f76f4e92
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Tue Aug 25 10:55:50 2026 -0700

    chore: sent-poller stamps per-pass date (midnight-safe for 24/7 loop), session-close quality gate
---
 scripts/watch-sent-stamp.mjs | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/scripts/watch-sent-stamp.mjs b/scripts/watch-sent-stamp.mjs
index 43a25b6..8e0694e 100644
--- a/scripts/watch-sent-stamp.mjs
+++ b/scripts/watch-sent-stamp.mjs
@@ -27,7 +27,8 @@ const fm = await import('/Users/macstudio3/Projects/filemaker-mcp/src/fm-client.
 const readJSON = (f, d) => { try { return JSON.parse(readFileSync(f, 'utf8')); } catch { return d; } };
 const pad = n => String(n).padStart(2, '0');
 const fmt = d => `${pad(d.getMonth() + 1)}/${pad(d.getDate())}/${d.getFullYear()}`;
-const today = fmt(new Date());
+// NB: today is computed PER PASS (not at module load) so the 24/7 --loop stamps the real
+// current date after crossing midnight, instead of freezing the date the process started.
 // rolling chase window: memos aged MIN_AGE_DAYS..MAX_AGE_DAYS (what any follow-up letter would cover)
 function chaseWindow(now = new Date()) {
   const hi = new Date(now); hi.setDate(hi.getDate() - MIN_AGE_DAYS);   // newest chaseable
@@ -45,7 +46,7 @@ function gsearch(q) { return new Promise(res => { const p = '/api/messages?accou
 // Idempotent: FIELD_SENT empty ('=') filter means a memo already stamped is never re-stamped
 // (so re-processing / overlapping emails can't move the 2nd-request date forward). LAYOUT carries
 // account + Date WP Sample Sent + FIELD_SENT + FIELD_2ND_DATE, so no cross-layout hop is needed.
-async function stampAccount(acct, win) {
+async function stampAccount(acct, win, today) {
   const rows = (await fm.findRecords(DB, LAYOUT,
     { account: `==${acct}`, 'Date WP Sample Sent': '=', [FIELD_SENT]: '=', 'today for client': win },
     { limit: 300 }).catch(() => ({ records: [] }))).records;
@@ -62,6 +63,7 @@ async function stampAccount(acct, win) {
 async function pass() {
   const seen = readJSON(LEDGER, {});
   const win = chaseWindow();
+  const today = fmt(new Date());   // per-pass so a midnight-crossing loop stamps the real date
   // NB: the em-dash in the full subject breaks Gmail matching over HTTP — match the ASCII prefix.
   // newer_than:2d so a poll gap never loses a send; the per-message ledger guards against re-stamping.
   const msgs = await gsearch('in:sent subject:"Sample Follow-Up" newer_than:2d');
@@ -72,7 +74,7 @@ async function pass() {
     const acctM = String(m.subject || '').match(/Acct\s*(\d+)/i);   // subject = "…(Acct NNNNN)"
     if (!acctM) { seen[m.id] = { skip: 'no acct in subject', subject: m.subject || '', to: m.to || '' }; continue; }
     const acct = acctM[1];
-    const stamped = await stampAccount(acct, win);
+    const stamped = await stampAccount(acct, win, today);
     seen[m.id] = { acct, stamped: stamped.length, patterns: stamped, to: m.to || '', at: new Date().toISOString() };
     acted += stamped.length;
     console.log(`  ✓ SENT → ${m.to || ''} (Acct ${acct}) → stamped ${stamped.length} memo(s) [${stamped.join(' + ')}] 2ndReq=${today}`);

← d5a63da sample-followup: rewrite sent-poller to match by Acct# from  ·  back to Sample Followup Sweep  ·  auto-data-snapshot: 2026-08-26T07:08:04 (1 data files) — dat bf752cc →