← back to Sample Followup Sweep
sample-followup: single-day rolling window (Entered==today-10), revert >10d backlog (TK-12087)
e3a7a73641d9776fd3465b316fbf08be6120b9be · 2026-09-23 14:17:05 -0700 · Steve Abrams
scheduled-run.mjs windowRange, send-reconciled.mjs WIN, and lib/sweep.js now
chase ONLY the cohort entered EXACTLY minAgeDays(10) calendar days ago instead
of the [10..60] cumulative backlog introduced by TK-12013/12014. WIDEN_DAYS/
CATCHUP/WIN remain as explicit manual recovery nets.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M lib/sweep.jsM scripts/scheduled-run.mjsM scripts/send-reconciled.mjs
Diff
commit e3a7a73641d9776fd3465b316fbf08be6120b9be
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Sep 23 14:17:05 2026 -0700
sample-followup: single-day rolling window (Entered==today-10), revert >10d backlog (TK-12087)
scheduled-run.mjs windowRange, send-reconciled.mjs WIN, and lib/sweep.js now
chase ONLY the cohort entered EXACTLY minAgeDays(10) calendar days ago instead
of the [10..60] cumulative backlog introduced by TK-12013/12014. WIDEN_DAYS/
CATCHUP/WIN remain as explicit manual recovery nets.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
lib/sweep.js | 9 +++++++++
scripts/scheduled-run.mjs | 15 +++++++--------
scripts/send-reconciled.mjs | 11 ++++++++---
3 files changed, 24 insertions(+), 11 deletions(-)
diff --git a/lib/sweep.js b/lib/sweep.js
index 1e10781..70ec5cb 100644
--- a/lib/sweep.js
+++ b/lib/sweep.js
@@ -13,6 +13,11 @@ function ageDays(entered, today) {
function sweep(rows, opts = {}, today = new Date()) {
const {
minAgeDays = 10,
+ // TK-12087 (Steve 2026-09-23): SINGLE-DAY rolling window. Chase ONLY samples entered EXACTLY
+ // minAgeDays calendar days ago — NOT the >10-day cumulative backlog. widenDays>0 extends the
+ // window's OLD end as an explicit recovery net (dedup makes overlap safe). maxAgeDays kept only
+ // as the absolute dead-lead ceiling for any widened/manual window.
+ widenDays = 0,
maxAgeDays = 60, // Steve 2026-08-14: >60 days = dead lead, don't chase
excludeDisco = true,
requireNotReceived = true,
@@ -33,7 +38,11 @@ function sweep(rows, opts = {}, today = new Date()) {
if (r.manual_add === true) { followUp.push({ ...r, age }); continue; }
if (age === null) { skipped.push({ row: r, reason: 'no request date' }); continue; }
+ // Single-day window: chase ONLY the [minAgeDays .. minAgeDays+widenDays] cohort (default = exactly
+ // minAgeDays). Anything younger is not yet due; anything older than the window is NOT part of today's
+ // batch (it was that earlier day's batch) — do not fold it back into a cumulative backlog.
if (age < minAgeDays) { skipped.push({ row: r, reason: `only ${age}d old (<${minAgeDays})` }); continue; }
+ if (age > minAgeDays + widenDays) { skipped.push({ row: r, reason: `outside single-day window (${age}d, want exactly ${minAgeDays}${widenDays ? `..${minAgeDays + widenDays}` : ''}d)` }); continue; }
if (age > maxAgeDays) { skipped.push({ row: r, reason: `dead lead (${age}d > ${maxAgeDays})` }); continue; }
if (r.second_request) {
diff --git a/scripts/scheduled-run.mjs b/scripts/scheduled-run.mjs
index 2fe336f..93df18e 100644
--- a/scripts/scheduled-run.mjs
+++ b/scripts/scheduled-run.mjs
@@ -131,14 +131,13 @@ function windowRange(today = new Date()) {
const lo = new Date(today); lo.setDate(lo.getDate() - MIN_AGE_DAYS - (CATCHUP_DAYS - 1));
return `${fmtDate(lo)}...${fmtDate(hi)}`;
}
- // Active follow-up window = requests aged [MIN_AGE_DAYS .. MAX_AGE_DAYS], INCLUSIVE.
- // hi = today - MIN_AGE_DAYS → includes the exactly-10-day cohort (FileMaker `lo...hi` range is inclusive).
- // lo = today - MAX_AGE_DAYS → the 60-day dead-lead floor.
- // Full cohort every run; the Sent-stamp set-difference is the dedup (see MAX_AGE_DAYS note above),
- // so this is robust to missed run days — nothing in the active window is ever silently dropped.
- const hi = new Date(today); hi.setDate(hi.getDate() - MIN_AGE_DAYS);
- const lo = new Date(today); lo.setDate(lo.getDate() - MAX_AGE_DAYS);
- return `${fmtDate(lo)}...${fmtDate(hi)}`;
+ // TK-12087 (Steve 2026-09-23): SINGLE-DAY rolling window — chase ONLY the cohort entered EXACTLY
+ // today-MIN_AGE_DAYS calendar days ago, NOT the >10-day cumulative backlog [10..60] that the
+ // TK-12013/12014 change introduced (reverted here). Each daily run drafts only that day's
+ // newly-10-day-old batch. The FileMaker Sent-stamp set-difference is still the dedup, and CATCHUP /
+ // WIN remain as explicit manual recovery nets if a run day is ever missed.
+ const d = new Date(today); d.setDate(d.getDate() - MIN_AGE_DAYS); // today-10, single calendar day
+ return `${fmtDate(d)}...${fmtDate(d)}`;
}
const norm = (s) => String(s || '').toLowerCase().trim();
const readJSON = (f, d) => { try { return JSON.parse(readFileSync(join(ROOT, f), 'utf8')); } catch { return d; } };
diff --git a/scripts/send-reconciled.mjs b/scripts/send-reconciled.mjs
index e964d67..f5a36f0 100644
--- a/scripts/send-reconciled.mjs
+++ b/scripts/send-reconciled.mjs
@@ -38,9 +38,14 @@ const _fmt = (d) => `${_pad(d.getMonth() + 1)}/${_pad(d.getDate())}/${d.getFullY
// Steve 2026-09-22 (TK-12013/TK-12014): full active window [today-MAX_AGE .. today-MIN_AGE] inclusive,
// NOT a narrow rolling catch-up band — so no older still-outstanding memo is ever dropped. Draft-dedup
// (reconciled-draft-ledger keyed by vendor+combo-sku) makes re-covering already-drafted memos a no-op.
-const _MIN_AGE = 10, _MAX_AGE = Number(process.env.MAX_AGE) || 60;
-const _hi = new Date(); _hi.setDate(_hi.getDate() - _MIN_AGE); // includes exactly-10d cohort (inclusive range)
-const _lo = new Date(); _lo.setDate(_lo.getDate() - _MAX_AGE); // 60-day dead-lead floor
+// TK-12087 (Steve 2026-09-23): SINGLE-DAY rolling window. Chase ONLY samples entered EXACTLY
+// today-10 CALENDAR days ago — NOT the >10-day cumulative backlog (the TK-12013/12014 change,
+// reverted here). Each daily run drafts only that day's newly-10-day-old batch. WIN overrides for
+// a manual one-off; WIDEN_DAYS>0 widens the low end as an explicit recovery net (dedup makes it safe).
+const _MIN_AGE = 10;
+const _WIDEN = Number(process.env.WIDEN_DAYS) || 0; // 0 = strict single day (today-10 only)
+const _hi = new Date(); _hi.setDate(_hi.getDate() - _MIN_AGE); // today-10
+const _lo = new Date(); _lo.setDate(_lo.getDate() - _MIN_AGE - _WIDEN); // = today-10 when WIDEN=0
const WIN = process.env.WIN || `${_fmt(_lo)}...${_fmt(_hi)}`;
// --- FileMaker: pull outstanding (Date WP Sample Sent empty) in WIN, group by vid ---
← 04e0986 auto-data-snapshot: 2026-09-23T14:17:00 (2 data files) — dat
·
back to Sample Followup Sweep
·
sample-followup: day-of-week-aware window (Tue 4-day catch-u a11a644 →