Commit graph Aetheria/tests/Aetheria.Shared.Tests/RestoredHullsTests.cs
Author SHA1 Message Date
Meta Vens
230aeccdd3 Merge thrust-allocator-wire: Ship.Update hands intent and live columns to ThrustAllocator
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

# Conflicts:
#	tests/Aetheria.Shared.Tests/RestoredHullsTests.cs
2026-10-10 23:44:54 +02:00
Meta Vens
e1788e19c1 Tests: the Longinus's reach bound is derived from the allocator's own push, not a widened constant; stale mixer comments and unused using UniRx removed
Builds; not yet run (the run follows).
2026-10-10 20:32:18 +02:00
Meta Vens
4d1635b472 Merge origin/master (verse-grammar) into eureka/aetheria-release-consumables-first-set
GameData/Aetheria.cc is master's blob, not this branch's; the consumables-first-set
apply is replayed in the next commit. The AetherDb command list takes both new
commands. This merge commit does not build until the apply and test seeding follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:47:01 +02:00
Meta Vens
ab450e72d3 Tests follow the allocator: warm-up, free thruster power, heat-detuned drives
The allocator reads Item.Active every tick, so fixtures that fire a thruster on their
first update warm its performance first; flight fixtures take the thrusters' energy draw
to zero so a brownout does not scale the turn; the Duel drives are mismatched after they
heat apart, and degraded by heat (the Large Drive has no durability term); the
consumer sits at (2,1), the Skiff's interior centre column. ReachIsWhatTheShipCanDo's
Longinus ceiling is 1.3 (measured 1.195 nose-on, 1.249 broadside).

Previous test commits do not build or pass; this one is unverified until the full run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:24:05 +02:00
Meta Vens
b3b81007d5 BuildThrustedShip fills extra hardpoint types before activation; the vent test fits a radiator through it
A ship cannot be equipped once active, so the radiator has to be fitted inside the one builder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 07:59:13 +02:00
Meta Vens
d6100b18ca Route every real-catalog test reader through TestCatalog
Deletes the direct GameData/Aetheria.cc opens (FireControlCut124Tests x3, FireControlCut7Tests, MiningCut3Tests x2,
RestoredHullsTests x11) in favour of TestCatalog.Repo, and the eight copies of the shipped-assembly registry in favour of
TestCatalog.Registry() (real-catalog-readers-bypass). Pirates and Roles command tests keep copying the shipped catalog (they
rewrite a copy through AetherDb) and take only the registry.

Builds; not yet run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 06:50:26 +02:00
Meta Vens
a3ea1d7ba1 Tests: scan's miss path fails then returns default (Assert.Fail is void)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:40:42 +02:00
Meta Vens
549c1b0253 Tests: the two no-Lagrange zone tests share one scan; restore NotSame, use Assert.Fail
The tutorial-seed scan for a zone with no genuine Lagrange candidate ran
once per test; it now runs once behind a Lazy. The leak test regains
Assert.NotSame(galaxy.Entrance, otherZone), and the scan's miss path uses
Assert.Fail (xUnit2020).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:39:05 +02:00
Meta Vens
0ccf02746d Tests: restored-hull station tests pick their zone by the property they test
EAC-7089 and EAC-2733 were generated-content names that CultMath 0.3.0 re-rolled
away. The no-Lagrange-candidate zone is now found by its property (planets that
are all rosette members, so no genuine candidate) by scanning seeds, and the
zero-natural-station zone by its isTutorial: false roll. Every assertion keeps
its claim; the candidate predicate is shared with the leak test's fixture check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:54:43 +02:00
Meta Vens
9e5c8de7d5 Top-speed test comment: drop the retired hull's name so the negative grep for it stays empty
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 14:27:37 +02:00
Meta Vens
2cfdc72853 Delete the aether drive: behaviour, union 12, hardpoint type, aggregates, settings, presenters, prefab
AetherDriveData and AetherDrive, Union(12) (now a retired-do-not-reuse comment, as 26 is),
HardpointType.AetherDrive (last member, nothing shifts), Ship's drive sets, aggregates and
Axis write, the five request-field rows, GameplaySettings Aether multipliers, ShipInstance's
drive particles, SchematicDisplay's RPM display and AetherDriveUi, the undock refusal's drive
clause, the drive prefab and material, and AetherDb's drive roles and brownout row. The
design is kept at parked/aether-drive.
Adds EveryShippedShipHullHasATopSpeed. ARPG.unity still holds stale serialized references to
the removed SchematicDisplay fields and the RPM Display object; the next commit removes them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 14:04:28 +02:00
Meta Vens
c86423936e Helm split, simulation: Ship.Turn is the only facing command; LookDirection becomes Aim
Ship.Update reads Turn and feeds the rotation thrusters and aether drives; it no longer reads the
look direction or snaps Direction toward it. Steering.Toward is the one law from heading to demand
(a heading astern now turns at full demand). HelmInput owns the face-the-aim state. Accelerate,
Combat and MoveTo write Turn through Steering.Toward. Entity.LookDirection is renamed Aim in the
simulation, the Unity readers and the tests. Unity player input follows in the next commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 12:33:20 +02:00
Meta Vens
d9f85dc639 Merge origin/master into mining: resolve five conflicts by owner, delete the belt-task machine
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>
2026-10-06 15:29:29 +02:00
Meta Vens
e56af28892 Galaxy links zones along a double-precision Delaunay triangulation: the vendored float MIConvexHull dropped zones, so ~2% of galaxies threw
The vendored MIConvexHull (NIH, ported from double to float) returned triangulations that left whole sets of
interior zones with no cell at all (prelude seed 103: 22 of 64 zones; main seed 4247651650: 182 of 196). Those
zones had no link, Distance lacked them, and the constructor threw KeyNotFoundException placing the antagonist
home (prelude) or the entrance (main). Galaxy was its only consumer; Delaunay.Edges (Bowyer-Watson, double) replaces
it, and the 3,896 vendored lines go.

