← 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
M scripts/watch-sent-stamp.mjs
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 →