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:
@@ -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}`);
|
||||
|
||||
Reference in New Issue
Block a user