Aetheria/docs/aetheria-fauna-map.md

33 KiB
Raw Blame History

Aetheria spaceborne fauna: map

Campaign aetheria-fauna. Target: docs/aetheria-fauna-target.md (content r2). Written by Imagination (agent imagination-aeth-fauna, session self-2026-10-13-ag2) against origin/master 0898725f. This map holds the body facts, the questions Self admits together with the campaign, the first cut as a draft spec, and the rest of the cut sequence. Prior art: docs/research/2026-10-13-fauna-prior-art.md (PA:<line>). Items tagged [M] there come from memory and are unverified.

Body facts

Probed at 0898725f. A file:line is a line on that commit.

# Question Result
B1 How does the sim hold things that are not ships? Three runtime collections on Zone, all ReactiveCollections with one adder and one remover: Mines (Zone.cs:44, Lay/StepMines), Clouds (:48, Vent/StepClouds), FloatingItems (:53, Release/expiry/TryPickUp). All three are stepped every tick as a KinematicBody (FloatingBodies.cs:25-44) on Zone.GetForce, have ids from NextBodyId (Zone.cs:131), are never saved and are not Entitys. Belts are the fourth kind, and they are not stepped (B3).
B2 What is an Entity's per-tick shape? Zone.Update (Zone.cs:274-305) runs PublishInbox, Update and Sense for every entity, then mines, clouds, floating items, Contacts.Step (O(n²) hull pairs, Contacts.cs:57-79) and FireControl.Step. Each active Sensor.Execute walks every zone entity and calls Zone.Obscuration per pair (Sensor.cs:186-234). Entity.Sense walks every other entity's signatures (Entity.cs:989-1047). Entity count therefore costs O(n²) per tick, and each entity carries a hull, gear, a thermal grid and Rx subscriptions (Entity.cs:262-330).
B3 How are belt rocks held? As members of a field: ChunkId(Field, Index) (Zone.cs:825-839). The comment there says "Field is deliberate, not belt": the operator's ruling generalizes chunks to other kinds of field. The pose is a pure function of zone time (AsteroidBelt.Pose, Zone.cs:800-808: orbit distance, phase, OrbitPeriod). Wear is a sparse dictionary read lazily (_wear, ChunkExists, ChunkRadius, Wear, Zone.cs:503-555). Visibility is ChunkVisibility = kind cross-section × area × light (Zone.cs:570-578). Nothing about a rock is stepped per tick. Members are saved through AsteroidBeltData.Asteroids (ZoneData.cs:107-117) and wear through ZonePack.ChunkWear.
B4 What is the targeting index's provider seam? ITargetProvider.Regions and Candidates (TargetingIndex.cs:65-72). A region carries distance, bearing and visibility bounds that must hold for every member. Within and Best are best-first over regions (Hjaltason and Samet). Two providers exist: EntityTargets (offers only the observer's VisibleEntities) and BeltTargets (orbit bands of 256, segments of 32, re-keyed lazily, BeltTargets.cs:22-190). BeltTargets widens every bound by slack (:109) and bounds size by _largestRadius (:35, :52, :181). Zone adds one BeltTargets per AsteroidBeltData body (Zone.cs:170-175).
B5 How are non-entities seen? Entity.ChunkInfo → PassiveInfo(position, visibility) → each sensor's PassiveRate → Sensor.Gain, the one gain rule (Entity.cs:427-454, Sensor.cs:242-263). ChunkVisible is info above TargetDetectionInfoThreshold. Chunks are never contacts: there is no reveal, no track and no Contact for a chunk. ContactOf and the reveal book are keyed by Entity (Entity.cs:232, :963).
B6 How is a non-entity target named? TargetRef holds an Entity or a ChunkId (Zone.cs:844-870). Chunk-aware sites: Entity.cs (10 reads, e.g. :379, :419, :531, :561-572, :1653-1655), FireControl.cs:139-142 (a chunk bears through its pose), ActionGameManager.cs:1174, :1419, :1453. A new member kind that is a ChunkId passes through all of them unchanged. A third TargetRef arm would touch every one.
B7 How does presentation bind sim things? ZoneRenderer subscribes to ObserveAdd/ObserveRemove on Mines, FloatingItems and Clouds and instantiates one prefab per thing (ZoneRenderer.cs:232-314). Belts are drawn every frame from Zone.EvaluateBelt into the renderer's own buffer through Graphics.DrawMeshInstanced (:536-570). Meshes are picked per belt at load (:431-437). No pose is stored on the sim side.
B8 What is the slime-mold experiment? Assets/Shaders/Compute/Slime/Slime.cs plus Slime.compute: a GPU Physarum swarm (agents sense a trail texture, turn and deposit; speed and turn follow the gravity slope; random respawn). It runs live in Scenes/ARPG.unity through Prefabs/RPG/Slime Renderer.prefab, with 131,072 agents and 32,768 trail particles in the scene override (ARPG.unity:19676-19712). ZoneRenderer sets only its radius (:198). Its spawn-at-suns code is commented out (:528-532). It is presentation only: it is nondeterministic, the sim never reads it, and it decides nothing. A Discord note from 2021-03-28 calls the agents "rendered in the actual Aetheria level as wee little trails".
B9 Tick model and dice? SimClock runs fixed steps at 60 per real second, caps catch-up at 0.1 s of real time, and gives presenters Lead (SimClock.cs:11-60, ActionGameManager.cs:1363). SimulationDice.For(zoneSeed, stream, ordinal) supplies deterministic streams (LootStream 1, MineStream 2).
B10 What do things cost? (scratch probe, deleted, never committed) Run on Yggdrasil (dotnet sdk 10, Release, 4 CPUs), a test in ContactRevealTests' fixture at 0898725f. Entities with one passive sensor each, ms per Zone.Update: 8 → 0.38, 32 → 2.85, 128 → 4.95. The ships' light was zero, so no track ever formed; this is the floor, and JIT warm-up inflates the 32 reading. Stepped bodies (KinematicBody.Step plus Zone.GetForce, a zone with no planets), ms per tick: 1k → 0.99 (includes JIT), 10k → 1.45, 100k → 16.1. Without GetForce: 10k → 0.10, 100k → 0.99. GetHeight loops every planet 4 times per GetForce (Zone.cs:627-717), so a real zone costs more. PassiveInfo with one sensor: 0.185 µs per call. Reading: a tick has 16.7 ms. Fauna as entities are unaffordable beyond tens. A stepped body per member is affordable only in the low thousands. Evaluating a member on demand, as chunks are, costs nothing per tick, and a query's cost is bounded by the index.
B11 Is there fauna lore? No. Vault/**/*.md has no krill, whale or microfauna; voidbot over AetheriaLore surfaces nothing; Discord has only the slime-agent remarks. Lore is open.
B12 Where would fauna touch aetheria-ew? aetheria-ew's BackgroundKind row (target: "Append-only enum (Rock, Debris, more by ruling)") and cut 6 ew-blend ("believed only inside a belt where real chunks are near ... light relative to nearby chunks"). The ruling ew-blend-presentation-ruled shows a believed background contact as "a faint background marker labelled as the kind". The ruling disguise-held-by-flying makes the motion test adversarial observation.
B13 Do the in-flight Hands passes overlap? ew-claims (origin/eureka/aetheria-ew-ew-claims-in-the-book) edits Sensing.cs, Entity.cs, Combat.cs, SchematicDisplay.cs, FireControl.cs and tests. loot-3 edits FieldDriver, TractorBeam, ItemPickup, the capability presenters and PickupTests. The first cut below touches ZoneData.cs, Zone.cs (AsteroidBelt, its construction), BeltTargets.cs, ZoneGenerator.cs and Settings.cs (ZoneGenerationSettings): no file overlap.
B14 What else reads every belt body? SectorRenderer.cs:84 counts AsteroidBeltData bodies as belts in the sector UI, so a fauna field would count as a belt there. ZoneRenderer.LoadPlanet draws it with asteroid meshes (:431). Both are presentation, and both are later cuts.

