fix(gate): name the offending FNXC stamp and which rule it broke (#3009)
**`main` is currently red**, so every open PR shows a failing Lint job that is not its own fault. #3004 is how I found it — its gates all pass in isolation and fail against main. ## Cause: an hour that does not exist ``` FNXC:OperatorScriptLaneAssumptions 2026-07-30-26:10 ^^ hour 26 ``` Four of them, across three files, from #2994. ## The part worth fixing is the message, not the stamps This gate counts **two** defects — a date after today, and an impossible clock time — but the failure text only ever explained the first: ``` scripts/reconcile-task-state-consistency.mjs: 2 future-dated FNXC stamp(s), baseline allows 0 A stamp dated after today (2026-07-31) records the change as happening in the future... ``` Every stamp in that file is dated `2026-07-30` or earlier — all valid past dates. So the message sends you to inspect stamps that are fine, and the natural conclusion is *the gate is broken*, not *the stamp is*. I spent several minutes reproducing the regex by hand and getting `future count = 0` before instrumenting the real script and finding `hits += impossibleClockTimes(source)`. A gate that detects the right defect and describes a different one is worse than a slightly less sensitive gate, because it spends the reader's trust. Now: ``` scripts/reconcile-task-state-consistency.mjs FNXC:OperatorScriptLaneAssumptions 2026-07-30-26:10 (impossible clock time) ``` **Mutation-verified**: restoring one `26:10` stamp reproduces the failure, and the message names it. ## The stamps: `2026-07-31-02:10`, not `23:59` Hour 26 on the 30th is the informal spelling of 02:10 the next day. Clamping to `23:59` would keep the file's stamps in a plausible order but silently move the event; this preserves what the author meant. Reversible either way — say the word if you would rather they were clamped. ## The 176-file baseline drop is unrelated `475 -> 183 known`. The clock crossed midnight, so yesterday's stamps are no longer future-dated, and the ratchet auto-lowers on drops by design. It rides along because the gate must leave a baseline matching reality — an allowance nothing occupies is somewhere a real regression can hide. It is not part of the fix. ## Verification - `check:fnxc-future-dates` — exit 0 (was **exit 1 on main**) - `check:lifecycle-columns`, `check:sql-column-literals`, `check:inert-flag-seams`, `check:lane-wiring` — all exit 0 - eslint clean ## Worth someone's attention beyond this PR `#2994` landed four impossible timestamps. The gate caught them, but only after the clock crossed midnight changed which files it reported — meaning the impossible-time check was live but effectively invisible until it collided with an unrelated drop. It is worth asking whether that check has ever produced a message anyone acted on before today. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -163,8 +163,10 @@ for (const [file, count] of Object.entries(baseline)) {
|
||||
}
|
||||
|
||||
const problems = [];
|
||||
const offendingFiles = [];
|
||||
for (const [file, count] of Object.entries(found)) {
|
||||
const allowed = baseline[file] ?? 0;
|
||||
if (count > allowed) offendingFiles.push(file);
|
||||
if (count > allowed) problems.push(` ${file}: ${count} future-dated FNXC stamp(s), baseline allows ${allowed}`);
|
||||
}
|
||||
/*
|
||||
@@ -201,12 +203,43 @@ if (tightened.length > 0) {
|
||||
}
|
||||
|
||||
if (problems.length > 0) {
|
||||
console.error("\n[check-fnxc-future-dates] future-dated FNXC stamp population changed:\n");
|
||||
console.error("\n[check-fnxc-future-dates] FNXC stamp population changed:\n");
|
||||
for (const line of problems.sort()) console.error(line);
|
||||
/*
|
||||
FNXC:FnxcStampHygiene 2026-07-31-07:45 (#3006 fixed the stamps; this fixes why they were hard to
|
||||
find): NAME THE OFFENDING STAMP, AND WHICH RULE IT BROKE.
|
||||
|
||||
This gate counts TWO defects — a date after today, and an impossible clock time — but the failure
|
||||
text only ever explained the first. Main went red on four `2026-07-30-26:10` stamps (hour 26) and
|
||||
the message sent every reader to inspect `2026-07-30`, a perfectly valid past date. The gate had
|
||||
detected the right thing and described a different one, so the natural conclusion was "the gate is
|
||||
broken", not "the stamp is". Confirming otherwise took reproducing the regex by hand, getting zero,
|
||||
and then instrumenting `scan()` to discover `hits += impossibleClockTimes(source)`.
|
||||
|
||||
A gate that misdescribes what it caught spends the reader's trust, which is worth more than the
|
||||
one re-read of already-failing files that printing the real offenders costs.
|
||||
*/
|
||||
for (const file of offendingFiles) {
|
||||
let source;
|
||||
try { source = readFileSync(join(REPO, file), "utf8"); } catch { continue; }
|
||||
const bad = [];
|
||||
STAMP.lastIndex = 0;
|
||||
for (const match of source.matchAll(STAMP)) if (match[1] > today) bad.push(`${match[0]} (dated after today)`);
|
||||
STAMP_TIME.lastIndex = 0;
|
||||
for (const match of source.matchAll(STAMP_TIME)) {
|
||||
if (Number(match[1]) > 23 || Number(match[2]) > 59) bad.push(`${match[0]} (impossible clock time)`);
|
||||
}
|
||||
if (bad.length > 0) {
|
||||
console.error(`\n ${file}`);
|
||||
for (const line of [...new Set(bad)]) console.error(` ${line}`);
|
||||
}
|
||||
}
|
||||
console.error(
|
||||
`\nA stamp dated after today (${today}) records the change as happening in the future, which makes\n`
|
||||
+ "the FNXC record — the project's why-does-this-exist trail — read out of order.\n"
|
||||
+ "Use the current date. If a count went DOWN, re-record the baseline in the same commit.\n",
|
||||
+ "the FNXC record — the project's why-does-this-exist trail — read out of order. An hour above 23\n"
|
||||
+ "or a minute above 59 is not a real time at all.\n"
|
||||
+ "Use the current date and a real clock time. If a count went DOWN, re-record the baseline in the\n"
|
||||
+ "same commit.\n",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user