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:
gsxdsm
2026-07-31 00:39:05 -07:00
committed by GitHub
parent 3a016b1f17
commit 9ad3a2a93a

View File

@@ -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);
}