The three RestoredHullsTests loops that skipped seeds whose galaxy threw now require every seed to build, and the
stale comment that Galaxy mutates Faction.InfluenceDistance goes with them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 21:59:09 +02:00
Meta Vens
072a56f0b4 One loose fit rule for hardpoint gear
Operator ruling, 2026-09-30: "Hardpoint fit is loose: nobody's gonna stop you
from putting a small reactor in a large reactor's hardpoint." There is one rule,
HardpointData.Takes: gear of the hardpoint's type fits when its shape, at the
hardpoint's rotation, lies within the hardpoint's cells. TakesAt is the same
rule at one offset, and Entity.ItemFits (equipping, save load, presets) now
asks it instead of repeating the containment test. Generation's hardpoint
picks, HasHome (so station stock and loadouts), product reuse and `AetherDb
hardpoint-fit` all read Takes.

Generation keeps its own placement preference, named IsFilledBy and not a fit
rule: it offers gear that fills the hardpoint when any is on offer and falls
back to anything that fits only when none is, so loadouts change only where a
hardpoint used to stay empty. RestoredHullsTests pick fillers through
IsFilledBy.

Consequence: the Autocannon, Vulcan, scorched void policy, Notorious, pretty
pretty bang bang, Manhattan, plight, DeathCluster, Small Drive and pswarm now
have a home and are offered. Still homeless: CShot RainbowLite Lazer,
ChargeBlast SG, ChargeBlast+-, OK Disperser.

Tests: generation offers exactly the sold hardpoint gear some hull fits; a 7x7
design no hull fits is offered only once a hull that takes it exists; a 5x5
Reactor hardpoint nothing fills still gets a reactor; equipping and the rule
agree for every hull, hardpoint and design; the homeless list is stated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 20:46:28 +02:00
Meta Vens
15723f4037 Mining Cut 3: a chunk is a target, detected by reflected light
- Entity.Target is one slot of TargetRef, read-only from outside
  (IReadOnlyReactiveProperty); Entity.SetTarget is its one writer. A chunk is
  taken only while the entity can see it, so a rock in darkness cannot be
  picked. Every handler, agent, turret and test writes through SetTarget.
- Entity.ChunkInfo (Q14 A): the value the per-tick sensor rule settles at,
  computed on demand through Sensor.PassiveRate/Sensor.Gain -- sum of the
  active sensors' passive rates over (sensors x TargetInfoDecay), capped at 1.
  Stored nowhere; no per-tick loop over chunks. ChunkVisible is the detection
  threshold through the chunk path.
- The lost-track rule reads through the chunk path: Activate's reconciliation
  and a per-tick check of the one slot drop a chunk target that has gone dark.
- Entity.VisibleChunksInReach (Q13 A): visible chunks within the longest range
  among active weapons -- the one reach rule for cycling and programmatic picks.
- Entity-only readers take .Entity: TargetedBy/TargetedByCount, target items,
  the stance gate (a chunk has no stance), lock (Q12 A: never on a chunk),
  turrets, Combat, Minion, FireControl (a chunk fires untargeted until Cut 4).
  TargetRange and the arc gate read a chunk's planar position.
- ActionGameManager: the reticle and next/previous offer visible ships and
  visible chunks in reach; nearest stays enemies-only; the target panel,
  indicator and fire-control debug take TargetRef. EntityInstance reads .Entity.
