CultNet selection cut1, commit 2e: mutation entries for the Mesh collapse

scripts/mutate-cultnet-selection-mesh.entries.ps1 covers the two rules moved in
commit 2d against tests/GameCult.Mesh.Tests: ResolveDefaultSelection's "no schema
filter when keys are given" default, and ReadDocumentFromSnapshotResponse's
recordKey-anchored exact read (including the foreign-schema-id decode fallback it
still honors).

M0: green, 87 passed (CultMeshStreamingTests). Verdicts:

- MESH-DEFAULT-Revert: KILLED
- MESH-DEFAULT-Loosening: SURVIVED
- MESH-KEY-Revert: KILLED
- MESH-KEY-Loosening: SURVIVED
- MESH-FOREIGN-Revert: KILLED
- Final: green, 87 passed. All restores byte-exact (working tree clean after the run).

Both SURVIVED entries are recorded as fixture gaps, not swapped for easier
mutants: no test in CultMeshStreamingTests.cs passes an explicit empty (non-null)
recordKeys array, and none varies a record key's case, so nothing distinguishes
MESH-DEFAULT-Loosening's `recordKeys != null` from the correct `recordKeys is
{ Count: > 0 }`, or MESH-KEY-Loosening's OrdinalIgnoreCase from the correct
Ordinal comparison. MESH-FOREIGN has a revert entry only; no distinct, still-wrong
"loosening" of "decode successfully as TDocument" was found that isn't already
one of the other two entries.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-22 03:54:36 +02:00
commit 4c33a62b29

View file

@ -0,0 +1,68 @@
# Entries for scripts/mutate-dotnet.ps1: CultNet typed selection, Cut 1, commit 2d - the CultMesh
# collapse (docs/cultnet-selection-cut.md, sections 4/7/11). Targets: src/GameCult.Mesh/CultMesh.cs
# (ReadDocumentFromSnapshotResponse's one exact read) and src/GameCult.Mesh/CultMeshSnapshots.cs
# (ResolveDefaultSelection's "no schema filter when keys are given" rule). Test project:
# tests/GameCult.Mesh.Tests.
#
# Every entry below carries its own File, so this runs as one invocation with both -Target paths.
#
# Two of the three loosening entries here SURVIVED against the current fixtures (recorded, not
# swapped for an easier mutant): MESH-DEFAULT-Loosening and MESH-KEY-Loosening. Both are genuine
# weaker readings of the rule they sit beside; no test in tests/GameCult.Mesh.Tests exercises an
# explicit empty (non-null) recordKeys array or a record-key case variation, so nothing currently
# distinguishes the loosened behavior from the correct one. That is a fixture gap in
# CultMeshStreamingTests.cs, not evidence the mutant is unreachable in principle.
@(
@{
Id = 'MESH-DEFAULT-Revert'
Rule = 'ResolveDefaultSelection: an explicit non-empty recordKeys with no schemaIds means no schema filter, not the document type''s own schema.'
Mutant = 'revert'
File = 'src/GameCult.Mesh/CultMeshSnapshots.cs'
Killer = 'FullyQualifiedName=GameCult.Mesh.Tests.CultMeshStreamingTests.SnapshotEndpoint_ProvidesTypedHandlesAndSchemaAliases'
Old = 'var schemas = schemaIds != null
? Clean(schemaIds)
: (recordKeys is { Count: > 0 } ? null : new[] { descriptor.SchemaId });'
New = 'var schemas = schemaIds != null ? Clean(schemaIds) : new[] { descriptor.SchemaId };'
},
@{
Id = 'MESH-DEFAULT-Loosening'
Rule = 'ResolveDefaultSelection: the no-schema-filter branch triggers on a non-empty recordKeys, not merely a non-null one.'
Mutant = 'loosening'
File = 'src/GameCult.Mesh/CultMeshSnapshots.cs'
Killer = 'FullyQualifiedName=GameCult.Mesh.Tests.CultMeshStreamingTests.SnapshotEndpoint_ProvidesTypedHandlesAndSchemaAliases'
Old = 'recordKeys is { Count: > 0 } ? null : new[] { descriptor.SchemaId }'
New = 'recordKeys != null ? null : new[] { descriptor.SchemaId }'
},
@{
Id = 'MESH-KEY-Revert'
Rule = 'ReadDocumentFromSnapshotResponse: a candidate must name this recordKey - the one exact read never returns a record under the wrong key.'
Mutant = 'revert'
File = 'src/GameCult.Mesh/CultMesh.cs'
Killer = 'FullyQualifiedName=GameCult.Mesh.Tests.CultMeshStreamingTests.DocumentHandle_ReadsRemotePeerSnapshotsAsTypedDocuments'
Old = 'if (!string.Equals(candidate.RecordKey, recordKey, StringComparison.Ordinal))
continue;'
New = ''
},
@{
Id = 'MESH-KEY-Loosening'
Rule = 'ReadDocumentFromSnapshotResponse: the recordKey match is exact (ordinal), not case-insensitive.'
Mutant = 'loosening'
File = 'src/GameCult.Mesh/CultMesh.cs'
Killer = 'FullyQualifiedName=GameCult.Mesh.Tests.CultMeshStreamingTests.DocumentHandle_ReadsRemotePeerSnapshotsAsTypedDocuments'
Old = 'if (!string.Equals(candidate.RecordKey, recordKey, StringComparison.Ordinal))'
New = 'if (!string.Equals(candidate.RecordKey, recordKey, StringComparison.OrdinalIgnoreCase))'
},
@{
Id = 'MESH-FOREIGN-Revert'
Rule = 'ReadDocumentFromSnapshotResponse: a foreign/runtime-generated schema id at the right recordKey still matches when its payload decodes as TDocument.'
Mutant = 'revert'
File = 'src/GameCult.Mesh/CultMesh.cs'
Killer = 'FullyQualifiedName=GameCult.Mesh.Tests.CultMeshStreamingTests.DocumentHandle_ReadsRemotePeerSnapshotsAsTypedDocuments'
Old = 'if (string.Equals(candidate.SchemaId, schemaId, StringComparison.Ordinal) ||
CultNetSchemaAliasMatching.Matches(candidate.SchemaId, descriptor) ||
TryDecodeAsMessagePack<TDocument>(candidate, out _))'
New = 'if (string.Equals(candidate.SchemaId, schemaId, StringComparison.Ordinal) ||
CultNetSchemaAliasMatching.Matches(candidate.SchemaId, descriptor))'
}
)