Rationale

  • Fauna are fields, not entities and not stepped bodies (B2, B3, B10). The chunk path already does what a swarm needs. It gives identity without allocation, a pose as a pure function of time, lazy wear and respawn, detection through the one gain rule, a target slot, an index provider and an instanced draw. Making a member a chunk of a fauna field reuses every one of those owners. The alternatives each add an owner: an entity per fish costs O(n²) per tick; a stepped body per member costs O(n) per tick and has no index provider; a third TargetRef arm touches about 15 sites (B6).
  • Krill first. Krill ride the orbital flow, which is exactly the motion belt rocks already have (B3). One bounded sway term per kind and a clustered generator give a cloud that moves like krill, and nothing else in the Body has to change. It is the smallest cut the operator can see run, and its tell ("drifts with the wind", PA:9) is the one the prior art says observers use first.
  • Sharks and whales need an anchor. Their tell is moving against the flow, so the field's frame has to move. fauna-motion-model decides whether that is a closed form plus sparse events or a stepped anchor. Either one is per swarm (a few dozen per zone), not per member.
  • Distance aggregation is a presentation and query question first. The index already bounds what any query examines (B4). Whether a school is one return or many is a question of what an observer is shown, so it is asked rather than built ahead of need.
  • The prop-hunt tax already exists. Garry's Mod charges hunters health for shooting real props, and Black Ops 4 makes props whistle (PA:35-36). In Aetheria, shooting a return to test it already shows a launch flash and spends heat and ammunition (aetheria-ew H1, H6), and a member that is shot breaks through Zone.Wear like a rock. No extra tax is proposed.

