- ActionGameManager: the visible hostile/friendly indicators and MenuMap.Position read DrawAhead.Position at Clock.Lead.
- DrawAhead.Round drops its lead parameter: the target's lead is the drawn time less its zone's step time, derived inside,
so a caller cannot pass two times that disagree. Projectile and BallisticFlightTests updated; the hit tests derive their
lead from the drawn time; two assertions that only varied the now-absent lead are removed.
- Zone.RailAt logs "Requested orbit is not part of this zone!" for a missing parent again (DrawnPoseTests.AMissingParentOrbitIsLoggedAtADrawnTime).
Builds: not yet compiled at this commit (verified by the run that follows).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GetOrbitPosition's step-time fill and RailAt each decided the rail chain (root at
FixedPosition, child at parent plus offset). The fill is now RailAt(id, _time).
DrawnPoseTests builds the sun on an off-centre FixedPosition (the ZoneGenerator
cluster-root shape) and adds a root test at step and drawn time, and an unset-orbit
OrbitalEntity fact for the DrawAhead guard.
All commits build, unverified until the job in the report.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Planets, belts, wells, loot, mines, stations, the chunk reticle and the flow
clock are evaluated through DrawAhead (Time, Orbit, Height, Position) at one
drawn time; the rail law lives once in Zone.Offset; OrbitPositionAt, GetHeight,
ChunkPose, EvaluateBelt and RideHeight take a time. Fixes the planet jumping
against a co-moving ship at display rates above the step rate.
Not yet built or run (Unity presenter files are verified by compile only on
Starfire).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PeriodAround's ring term is one rule: self feels the mean, over the members whose pull it takes (itself if bodied, else every
bodied ring member), of half the rest of the ring. A station on a binary's ring no longer stands still. The 0.1 u window is a
named constant, pinned just inside and just outside it; a bodyless node's children are pinned never to read the node's parent's well.
Both commits build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zone.PeriodAround is the one owner: round a body, the pull its well gives at the rail's distance
(GravityField.Acceleration); round a bodyless node, the ring law (strength of the orbits inside, plus
half of what shares the ring). Orbit.Period and AsteroidBelt.Periods are written once at zone build,
before placement; BeltTargets copies the belt's periods. A parentless orbit has no field and stands at
its phase. Fixtures lose OrbitPeriod and gain wells where their rails must move. Builds; the new
OrbitsFromFieldTests follow in the next commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
core-at-roche-and-exaggeration-up: PlanetSettings.CoreRadius and Planet.CoreRadius are
deleted; the well's softened core reads the RocheLimit curve (one curve of mass). The
core < Roche guard cannot fail and is replaced by a Roche-limit-positive-and-finite
check. Settings.asset: CoreRadius removed, HeightExaggeration 10 -> 4.0983607
(10 / (7.32 / 3)). Tests pin the new curves and the peak drawn slope / 2.44x steeper tail.
Not yet built or run at this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zone.WellReference owns each body's reference zero (GetHeight uses it). Gravity.cginc mirrors GravityField.Potential;
Brushes/Gravity Field draws depth = -h on layer 9 from a StructuredBuffer that GravityLowering.Lower fills from the
zone each frame; GravityLowering.CreateField makes the one quad. GravityFieldParityVerify renders it and compares every
texel with -Zone.GetHeight (max error 0.0059 u), the gradient with GetForce (0.03 degrees) and the wiring.
Deleted: the Gravity Well children of Planet, Gas Giant, Sun and PlanetOutpost prefabs, Wormhole 1's three gravity
children, their overrides in Main Menu and TestScene, five wormhole/wave materials and two brush shaders,
Planet.GravityWellDepth/Radius, BodyData keys 6-7, PlanetSettings.GravityDepth/GravityRadius (and the Settings.asset
keys). Surface Displacement culls layer 9 only. Headless build and tests not yet run at this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A WellStrength that evaluates negative, NaN or infinite now throws at construction (it built an
antimass well with NaN height). New tests: the core-inside-Roche guard judged at every mass on
curves that cross in the generated range; per-body multipliers move neither strength, core nor
Roche limit; potential and force consistency inside the cores' outer bands (0.5 R to R); a
moving Ship feels the field where it has moved to.
Not built or run locally; verified in the Yggdrasil job.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GravityField is the law of one well (logarithmic tail, softened core).
Zone.GetForce and GetHeight sum it over the bodies; GetNormal derives from
the force for ship tilt; Flow and Ship read GetForce. Well strength, core
radius and Roche limit are PlanetSettings curves of mass, height
exaggeration K one global. Deletes GravityAcceleration, GravityReferenceRate,
GravityStrength, the PowerPulse well, GetNormal's finite differences and
BodyData.GravityDepthExponent. Settings.asset carries the calibrated curves.
Tests: PotentialOwnerTests, the moon's moon placement, retired keys over every
BodyData type, a body-derived giant sweep. Not yet built or run (verify job
follows); this commit may not build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GetOrbitPosition places a parent as a side effect of placing its child, so with a child listed first the parent's previous position was already its new one and its Velocity came out zero. The constructor's unread PreviousPosition write is dropped; the first Update takes it from the placed Position. The velocity test now runs moon-first and planet-first packs over varying dt. Builds not verified yet (verify follows).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GetHeight sums body wells only. Removes RadialWaves, GasGiant wave fields,
GasGiantData keys 14-17, PlanetSettings Zone*/Wave* fields, the minimap
gravity range, the Boundary brush and Gravity Waves objects from the scene
and both prefabs. The five bowl fixtures become one WellFixture well at the
origin with unchanged expected numbers. Not yet built or tested: this commit
has not been through the Yggdrasil job.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Orbit.Position and PreviousPosition are set from GetOrbitPosition at the pack's
time in the constructor, so wells, lights and poses are valid before the first
Update. AddOrbit and CreateOrbit had no caller. Tests not yet run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PickupTests: TheBeamTakesFromWhicheverBayCanStore is a theory over which bay is full.
CapabilityPresentationSmoke: tracked ship's hull hit is drawn; a beam above the held point stays on the ground plane; a grab at the beam keeps its aim; the other entity's grab reaches its own bound beam and not this one.
FloatingItemsDrawnSmoke: a held item's label comes up; the test scene's stand-in pose equals the zone's held pose.
Zone.HeldPull is the one smoothstep of the held pose; FieldDriver's tendril and FieldTester's stand-in call it (FieldTester.PulledPosition, PullProgress), deleting the two copies.
All commits build (Unity compile and both smokes green).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- VolumeSampling registers FLOW_GLOBAL / FLOW_SLOPE with GlobalKeyword.Create (the constructor only looks up).
- FlowField.Scroll folds each Tri3D octave's phase modulo 1 in double (float2, [0,1)); Zone.Flow evaluates at Zone.FlowScroll.
- FlowField.Cycle: the nebula's flow cycle and Stardust's particle lifetime phase come from zone time (_FlowCycle, phase uniform), not _Time.y / Time.time.
- FlowFieldParityVerify reaches the keyword-gated flow(), fails on logged errors, tolerance 3e-4; mutants updated.
Not built or run when committed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FlowField (ServerShared) is the flow law; Zone.Flow/FlowScroll read PlanetSettings.Flow at zone
time. The Flow block moves from DefaultEnvironment to PlanetSettings (ZoneEnvironment key 1 is
retired), VolumeSampling's frame-clock scroll accumulator and the Stardust leftover field are
deleted, and slopeFlow returns 0 for a zero gradient. Unity parity probe follows in the next
commit; this commit has not been built in Unity.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: Behaviors.cs keeps Union(48, PickupData) and Union(49, LaunchSuppressorData);
Zone.cs keeps the departure record on _entities with master's Visibility (emissions-heard);
ContactRevealTests.cs keeps master's removal of the emission-departure test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GrabEvent carries a FloatingBodyId; Zone gains TryHold/SetHeldProgress/Free and places a held item instead of
integrating it; Entity.TractorPower is derived from Pickup.Power and TractorCommand is the pilot's input
(ActionGameManager writes it undamped, the Time.deltaTime ramp is deleted). Binder and FieldTester change the
delegate type only. PickupTests and a save round-trip pin for the player's zone index.
Group A builds; no beam is on any generated ship until group B.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Entities is a MemberView with no mutating member; Admit installs the death watch with the
membership and refuses a member already present, Remove takes it away with the membership.
The Reset and duplicate-Add bypass paths of the death watch cannot be spelled any more.
Tests move to Admit/Remove; Admit also activates, so the explicit Activate calls that followed
a raw Add are dropped. Unity call sites route through Zone.Remove.
Builds: unverified at this commit (verified in the job at the head).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WatchDeath returns when the entity already holds a watch, so a duplicate
Entities.Add neither throws nor leaves a second live subscription;
Entities.Clear (ObserveReset) disposes every watch. Adds
AMemberAddedTwiceStillDropsEachItemOnce, AClearedZoneDropsNothingWhenAFormerMemberDies
(finding duplicate-add-leaks-second-watch) and
APickUpOfExactlyTheRoomLeftFitsAndOneMoreChangesNothing (finding
pickup-room-arithmetic-unpinned, from Soul's boundary probe). Unverified
until the Yggdrasil job; no commit here is known not to build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SetSignature refuses Emission; Sensor.PingVisibility (key 7) is retired and
Ping() writes no signature; Sense's Emission skip, the departure subtraction
and SignatureOn are deleted. Tests: SetSignatureRefusesEmission,
APingNoLongerBrightensThePinger; fixtures drop PingVisibility.
Not yet built or tested at this commit (verify job follows).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One conflict, Zone.cs death subscription against contact-reveals' _departures insert: kept both.
The resolution already routes the death subscription through WatchDeath/UnwatchDeath (next
commit's fix target is the same line, so this commit resolves it in that shape).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contacts.Step exchanges normal velocity between touching hull discs (PostCollisionVelocity moved verbatim
from ShieldManager) and publishes Zone.Contacted; ShieldManager presents it; HullCollider.OnCollisionEnter
and ShieldManager's ship-on-ship branch are deleted. Not yet built or tested (verification follows).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zone.Release is the one adder, StepFloatingItems and TryPickUp the only removers; DropLoot runs from the one
death subscription before the entity leaves the zone. Three C# default settings. Unity's drop path is untouched
(loot-1-present deletes it). Not yet built or run at this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RecordReveal gates on zone membership and EndReveals is the only way out; the observer's Zone.Entities handler no longer wipes the book and PublishInbox no longer decides membership. Zone records each removal as a one-tick Departure that each observer's Sense judges under the light-act gate; a departed subject's entries are pruned once older than BearingMemory. Unverified build at this commit (first test run follows).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- RevealsOf returns the book and nothing the subject's gear or Durability says (ruling contact-reads-book-aim-reads-live); FireControl.IsRevealed stays the live aiming read.
- PublishInbox records a paint's reveal only for an emitter still in the zone.
- Sense moves from Entity.Update to a Zone.Update phase after all entities have updated, so reveals do not depend on list order.
- ChunkInfo calls PassiveInfo(position, brightness) in the original multiplication order (PassiveInfoPerSignature becomes PassiveInfo with the signature argument).
- Tests: unseen destruction, ghost paint, Search paint reveal, no self-sense, quiet tracked ship levels, list-order independence.
Not built or run locally; verified on Yggdrasil after the next commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deletes the stateless interval crossing (_previousRadius, PreviousRadius, Crossing) and the allied-pinger scan over
other ships' Sensors. The pinging Sensor remembers the subjects its wavefront has reached (_reached), in the beam
or not, and a subject's first sweeping tick inside the radius is its one crossing. On it the pinger paints, credits
itself and posts a PingReturn to each ally; Entity publishes returns with paints (PublishPaints is now
PublishInbox) and the ally's own sensors credit them for one tick at the geometric mean range, inside
Rt x Rr < PingRange^2, through the geometric mean of both legs' obscuration (ruling ally-echo-bistatic-oval).
This commit builds and replaces the sensor the previous commit's tests were written against.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Zone.Update publishes every entity's paints before any entity updates (Entity.PublishPaints), so a broken lock clears on the same tick whatever the entity order.
- ActionGameManager.ApplyLockProgress is the one lock-indicator curve, called by the outgoing and the incoming indicator.
- LockWarningTests drive the real LockWeapon through Zone.Update; pins on strength across 0.15..1.0, the direction and sensor progress terms, and both entity orders.
Not yet run: this commit's tests have not been built.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zone.Vent is the one adder, StepClouds the one remover, Zone.Obscuration the
one reader: the product of (1 - opacity) over clouds whose disc the segment
touches (closest point of the segment to the centre within Radius), 1f when
none. Derived from live clouds at each call, never cached. VapourDump vents
once on its first run, drifting with its venter. Nothing reads Obscuration
yet (Sensor.Gain takes it in the next commit); this commit builds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MineLayerData (union 40) lays through InstantWeapon -> FireControl.Fire -> Lay;
Zone.Mines steps each mine as a KinematicBody after the entity loop, arms it,
triggers it on a hostile hull its blast disc touches (FireControl.Touches, the
coverage Detonate uses) and detonates it through FireControl.Detonate at the
fuse delay or lifetime. Hostile trigger, faction-blind blast, no owner leash.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The law ships felt (normalize(f) * strength * (1/(1-|f|^2) - 1), gated at |f|^2 .001) is
now Zone.GravityAcceleration, per second via a 60 Hz reference rate; Ship.Update applies it
times delta, so ships push as before at 60 fps. Zone.GetForce's separate law is gone and
delegates to it. Floating-body drift at 60 fps now matches the legacy GridObject magnitude.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
ZonePack.BossSpawned records the spawn, since a dead boss leaves no entity. RunGoal.ExitOpen requires it, so an exit zone
without a boss stays sealed, and ZoneGenerator logs that configuration by field and code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mining owns belts, chunks and target picking; scenarios owns admission and run
staging; master owns ships and fire control (rule scenarios-cut.md S4).
- AetheriaStores.CatalogTypes: nine-type union, master's list plus FieldKindData, no Scenario.
- tools/AetherDb/Program.cs: both cases (ship-authoring, field-kinds), one usage string.
- Zone.cs: master's MineAsteroid dropped (mining deleted its inputs); scenarios' SettleBelts and its three calls deleted.
- RunStartTests: the two belt-task tests deleted (they read belt.Transforms and MineAsteroid).
- FireControlCut124Tests: master's strict Exact assertions; mining's CompatibleDrift allowance and its comment dropped.
- GameData/Aetheria.cc: master's pointer 70a7b0a9. This commit lacks the Asteroid field kind in the catalog, so
TheShippedCatalogHasAPickableFieldKind fails here by design; the next commit rebuilds it.
Builds: this commit builds (verified at the end with commit 2); the catalog pin test fails at this commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zone.Targets (TargetingIndex) answers Within and best-first Best over
ITargetProvider regions. EntityTargets offers the observer's VisibleEntities;
BeltTargets keys each belt's rocks into kinetic bands and bounds each segment's
annular sector for distance, bearing and visibility. Entity.VisibleChunksInReach
queries the index with DetectionReachPerVisibility, an upper bound from the
sensors' hull. Zone.ChunksNear and AsteroidBelt.InnerRadius are deleted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Soul's F5 and F6. The asteroid-belt tasks read the clock, the orbits and each
belt's damage and respawn state. Update advanced the clock before waiting on
last tick's tasks (Soul measured 242k transforms placed for the next tick in
576 of 3000 ticks), MineAsteroid wrote damage and respawn state under a
running task, CreateOrbit and AddOrbit wrote the orbits, and a task that threw
stayed in the list, so Wait rethrew it on every tick after.
SettleBelts waits for the tasks and clears the list even when one failed; Update
settles before it advances the clock, and MineAsteroid, CreateOrbit and
AddOrbit settle before they write.
Tests: over 3000 ticks every asteroid of a belt is placed for the time of the
tick that started its task; mining every asteroid of a belt, chipping and
breaking each four times a tick for 1000 ticks, never races the task; a task
that fails surfaces once and later ticks run clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Update cleared `_updatedOrbits` and only then waited on the previous tick's
asteroid-belt tasks, which read the cache through GetOrbitPosition. A task
still running could see the set mid-clear ("concurrent update ... corrupted its
state") or recompute an orbit into last tick's position. The wait now comes
first.
Test (Soul's orbit counter, committed): 3000 back-to-back 0.02 s ticks of a
quiet arena, and no orbit stands still against its parent. Clearing first gave
500+ stalls or the corruption exception. Measured against the parent because a
moon can genuinely pause in zone space: one such reading (tick 1321, a moon
whose motion cancels its planet's) is real motion, not the race.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- FieldKindData, a catalog document (Q11): a kind's reflectivity per schematic
cell and its generation weight. AetheriaStores routes it to the catalog.
- AsteroidBeltData.Kind, key 10 (Q16 B). FieldKinds.Assign is the one pick (a
weighted choice by a stable hash of the belt's key); FieldKinds.Ensure gives
a kindless belt its kind and saves it. ZoneGenerator calls it at generation
and Zone calls it on first load, so an old belt is assigned once by the same
rule and keeps its kind after later catalog edits.
- Zone.ChunkVisibility: Reflector's rule for a chunk -- kind cross-section x
area in schematic cells x the light on it. Stored nowhere; a worn chunk dims,
a broken one or a kindless belt's is dark.
- Zone.ChunksNear: the one chunk query, skipping belts whose annulus cannot
reach. AsteroidBelt gains InnerRadius for that skip.
- TargetRef: an entity, a chunk, or none, with value equality and no ==.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Zone.Admit(entity, piloted) adds the entity to Entities, activates it and,
when piloted, gives it the zone's Minion. It is the only writer of
Entities.Add and the only creator of agents (docs/scenarios-cut.md, 2.2).
The constructor's pack loop admits every packed ship but the player's as
piloted and keeps its own origin-scatter rule, which is generation
placement, not admission. Entity.TryUndock admits the undocked ship
unpiloted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflict in Zone.cs: kept master's TryGetShot, kept Cut 1's deletion of the BeltUpdates field.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
EntityInstance handed effect managers the shooter's selected target whatever
FireControl decided. A fused round whose target was invalid or out of arc
flies at none, and GuidedProjectileManager dropped a launcher shot with no
target entirely, so a real burst had no missile.
- Zone.TryGetShot: a presentation reads the shot the simulation committed
(its target, its burst point) by the ShotId OnFire hands it.
- EntityInstance passes the shot's own target; a launcher round with none
flies to the shot's burst point.
Unity-only; compile-checked by reading.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Zone.Wear did not check whether the chunk was already broken. A later hit
read wear.Damage starting from 0 (the value ChunkWear carries while
BrokenUntil is in the future), stayed under hitpoints, and wrote
BrokenUntil = null -- un-breaking a chunk mid-respawn.
Damage delivered to a chunk that is currently broken (zone time <
BrokenUntil) now changes nothing: Wear returns false (this hit did not
break it -- it was already broken) and BrokenUntil, and Damage, are left
untouched. The chunk stays absent until its original respawn time, then
resumes wear from zero as before.
Adds two behavioural tests: a re-hit inside the respawn window, both
below and above hitpoints, must not un-break the chunk or move its
respawn time; and the same rule survives a save/reload round trip.
Confirmed by reverting the fix and watching both new tests fail with the
chunk reappearing early, then restoring the fix byte-exactly.
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>
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>
The operator's play smoke crashed in LockWeapon reaching EntityInfoGathered
through a raw indexer as a ship vanished. Cut 5.6 moved death removal into the
simulation and did half the transition: Zone.Entities.Remove with no Deactivate,
where TryDock has always paired the two. The dead ship kept live subscriptions
and stale references while every survivor dropped it, and because Zone.Update
iterates Entities.ToArray() it received one more Update after the world had
forgotten it. Entity.Update never consulted _active; the flag existed and
nothing asked it.
Death now removes and deactivates in one transition, and a deactivated entity
does not update. Deliberately NOT a TryGetValue in LockWeapon: that silences one
call site and leaves every other behaviour running on a corpse.
Also re-anchors the seven mutations stranded by the diagnostics refactor
(72c0109c), which routed HitProbability through Inspect and rewrote the lines
four harnesses pin -- two of them no-op controls, so those harnesses were
defending nothing. The rules are unchanged; only the text moved. Anchors stay
strict: loudly stale is what surfaced this within one run.
The heat spike, motionless ships and shots that registered no hits remain
UNEXPLAINED and open. See the map's 8.3 -- unreproducible since the diagnostics
changed timing, so recorded with the unclamped dt as the place to probe rather
than patched speculatively.
226 tests pass.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>