Every mutant of the Cut 1-2 chunk surface (Zone.ChunkExists, ChunkRadius,
Wear, EvaluateBelt, ChunkPose, PackZone/unpack, AsteroidBelt.Pose and its
sizes, ChunkId) was run against MiningCut1Tests and MiningCut2Tests. Six
survived; five are behaviour and get a test each:
- ChunkExists is false for a body that is not a debris field (a planet) and
for a key naming no body.
- A hit on the exact respawn instant lands on the live chunk (Wear agrees
with ChunkExists and ChunkRadius about the boundary).
- A chunk worn after respawning packs with no BrokenUntil.
- ChunkPose carries the worn size, and zero for a broken chunk.
- Chunk rotation wraps at one turn (the fixture never reached one before).
The Cut 2 fixture gains a non-belt body, and its settings the three curves
that body's constructor reads. The sixth survivor, a ChunkId hash that
ignores the index, changes no observable behaviour.
Tests only; no production change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MiningTool (its Execute range-checked against nothing, since no writer ever
set AsteroidBelt/Asteroid) and Zone.MineAsteroid (mints nothing; the
SimpleCommodity drop was commented out) are deleted, per Q1=A: "Bring back
the drill, indeed." Union tag 26 is retired, not reused. ResourceScanner's
dead survey Execute and its scan-target fields go too; its authored Range/
MinimumDensity/ScanDuration stay pending Q2. PlanetSettings.MiningDifficulty
had one reader, now gone.
Chunk wear (damage, broken-until) moves off AsteroidBelt's three per-belt
dictionaries and onto Zone as the one owner (Q3=A): Zone.Wear is the only
writer, ChunkExists/ChunkRadius are the readers, and respawn is a lazy
comparison against zone time, never a per-tick loop. ChunkId is
(CultRecordKey Field, int Index) -- "Field", not "Belt", because chunks are
meant to generalize past asteroid belts to other debris fields later.
Persisted in ZonePack's new key 6 (List<ChunkWearPack>, nullable so an older
record with no key 6 loads with no wear instead of throwing), pruned of
healed, non-broken entries at pack.
The break threshold stays strictly greater-than, matching today's behaviour:
damage exactly equal to hitpoints does not yet break the chunk.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Stryker over Zone.cs (231 mutants, scoped by --mutate to this cut's file)
found two real gaps, both NoCoverage/Survived, not equivalents:
- Zone.EvaluateBelt and AsteroidBelt.Evaluate -- the renderer's own path
(ZoneRenderer.cs Update, AsteroidBeltUI.Update) -- were never called by any
test, so the whole batch body and its loop bound were unreached.
EvaluateBeltFillsEveryAsteroidExactlyAsChunkPoseWould pins that the batch
path fills every index exactly as the per-index ChunkPose would, closing
the gap without duplicating the orbit formula a second time.
- AsteroidBelt.Radius (data.Asteroids.Max(a => a.Distance)) had no assertion;
a Max-to-Min mutant survived. BeltRadiusIsTheFarthestAsteroidsDistance pins
it, since the renderer's visibility bounds and minimap mesh both size off
this field.
Full suite: 262/262 green (260 + these 2).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zone.Update no longer threads a per-belt Task.Run over a double-buffered
Transforms/OrbitPosition pair. AsteroidBelt.Pose/Size are the one place a
chunk pose is computed, a pure function of zone time; Zone.ChunkPose and
Zone.EvaluateBelt read them at the zone's own time. MiningTool,
ResourceScanner and ZoneRenderer (belt matrices and AsteroidBeltUI) all read
through Zone instead of the deleted per-tick buffers; the renderer owns its
own float4[] buffer, filled fresh every frame.
Deleted: System.Threading/.Tasks usings, BeltUpdates, the wait loop, the
per-belt copy+Task.Run, NearestAsteroid (dead/broken reader), the private
UpdateAsteroidTransforms, and AsteroidBelt's four pose fields plus their
allocations. ~65 lines, matching the cut map's estimate.
miningprobe, densest zone of the operator's save (EAC-2265, 809 asteroids,
8 belts), rebuilt against this cut and rerun 10x each side:
before (branch point, 8bd25f6f/e729e6d6):
Zone.Update wall 541-3654 us/tick, CPU 120-391 us/tick (7 clean runs)
3/10 runs crashed: InvalidOperationException from Zone.GetOrbitPosition,
the documented _updatedOrbits/Orbits data race between the main thread's
Clear() and a belt Task.Run
after (this commit):
Zone.Update wall 95-185 us/tick, CPU 99-152 us/tick, in line with the
31-39 us "no belt work" baseline the map recorded -- belts are free per
tick
0/10 races
ChunkPose has no buffer to go stale: ~0.12 u of parent+asteroid motion is
visible across one 17 ms tick instead of being one tick behind
tests/Aetheria.Shared.Tests/MiningCut1Tests.cs (new, 260/260 suite green):
ChunkPoseIsTheOrbitAtZoneTime, ChunkPoseDoesNotDependOnUpdateCadence,
DamagedChunkShrinksAndABrokenOneHasNoSize -- each expected value is the
closed-form orbit/size rule reimplemented in the test, never a call to
AsteroidBelt.Pose/Size.
Negative grep (Zone.cs, "Zone Display", ServerShared/Behaviors) for
Task\.Run|System\.Threading|NewTransforms|\.Transforms\b|BeltUpdates|
NearestAsteroid|\.OrbitPosition\b|NewOrbitPosition: one hit, the commented
line in MineAsteroid that Cut 2 deletes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>