Questions for the operator (draft; Self admits them with the campaign)

The prior-art file (docs/research/2026-10-13-fauna-prior-art.md) was read before these recommendations were written. One rule shapes every option below: under disguise-held-by-flying, no option offers static hiding or an automatic mimic, and the only flying aid is a generic orbit sync.

  1. fauna-in-sim: where do fauna live: sim or presentation?

    • Options:
      • sim-perceivable-present-ambient: anything a sensor can perceive or a shot can hit is in ServerShared; sub-detection ambient life (the slime trails) stays presentation and decides nothing.
      • presentation-only: fauna are drawn only; they cannot be contacts or targets.
      • sim-everything: the slime agents move into the sim too.
    • Recommended: sim-perceivable-present-ambient. A prop must be in the sensor picture, and sensing is sim (B5). The slime swarm is 131k nondeterministic GPU agents (B8).
    • Depends: everything.
  2. fauna-identity: what names a fauna member?

    • Options:
      • field-member: a member is a ChunkId of a fauna field, and fields gain fauna kinds.
      • fauna-arm: a third TargetRef arm with its own id.
      • entity: each fauna is an Entity.
    • Recommended: field-member. It reuses every chunk owner (B3, B6), and ChunkId was named for this. entity is ruled out by cost (B10).
    • Depends: cut 1 and every later cut.
  3. fauna-motion-model: how do fauna move?

    • Options:
      • closed-form-plus-events: each class's law is a pure function of zone time, and reactions (scatter, flee, pursue) are sparse event records that decay lazily, as wear does.
      • stepped-anchor: each swarm's frame is a KinematicBody with a steering law, stepped per tick, and members are closed-form offsets from it.
      • boids: members are stepped individually with neighbour search (Reynolds; spatial hashing, PA:20-21).
    • Recommended: closed-form-plus-events. It is deterministic at any step size, costs nothing per tick and saves for free. A law written in closed form is also the clearest thing a player can learn, which disguise-held-by-flying needs. stepped-anchor is the fallback if a class's law cannot be written in closed form. boids costs per member per tick (B10), and the prior art found no deterministic server-side swarm source (PA:24).
    • Depends: cuts 2 onward. Cut 1 (krill ride the flow) is closed form under any option except boids.
  4. fauna-interaction: do sharks and whales act on ships?

    • Options:
      • props-react: fauna never harm or block ships; they react (schools scatter, sharks veer, whales sound off), and the reaction is a tell.
      • sharks-bite: sharks attack ships that come close.
      • whales-block: whales are hulls in Contacts.Step.
    • Recommended: props-react for this campaign. Danger moves the game from prop hunt to hazard avoidance, and blocking hulls would need entity contact (B2). Either can be a later campaign.
    • Depends: the anchor and disturbance cuts.
  5. fauna-kill-harvest-lure: can fauna be killed, harvested or lured?

    • Options:
      • shootable-respawning: members break and respawn through Zone.Wear like rocks; harvest and lures are later.
      • untouchable: shots pass through fauna.
      • harvest-economy: fauna yield goods through the mining yield table.
    • Recommended: shootable-respawning. It comes free with field-member, and shooting to test a return is the prop-hunt check, taxed by its own launch flash and heat (PA:35-36).
    • Depends: cut 1 (it inherits rock wear unless this is ruled untouchable).
  6. fauna-habitat: where do fauna live?

    • Options:
      • hosted-by-bodies: each class favours host bodies (krill and minnows around planets and in belt orbits, whales around gas giants, sharks wherever minnows are), placed by the zone generator from the zone seed.
      • everywhere: uniform scatter.
      • authored-only: fauna appear only in scenario zones.
    • Recommended: hosted-by-bodies. Fauna then become a readable feature of a zone's bodies, and hiding has a geography.
    • Depends: cut 1's generator rule (it places krill on planet orbits by default).
  7. fauna-distance-representation: how does a school read at range?

    • Options:
      • aggregate-far-individual-near: beyond a class's resolution distance an observer sees one return per school, with summed light at its centroid; near it, it sees members.
      • individuals-always: every member that passes ChunkVisible is its own return.
      • aggregate-only: a school is always one return.
    • Recommended: aggregate-far-individual-near. It matches how radar and sonar read layers and flocks (PA:9, PA:14), keeps the HUD legible, and gives a ship near a school a place to hide that ends when the hunter closes in.
    • Depends: the contact-presentation cut and aetheria-ew's blend judge.
  8. fauna-disguise-path: how does a ship pass as fauna?

    • Options:
      • returns-alike-plus-blend: below Identified, a ship's return and a fauna return share one marker, so any ship can hide among fauna by how it flies, with no gear; aetheria-ew's blend gear carries the disguise past identification by claiming a fauna BackgroundKind, judged by an envelope that includes the class's motion law.
      • blend-only: only blend gear lets a ship pass as fauna.
      • returns-alike-only: no gear; identification always ends the disguise.
    • Recommended: returns-alike-plus-blend. aetheria-ew made imitation gear scarce (ew-imitation-availability-ruled), so blend-only would make prop hunt a rare event. With returns-alike-plus-blend, gearless hiding is the common game and gear is the event.
    • Depends: aetheria-ew ew-blend (cut 6) and contact presentation. This is a cross-campaign question, and Self may file it in aetheria-ew.
  9. fauna-shadowing: can a ship hide in a whale's return?

    • Options:
      • defer: whales are bright, separate returns for now.
      • merge-within-resolution: an observer who cannot resolve two returns sees one.
    • Recommended: defer. Sensing has no resolution concept today (B5). Adding one is a sensing change, which belongs to aetheria-ew or a sensing campaign, not to fauna.
    • Depends: nothing in this campaign.

