// Guard against publication dates that sit in the future. // // pub_date_effective is what the autonomy coordinator uses to derive a // prediction's information_cutoff (max pub_date_effective across an event's // articles), so a single bogus feed date drags the cutoff forward and quietly // breaks evidence-cutoff enforcement and outcome scoring. Production currently // has exactly one such row, but one is enough to poison an event. // // Tolerance: 48 hours. It has to swallow the legitimate cases — // * date only strings ("2026-08-29") are stored as midnight UTC, and a // publisher in UTC+14 can legitimately stamp tomorrow's date, // * feeds that emit local time without an offset, worst case ~14h ahead, // * modest clock skew on the publisher's box. // 48h covers all of that with room to spare while still catching anything // genuinely wrong — the offending production row is about four months out. const DEFAULT_TOLERANCE_MS = 48 * 60 * 60 * 1000; function toleranceMs() { const hours = Number(process.env.INGEST_FUTURE_PUB_DATE_HOURS); if (Number.isFinite(hours) && hours > 0) return hours * 60 * 60 * 1000; return DEFAULT_TOLERANCE_MS; } // Returns { ok, value, skewMs, toleranceMs }. `value` is null when the date is // implausible so the caller can fall back to ingestion time. The article itself // is never dropped for this — a bad date is not a bad article. function checkPubDate(value, now = Date.now(), tolerance = toleranceMs()) { if (!value) return { ok: true, value: null, skewMs: 0, toleranceMs: tolerance }; const parsed = new Date(value).getTime(); if (Number.isNaN(parsed)) return { ok: true, value: null, skewMs: 0, toleranceMs: tolerance }; const skewMs = parsed - now; if (skewMs > tolerance) { return { ok: false, value: null, skewMs, toleranceMs: tolerance }; } return { ok: true, value, skewMs, toleranceMs: tolerance }; } // Same check, but it also does the shouting. Keeps ingest.js readable and makes // sure every clamp lands in the logs with the source and the offending value. function guardEffectivePubDate(pubDate, fallback, context = {}) { const verdict = checkPubDate(pubDate); if (verdict.ok) return pubDate || fallback; const days = (verdict.skewMs / 86400000).toFixed(1); console.warn( `[ingest] refusing future pub date from "${context.source || 'unknown source'}": ${pubDate} is ${days} days ahead ` + `(tolerance ${Math.round(verdict.toleranceMs / 3600000)}h) — pub_date_effective falls back to ${fallback}. url=${context.url || 'n/a'}` ); return fallback; } module.exports = { checkPubDate, guardEffectivePubDate, DEFAULT_TOLERANCE_MS, };