← back to Sample Followup Sweep
sample-followup: make Tuesday window weekday-aware (cover Fri/Sat/Sun/Mon -10)
c1ce8fa6d7a888650fd3e32f26146ffded152593 · 2026-09-01 10:33:33 -0700 · Steve
windowRange() had no day-of-week awareness despite comments claiming Tuesday
catch-up; it applied a flat CATCHUP_DAYS=7 band [today-16..today-10] every run.
Now each run covers the 10-day cohorts for [prevScheduledRun..yesterday]: Tuesday
covers Fri/Sat/Sun/Mon each -10 (=[today-14..today-11]); Wed/Thu/Fri cover the
single prior day (today-11). Today's cohort is deferred to the next run.
CATCHUP env preserved as an explicit manual wide-recovery override.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M scripts/scheduled-run.mjs
Diff
commit c1ce8fa6d7a888650fd3e32f26146ffded152593
Author: Steve <steve@designerwallcoverings.com>
Date: Tue Sep 1 10:33:33 2026 -0700
sample-followup: make Tuesday window weekday-aware (cover Fri/Sat/Sun/Mon -10)
windowRange() had no day-of-week awareness despite comments claiming Tuesday
catch-up; it applied a flat CATCHUP_DAYS=7 band [today-16..today-10] every run.
Now each run covers the 10-day cohorts for [prevScheduledRun..yesterday]: Tuesday
covers Fri/Sat/Sun/Mon each -10 (=[today-14..today-11]); Wed/Thu/Fri cover the
single prior day (today-11). Today's cohort is deferred to the next run.
CATCHUP env preserved as an explicit manual wide-recovery override.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
scripts/scheduled-run.mjs | 55 +++++++++++++++++++++++++++++++----------------
1 file changed, 37 insertions(+), 18 deletions(-)
diff --git a/scripts/scheduled-run.mjs b/scripts/scheduled-run.mjs
index 412c573..0c02de6 100644
--- a/scripts/scheduled-run.mjs
+++ b/scripts/scheduled-run.mjs
@@ -39,19 +39,22 @@ const FIELD_SENT = 'Date Email Sent to Vendor after 10 Days';
// Steve 8/25: on a real send, ALSO stamp the 2nd-request date (chosen target — no dedicated
// "2nd request date" field is exposed to the Data API; this real date field is on the same layout).
const FIELD_2ND_DATE = 'Date Sample Request Letter Sent';
-// Steve 8/20 (REVISED): chase ONLY memos that JUST crossed the 10-day mark — a rolling 3-day
-// catch-up band, NOT the whole back-catalog. "10 days old exactly, for the past 3 days" → on 8/20
-// that is entered dates 8/8, 8/9, 8/10. The open-ended `...hi` was pulling every old memo; this
-// bounds it to [today-10-(CATCHUP-1) .. today-10]. Each daily run picks up the one new day; the
-// 3-day width is a safety net so a skipped run still catches the memos it would have missed.
const MIN_AGE_DAYS = 10;
-// Steve 8/20 (FINAL): chase memos EXACTLY 10 days old — EXCEPT Tuesday, which also covers the
-// Sat/Sun/Mon the job didn't run (schedule = Tue–Fri). Tuesday → 4-day band [today-13..today-10]
-// (memos that turned 10 on Sat/Sun/Mon/Tue); every other run day → 1 day (entered today-10).
-// Env CATCHUP overrides for a manual catch-up.
-// Rolling 7-day catch-up band by default so a missed/unloaded morning is always caught up next run
-// (the draft-ledger dedup + Sent-based suppression prevent any duplicate drafts). Env CATCHUP overrides.
-const CATCHUP_DAYS = Number(process.env.CATCHUP) || 7;
+// Steve 9/01 (FINAL RULE — weekday-aware catch-up): the schedule is Tue–Fri (Mon/Sat/Sun never run).
+// Each run chases the memos that turned exactly 10 days old on the days SINCE the last run — i.e. the
+// cohorts for [previous-scheduled-run-day .. yesterday]. Today's own cohort (entered today-10) is
+// ALWAYS deferred to the next run, so the newest day is never chased prematurely.
+// • Tuesday: last run was Friday, and Sat/Sun/Mon didn't run → cover Fri, Sat, Sun, Mon → 10 days
+// before EACH → entered band [Mon-10 .. Fri-10] = [today-11 .. today-14]. (Steve, verbatim:
+// "on tuesday we go back to the last day, friday and add friday, saturday, sunday, monday
+// then 10 days before each.")
+// • Wed/Thu/Fri: last run was yesterday → cover just yesterday → single day entered today-11.
+// Coverage is gapless and non-overlapping week-to-week (verified). The old code IGNORED the weekday
+// and applied a flat CATCHUP_DAYS band every run — that's the bug this replaces.
+// Env CATCHUP (manual override): if set, revert to the old fixed rolling band [today-10-(N-1)..today-10]
+// as an explicit wide recovery net (e.g. after a silently-missed run). Dedup makes the overlap safe.
+const RUN_WEEKDAYS = new Set([2, 3, 4, 5]); // Tue(2) Wed(3) Thu(4) Fri(5)
+const CATCHUP_DAYS = Number(process.env.CATCHUP) || 0; // 0 = use the weekday-aware band (default)
const SHIP_TO = { name: 'Designer Wallcoverings', line1: '15442 Ventura Blvd. #102', city_state_zip: 'Sherman Oaks, CA 91403', phone: '1-888-373-4564' };
// --- FileMaker client (reuse the connector; creds from ~/.claude.json) ---
@@ -66,12 +69,27 @@ const fm = await import('/Users/macstudio3/Projects/filemaker-mcp/src/fm-client.
const pad = (n) => String(n).padStart(2, '0');
const fmtDate = (d) => `${pad(d.getMonth() + 1)}/${pad(d.getDate())}/${d.getFullYear()}`;
+// Most recent scheduled run day strictly before `today` (walks back over weekend / off days).
+function prevScheduledRun(today) {
+ const d = new Date(today);
+ do { d.setDate(d.getDate() - 1); } while (!RUN_WEEKDAYS.has(d.getDay()));
+ return d;
+}
function windowRange(today = new Date()) {
- // hi = the memo that hits 10 days old TODAY (entered today-10).
- // lo = the memo that hit 10 days old CATCHUP_DAYS-1 days ago (entered today-10-(CATCHUP-1)).
- // On 8/20 with CATCHUP=3 → hi=8/10, lo=8/8 → FileMaker range "08/08/2026...08/10/2026".
- const hi = new Date(today); hi.setDate(hi.getDate() - MIN_AGE_DAYS);
- const lo = new Date(today); lo.setDate(lo.getDate() - MIN_AGE_DAYS - (CATCHUP_DAYS - 1));
+ if (CATCHUP_DAYS > 0) {
+ // Manual override — old fixed rolling band [today-10-(N-1) .. today-10].
+ const hi = new Date(today); hi.setDate(hi.getDate() - MIN_AGE_DAYS);
+ const lo = new Date(today); lo.setDate(lo.getDate() - MIN_AGE_DAYS - (CATCHUP_DAYS - 1));
+ return `${fmtDate(lo)}...${fmtDate(hi)}`;
+ }
+ // Default weekday-aware band: cover the 10-day cohorts for [prevScheduledRun .. yesterday].
+ // hi = yesterday - 10 (= today - 11; today's own cohort is deferred to next run)
+ // lo = prevScheduledRun - 10
+ // Tue → prevRun=Fri → [today-14 .. today-11] (Fri/Sat/Sun/Mon each -10).
+ // Wed/Thu/Fri → prevRun=yesterday → single day today-11.
+ const prev = prevScheduledRun(today);
+ const hi = new Date(today); hi.setDate(hi.getDate() - (MIN_AGE_DAYS + 1));
+ const lo = new Date(prev); lo.setDate(lo.getDate() - MIN_AGE_DAYS);
return `${fmtDate(lo)}...${fmtDate(hi)}`;
}
const norm = (s) => String(s || '').toLowerCase().trim();
@@ -212,7 +230,8 @@ const run = async () => {
mkdirSync(join(ROOT, 'data', 'runs'), { recursive: true });
writeFileSync(join(ROOT, 'data', 'runs', `scheduled-${today.replace(/\//g, '-')}.json`), JSON.stringify(report, null, 2));
- console.log(`Scheduled follow-up — ${mode} · window ${win} (>=${MIN_AGE_DAYS}d, ${CATCHUP_DAYS}-day catch-up band)\n`);
+ const bandDesc = CATCHUP_DAYS > 0 ? `${CATCHUP_DAYS}-day manual catch-up band` : 'weekday-aware band (Tue covers Fri–Mon)';
+ 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]' : ''}`));
← c024fa0 auto-data-snapshot: 2026-09-01T08:28:16 (1 data files) — dat
·
back to Sample Followup Sweep
·
sample-followup: chase today's 10-day cohort today; Tue fold f546361 →