fix(gate): the move-target ratchet could not see a file until it was committed (#3256)

## What

**#3254 fixed this blind spot in the census. It was still present
here.** Found by re-running that probe against the other four lifecycle
gates. No product change.

`git ls-files` lists **tracked** files only, so a brand-new file
containing `moveTask(id, "done")` scored **0** locally and flipped the
ratchet the moment it was staged. The author sees a green gate, commits,
and CI disagrees — the worst possible feedback order.

It is also the exact shape that made my own first census probe measure
nothing while reading as "no gap", which is how the class was found in
the first place.

Fixed the way #3254 did — `--cached --others --exclude-standard` — plus
a dedupe, because a path can appear under **both** flags in some index
states and would otherwise count twice against a baseline expecting one.

## Measured

```
untracked probe:            0 detected before  ->  1 after
--strict on a clean tree:   green before and after   (no false positives)
lint clean; fnxc-future-dates: none added
```

## The other gates, measured in the same pass

| gate | sees untracked files? |
|---|---|
| `lifecycle-column-census` | ✅ since #3254 |
| `check-sql-column-literals` | ✅ already |
| `check-inert-sync-lane-conversions` | ✅ already — walks the filesystem
with `readdirSync` |
| `check-lane-wiring` | n/a — does not use `ls-files` |
| `check-move-target-literals` | ❌ → **fixed here** |

This was the last gate with the gap. All five now agree about what a
file is.

## Correction to #3250

I wrote there that this script *"has no export seam and runs at
import"*, and used that to justify shipping without a unit test. **It
does have a seam** — an `isEntryPoint` guard — so a test could import it
without triggering the scan.

That does not change #3250's conclusion (its revert-proof measurement
stands on its own), but the stated reason was wrong, and it was wrong in
the direction that excused less testing. Correcting it here rather than
leaving it as precedent.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Local source-file checks now include newly created and untracked
files.
* Duplicate file entries are removed when files appear in multiple Git
states.
  * Existing tracked-file and CI scanning behavior remains unchanged.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
gsxdsm
2026-07-31 15:45:36 -07:00
committed by GitHub
parent 9690f46439
commit 1660b136d7

View File

@@ -103,9 +103,20 @@ if (isEntryPoint) {
let files;
try {
files = execSync(
"git ls-files 'packages/*/src/**/*.ts' 'packages/*/src/*.ts' 'packages/*/src/**/*.tsx' 'packages/*/app/**/*.ts' 'packages/*/app/**/*.tsx'",
/*
FNXC:MoveTargetRatchet 2026-07-31-22:30 (#3254's finding, same blind spot here):
`--cached --others --exclude-standard` so a BRAND-NEW file is visible before it is committed.
Plain `git ls-files` lists TRACKED files only, so a new file with `moveTask(id, "done")` scored
0 locally and flipped the ratchet the moment it was staged — the author sees a green gate, then
CI disagrees. Changes nothing in CI (nothing is untracked there) and nothing for the tracked
population; it only makes the local reading honest. #3254 made the same change to the census.
*/
"git ls-files --cached --others --exclude-standard 'packages/*/src/**/*.ts' 'packages/*/src/*.ts' 'packages/*/src/**/*.tsx' 'packages/*/app/**/*.ts' 'packages/*/app/**/*.tsx'",
{ encoding: "utf8", maxBuffer: 64 * 1024 * 1024 },
).split("\n").map((f) => f.trim()).filter(Boolean)
/* A path can appear under both --cached and --others in some index states; scanning it twice
would double-count its hits against a baseline that expects one. */
.filter((f, i, all) => all.indexOf(f) === i)
.filter((f) => !f.includes("__tests__") && !/\.(test|spec)\.tsx?$/.test(f));
} catch (err) {
/* FAIL CLOSED: an unreadable file list means nothing was checked, which must not read as clean. */