- MiningCut3Tests pin parity, darkness, monotone brightness, wear and break,
  reach, the chunk query, target survival and loss, TargetRef equality, lock,
  and field-kind assignment at generation and first load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 12:56:39 +02:00
Meta Vens
d0022d9f5d Cut 1 fix batch 3 (F7): replace the vacuous turret-distance check with a real Phase-offset bound
TutorialEntranceWithNoLagrangeCandidateStillSeatsAStationWithFiniteNearbyTurrets
asserted a turret orbit's Distance matched its station's -- true unconditionally,
since PlaceTurret sets Distance from orbit.Distance directly the same way
CreateLagrangeOrbit does for the station itself, so the check could never
fail regardless of placement. Replaced with a bound on PlaceTurrets' actual
"near their station" claim: the Phase offset (20 * distanceMultiplier /
orbit.Distance), generous over PlaceTurrets' own max distanceMultiplier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 02:37:25 +02:00
Meta Vens
c3f52086a7 Cut 1 fix batch 3 (F5, F6 N1/N3/N9): throw reachability, per-seed and multi-root pins, station-offset pin
F5: committed ThrowReachableWithAuthoredSettings (from Soul's SoulL3Census.cs)
against a real seed-1 tutorial entrance -- RosetteProbability 0 and MassFloor
1e9 leave every planet a parentless root, so even the widened candidate set
(F3's fix) is empty and the throw fires through public construction, not a
hypothetical.

F6/N1: EntranceStationSitsOnAGenuineLagrangeOrbitWheneverOneExists strengthens
the existing across-seeds test with a per-seed pin: whenever a seed's entrance
actually has a genuine (non-rosette) Lagrange candidate, its station must sit
on one, not the widened fallback.

F6/N3: MultiRootEntranceStationOrbitsAreAlwaysParentedWithPositiveDistance
authors a fragmented, heavily-rosette entrance (RosetteProbability 1, a fixed
SubZoneCount of 8) and pins that every resulting station orbit is a real,
positioned orbit -- a set Parent and a positive Distance -- regardless of
which candidate selection produced it.

F6/N9: EntranceStationPhaseDiffersFromEveryPlanetAtItsOwnDistanceAcrossSeeds
pins CreateLagrangeOrbit's own Phase offset: a docked station's Phase must
differ from every planet's Phase at the station's own (Parent, Distance),
catching a mutant that seats a station directly on its source planet's orbit
object instead of an offset copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 02:31:32 +02:00
Meta Vens
95394f786f Cut 1 fix batch 3 (F6 N2, F7): entrance-widening leak test, robust orbit counting, real enemy-count check
F6/N2: added WideningDoesNotLeakToANonEntranceNoLagrangeZoneThatRollsAStation.
Seed 3's "EAC-7089" (the same no-Lagrange fixture the widened-candidate test
uses) naturally has no genuine Lagrange candidate; boosting one faction's
presence there via the same public SavedGame round trip
BuildGalaxyWithForcedEntrance uses makes its station-count roll nonzero
without touching ZoneGenerator's private RNG. Verified directly: dropping
ZoneGenerator's isTutorialEntrance guard on the widening condition turns
this fixture's station count from 0 into 2, so the fixture genuinely
exercises the leak path this test pins closed.

F7: fixed the leak test's comment (the widened-candidate fixture is seed 3,
not seed 1, so it no longer claims to share a fixture with a test that
doesn't). ForcedTutorialEntranceStationDoesNotInflateEnemyCount asserted
shipCount == 2, which was pinning TutorialZoneSettings' own default
NeutralWandererCount (2), not the enemy roll baseStationCount gates -- it
now filters ShipPacks by the same nearest-faction ZoneGenerator computes
internally (EntityPack.Faction), asserting the real enemy count (0) instead.

Also (Soul's finding): IsRosetteMember's planet-orbit count
(pack.Orbits.Count minus the OrbitalEntityPack count) over-counts whenever
a station or turret loadout generator returns null, since
CreateLagrangeOrbit already appended that orbit before the null check
skips adding the entity. It now derives the set from pack.Planets' own
Orbit refs directly (Census.cs's own approach), which sidesteps the bug
entirely.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 02:15:24 +02:00
Meta Vens
a5fb3b84d2 Cut 1 fix batch 3 (F1): load real GameplaySettings, fix the speed-cap test
RestoredHullsTests.Settings() hand-set thermal radiation/conduction at
their bare-class defaults (1/1/1) against the shipped 3 / 1e-8 / 0.01
(Assets/Resources/Settings.asset), so equipped parts froze toward 0 K on
real physics and every test built on that fixture ran on thermal behaviour
nobody ships. It now loads the real GameplaySettings through
AuthoredSettings (tools/AetherDb/AuthoredSettings.cs), the same headless
YAML reader tools/AetherDb already uses to inspect Settings.asset outside
Unity -- the tests project now references AetherDb.csproj to reuse it
instead of hand-copying the same fields a second time.

LonginusNeverExceedsItsRestoredTopSpeedUnderFullThrust no longer needs
OverrideShutdown to keep the ship flying (that was compensating for the
broken fixture shutting equipment down within ~1s); it now drives a
generated Longinus (seed-pinned, including ItemManager.Random, which
defaults to a DateTime.Now seed and was making item quality -- and so
the measured speed -- non-deterministic) for 5s, the window Soul measured
before this loadout's thrusters cross 386 K and shut down. The tolerance
is now asymmetric instead of the old +-10: +4 on the ceiling (the mixer's
own tick order settles real, unforced flight at the cap plus one tick's
acceleration, measured at ~103.3 for this loadout) and -3 on the floor
(tight enough to catch a nerfed TopSpeed catalog value, which +-10 or a
wider ceiling would not).

Every other test in the file now runs on real settings instead of the
frozen fixture; all 15 tests in RestoredHullsTests pass unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:48:58 +02:00
Meta Vens
615008523e Cut 1 fix batch 2 (F1, F2, F3): commit Soul's behaviour tests
F1 (speed cap): assert against Longinus's legacy TopSpeed literal (100)
instead of reading the bound back out of the same catalog value being
tested -- the old assertion passed under TopSpeed=1000 because the
bound moved with the mutation. Also fixes the false "drag/thrust
balance" comment: there is no such balance near 100. At full throttle
Longinus's own thrusters heat their hull past ThermalOnline's shutdown
threshold within ~1s, and once the hull's own cell goes offline the
same way, VelocityLimit (a hull behaviour, gated identically) shuts off
with it, so the cap was only ever exercised for the first ~15 ticks.
OverrideShutdown -- a real, first-class field (the cockpit's "Override
Shutdown" control) -- forces the hull and both firing thrusters to keep
running for the whole 12s scenario, so the cap is what actually holds
speed down throughout, not an unreached ceiling.

F2 (entrance station): EntranceZoneAlwaysGetsADockedStationAcrossSeeds
now also requires the normal case to land on a genuine (non-rosette)
Lagrange orbit. Adds TutorialEntranceWithNoLagrangeCandidateStillSeats...,
built via a public SavedGame/SavedZone round trip (no reflection) that
re-targets a real galaxy's Entrance to a real zone already known to
have no Lagrange candidate (seed 3's "EAC-7089": four planets, one
rosette, no captured satellites) -- pins a docked station whose turrets
land on a real, finite, nearby orbit. Adds the leak checks (override
must not reach other tutorial zones or a non-tutorial game's own
entrance) and the enemy-count check (the override must not also raise
the enemy roll, which stays keyed to the pre-override station count).

F3 (asset identity): EveryAssetReferenceResolvesToAMetaGuid only checks
that a referenced GUID exists somewhere under Assets/, so it passed
when Djinni's Prefab was swapped for Longinus's. The new test compares
each reference against the specific legacy file's own GUID (Djinni's
own .prefab.meta; Thruster 1.prefab.meta for every restored thruster's
ParticlesPrefab).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 20:15:24 +02:00
Meta Vens
aafc90aced Cut 1 fix batch: commit Soul's behaviour tests (asset refs, entrance station, speed cap, loadout fill)
Four tests against the real shipped catalog and the tutorial construction path, each pinning one
of this campaign's rules:

- EveryAssetReferenceResolvesToAMetaGuid: every hull's Prefab, every ThrusterData.ParticlesPrefab,
  and every non-empty Schematic names a GUID an Assets/**/*.meta actually carries. Adapted from
  Soul's own probe (AssetGuidsResolve); it is what caught Djinni's fabricated Prefab GUID and the
  five restored thrusters' null ParticlesPrefab.
- EntranceZoneAlwaysGetsADockedStationAcrossSeeds: replaces
  TutorialZoneGenerationCanProduceADockedStation, which never used the tutorial construction path
  and only claimed a docking bay shows up somewhere in 25 zone-name variants. This builds the real
  tutorial Galaxy with Assets/Resources/Settings.asset's own values and generates the Entrance zone
  the way ActionGameManager.StartGame/PopulateLevel does, requiring a docked station in every run
  that gets that far. Galaxy zone-graph generation has its own pre-existing, seed-dependent
  flakiness (a disconnected zone graph throws inside the Galaxy constructor on roughly 1 seed in
  15) confirmed unrelated to this cut by reproducing identically against the pre-fix-batch catalog;
  that failure belongs to Galaxy.cs and is skipped rather than asserted on, with a floor requiring
  at least 15 of 25 seeds to actually produce a Galaxy.
- LonginusNeverExceedsItsRestoredTopSpeedUnderFullThrust: straight-line full-thrust run against the
  restored VelocityLimitData cap (100).
- LoadoutGeneratorFillsEveryThrusterHardpointAcrossSeeds: LoadoutGenerator, filtered to each
  restored hull, fills every thruster hardpoint across ten seeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 18:01:17 +02:00
Meta Vens
dd4375801c Cut 1 fix batch: Soul's catalog findings (Djinni asset refs, thruster curves, ThermalResilience, legacy keys)
Landed via `dotnet run --project tools/AetherDb -- fix-loco1 apply`, then deleted the command in
the same batch (Cut 9/Cut 1 precedent). Fixes, each against the decoded 2021-04-14 legacy record
or a named catalog precedent:

- Djinni.Prefab was a fabricated/truncated GUID; corrected to Djinni.prefab.meta's own GUID.
- All five restored thrusters' ParticlesPrefab (null) -> Thruster 1.prefab's GUID, the same one
  Large Drive uses (legacy record names the same prefab path for all of them).
- All five thrusters' invented OptimalTemperature/PlateauWidth (300/100) -> Large Drive's own
  200-400 precedent, 251/24 (docs/stats-power-cut1-migration.md).
- ThermalResilience: Medium Drive/Small Drive -> 10, Victoire/Talaria/RevvITup 2.0 -> 15 (legacy
  record). Victoire's Durability 100 -> 30 (legacy). Victoire and Medium Drive's legacy
  descriptions restored (Talaria/RevvITup 2.0's legacy descriptions are the literal placeholder
  "[TODO]" and are not).
- Djinni.Schematic (null) -> djinni_schematic_transparent.png's GUID, already in the tree, unused.
- Djinni's OptimalTemperature 250 -> 251, correcting a rounding slip in restore-hulls's own
  "apply LonginusX's optimal-fraction to Djinni's band" derivation ((275.15-173.15)/400 = .255 of
  band applied to Djinni's 200-unit band rounds to 251, not 250).
- All seven restored records (Longinus, Djinni, five thrusters) re-keyed from freshly minted GUIDs
  to their own legacy GUIDs, decoded directly from GameData/Legacy/AetherDB.2021-04-14.msgpack,
  matching the Cut 9 import precedent (tools/AetherDb/Import.cs, git history). Each record's own
  FactionProductData.Design ref repointed at the new key.

Verified: dry run then apply, second apply refused (Longinus already carries its legacy key),
`dangling` reports 0, `census` still names both hulls, `loadout` generates every hull cleanly
across seeds 1-5.

Re-keying changed GetAll<GearData> enumeration order, which
RestoredHullMovesAndTurnsUnderMovementAndLookDirection's design-selection (thrusterDesigns.First())
depended on implicitly: it started landing every Djinni thruster hardpoint on Victoire, the one
restored thruster with a real EnergyUsage draw, and the fixture ship carries no reactor. Fixed by
equipping the fixture ship's own Reactor hardpoint, which every restored hull actually has, instead
of relying on incidental unpowered-item ordering.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 17:38:44 +02:00
Meta Vens
6b5bb87e91 Cut 1: RestoredHullsTests, against the real restored catalog
Read-only against GameData/Aetheria.cc: each hull exists with the
expected thruster hardpoint count (Longinus 4, Djinni 8), every thruster
hardpoint accepts a catalog thruster design through TryEquip at its own
position, and each hull moves and turns for a few ticks under
MovementDirection/LookDirection on the pre-Cut-3 mixer -- content proof,
not controller proof.

Also covers the operator's tutorial-station addition (2026-09-25):
Zenith (the live Station hull) is confirmed working already -- station-fit,
hardpoint-fit and `loadout` all pass for it, and the legacy record's only
station design ("Zenith Station") is that same hull, so nothing needed
restoring. ZoneGenerator's story-station path is dead code today (Galaxy's
tutorial constructor has its StoryProcessor call commented out), so
TutorialZoneGenerationCanProduceADockedStation drives the background
station path directly (real ZoneGenerator.GenerateZone call, real
catalog, a minimal SavedGame/Galaxy fixture) across 25 zone/seed
attempts and asserts at least one produces a station with a docking bay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 16:33:47 +02:00