First cut (draft spec): fauna-krill-swarms

Waits on: rulings on fauna-in-sim, fauna-identity, fauna-habitat (recommended options assumed) and fauna-kill-harvest-lure (shootable-respawning assumed; under untouchable, Wear refuses fauna fields and that adds one test). Base: origin/master; branch eureka/aetheria-fauna-krill-swarms. Overlap with in-flight passes: none (B13). Model: Sonnet Hands. Cap: 100 calls. Estimate: about 7 files, +260/-10 lines, of which about 150 are tests.

Objective: krill clouds exist in generated zones as fields of a fauna kind. They are sensed, targeted, shot and drawn through the chunk path, and they move by the krill law: they ride the orbital flow, and each member sways within a bounded radius.

Scope:

  1. FieldKindData (ZoneData.cs:122-136) gains two append-only keys, SwayRadius (Key(3), world units, default 0) and SwayPeriod (Key(4), seconds), and SwarmWeight (Key(5), default 0): how often swarm generation picks this kind. A rock kind has sway 0 and swarm weight 0. A krill kind has belt GenerationWeight 0, so belts never draw it (ZoneData.cs:143-158, "Zero never picks it").
  2. FieldKinds.Assign (ZoneData.cs:143) takes the weight to read, so belts and swarms share one weighted pick (one rule, one path). Add no second picker.
  3. AsteroidBelt (Zone.cs:785-808) takes the field's resolved sway when Zone builds it (Zone.cs:170-175). Pose adds a bounded closed-form offset: SwayRadius * float2(cos(2π(t/SwayPeriod + h₁(i))), sin(2π(t/(SwayPeriod·φ) + h₂(i)))). Here h₁ and h₂ are hashes of the member index (SimulationDice.Mix of it), and φ is a fixed irrational ratio so the path is not a circle. The offset's length is at most SwayRadius·√2, and that figure is the bound the provider uses. Radius (:791) adds it too. With sway 0, the pose is bit-for-bit today's.
  4. BeltTargets (BeltTargets.cs:109) adds the field's sway bound to slack, so every region's distance and bearing bounds still hold for every member. No other change to the provider.
  5. ZoneGenerator.GenerateZone (ZoneGenerator.cs:40-210) places krill swarms after the bodies exist. For each planet or gas giant body, with a probability from ZoneGenerationSettings (Settings.cs:109-135, new fields KrillSwarmProbability, KrillSwarmCount, KrillSwarmArc (turns) and KrillSwarmWidth (world units), with code defaults), it adds an AsteroidBeltData whose orbit is parented to that body. Its members are clustered: phase within KrillSwarmArc of one centre and distance within KrillSwarmWidth of one radius. Its Kind is picked by FieldKinds.Assign on SwarmWeight. Draw from the generator's existing random (:52), so the result follows the zone seed. No kind with positive SwarmWeight means no swarms.
  6. Out of scope (later cuts): a krill look in the renderer (krill draw through the belt path as small rocks); the SectorRenderer belt count; the catalog authoring of a Krill kind (cut 1b); obscuration.

