Symptom
While running npm test (specifically jest --ci --runInBand) locally against this repo's own working directory, git status intermittently reports ~82 files under packages/installer/, packages/aiox-install/, and packages/aiox-pro-cli/ as deleted (D), and package-lock.json gets rewritten without anyone requesting it — while the test run is in flight.
Root cause (process evidence)
During a jest --ci --runInBand tests/cli/pro-buyer.test.js tests/integration/wizard-ide-flow.test.js run, the process tree showed five concurrent real npm installs running against this checkout:
npm-cli.js install --no-audit --no-fund (PIDs 76428, 100864, 87052, 13072)
cmd.exe /c ""C:\Program Files\nodejs\npm.cmd" install --no-audit --no-fund" (PID 45680)
These are not mocked/sandboxed — they are real npm install invocations, spawned by test suites, executing against (or dangerously close to) the repository's own working tree/node_modules, not an isolated temp directory. PID 45680 is notably the exact cmd.exe /c + npm.cmd + shell:true invocation pattern that PR #823 replaces in pro-setup.js for production code — the test suite itself reproduces the pattern our own fix is trying to move away from.
With --runInBand, this run took over an hour to complete, which massively widens the window during which the working tree is in this contended, half-written state.
Real impact (not theoretical)
On 2026-08-15, this exact repo/working-directory had:
tests/installer/pro-setup-auth.test.js (26,582 bytes) — 100% zero-filled (every byte \x00)
.aiox-core/data/entity-registry.yaml (593,494 bytes) — 100% zero-filled (every byte \x00)
.git/index — corrupted (bad signature 0x00000000)
This caused permanent loss of uncommitted work (a full set of unit tests written in a prior session had to be rewritten from scratch, with no recoverable copy — git fsck --lost-found scanned 2,875 dangling blobs with zero hits for the lost content).
The working hypothesis (not proven, but consistent with all observed evidence) is that one of these concurrent/interrupted npm install processes was writing to those two files (or to .git/index) at the moment something interrupted it (process kill, host-level interruption, or a Windows filesystem hiccup on a long-held write), leaving them zero-padded instead of fully written.
Aggravating factor
--runInBand serializes test execution, which is likely why the window stayed open for over an hour instead of failing fast — a parallelized run would have shortened (though not eliminated) the exposure window.
Suggested fix
Test suites that need to exercise real npm install behavior (e.g. Pro artifact installation, dependency installer tests) should:
- Always target an isolated temp directory via
--prefix/cwd, never the repository's own working directory or node_modules.
- Never allow more than one such install to run concurrently against overlapping paths.
Worth auditing (not confirmed as the source, but likely candidates given the pattern):
tests/installer/pro-setup-target-install.test.js
tests/pro-wizard.test.js
tests/cli/pro-buyer.test.js
tests/integration/wizard-ide-flow.test.js
Secondary, related finding: validate:port-denylist scans the whole working tree, not the diff
While working around this incident, npm run validate:port-denylist (invoked via the pre-push husky hook) blocked a legitimate push citing a false positive: a stray untracked file elsewhere in the working directory (unrelated to the branch/commit being pushed) containing the literal checklist line - No secrets/credentials in diff, which the scanner's [secrets-path] pattern matched as if it were an actual secret path.
Two problems here:
- False positive: matching on the phrase "secrets/credentials" rather than an actual secret value/path.
- Wrong scope: the scanner reported
Files scanned: 1298/1299 — it walks the entire working directory rather than the files actually in the diff/commits being pushed. This means any untracked file sitting in a contributor's working tree (regardless of branch, regardless of whether it's part of the push) can block git push for everyone, and is itself a contributing factor to the kind of working-tree instability described above (contributors reaching for --no-verify under pressure to get unblocked).
Suggest scoping validate:port-denylist to git diff --name-only <base>...HEAD (or the equivalent set of files actually being pushed) instead of a full filesystem walk.
Labels
Suggest: bug, high priority (data-loss-adjacent).
Symptom
While running
npm test(specificallyjest --ci --runInBand) locally against this repo's own working directory,git statusintermittently reports ~82 files underpackages/installer/,packages/aiox-install/, andpackages/aiox-pro-cli/as deleted (D), andpackage-lock.jsongets rewritten without anyone requesting it — while the test run is in flight.Root cause (process evidence)
During a
jest --ci --runInBand tests/cli/pro-buyer.test.js tests/integration/wizard-ide-flow.test.jsrun, the process tree showed five concurrent real npm installs running against this checkout:These are not mocked/sandboxed — they are real
npm installinvocations, spawned by test suites, executing against (or dangerously close to) the repository's own working tree/node_modules, not an isolated temp directory. PID 45680 is notably the exactcmd.exe /c+npm.cmd+shell:trueinvocation pattern that PR #823 replaces inpro-setup.jsfor production code — the test suite itself reproduces the pattern our own fix is trying to move away from.With
--runInBand, this run took over an hour to complete, which massively widens the window during which the working tree is in this contended, half-written state.Real impact (not theoretical)
On 2026-08-15, this exact repo/working-directory had:
tests/installer/pro-setup-auth.test.js(26,582 bytes) — 100% zero-filled (every byte\x00).aiox-core/data/entity-registry.yaml(593,494 bytes) — 100% zero-filled (every byte\x00).git/index— corrupted (bad signature 0x00000000)This caused permanent loss of uncommitted work (a full set of unit tests written in a prior session had to be rewritten from scratch, with no recoverable copy —
git fsck --lost-foundscanned 2,875 dangling blobs with zero hits for the lost content).The working hypothesis (not proven, but consistent with all observed evidence) is that one of these concurrent/interrupted
npm installprocesses was writing to those two files (or to.git/index) at the moment something interrupted it (process kill, host-level interruption, or a Windows filesystem hiccup on a long-held write), leaving them zero-padded instead of fully written.Aggravating factor
--runInBandserializes test execution, which is likely why the window stayed open for over an hour instead of failing fast — a parallelized run would have shortened (though not eliminated) the exposure window.Suggested fix
Test suites that need to exercise real
npm installbehavior (e.g. Pro artifact installation, dependency installer tests) should:--prefix/cwd, never the repository's own working directory ornode_modules.Worth auditing (not confirmed as the source, but likely candidates given the pattern):
tests/installer/pro-setup-target-install.test.jstests/pro-wizard.test.jstests/cli/pro-buyer.test.jstests/integration/wizard-ide-flow.test.jsSecondary, related finding:
validate:port-denylistscans the whole working tree, not the diffWhile working around this incident,
npm run validate:port-denylist(invoked via thepre-pushhusky hook) blocked a legitimate push citing a false positive: a stray untracked file elsewhere in the working directory (unrelated to the branch/commit being pushed) containing the literal checklist line- No secrets/credentials in diff, which the scanner's[secrets-path]pattern matched as if it were an actual secret path.Two problems here:
Files scanned: 1298/1299— it walks the entire working directory rather than the files actually in the diff/commits being pushed. This means any untracked file sitting in a contributor's working tree (regardless of branch, regardless of whether it's part of the push) can blockgit pushfor everyone, and is itself a contributing factor to the kind of working-tree instability described above (contributors reaching for--no-verifyunder pressure to get unblocked).Suggest scoping
validate:port-denylisttogit diff --name-only <base>...HEAD(or the equivalent set of files actually being pushed) instead of a full filesystem walk.Labels
Suggest:
bug, high priority (data-loss-adjacent).