* fix(#4141): ignore Stryker's sandbox in eslint's global ignores `stryker.config.mjs` sets `tempDirName: '.stryker-tmp'`, `.gitignore` ignores it, and Stryker's own always-ignored list carries it. eslint's global `ignores` did not, and there is no `.eslintignore`. A mutation run that dies before cleanup — chunk timeout, CI cancellation, Ctrl+C, a crash — leaves a full sandbox copy of the tree at `.stryker-tmp/sandbox-*/`, and `npm run lint` then lints the copy. The failure is not the duplicate-file failure that would suggest, which is what makes it confusing: the `local` plugin is registered only in path-scoped config blocks (`tests/**/*.cjs`, `scripts/**/*.cjs`, `hooks/**`), and a copy at `.stryker-tmp/sandbox-*/tests/*.cjs` matches none of them, so every inline `// eslint-disable-next-line local/<rule>` the original carries becomes `Definition for rule 'local/<rule>' was not found` at the copy's path. The error text names rules rather than paths, so the first read is "my change broke the local plugin" — observed as 834 errors on a branch whose own diff was clean, from a sandbox eight days stale. CI never sees this (fresh checkout), so it is purely a local-contributor tax, but a loud and misleading one. The regression test asserts the invariant the way ESLint resolves it — `isPathIgnored()` over real flat-config precedence, not a textual scan of the ignores array — mirroring the #551 block it sits beside, and carries the control that the real `tests/worktree.test.cjs` at the mirrored path is still linted, so a pattern widened enough to swallow the tree cannot pass. Scoped to the reported bug: `reports/` may deserve the same treatment, but no lint failure has been reproduced from it, so it is not claimed here. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zgWzB96uz3LVKdJePYTLR * chore(#4141): backfill the changeset PR number Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017zgWzB96uz3LVKdJePYTLR * chore(#4141): restore the file's 2-blank-line block separator Review nit: the #4141 block left three blank lines before the RETIRED block, where every other block boundary in this file uses two. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mw7WhYnrWdY8cmj8NSJj4R --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
5
.changeset/kind-finches-hum.md
Normal file
5
.changeset/kind-finches-hum.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 4178
|
||||
---
|
||||
**`npm run lint` no longer fails on a leftover Stryker sandbox** — `eslint.config.mjs` now ignores `.stryker-tmp/**`, the scratch directory Stryker itself, `.gitignore` and `stryker.config.mjs` already treat as disposable. A mutation run interrupted before cleanup used to leave a copy of the tree there, and linting that copy reported the path-scoped `local/*` rules as undefined — hundreds of "Definition for rule … was not found" errors on a clean branch. (#4141)
|
||||
@@ -78,6 +78,11 @@ export default tseslint.config(
|
||||
'.worktrees/**',
|
||||
'.claude/**',
|
||||
'coverage/**',
|
||||
// #4141: Stryker's sandbox (tempDirName in stryker.config.mjs, also gitignored
|
||||
// and always-ignored by Stryker itself). A run that dies before cleanup leaves a
|
||||
// copy of the tree here; linting it reports the path-scoped `local/*` rules as
|
||||
// undefined, which reads as the plugin being broken rather than as scratch space.
|
||||
'.stryker-tmp/**',
|
||||
'**/*.generated.cjs',
|
||||
// ADR-457: tsc-generated runtime artifact — lint the src/*.cts source, not the emitted .cjs.
|
||||
'gsd-core/bin/lib/claude-orchestration.cjs',
|
||||
|
||||
@@ -124,6 +124,54 @@ describe('ESLint coverage tracks the bin/lib TS migration (ADR-457 / #537)', ()
|
||||
}
|
||||
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────
|
||||
// #4141 — Stryker's sandbox is scratch space and must not be linted
|
||||
// ────────────────────────────────────────────────────────────────────────
|
||||
{
|
||||
const { describe: __d, test: __t, before: __b } = require('node:test');
|
||||
const __assert = require('node:assert/strict');
|
||||
const __path = require('node:path');
|
||||
const { ESLint: __ESLint } = require('eslint');
|
||||
|
||||
const __ROOT = __path.resolve(__dirname, '..');
|
||||
// `stryker.config.mjs` sets tempDirName '.stryker-tmp', .gitignore ignores it, and
|
||||
// Stryker itself always ignores it. A run that dies before cleanup (chunk timeout,
|
||||
// CI cancellation, Ctrl+C) leaves a whole sandboxed copy of the tree behind, and
|
||||
// `npm run lint` then lints the copy: the `local/*` plugin is registered only in
|
||||
// path-scoped blocks (tests/**, scripts/**, hooks/**), none of which match
|
||||
// `.stryker-tmp/sandbox-*/…`, so every inline `local/…` disable comment in the copy
|
||||
// becomes "Definition for rule … was not found". Verified as ESLint resolves it, not
|
||||
// as a textual scan of the ignores array — same approach as the #551 block above.
|
||||
const __SANDBOX_FILE = '.stryker-tmp/sandbox-nBAAwa/tests/worktree.test.cjs';
|
||||
const __REAL_FILE = 'tests/worktree.test.cjs';
|
||||
|
||||
let __eslint;
|
||||
__b(() => {
|
||||
__eslint = new __ESLint({ cwd: __ROOT });
|
||||
});
|
||||
|
||||
__d("ESLint ignores Stryker's sandbox (#4141)", () => {
|
||||
__t('a file inside .stryker-tmp is ignored', async () => {
|
||||
__assert.equal(
|
||||
await __eslint.isPathIgnored(__path.join(__ROOT, __SANDBOX_FILE)),
|
||||
true,
|
||||
`${__SANDBOX_FILE} is a Stryker sandbox copy and must never be linted`,
|
||||
);
|
||||
});
|
||||
|
||||
// Control: without this the assertion above would also pass if the ignore
|
||||
// pattern were widened enough to swallow the real tree.
|
||||
__t('the real file at the mirrored path is still linted', async () => {
|
||||
__assert.equal(
|
||||
await __eslint.isPathIgnored(__path.join(__ROOT, __REAL_FILE)),
|
||||
false,
|
||||
`${__REAL_FILE} is committed source and must stay linted`,
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────
|
||||
// RETIRED: folded:bug-3054-stale-gsd-next-references (consolidation epic #1969 B8 #1977)
|
||||
// ────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user