Tests (new tests/Aetheria.Shared.Tests/FaunaSwarmTests.cs, as a partial class MiningCut3Tests to reuse BuildSceneAt and Belt (MiningCut3Tests.cs:74-130); kinds are authored in the test catalog, never re-derived):

  • SwayIsBoundedAndPure: for a swaying field, every member's ChunkPose at many times lies within the sway bound of its orbital slot (AsteroidBelt.Pose with sway 0). A zone stepped to t by 1/60 and by 1/7 gives equal poses (fauna-pose-pure).
  • RockPosesUnchanged: a sway-0 kind's poses equal the pre-cut formula. The existing MiningIndexTests and MiningCut3Tests stay green.
  • RegionBoundsHoldForEveryMemberOfASwayingField: the pattern of BeltRegionBoundsHoldForEveryRock (MiningIndexTests.cs:105) over a swaying field at several times (fauna-bounds-hold).
  • KrillRideTheFlow: a swarm's centroid velocity over one sway period equals the orbital velocity at its mean distance, within the sway bound divided by the interval (fauna-tell-holds).
  • KrillAreSeenThroughTheOneGain: a krill member is ChunkVisible exactly when PassiveInfo at its pose × ChunkVisibility passes the threshold; its visibility scales with the kind's cross-section (fauna-one-gain).
  • KrillBreakAndRespawn: Zone.Wear breaks a krill member and it respawns at zone time, as a rock does. Under untouchable, this test asserts the refusal instead.
  • GeneratedZonesHostKrill: with a catalog that holds one rock kind (belt weight 1) and one krill kind (belt weight 0, swarm weight 1), a generated zone has swarms only on planet or gas giant orbits, each with the krill kind, members inside the arc and width, and no belt with the krill kind. Generating the same galaxy zone twice gives equal swarms. Use RestoredHullsTests.TutorialZoneSettings() with the krill fields set.

