Both are pure decision kernels the lane's own membership rule covers
(target-annotation-serde: parse/validate/normalize the .ad comment-line
codec, zero I/O; snapshot-occlusion: pure covered/not-covered decision
where a wrong answer silently blocks or mis-allows a tap) but were
excluded from KERNEL_MODULES.
Fixing the harness's packages/*/src blind spot was required, not
optional: test-scope.ts, ownership.ts, and vitest.mutation.config.ts
all hardcoded `src/` as the only place a kernel's tests could live.
target-annotation-serde's own tests live under
packages/ad-script/src/internal/__tests__/, so without this fix the
module would score 0% from day one — not from weak tests, but because
its test file was silently invisible to the lane. Widened the same
three places, plus mutation-affected.yml's path filter and
isTestFile/ownedTestFiles in ownership.ts, to also recognize
packages/*/src/**/*.test.ts (mirroring vitest.config.ts's own
unit-core project include list).
Triaged every surviving mutant from the initial run: real coverage
gaps got a new/adjusted test (kill-with-test), everything else is
documented equivalent with an inline comment at the mutation site
explaining the invariant that makes it unobservable (redundant
early-returns, JSON.stringify dropping undefined-valued keys,
Number.isFinite/isSafeInteger's total-function safety, caller-enforced
positiveRect/candidate invariants, etc). Baseline recorded from the
actual measured run, not inherited or guessed: 94.03% (315/335) and
89.74% (175/195).
Summary
Adds two pure decision kernels to the mutation lane's
KERNEL_MODULESregistry (issue #1415), per the registry's own membership rule ("pure decision kernels only — a surviving mutant here means a silently wrong agent-facing decision"):target-annotation-serde—packages/ad-script/src/internal/target-annotation-serde.ts: parse/validate/normalize the.adtarget-annotation comment-line codec. Zero I/O.snapshot-occlusion—src/snapshot/snapshot-occlusion.ts: the covered/not-covered decision; a wrong answer silently blocks or mis-allows a tap.packages/replay-testwas considered and correctly excluded — it spawns subprocesses, which the registry's own doc comment rules out by construction.The harness had a
packages/*/srcblind spottarget-annotation-serde's own tests live inpackages/ad-script/src/internal/__tests__/, not under rootsrc/. Three places in the mutation lane hardcodedsrc/as the only place a kernel's tests could live:scripts/mutation/test-scope.ts(relatedTestFiles's filter,threadHostileTestFiles's walk root)scripts/mutation/ownership.ts(isTestFile,ownedTestFiles's walk root)vitest.mutation.config.ts(the fallbackincludelist)Without fixing this, adding the module would have "worked" in the sense that Stryker would run — but with zero tests in scope, so every mutant would survive. That's not a weak-test signal, it's the harness silently failing to find real coverage. Confirmed the fix works empirically: before the fix,
target-annotation-serdemeasured 0% (all NoCoverage); after, tests exercised it correctly and the initial run measured 68.96%.Widened all three to also recognize
packages/*/src/**/*.test.ts, mirroringvitest.config.ts's ownunit-coreprojectincludelist (which already coverspackages/*/src/**/*.test.ts— the gap was purely in the mutation lane's downstream derivation, not in the base test runner). Also widenedmutation-affected.yml's PR-trigger paths and added an assertion inworkflow.test.tslocking in thepackages/*/src/**/*.test.tstrigger glob, since ownership is now derived from that directory too.CI wiring
mutation-weekly.yml: addedtarget-annotation-serdeandsnapshot-occlusionshard jobs, bumped--expect-shards8 → 10.mutation-affected.yml: added both modules' owned source paths, plus thepackages/*/src/**/*.test.tstrigger glob.stryker.config.json: mirroredKERNEL_MODULES' newmutateglobs (asserted byscripts/mutation/config.test.ts).Baseline
Recorded from the actual final measured run — not inherited or guessed, per the ratchet's own design (it gates baseline stability, not score; a new module's first baseline must reflect its real, current test strength):
target-annotation-serdesnapshot-occlusionBoth start
status: new,gating: false,stableRuns: 0— consistent with every other module in the registry. This PR changesstryker.config.json's content, which per its own header comment invalidates the other 5 modules' recorded baselines as non-comparable until the next full sweep re-records them (provenance-drift,stableRunsreset to 0) — expected, harmless whilegating: false, and identical to what any edit to that file already does.Surviving-mutant triage
Started at 68.96% / 65.13% on the first real run (after the harness fix). Iterated: 21 new tests added across both files for genuine coverage gaps, confirmed each kill with a live Stryker run, then classified everything still standing. Every remaining survivor has an inline
// Mutation-lane note:comment at its mutation site explaining why it's unobservable — this table is the index into those.target-annotation-serde.ts— 20 remaining, all equivalentutf8ByteLength('utf8'arg)Buffer.byteLengthfalls back to utf8 for any unrecognized encoding, including''— verified empiricallytruncateToUtf8Bytesearly return (<= maxBytes) ×2endwhether or not the early return fires —value.slice(0,0)is always''truncateToUtf8Bytesloop guard (end > 0) ×2maxBytesis always a non-negative constant at every call sitetruncateToUtf8Bytessurrogate-check guard (end > 0) ×2charCodeAt(-1)returnsNaN;NaNcomparisons are always false, so entering the block is a no-op whenend === 0buildCanonicalTargetAnnotationObject/buildAncestryEntryObject/buildScrollRegionObject/parseScrollRegionFieldoptional-include guards (x !== undefined ? obj.x = x) ×5JSON.stringifyomits keys withundefinedvalues — assigningobj.x = undefinedand never assigning it serialize identically. (The "never assign a present value" side of each guard is real and tested — new/adjusted tests cover it.)parseTargetAnnotationCommentLine!trimmed.startsWith('#')guard ×2TARGET_ANNOTATION_LINE_REis itself anchored on a leading#; any non-#line fails the regex too and reaches the same{kind:'none'}via the next checkparseTargetAnnotationCommentLinepayload.trim()+ its NoCoverage fallback textJSON.parsealready tolerates surrounding whitespace (verified empirically); the fallback text is unreachable in a way that changes the observable error messageparseNonNegativeIntField/parseFiniteNumberFieldtypeof value !== 'number'clause ×3 (one LogicalOperator, two Conditional)Number.isSafeInteger/Number.isFinitenever throw and returnfalsefor any non-number, so whenevertypeofwould fire, the other clause already does too. TheNumber.isFinitecase is additionally unreachable via the real NaN/Infinity distinction —JSON.parsehas no token for either, so a parsed number is always finite.snapshot-occlusion.ts— 20 remaining, all equivalentannotateCoveredSnapshotNodesnodes.length < 2early returncoveredPositionscan never be non-empty, so the other early return a few lines down returnsnodesby the same reference anywayoverlayPositionsnon-overlay ArrayDeclaration branchfindCoveringNode!targetRectguardvisibleCoverRect's recursive call) only ever passes a node whose rect was already confirmed positivefindCoveringNodeself-coverage boundary (<=vs<) ×2position === targetPositionimpliescandidate === target, soareRectsApproximatelyEqualinvisibleCoverRectalways excludes it regardlesscanCoverPoint/visibleCoverRect!candidateguards ×3candidatePositionalways comes fromscan.overlayPositions, itself built by mapping overscan.nodes— always a valid indexvisibleCoverRect!isOverlayLikeNode(candidate,...)re-check ×2overlayPositionsbecause this exact predicate, over the exact same pristine(node, byIndex, options), already returned true when the array was builtisCandidateTouchNode/isOverlayLikeNodeown!positiveRectguards ×4findCoveringNode's!targetRectorvisibleCoverRect's!candidateRectcheck on the same datahasRenderableAdditionalOverlayAncestor/isSnapshotAncestorbyIndex.getternaries ×4Map.geton a key that was never set (includingundefined) already returnsundefined— identical to the ternary's else-branchisRenderableAdditionalOverlayNodeoptional-chaining removalisAdditionalOverlayRootNodealready confirmedoptions.isAdditionalOverlayNodeis a real function;optionsis never replaced mid-walkisRenderableAdditionalOverlayNodepositiveRect(node.rect) !== nullnormalizeNodeKindfallback text (?? '')Gates
pnpm mutation:test— 45/45 ✓pnpm mutation:check --modules target-annotation-serde,snapshot-occlusion --report .tmp/mutation/mutation.json— bothheld, no regression ✓pnpm typecheck✓pnpm lint✓