fix(fnxc): the gate compared author stamps against ONE machine's calendar — five reds in two hours (#3277)
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
Reference in New Issue
Block a user