Hand mutants (one list to mutation-table.sh, each expected to fail its test):

  • drop the sway term in Pose;
  • sway radius × 2 in Pose but not in the bound (the bound must catch it);
  • omit the sway bound from slack;
  • Assign reads GenerationWeight for swarms;
  • the generator ignores KrillSwarmArc (full ring);
  • the generator uses a new System.Random instead of the zone's random;
  • the sway uses _time rounded to the frame (a function of dt).

Verification: one Yggdrasil job: build, the full Aetheria.Shared.Tests, and the mutant table. Recipe: F:\Projects\eureka-handoff-aux-2026-10-12\hands-aetheria-common-ag.md.

Promises for Soul: fauna-pose-pure, fauna-bounds-hold, fauna-tell-holds (krill), fauna-one-gain, rocks unchanged, no per-tick member loop (grep Zone.Update and the step functions for any loop over Asteroids), and generation that is deterministic per zone.

Cut sequence r1 (superseded by the r2 sequence below)

  1. fauna-krill-swarms: krill fields through the chunk path, with the sway law and generation (drafted above). 1b. fauna-krill-catalog: an AetherDb command that authors the Krill field kind (cross-section, sway, swarm weight).
  2. fauna-field-presentation: fauna kinds pick their own look (instanced sprites or particles per kind, not asteroid meshes); SectorRenderer counts belts only. Waits on 1.
  3. fauna-minnow-schools: the minnow kind; the sway law gains a fast milling term per kind. Waits on 1.
  4. fauna-anchors: a field frame that moves across the flow under a class law (shark patrol and sprint, whale arcs), per fauna-motion-model. Waits on that ruling and 1.
  5. fauna-sharks-and-whales: the two kinds and their generation (sharks near schools, whales around gas giants), thermal light for whales. Waits on 4, fauna-habitat.
  6. fauna-disturbance: sparse disturbance records; schools scatter from hulls and shots, sharks veer (props-react). Waits on 4, fauna-interaction.
  7. fauna-returns: what an observer is shown of fauna (background markers, aggregate far and individual near), one marker shared with unidentified ship returns. Waits on fauna-distance-representation, fauna-disguise-path, and aetheria-ew's contact presentation.
  8. fauna-in-ew-blend: fauna BackgroundKinds with motion-law envelopes in aetheria-ew's judge. Owned by aetheria-ew cut 6 or after it, per fauna-disguise-path.
  9. fauna-krill-obscure: dense krill thin sight lines through Zone.Obscuration, as vapour clouds do (one gain term, no second one). Waits on 1, an operator yes.
  10. orbit-sync-helper: the generic flying aid that holds a ship on the local orbital flow. It is not fauna-specific and may belong to the flight campaign. Waits on an operator yes.

r2: the flow, placement and the content the rulings opened

Written by Imagination (agent imagination-aeth-fauna-r2, session self-2026-10-13-ag2) against origin/master 1746e8bd, after the operator ruled on the nine questions above. The questions above are answered; their rulings are in the mind. This section adds the body facts the flow ruling needs, the forks it opened, and the r2 cut sequence, which replaces the r1 sequence above. The krill draft above stays as the shape of the krill cut; it is re-mapped against the flow owner and the placement ruling when it is about to run.

Body facts (r2)

Probed at 1746e8bd.

