gate: the census could not see a file until it was committed (#3254)

## Why I went looking

The fleet directive is to claim the largest census file cluster. There
is no cluster — the backlog is **0 guards / 0 files**. So the useful
question is whether that 0 is *true*, since the whole phase steers by
it. I had just found a blind spot in my own ratchet (#3252), so I probed
this one the same way.

## What the probes found

A plain, unremarkable guard in a new file scored **zero**:

```ts
// packages/engine/src/probe-helper.ts
export function g(task: { column: string }): boolean {
  return task.column === "in-review";
}
```

Not a cast, not an obfuscation — the exact canonical shape the census
exists to count. It scored 0 in six different directories, and it scored
0 with every cast variant too, which is what initially made this look
like a repeat of #3252.

It is not. The same guard pasted into `scheduler.ts` counted immediately
(0 → 2 with two probes, casts included). The census walks expressions
fine. The miss was **file discovery**: `git ls-files` lists **tracked
files only**, so the file did not exist as far as the census was
concerned. `git add` it and `--strict` goes to exit 1 on the spot.

## What this does and does not mean

**It does not mean the backlog number is wrong.** Everything on `main`
is committed, so CI has always seen the whole tree, and I re-confirmed
the committed totals are unchanged by this PR: `{"column": 0, "role":
12, "status": 185, "deliberate": 148}`. **Backlog 0 is real.** I want
that stated plainly rather than buried, because "ratchet has a hole"
invites the opposite reading.

**What it does mean** is that the census was blind at the one moment
anyone actually consults it. A worker adds a helper, runs the census
against their own work, reads 0, commits — and the guard lands,
attributed to a push rather than to the edit that introduced it. The
instrument was answering about the last commit while being asked about
the working tree.

## The fix

`--cached --others --exclude-standard`, plus a dedupe (a path can appear
under both flags in some index states, which would double every guard in
that file).

| case | before | after |
| --- | --- | --- |
| untracked new file with a guard | 0 | **1** |
| same file, staged | 1 | 1 (dedupe holds — not 2) |
| ignored path (`dist/`) | 0 | 0 (build output still excluded) |
| committed tree | 0 | 0 (backlog unchanged) |

## The part worth keeping

This also **aligns the scope with `check-inert-sync-lane-conversions`**,
which walks the filesystem via `readdirSync` and so always saw untracked
files.

That mismatch is not cosmetic — it is what made #3252 expensive. The
same probe was *caught* by one instrument and *missed* by the other, and
I spent a full investigation treating that as a claim about expression
walking when part of it was two tools disagreeing about which files
exist. When instruments in one program disagree on their own domain,
every differential between them is unreadable until you notice.

## Verification

- Mutation-verified in both directions on all four cases above.
- 53 `lifecycle-column-census.test.ts` tests pass.
- All eight ratchets exit 0; `pnpm test:gate` exit 0.
- Working tree confirmed clean after every probe.

## What I did not do

I did not touch `role: 12` or `status: 185`. Those are different metrics
with no inertness proof behind them, and driving them down is a separate
unit that needs saying explicitly — a conversion there could be cosmetic
and nothing currently would catch it.
This commit is contained in:
gsxdsm
2026-07-31 15:34:57 -07:00
committed by GitHub
parent cf4418e3db
commit 78d5efbcaf

View File

@@ -109,14 +109,39 @@ let files;
rather than as the "file not found" the caller would expect. (#3230 review — coderabbitai.) */
const injectedList = CENSUS_FILE_LIST?.split(/[,\n]/).map((f) => f.trim()).filter(Boolean);
try {
/*
FNXC:LifecycleColumnCensus 2026-07-31-23:05 (the census could not see a file until it was committed):
`git ls-files` lists TRACKED files only, so a brand-new file was invisible to this census until the
author ran `git add`. Measured: a new `packages/engine/src/probe-helper.ts` containing a plain
`task.column === "in-review"` scored 0 while untracked, and flipped the ratchet to exit 1 the moment
it was staged.
That is backwards for the one moment the number is actually consulted. A worker adds a helper, runs
the census to check their work, reads 0, and commits — and the guard surfaces later, in someone
else's CI run, attributed to a push rather than to the edit that introduced it.
`--others --exclude-standard` adds untracked-but-not-ignored files. It changes nothing in CI (there
is nothing untracked there) and nothing for the tracked backlog; it only makes the local reading
agree with the one CI will produce after the commit. Ignored files stay excluded, so build output
and `dist/` do not leak into the count.
Note the deliberate scope difference from `check-inert-sync-lane-conversions`, which walks the
filesystem with `readdirSync` and therefore always saw untracked files. The two instruments
disagreeing on WHICH FILES EXIST is how one probe can be caught by one and missed by the other
(#3252) — that discrepancy cost a full investigation to attribute, so the scopes are aligned here.
*/
const PATHSPECS = "'packages/*/src/**/*.ts' 'packages/*/src/*.ts' 'packages/*/src/**/*.tsx' 'packages/*/app/**/*.ts' 'packages/*/app/**/*.tsx' 'plugins/*/src/**/*.ts' 'plugins/*/src/**/*.tsx'";
files = injectedList !== undefined ? injectedList : execSync(
"git ls-files 'packages/*/src/**/*.ts' 'packages/*/src/*.ts' 'packages/*/src/**/*.tsx' 'packages/*/app/**/*.ts' 'packages/*/app/**/*.tsx' 'plugins/*/src/**/*.ts' 'plugins/*/src/**/*.tsx'",
`git ls-files --cached --others --exclude-standard ${PATHSPECS}`,
{ encoding: "utf8", maxBuffer: 64 * 1024 * 1024 },
)
.split("\n")
.map((f) => f.trim())
.filter(Boolean)
.filter((f) => !f.includes("__tests__") && !/\.(test|spec)\.tsx?$/.test(f));
/* A path can be listed by both --cached and --others in some index states; counting it twice would
double every guard in it. */
files = [...new Set(files)];
} catch (err) {
// FAIL CLOSED: if the file list cannot be produced, nothing has been checked.
console.error(`lifecycle-column-census: could not list files — ${err?.message ?? err}`);