fix(gate): the fnxc check WROTE to the tree it was checking — nine PRs chased three defects (#3287)

Root-cause fix for the duplicate-PR pileup tracked in #3267. **Running
the check modified the working tree.**

## Reproduction

```
clean:  0 files dirty
$ node scripts/check-fnxc-future-dates.mjs        # no flags, no --update-baseline
  exit 0
after:  1 file dirty   →   M scripts/lib/fnxc-future-dates-baseline.json
```

`:227` auto-tightened and `writeFileSync`'d on every run.

## Why that produced nine PRs

The tightening is **right in substance** — the comment above it explains
why banking a stale allowance is worse than re-recording. Doing it as a
*side effect of checking* is what hurt: every worker who ran the gate
received an identical uncommitted diff they had not written, and
reasonably committed it.

The clearest evidence is #3283 and #3285 — five minutes apart, `+0/-1`
each, both deleting the same baseline line. **Neither author wrote that
line.** The gate wrote it, in both of their checkouts.

I also mis-attributed my own dirty tree to leftover work while
retracting a measurement on #3277/#3278. The dirt was this script.

## The change

Still computed, still reported loudly — only **written** under
`--update-baseline`:

```
[check-fnxc-future-dates] baseline CAN BE TIGHTENED for 1 file(s):
  packages/cli/src/__tests__/cli-active-count-lanes.test.ts: 10 -> 5
  run `pnpm check:fnxc-future-dates --update-baseline` to record it (one commit, one author)
```

**A plain run stays green rather than failing on a tightening.** Stamps
age into the past on their own, so failing would redden main on a clock
tick — which is precisely why the auto-write existed. Report, don't
enforce.

## Measured, both directions

| scenario | result |
|---|---|
| stale allowance, plain run | reports + hint; baseline **unchanged**
(verified still inflated at 10) |
| stale allowance, `--update-baseline` | `baseline written: 122 stamp(s)
in 63 file(s)`; value reset to 5 |
| clean tree, plain run | exit 0, **zero files dirty** |
| `census --strict` / eslint | 0 / clean |

The first row is the one that matters: I inflated an allowance, ran the
check, and confirmed the file was **still inflated afterwards**.
Asserting only "exit 0, no diff" would have passed even if the write had
silently succeeded and produced no net change.

## Scope

One script. CI is unaffected — it never committed the side-effect write,
so that write was always discarded there. The only behaviour change is
that an interactive run no longer edits your tree.

This is a smaller intervention than the claim-protocol I proposed
earlier in #3267, and I now think that one was treating a symptom:
workers were not colliding because they lacked a protocol, but because
the tool handed each of them the same diff.
This commit is contained in:
gsxdsm
2026-07-31 18:07:32 -07:00
committed by GitHub
parent f2f6795010
commit 9abec9f235

View File

@@ -149,7 +149,8 @@ function scan() {
const found = scan();
if (process.argv.includes("--update-baseline")) {
const updateBaseline = process.argv.includes("--update-baseline");
if (updateBaseline) {
writeFileSync(BASELINE, `${JSON.stringify(found, null, 2)}\n`);
const total = Object.values(found).reduce((a, b) => a + b, 0);
console.log(`[check-fnxc-future-dates] baseline written: ${total} stamp(s) in ${Object.keys(found).length} file(s)`);
@@ -219,14 +220,35 @@ for (const [file, allowed] of Object.entries(baseline)) {
const count = found[file] ?? 0;
if (count < allowed) tightened.push(` ${file}: ${allowed} -> ${count}`);
}
/*
FNXC:FnxcStampHygiene 2026-08-01-00:55 (a CHECK must not modify the tree it is checking):
This block used to rewrite the baseline on every plain run. The tightening itself is right — the
comment above explains why banking a stale allowance is worse — but performing it as a SIDE EFFECT of
checking handed every worker an identical uncommitted diff they had not written.
Measured cost: on 2026-07-31/08-01 nine PRs chased three defects in this gate's area, and two of them
(#3283, #3285, five minutes apart, `+0/-1` each) deleted the SAME baseline line. Neither author wrote
it; the gate wrote it, in both of their checkouts, and each reasonably committed what they found. I
also mis-attributed my own dirty tree to leftover work and retracted a measurement partly on that
basis.
So: still computed, still reported loudly, but only WRITTEN under --update-baseline. A plain run is
read-only and stays green — failing on a tightening would redden main every time a stamp simply ages
into the past, which is exactly why the auto-write existed.
*/
if (tightened.length > 0) {
for (const [file, allowed] of Object.entries(baseline)) {
const count = found[file] ?? 0;
if (count < allowed) { if (count === 0) delete baseline[file]; else baseline[file] = count; }
}
writeFileSync(BASELINE, `${JSON.stringify(baseline, null, 2)}\n`);
console.log(`[check-fnxc-future-dates] baseline TIGHTENED for ${tightened.length} file(s):`);
console.log(`[check-fnxc-future-dates] baseline CAN BE TIGHTENED for ${tightened.length} file(s):`);
for (const line of tightened.sort()) console.log(line);
if (updateBaseline) {
for (const [file, allowed] of Object.entries(baseline)) {
const count = found[file] ?? 0;
if (count < allowed) { if (count === 0) delete baseline[file]; else baseline[file] = count; }
}
writeFileSync(BASELINE, `${JSON.stringify(baseline, null, 2)}\n`);
console.log("[check-fnxc-future-dates] baseline re-recorded.");
} else {
console.log(" run `pnpm check:fnxc-future-dates --update-baseline` to record it (one commit, one author).");
}
}
if (problems.length > 0) {