# Question Result
B15 What is the flow? Volumetric.cginc: globalFlow(pos) = Tri3D(pos/_FlowScale - (0,_FlowScroll,0)) * _FlowAmplitude with y halved (:130-135); Tri3D is the cross product of two normalised tri3 folds, the second at the golden ratio (:46-47, :69-74); slopeFlow(grad) = normalize(grad) turned 90 degrees times _FlowSwirlAmplitude plus normalize(grad) times _FlowSlopeAmplitude (:137-141); flow(pos) sums the two under the keywords FLOW_GLOBAL and FLOW_SLOPE (:148-161). Consumers: Stardust.compute:139-142 (each particle is placed at -flow * lifetime * period, a closed-form advection over a cycle) and the nebula's noise advection (Volumetric.cginc:185-208).
B16 Who owns it today? The renderer. VolumeSampling.Update pushes the parameters from environment.Flow and accumulates _flowScroll += GlobalScrollSpeed * Time.deltaTime on the frame clock (VolumeSampling.cs:96-106). environment is ActionGameManager.CurrentEnvironment, which always returns Settings.DefaultEnvironment (ActionGameManager.cs:222-228), a field of the presentation GameSettings ScriptableObject (GameSettings.cs:40). ZoneEnvironment and FlowSettings are already ServerShared MessagePack types (Environment.cs:8-61), but no zone holds one. The sim cannot see the flow.
B17 What flow is live? Assets/Resources/Settings.asset:396-402 (LFS): GlobalScale 512, GlobalAmplitude 15, GlobalScrollSpeed 0.025, Period 8, SlopeAmplitude 0, SwirlAmplitude 0. Only the global term runs; FLOW_SLOPE is off in the shipped game. Probe: the horizontal global flow averages 6.06 u/s (max 12.2) across a zone, the same as the rail speed 2π = 6.28 u/s.
B18 Does the slope term have a sim twin? It reads _NebulaSurfaceHeight, a GPU render of brush meshes (Assets/Shaders/Brushes/*), which is a second lowering of the heightfield Zone.GetHeight owns (Zone.cs:627-660). Its getUV negates world position (Volumetric.cginc:81-84), so its texel gradient points downhill in world terms. normalize of a zero gradient is NaN (:139); today the keyword keeps that from running.
B19 What clock do presenters use? Zone time: Zone._time (double, Zone.cs:70). Belts are drawn at it (ZoneRenderer.cs:543-547), gas giant wave phase too (:587-588). Zone.Settings is the public PlanetSettings (Zone.cs:59), the world-law block every zone already receives.
B20 Placement prior art and probe docs/research/2026-10-13-fauna-placement-prior-art.md. A CPU transliteration of the operator's Slime.compute with her prefab's parameters, fed by Zone.GetNormal: bit-identical run to run, 9 s for 40k agents × 600 steps on 256², trail on the shoulders of the wells and in filaments between them (0 % in flat space, 5.7 % in cores), correlation 0.28 with a closed-form slope-suitability map. Adding the flow reshapes the network (correlation 0.34 with the no-flow map).
B21 Is a generated zone recomputed? No. A zone pack is generated once, on first entry (ActionGameManager.cs:727, ??=; the entrance in RunStart.cs:29), and saved with the galaxy (Galaxy.cs:70). Placement computed at generation is saved; it never has to reproduce on another machine.
B22 Can Unity check a shader against C#? Yes. ShieldPanelCut3Verify.cs:18, 47-55 dispatches compute kernels and reads them back in batchmode run without -nographics (verified 2026-09-18), driven by tests/mutation_tests_shield_panel_cut3.py. That is the precedent for the flow parity check. Hands worktrees on Starfire have imported Libraries (C:\aeth-wt\lock-on\Library and others).
B23 Overlap with in-flight passes ew-claims (Weapon.cs, Entity.cs, Sensing.cs, SchematicDisplay.cs, tests), scenarios-lock-on-r3 (Scenarios/LockOn.cs, Scenarios.cs, a test) and sensor-catalog-r2 (Behaviors, Listener.cs, Entity.cs, GameData/Aetheria.cc, AetherDb, tests) touch none of the flow cut's files. The flow cut edits Settings.asset (LFS); none of the three does.

Decisions made here, not forks

  • The scroll becomes zone time. sim-knows-the-flow makes the flow a sim fact, and fauna-pose-pure needs it to be a function of zone time. _FlowScroll is therefore GlobalScrollSpeed × Zone.Time, derived, and the frame-clock accumulator is deleted. Where no zone exists (menus), the same function runs on the presentation clock; that is a different input to the one function, not a second law. _FlowPeriod and the noise phases on _Time stay presentation: they animate how the nebula shows the flow, not the flow.
  • The flow settings live with the zone's world laws. FlowSettings moves from ZoneEnvironment (read by no zone) to PlanetSettings.Flow, which every Zone already receives (B19). One zone flow for every zone, as today. Per-zone environments (Sol's cloudless bubble) are a later cut that adds a zone-pack override; the owner stays Zone.
  • The slope term is ported and guarded. It is part of the law the ruling names, so the owner carries it; its term is skipped when its amplitudes are zero (as the shader's keyword does), and a zero gradient gives zero, not NaN, on both sides. Its parity is pinned as a function of the gradient; making the heightfield's two lowerings agree is follow-up work (flow-slope-gradient-parity).

Questions (admitted in the mind)

aetheria-fauna:question:fauna-placement, :fauna-flow-coupling, :flow-moves-what, :flow-slope-terms. The options and recommendations are in the mind; the research for the first is the placement prior-art file.

Cut sequence (r2, one line each; each is mapped when it is about to run)

  1. fauna-flow-field (cut spec admitted): the flow's owner moves into ServerShared (FlowField, Zone.Flow), the shader lowers it from zone time, and a Unity parity check pins HLSL to C#.
  2. fauna-habitat-placement: the generation pass per fauna-placement; its settings record in ServerShared; food at well shoulders and wormhole mouths; Sol excluded; budget measured. Waits on 1 and that ruling.
  3. fauna-krill-swarms: the krill draft above, re-mapped: krill fields placed by 2, swaying and drifting by fauna-flow-coupling. Waits on 1, 2. It cannot run beside 1: it needs Zone.Flow, and both edit Zone.cs and Settings.cs.
  4. fauna-krill-catalog: AetherDb authors the Krill field kind.
  5. fauna-field-presentation: fauna kinds pick their own look; SectorRenderer counts belts only.
  6. fauna-minnow-schools: minnows, fast milling, grazing the krill filaments.
  7. fauna-anchors: field frames that move across the flow under class laws (fauna-motion-model-ruled).
  8. fauna-sharks-and-whales: the two classes, generated by 2's map (sharks on steep flanks, whales round gas giants).
  9. fauna-disturbance: sparse reaction records; schools scatter, sharks veer; reactions are tells.
  10. fauna-attacks-strike: sharks strike and minnow schools scour, through the ordinary damage path.
  11. fauna-attacks-signal: whale song as noise in the shared signal space (with aetheria-ew), drifter discharge.
  12. fauna-harvest-goods: catalog goods per kind (krill paste, photophores, heat-organs, whale bone with strange matter, spider silk) and yields through the economy's yield table; fields refill by respawn.
  13. fauna-harvest-gear: resonance fishing, nets and trawls as gear with makers (the lore names Orbital Forge's retooled resonators); trawls leave straight-edged channels.
  14. fauna-void-spiders: nets strung across trojan points, snaring and tethering what drifts in; silk.
  15. fauna-gas-giant-drifters: a cloud-deck class bound to gas giants.
  16. fauna-edge-giants: larger classes found only far from Sol; the class mix scales with distance from Sol.
  17. fauna-springs: wormhole mouths feed placement and the refill tide.
  18. fauna-genre-uses: cultivators' harvesting techniques, necrotech-bound fauna, mecha that ride fauna; each lands with its owner's campaign, none trimmed for genre consistency.
  19. sol-flatspace-zone: a per-zone environment override; Sol is cloudless, flowless and fauna-free, with krill haze outside its Oort edge.
  20. fauna-returns: what an observer is shown (fauna-distance-ruled, fauna-disguise-path-ruled).
  21. fauna-in-ew-blend: fauna background kinds with motion-law envelopes in aetheria-ew's judge.
  22. fauna-krill-obscure: dense krill thin sight lines through Zone.Obscuration. Waits on an operator yes.
  23. flow-moves-matter: loose matter drifts with the flow, per flow-moves-what.
  24. flow-slope-gradient-parity: the brush-rendered heightfield and Zone.GetHeight agree, so the slope term can be switched on (flow-slope-terms).
  25. orbit-sync-helper: the generic flying aid that holds a ship on the local orbital flow. Waits on an operator yes.