fix(#4106): drop the orphaned test:mutation:since script (#4179)

* fix(#4106): drop the orphaned test:mutation:since script

`"test:mutation:since": "stryker run --incremental --since origin/next"`
could not run. Stryker has no `--since` flag — verified against the pinned
`@stryker-mutator/core` 9.6.1, whose options schema has no `since` property
and whose CLI exits `error: unknown option '--since'`.

It went unnoticed because nothing invokes it. `.github/workflows/mutation.yml`
uses the per-module matrix instead: `scripts/mutation-matrix.cjs` computes the
changed covered modules and each shard runs its own
`npx stryker run --incremental --mutate "${MUTATE_GLOB}"`. That path works, so
no gate ever exercised the script.

It is still worth removing rather than leaving: its name makes it the obvious
thing to reach for when a reviewer asks for a mutation score on changed scope,
and the Commander parse error it produces says nothing about the matrix being
the real entry point — the next person has to reverse-engineer mutation.yml to
find that out.

Delete rather than repoint. The issue offered both, and the maintainer had not
picked; delete is the option that stays scoped to the reported bug. The
suggested replacement (`node scripts/mutation-matrix.cjs --base origin/next`)
emits a CI matrix rather than running mutants, so shipping it under a
`test:mutation:*` name would be a second, differently-shaped claim, and
documenting the per-module invocation in CONTRIBUTING.md is enhancement-shaped
work that belongs in its own issue. The discovery command is named in the
changeset instead. Flipping this PR to the pointer script is a one-line change
if that is preferred.

The regression test asserts that every `stryker` script in package.json passes
only flags Stryker's own options schema defines. It carries its own control —
the historical `--incremental --since origin/next` string is checked to still
be rejected by the same predicate — so the sweep cannot go quietly vacuous if
the remaining invocations lose their flags.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zgWzB96uz3LVKdJePYTLR

* chore(#4106): backfill the changeset PR number

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zgWzB96uz3LVKdJePYTLR

* docs(#4106): mark the changeset docs-exempt

`type: Removed` makes docs-lint require a `docs/` change. Nothing under
`docs/` or in CONTRIBUTING.md ever referenced `test:mutation:since` — the
script was orphaned and could not run — so there is no documented behaviour
to update. Using the gate's own per-fragment escape hatch rather than
weakening the changeset type to dodge the requirement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017zgWzB96uz3LVKdJePYTLR

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
Adnan
2026-09-05 07:21:02 +01:00
committed by GitHub
parent c6efe2905c
commit 0f3959516c
3 changed files with 80 additions and 1 deletions

View File

@@ -0,0 +1,7 @@
---
type: Removed
pr: 4179
---
**Dropped the `test:mutation:since` npm script** — it passed `--since`, which Stryker 9.x does not accept (`error: unknown option '--since'`), so it could not run at all. Nothing invoked it: the mutation gate runs the per-module matrix from `scripts/mutation-matrix.cjs` instead, so no workflow regresses. To see which modules a change puts in scope, run `node scripts/mutation-matrix.cjs --base origin/next --print`. (#4106)
<!-- docs-exempt: contributor tooling only — no docs/ page or CONTRIBUTING.md section referenced this script (grep for `test:mutation` across docs/ and CONTRIBUTING.md returns nothing), and it could never run, so there is no documented behaviour to update. -->

View File

@@ -155,7 +155,6 @@
"test:coverage:report": "c8 report --reporter text --reporter json-summary --merge-async --include 'gsd-core/bin/lib/**/*.cjs' --exclude 'tests/**' --all && node scripts/check-coverage-gate.cjs",
"test:coverage:all": "npm run test:coverage",
"test:mutation": "stryker run",
"test:mutation:since": "stryker run --incremental --since origin/next",
"gen:features": "node scripts/gen-features.cjs",
"gen:state-md-docs": "node scripts/gen-state-md-docs.cjs"
},

View File

@@ -431,3 +431,76 @@ test('--cwd pointing at a non-existent path fails uniformly across families', ()
});
});
}
// ────────────────────────────────────────────────────────────────────────
// #4106 — every Stryker flag in package.json is one Stryker actually accepts
// ────────────────────────────────────────────────────────────────────────
{
const { describe: __d, test: __t } = require('node:test');
const __assert = require('node:assert/strict');
const __fs = require('node:fs');
const __path = require('node:path');
const __ROOT = __path.resolve(__dirname, '..');
const __PKG = require(__path.join(__ROOT, 'package.json'));
const __SCHEMA = __path.join(
__ROOT, 'node_modules', '@stryker-mutator', 'core', 'schema', 'stryker-schema.json',
);
// Stryker's CLI options are generated from its own options schema, so the schema's
// property names ARE the accepted long flags. Three commander-level flags have no
// schema entry because they are not run options.
const __CLI_ONLY = new Set(['files', 'help', 'version']);
/** Long flags a `stryker …` npm script passes, normalised (`--mutate=x` → `mutate`). */
function __strykerFlags(script) {
if (!/(^|\s)(npx\s+)?stryker(\s|$)/.test(script)) return null;
return script
.split(/\s+/)
.filter((tok) => tok.startsWith('--'))
.map((tok) => tok.slice(2).split('=')[0]);
}
__d('package.json Stryker scripts pass only flags Stryker accepts (#4106)', () => {
const accepted = new Set([
...Object.keys(JSON.parse(__fs.readFileSync(__SCHEMA, 'utf-8')).properties || {}),
...__CLI_ONLY,
]);
// Guard: a moved/renamed schema would otherwise reject every flag for the wrong reason.
__assert.ok(
accepted.size > 20,
`no options schema at ${__SCHEMA} — @stryker-mutator/core may have moved it`,
);
__t('the checker rejects the flag this bug shipped', () => {
// `--since` is what `test:mutation:since` carried; Stryker 9.x exits with
// "error: unknown option '--since'". Pinning it here keeps this suite honest
// if every real invocation later drops its flags.
const flags = __strykerFlags('stryker run --incremental --since origin/next');
__assert.deepEqual(flags, ['incremental', 'since']);
__assert.equal(accepted.has('incremental'), true);
__assert.equal(accepted.has('since'), false, '--since is not a Stryker option');
});
__t('every stryker script in package.json is runnable', () => {
const offenders = [];
let scanned = 0;
for (const [name, script] of Object.entries(__PKG.scripts || {})) {
const flags = __strykerFlags(script);
if (flags === null) continue;
scanned += 1;
for (const flag of flags) {
if (!accepted.has(flag)) offenders.push(`${name}: --${flag}`);
}
}
__assert.ok(scanned > 0, 'no stryker script found — the sweep matched nothing');
__assert.deepEqual(
offenders,
[],
`package.json scripts pass flags Stryker does not accept: ${offenders.join(', ')}`,
);
});
});
}