From 1e7e6baef1f37887930b1e2d4cf895cb58f464dd Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Fri, 31 Jul 2026 17:15:05 -0700 Subject: [PATCH] =?UTF-8?q?fix(fnxc):=20the=20gate=20compared=20author=20s?= =?UTF-8?q?tamps=20against=20ONE=20machine's=20calendar=20=E2=80=94=20five?= =?UTF-8?q?=20reds=20in=20two=20hours=20(#3277)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root-cause fix for tonight's repeated red `main`, instead of repointing stamps one at a time — **four PRs across three lanes did that in ninety minutes** (#3261, #3269, and my #3263 and #3272, two of which I closed as superseded by concurrent work). ## The defect The fleet writes stamps from **many** machines; this gate evaluates them on **one**. #2941 fixed the case where the author sits **west** of the runner — a correct 5pm-in-California stamp read as "tomorrow" under a UTC comparison — by switching to the runner's **local** calendar. The mirror case was left open, and that is what broke `main`: | commit | landed (PDT) | = UTC | stamp written | |---|---|---|---| | `9094d1640e` | 16:12 | 23:12 Jul 31 | `2026-08-01-00:20` | | `e52da740a5` | 16:32 | 23:32 Jul 31 | `2026-08-01-00:50` | | `3f95c6d53e` | 16:40 | 23:40 Jul 31 | `2026-08-01-01:05` | Those are **neither** the runner's local date **nor** UTC. They are the *author's* local date in a UTC+1 container — and they are **correct** by this project's own convention ("authors write the local date"). The gate, running in PDT, called all three "tomorrow" and reddened `main` for every other lane. ## The fix A stamp is future only if it is ahead of **both** the local and UTC calendar dates. - Accepts both honest directions (author east or west of the runner). - **Preserves #2941**, doesn't revert it — west of Greenwich the local date is the earlier of the pair, so the 5pm-in-California case still passes. - Still catches an invented date: `scheduler.ts`'s `2026-08-06` stamp (six days out) remains counted, and a mutation probe at `2026-09-15` fails the gate. ## AGENTS.md corrected in the same commit It still instructed **`date -u`**, which describes the *pre-#2941* gate. That instruction is now the one that **produces** the failure from any machine east of the runner — I followed it myself earlier tonight and repointed stamps that were already correct. Rewritten to say: write your own local date; the gate accepts anything not ahead of both calendars. ## Baseline Auto-tightened for **37 files** — the gate's no-author drop path. Those allowances were false positives carried since the UTC-only era, so this **strengthens** the ratchet rather than widening it (`scheduler.ts` 2 → 1, keeping the genuinely-invented stamp counted). ## Verification ``` check-fnxc-future-dates green check-inert-sync-lane-conversions green check-lane-wiring green check-sql-column-literals green census --strict green MUTATION: FNXC:MutationProbe 2026-09-15-10:00 → gate fails (real future dates still caught) ``` No changeset: tooling/gate + internal docs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) --- AGENTS.md | 2 +- scripts/check-fnxc-future-dates.mjs | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c7d362b954..aceb41974b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -387,7 +387,7 @@ Note: the embedded main-content views Workflows (`_WorkflowEditorView`), Import ## FNXC_LOG comments: - Please whenever you're working on a codebase. I want you to add comments describing the date of the change (must be in this format yyyy-MM-dd-hh:mm) and describing the requirements or the change in requirements that made you implement certain functionality. - - **Take the timestamp from `date -u`, not your local clock.** `check-fnxc-future-dates` validates against UTC, so a stamp written from a clock behind UTC is a FUTURE stamp the moment UTC rolls over — and `pnpm lint` passes locally, because the local date agrees with what you wrote. It surfaces only as a red `main` for everybody else. This cost the fleet four separate breakages in one day (`scheduler.ts`, a scheduler PG test, and `task-update.ts` twice, by different authors); every one was a real time on the wrong day. The gate also rejects an impossible clock time, so an hour above 23 or a minute above 59 fails for a different reason. + - **Write your own local date and a real clock time.** `check-fnxc-future-dates` accepts a stamp that is not ahead of BOTH the runner's local calendar and UTC, which is what makes it safe for a fleet whose machines sit in different zones. Do NOT reach for `date -u` to satisfy it: this instruction previously said to, back when the gate compared against UTC alone, and following it from a machine EAST of the runner writes tomorrow's date and reddens `main` for everybody else — five separate breakages in two hours on 2026-07-31, all invisible to their authors because `pnpm lint` passed locally. An earlier round of the same defect from the other direction (authors WEST of the runner, whose correct 5pm stamp read as "tomorrow" under a UTC comparison) cost four breakages in one day. Both directions are now accepted; a genuinely invented date — days out rather than hours — still fails. The gate also rejects an impossible clock time, so an hour above 23 or a minute above 59 fails for a different reason. - I want you to write FNXC:Area-of-product in front of all your comments so they can be grepped. - Most of this should be written as jsdocs but you can add short comments around for the important variables and more complex parts of the codebase. - The idea is to encode the requiements of the system (especially software behavior, UX, and important technical decisions) into the code so it's clearer later why a certain piece of code was written. diff --git a/scripts/check-fnxc-future-dates.mjs b/scripts/check-fnxc-future-dates.mjs index 815c1f8c90..be75f2a998 100644 --- a/scripts/check-fnxc-future-dates.mjs +++ b/scripts/check-fnxc-future-dates.mjs @@ -94,13 +94,33 @@ FNXC:FnxcStampHygiene 2026-07-31-03:40 (#2941 review): `toISOString()` is UTC, s Greenwich it rolls the date forward for part of each day — a stamp written correctly at 5pm in California read as "tomorrow" and failed the gate. Authors write the local date, so the comparison has to use the local one. + +FNXC:FnxcStampHygiene 2026-08-01-00:10 (five reds in two hours — LOCAL alone is not enough either): +The fleet writes stamps from MANY machines and this gate evaluates them on ONE. #2941 fixed the +author-west-of-the-runner case; the mirror case is an author EAST of it, and that is what broke main +five times in two hours. Measured: three direct-to-main commits landed at 16:12/16:32/16:40 PDT — +23:12/23:32/23:40 UTC on the 31st — carrying stamps of 2026-08-01-00:20/00:50/01:05. Those are +neither the runner's local date nor UTC; they are the AUTHOR's local date in a UTC+1 container. The +gate, running in PDT, called every one of them "tomorrow" and reddened main for every other lane. + +So "future" cannot mean "after the runner's calendar". It means after EVERY calendar a correct +author could plausibly be writing from, which is bounded below by the runner's local date and above +by UTC (or vice versa west of Greenwich). Comparing against the LATER of the two accepts both +honest cases and still catches a genuinely invented date — the 2026-08-06 stamp in scheduler.ts, +six days out, fails under this rule exactly as it did before. + +This preserves #2941's fix rather than reverting it: west of Greenwich the local date is the earlier +of the pair, so a 5pm-in-California stamp still passes. */ const now = new Date(); -const today = [ +const localToday = [ now.getFullYear(), String(now.getMonth() + 1).padStart(2, "0"), String(now.getDate()).padStart(2, "0"), ].join("-"); +const utcToday = now.toISOString().slice(0, 10); +/** The later of the two — a stamp is future only if it is ahead of both. */ +const today = localToday > utcToday ? localToday : utcToday; function scan() { const counts = {};