The `==`->`!=` and `< 0`->`> 0` mutants on Extract's orientation check are
both Killed. The one survivor, `OrientationSign[axis] <= 0`, is an
equivalent mutant, not a coverage gap: OrientationSign only ever holds ±1,
so `< 0` and `<= 0` agree on every value it can produce. No vstest
coverage-based false survivor has actually been confirmed for
GameCult.Geometry; remove the withdrawn claim and keep the correct notes
about full-SHA --since, safe.directory, and config-change scope fan-out.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Stryker's diff-scoped run against this cut reported CultGeometrySurfaceNets
.Extract's `if (inside == (OrientationSign[axis] < 0))` - the sole authority
for quad winding after F4's fix deleted the geometric cross-product fallback
- as a Survived Equality mutation (== -> !=). Hand-mutating it and running
dotnet test directly failed 4 of 38 SurfaceNetsTests (the three per-axis
corner-order tests and the outward-winding test), confirming it is a real
vstest coverage-based test-selection miss, not a genuine gap. Recorded this
as a second confirmed case alongside the mtp/static-readonly-initializer
caveat, per this cut's verification requirement to hand-confirm any survivor
that looks like it must be covered.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The three TryGetOtherEndpoint survivors were triaged as a vstest coverage
false-survivor, mirroring the confirmed mtp/static-readonly caveat above.
That triage was wrong: the actual mutant is `return false` -> `return true`
inside each per-axis guard, which is equivalent (it makes the method report
an edge going nowhere, so the caller compares a sample against itself and
skips it exactly as it would with a correct `false`). Rewrote the paragraph
to say so, and removed the unsupported claim that this project has a
confirmed vstest false-survivor case. Also recorded, per this cut's
verification requirements, that `--since` needs a full commit SHA and that
`safe.directory` must be set in a container.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three TryGetOtherEndpoint boundary-guard mutants (per-axis "if (x + 1 >=
sizeX) return false;" forced to "if (true) return false;") were reported
Survived despite being exercised by the X/Y/Z open-boundary tests. Hand-
mutating all three and running dotnet test directly failed 11 of 24
SurfaceNetsTests, confirming a vstest-runner coverage-attribution false
survivor (same class of caveat already recorded for CultMath's mtp runner,
here under the default runner). Recorded under Known tool caveat.
Separately, adding stryker-config.json in the same cut that first uses it
defeats --since line scoping: Stryker fell back to mutating the whole
GameCult.Geometry.csproj glob ("Non-CSharp files in test project were
changed"), not just the new files, which is why the run's survivor list
includes CultGeometryIsoSurface.cs, CultGeometryDocuments.cs, and
CultGeometryPrimitives.cs. Recorded under Follow-ups so a later diff-only
cut isn't surprised by the difference.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds SurfaceNetsTests.cs and QuadNormalsTests.cs covering the sign
convention, edge naming, outward winding, vertex placement (midpoint and
unequal-crossing means, origin/cellSize), closed-sphere welding and
Euler characteristic, an ambiguous checkerboard cell's even-but-not-2 edge
usage, open-boundary quad suppression, determinism/ordering, and
face-weighted normals (sliver-vs-large-quad area weighting, unit cube
normals, zero-area/untouched-vertex degenerate case). Adds
stryker-config.json (vstest runner; this project is NUnit 4, not xunit.v3)
and records GameCult.Geometry as scoped, diff-only, in
docs/mutation-testing.md's follow-ups.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CultMath.Tests is on xunit.v3. Stryker's default vstest runner cannot
capture coverage against xunit.v3's test host and hangs retrying test
sessions indefinitely (observed: 2096 mutants queued, zero completed after
~9 minutes of retries). Stryker's Microsoft.Testing.Platform runner
(-t mtp, still preview) works correctly against xunit.v3 and completed the
same 1351-mutant run in ~4.5 minutes. Pin it in config so a plain
`dotnet stryker` works without a CLI flag.
Add docs/mutation-testing.md: how to run it, the --since form for a single
cut, the triage rules, a known coverage-capture caveat in the mtp runner
(confirmed by hand-mutation against static readonly fields), and the
packages still needing their own config.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>