Repository navigation
[quality] All 8 surviving mutants in validate-architecture-assets.mjs are equivalent — annotate them so the gate can reach 100% #1806
Description
Finding
scripts/lib/validate-architecture-assets.mjs is the callable core of the asset
security gate (extracted in #1787). A StrykerJS run over it scores 97.44%,
with 8 surviving mutants. I analysed all 8 individually: none of them is
killable by a behaviour test. They are equivalent mutants and dead data, so
the mutation backlog cleared in #1788 and #1790 cannot be driven to zero here,
and every weekly scheduled run re-reports the same 8 for someone to
re-investigate.
This is not a coverage gap. The file is at 100% line and region coverage
under the --require-source-files / --check-source-file-regions 100 gate; the
mutants survive because the mutated expressions have no observable effect, not
because no test runs them.
Run / provenance
# clone of cncf/endusers at main 5511207
npm run test:mutation -- --mutate 'scripts/lib/validate-architecture-assets.mjs,scripts/lib/duplicate-check.mjs'
Local run 2026-10-22, 9m32s: 312 killed, 2 timeout, 8 survived, 0 no-coverage.
duplicate-check.mjs scored 100.00%. For contrast I also ran
--mutate 'src/components/shared/*.js,src/components/hooks/*.js' at the same
revision: 152 mutants, 100.00%, zero survivors — the rest of the tree really
is clean, which is what makes these 8 stand out.
Why each survivor is unkillable
| Line | Mutant | Why no test can kill it |
|---|---|---|
| 107 | reason: 'holds only font files…' → "" |
Dead data. Nothing reads .reason; exemptTopLevelDirs is consulted only through .get(), and only .extensions is used. |
| 119 | 'web app manifest; JSON data…' → "" |
Dead data. exemptTopLevelFiles is consulted only through .has(); the Map value is never read. |
| 120 | 'crawler directives; plain text…' → "" |
Same as line 119. |
| 152 | validateAsset(file, false, …) → true |
Unreachable by design. quality is consumed only by validateSvg(), and the only exemption's FONT_EXTENSIONS (.ttf/.woff/.woff2) contains no .svg, so validateSvg() is never called on this path. |
| 268 | writeFileSync(path, source, 'utf8') → "" |
Truly equivalent. Node treats '' as the default encoding: fs.writeFileSync(p, 'hé', '') round-trips byte-identically to 'utf8' (verified on node 22/26). |
| 348 | same as 268 | Same as 268. |
| 417 | if (!stats) return 'missing' → "" |
Equivalent. Callers compare assetRootKind() only against 'symlink' (line 394) and 'directory' (line 402); every other value takes the same branch. |
| 419 | ? 'directory' : 'other' → ? 'directory' : "" |
Same as 417 — the 'other' arm is only ever "not 'directory'". |
Recommendation
Annotate the eight sites so Stryker stops reporting them, which makes the file's
score reflect reality and leaves runtime behaviour untouched. This is the
smallest additive change; it needs no test edits and no API change.
Apply in scripts/lib/validate-architecture-assets.mjs:
@@ -104,8 +104,9 @@
'fonts',
{
+ // Stryker disable next-line StringLiteral: reviewer documentation; no code reads .reason
reason:
'holds only font files (.ttf/.woff/.woff2); never SVG or markup',
extensions: FONT_EXTENSIONS,
},@@ -117,4 +118,5 @@
const exemptTopLevelFiles = new Map([
+ // Stryker disable next-line StringLiteral: reviewer documentation; only .has() reads this Map
['manifest.json', 'web app manifest; JSON data, never parsed as markup'],
+ // Stryker disable next-line StringLiteral: reviewer documentation; only .has() reads this Map
['robots.txt', 'crawler directives; plain text, never parsed as markup'],
]);@@ -150,3 +152,4 @@
for (const file of walk(path)) {
+ // Stryker disable next-line BooleanLiteral: quality is read only for .svg, which FONT_EXTENSIONS excludes
validateAsset(file, false, exemption.extensions);
}@@ -266,3 +269,4 @@
if (!quality) {
if (shouldFix && source !== original) {
+ // Stryker disable next-line StringLiteral: node treats '' as the default utf8 encoding
writeFileSync(path, source, 'utf8');
}@@ -347,2 +351,3 @@
}
+ // Stryker disable next-line StringLiteral: node treats '' as the default utf8 encoding
writeFileSync(path, source, 'utf8');
}@@ -415,5 +420,7 @@
function assetRootKind(dir) {
const stats = lstatSync(dir, { throwIfNoEntry: false });
+ // Stryker disable next-line StringLiteral: callers only compare against 'symlink' and 'directory'
if (!stats) return 'missing';
if (stats.isSymbolicLink()) return 'symlink';
+ // Stryker disable next-line StringLiteral: callers only compare against 'symlink' and 'directory'
return stats.isDirectory() ? 'directory' : 'other';
}After this the file should report 100.00% with 0 survivors; re-run the command
above to confirm. The break threshold in stryker.config.mjs is unaffected —
it is a floor, and this only removes noise from above it.
Decision taken by hive
Two shapes were possible for lines 107/119/120: annotate the dead reason text,
or delete it and move the wording into the surrounding comments (turning
exemptTopLevelFiles into a Set). I chose annotation because it is additive,
keeps the reviewer-facing reason adjacent to the entry it justifies — which is
the stated purpose of those Maps — and changes no runtime behaviour. Deleting
the field would change the data structure of a security gate for a cosmetic
score, which is the worse trade.
Why this is an issue and not a hold-gated PR
The entire change is to scripts/lib/validate-architecture-assets.mjs, which is
source, not test code. The quality lane is restricted to test-only changes, so
it cannot open the PR for this one; it needs a human or a lane that may touch
source. Nothing here lives in .github/workflows/, so there is no token
restriction — only a lane-scope one. The diff above is mechanical and complete.
Priority
- Impact: medium — no behaviour is wrong today; the cost is a permanently
sub-100% score on a security gate and recurring re-investigation of 8
known-benign mutants on every weekly cron. - Effort: low — eight comment lines, verified by re-running one command.
🐝 Hive Agent: quality | Instance: hosted-available-lke648397-260827-5n31 | SHA: 5511207
— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88
Activity
mrbobbytables commentedon Oct 22, 2026
Agreed on all eight; the same analysis was reached independently in the Group F pass. #1808 (in the merge queue) annotates exactly these sites in scripts/lib/validate-architecture-assets.mjs (lines 107, 119, 120, 152, 268, 348, 417, 419 at 5511207) with Stryker disable directives carrying the reason, and the scoped run reports them as Ignored with 0 survivors. #1807 documents the directive convention in CONTRIBUTING, including that an Ignored mutant does not need an issue. Closing in favour of #1808.