68 KiB
Scenarios: Cut Map
Date: 2026-09-30, 02:10 CEST. Imagination pass.
Status: cut map, ruled 2026-09-30 (see "Rulings" below). Nothing has landed. The target lives in section 0 until Self splits it out into
docs/scenarios-target.md; this document owns the means.
Anchors are against Aetheria origin/master dcd7bbc5 (the fire-control merge), read in the clone
C:\ws72-fuse5 with git show/git grep. The operator's tree F:\Projects\Aetheria was not touched. No
build ran and no Yggdrasil job ran. (read) marks a claim read from source at the anchor. (probe) marks a
claim measured against a real file.
Rulings (operator, 2026-09-30)
- Q1 A. The arena is the entrance zone of a small prelude galaxy. Operator: "A is fine, I guess, since some of the stuff we'll be testing won't work in an isolated zone with no galaxy."
- Q2 B. The agent records results with
AetherDb record; the menu shows them read-only. - Q3 A. Test designs are committed catalog designs with no product.
- Q4: none of A-C. "Debug console commands are hella useful in general and fun for sandbox gamers, never toss a
single one." Cut 0 is cancelled:
give(with its hull route),spawnturretand every other console command stay. The neutral wanderers are not a console command and no ruling retires them, so they stay too. Scenarios coexist with the console; they are not its replacement. - Smoke weapons. The operator will not restore their tree's uncommitted
GameData/Aetheria.ccthemselves: "No I won't, but you may." When Cut 4 lands, Self runsgit checkout -- GameData/Aetheria.ccinF:\Projects\Aetheria. That one file is the only exception to the rule that agents do not touch the operator's tree. - The ten real designs with no product (operator, 2026-09-30). Soul's census of Cut 1 found that "no product"
was not a clean marker for Q3 test designs, because ten shipped designs have none. The operator ruled:
- Industrial gear: Assembly Line, Deep Ore Extractor, Surface Ore Extractor, Industrial Thermostatic Heater,
Refinery, Shipyard. "An old carryover from this Aetheria repo's early history as an RTS game. We'll want those
when we eventually bring production back, but it's also fine to just delete them since the new economy won't
look anything like the old one." Self: delete them. Git history keeps them.
Correction, same day: the Industrial Thermostatic Heater stays. Operator: "that industrial heater is sort
of needed, though, because currently stations are mostly idle so there's nothing to keep them above freezing
and they often cool to near invisibility." Only the other five are deleted. The census test allows product-less
gear that stations actually equip, and that set is derived from station generation, not from a hand list.
Superseded, same day. Stations have never equipped the heater. Operator: "Stations freezing is an existing
defect, I don't think I ever made those heaters spawn." Station-heater ruling A: "Yep, A. The thermostatic
behavior is the same one used in ship cockpits to keep the player from freezing, and the industrial version
should fit anywhere as a generic equippable tool."
- Every generated station spawns with one heater, through station generation.
- The heater shares the cockpit thermostat's owner.
- It is equippable on any hull.
- Self default: as generic gear it gets a product, so the census test needs no station exception. Station power and the Tractor Beam (operator, 2026-09-30):
- "Yep, we need a station reactor." Author a reactor that fits Zenith's 16-cell reactor slot, with a product, so the station heater has power.
- "Nope, ships don't need these, 'fits anywhere' was a description of tool-type gear, which doesn't require a specific hardpoint." The heater keeps its 12-cell shape. The ship-heater test is dropped.
- "Screw old saves." No retrofit of heaters onto stations loaded from existing saves.
- "Tractor Beam is supposed to be for item pickup. A ship without a pickup behavior shouldn't be able to pick things up, but there is currently no such limitation AFAIK." Picking up items requires a pickup Behavior, which the Tractor Beam carries.
- Starting loadouts (operator): "Yep, everyone gets tractor beams. Pickup behavior was previously discussed as
part of the field shield map, as one of the behaviors which we'll have various visual effects for. The tractor
beam item would use the existing pickup VFX but with the standardized pickup animation." Every generated ship
and the player's starting ship carry a Tractor Beam. The pickup Behavior and its presentation follow
docs/shield-presentation-contract.md("Shield and Pickup Presentation Contract"). - Autocannon (operator): "Leave the autocannon unsold, we'll need to author a bunch more hulls before all the gear variety in the game has a home." Self's implementation: generation and stock never offer gear that no hull's hardpoint can fit. The rule is derived from hardpoint fit, not a hand list. The Autocannon keeps its product and becomes sold automatically once a hull fits it. The census test is unaffected.
- Station reactors by manufacturer (operator, 2026-09-30): "On station reactors: A is a compensator that breaks
the setting; a station with no access to Zhestokost gear should not have Zhestokost gear, so B."
- Batch 2 (
b3e7995c) authored a Zhestokost Station Reactor scaled from Manhattan. A faction without Zhestokost allegiance got no reactor, so its station heater had no power. - The fix is content, not a generation exception: station reactors are authored for other manufacturers, so that every faction's stations can power their heater from gear the faction can actually get.
- The heater must be reachable the same way.
- Batch 2 (
- Soul on batch 2 (
89ef80d2..adad301b, 2026-09-30): do not merge.- F1 (high): the station reactor burns out after about 20 idle minutes. By minute 40 all 128 cells are below freezing, and at 120 minutes the hull reads 155 K. Minutes 0-20 overshoot to 447 K, which makes the station a sensor beacon.
- F2 (high): the idle test samples minutes 5-10 and does not pay for the heater. The inert-thermostat mutant S17 survives.
- F3 (medium, operator question):
Entity.ItemFits(the item fits inside the hardpoint; used for equip, save load and presets) andHardpointData.Takes(the item fills the hardpoint; used by generation andHasHome) are two fit rules. UnderTakes, about 15 designs players can mount are kept out of station stock. - F4 (high): LRMM72 and SRMM72 have no
DamageCurvebut now have products, and 22 of 300 NPCs carry them.SampleDpsthrows an NRE atInstantWeapon.cs:64. Autocannon, pswarm and plight are also missing curves. - F3 RULED (operator, 2026-09-30): "Hardpoint fit is loose: nobody's gonna stop you from putting a small
reactor in a large reactor's hardpoint." One fit rule: an item fits if its shape fits within the
hardpoint. Generation,
HasHome, stock, equip, save load, presets andAetherDb hardpoint-fitall use it. So the Autocannon now has a home (the Turret) and is sold. The Autocannon ruling's own rule, "never stock gear no hull can fit", is unchanged. - Batch 3 rulings (operator, 2026-09-30):
- Soul on batch 4 (
2125f2b1..aa3baf12, 2026-10-01): do not merge yet.-
Blocker: origin outside the hardpoint. The item origin can land outside its hardpoint (
Entity.cs:825,:879-885). Every other hardpoint lookup reads the cell under the origin,Hardpoints[item.Position]: ArcFor, barrels, thrusters,ActionGameManager. For an L-shaped hardpoint that lookup returns nothing, so Unity throws on the first shot. It is latent: every catalog hardpoint is rectangular today. Two ways to fix it:- keep the origin inside the hardpoint;
- look the hardpoint up by the item's cells.
Probe:
scratchpad/soul5/SoulScen5Probes.csP30. -
Minor:
IsFilledByis its own oracle in the test.- Last-row search and thermostat-order are unpinned.
- A gun can't be dragged within its own hardpoint (
InventoryPanel.cs:389). - The new station-reactor products drop their source products' quality spread.
-
Thermal: the Vulcan Station Reactor burns out within 10 idle minutes. That goes to the thermal campaign. Core Power ship reactors also shut down from heat in fights.
-
Queued, not dispatched: the operator ordered a drain on 2026-10-01.
-
- F1 re-ruled after batch 4 (operator, 2026-09-30): "A, separate thermal balance cut". Radiators did not fix it. Heat cannot cross the hull to Zenith's edge radiators: the reactor runs at 360-436 K while the radiator cells sit at their 278 K floor. Every station reactor wears out between minute 20 and minute 100. Ships show the same imbalance under load: MoveOnPro runs at about 480 K and is thermally shut down for most of a fight (S8). Thermal balance becomes its own campaign, measured by a steady-state harness across all hulls at idle and under load. Its levers are reactor idle heat, hull conductivity, reactor tolerance and radiator placement. Scenarios Cut 1 merges with the station freeze recorded as a known defect, which predates this work. The 120-minute idle test is held for that campaign.
- Idle station reactor overheating and burnout (F1): "Stations should have radiators" (option C). The station has a heat source and no sink.
- Per-manufacturer station reactors: "Keep the variety, not every galaxy will have Zhestokost in it". The census showed every main-sector galaxy holds all 12 factions today. The reactors are authored anyway.
- Rossum & Douglas missing from its own allegiance: the operator asked "Does allegiance to oneself even make sense?". Self's proposal: a faction always reaches its own manufacturer's gear, and allegiance lists only other factions. Ruled: "removing the 11 self-entries was my intention". Own-faction reach is implicit in the one reach function.
- Soul on batch 4 (
- Soul on batch 3 (
adad301b..2125f2b1, 2026-09-30): do not merge yet.- S1: the new belt-failure test is flaky and failed 5 of 8 runs.
- S2: a second occupancy rule. Entity.cs:820-823 requires every hardpoint cell to be free, while tool gear may fill a hardpoint's spare cells. So a generated Turret refuses its own gun back (40 of 955). Operator question.
- S2 RULED B (operator, 2026-09-30): "B is the design intent, saving slots in a hardpoint can be a valid tradeoff for crowded ships if you really need to fit an extra tool". A hardpoint item needs only its own cells free. A hardpoint's leftover cells may hold general (tool) gear. A hardpoint still holds at most one hardpoint item; option C, two hardpoint items sharing one hardpoint, was not chosen.
- S3: placement searches fewer offsets than
Takes, so an L-shaped hardpoint crashes generation. Latent. - S4: asteroid respawn timers never count down, so mined asteroids never return. Already fixed by mining:
codex/miningdeleted the belt task threading (Cut 1,22a54ccb),MineAsteroidandRespawnTimers(Cut 2). Respawn isZone.ChunkWear.BrokenUntil. Merge rule: when scenarios and mining meet, mining's side wins for belt and chunk code. Corrected 2026-10-01 by mining Cut 3 Soul (rehearsal62b7cefb/393334c4, 557/557): scenarios'CreateOrbit/AddOrbitchanges are onlySettleBelts()calls, so dropSettleBeltsand all three calls (lines 169/190/324 on scenarios); they merge cleanly but reference the deletedBeltUpdates. Delete scenarios testsRunStartTests.cs:639and:669(deleted belt API). Conflicts:CatalogTypestake both;Zone.csdeleteMineAsteroid;FireControlCut124Tests.cs:2609take scenarios' strict Exact;GameData/Aetheria.cctake scenarios'70a7b0a9then rerunAetherDb field-kinds apply. - S5: a shield charges while unbilled. This is documented design.
- S6: pins are missing for the filling preference, product reuse and the thermostat band.
- Held: F2 (no unbilled running in the catalog, players only warmer), F4 (no nulls; no exceptions in 24k-tick ambient runs or crowded fights), the census, rotation, and no other fit checker.
- F5:
_timeis advanced before the belt wait. F6: mining races the belt tasks. F7: reuse by cell count is unpinned. - Soul did not reproduce the Zhestokost gap: every station in 5 seeds and 12 factions got a reactor. The premise behind the per-manufacturer reactor ruling is being checked (census) before any authoring.
- Tractor/pickup (
adad301b, Hands stopped at the fork): the ruled design isheadless-playground-cut.mdfork L, which needs loot as simulation bodies. That substrate does not exist, so it becomes its own campaign, typed in the new session (F:\Projects\HANDOFF-eureka-typed-2026-09-30.md). The census rule "price 0 is unsold" waits for it.
- Weapons: Autocannon, LRMM72, SRMM72, Tractor Beam. "yep that's a content gap". They get products.
- After both, "no product" means exactly "Q3 test design". A census test pins that: every product-less design in the shipped catalog is a scenario test design.
- Industrial gear: Assembly Line, Deep Ore Extractor, Surface Ore Extractor, Industrial Thermostatic Heater,
Refinery, Shipyard. "An old carryover from this Aetheria repo's early history as an RTS game. We'll want those
when we eventually bring production back, but it's also fine to just delete them since the new economy won't
look anything like the old one." Self: delete them. Git history keeps them.
Correction, same day: the Industrial Thermostatic Heater stays. Operator: "that industrial heater is sort
of needed, though, because currently stations are mostly idle so there's nothing to keep them above freezing
and they often cool to near invisibility." Only the other five are deleted. The census test allows product-less
gear that stations actually equip, and that set is derived from station generation, not from a hand list.
Superseded, same day. Stations have never equipped the heater. Operator: "Stations freezing is an existing
defect, I don't think I ever made those heaters spawn." Station-heater ruling A: "Yep, A. The thermostatic
behavior is the same one used in ship cockpits to keep the player from freezing, and the industrial version
should fit anywhere as a generic equippable tool."
0. Target
0.1 The request, in the operator's words (2026-09-30)
"I feel like this would be much more manageable as a list of things to verify over time rather than a monolithic smoke script that doesn't respect how the game works. Move gear onto Djinni? That's a lot of items to spawn, and am I expected to have the catalog memorized to spawn them? If you want me to test a bunch of behaviors under specific conditions, find a way to spawn me in an arena where those conditions are present. If you want me to kill a guy and loot him, spawn me next to a ship with no armor or weapons, that sort of thing. I wouldn't mind a scenario affordance in the main menu for this sort of job, that way the test fixtures can be authored, retained and available. But that sounds like a Eureka cut."
What those words settle, so no question below re-asks them:
- A scenario is an arena whose conditions are present at spawn. The player's ship and every other entity are already fitted and placed. The operator never spawns gear by catalog name and never fits a ship by hand.
- The fixtures are authored and retained, and the main menu lists them.
- Verification is a list of behaviours checked over time, not a play order.
0.2 Ends
- The main menu lists scenarios. Picking one starts a run in its arena.
- A scenario is typed catalog data. It is authored once, kept in git, and replayed at any later revision.
- Every operator check is a typed check record: what to do, what to observe, where the rule came from, and the scenario that sets it up. Results accumulate as typed records carrying the revision they were observed at.
- Checks A-F and checklist steps 7-20 are the first consumers (section 5).
0.3 Invariants
- One owner decides what a new run starts with. A plain New Game and a scenario launch share the menu path,
the galaxy build,
StartGameand the staging owner. Scenario launch is New Game with a scenario as input; there is no second boot path. A plain New Game is the absent-scenario case. - One primitive admits an entity into a zone. Zone construction, staging, warp arrival and undock all admit through it, and agents are created there and nowhere else.
- A scenario sets conditions and nothing else. Once staged, its entities are ordinary run state. The rules under test run through the same code whether or not a scenario started the run. No scenario-only branch exists in simulation code.
- Fixtures are typed state. Scenario, loadout presets and test designs are CultCache catalog records. Checks and results are CultCache records. No load-bearing JSON, text scripts or prose checklists.
- A scenario that cannot stage is refused before anything is cleared, with its failures listed.
- The catalog stays read-only at runtime (
AetheriaStores.cs:13-21).
0.4 Not in scope
- The full run lifecycle owner (
Run) ofdocs/headless-playground-cut.mdCut 1 (warp, dock, save, death as one ServerShared owner) and itsAetherDb playinterpreter (Cut 3). This map takes over only the new-run staging half. The playground map is stale (anchors1e647953, fork S superseded by settings-globals) and must be re-anchored before Hands. When it lands, itsnewcommand stages aScenariorecord instead of reading a.playtext line. - Brokkr. It is not needed for any cut here: agents author scenarios headless through
AetherDb, and the operator launches them from the menu. Brokkr's Unity actions (setEditorPlayState,captureEditorView, scene edits;F:\Projects\Brokkr1357705, read) have no way to pick a scenario in the menu. Agent-driven Unity play would need an Aetheria install cut plus a "launch scenario" intent. Recorded as a follow-up, not a fork. - The ship-purchase docking-bay gap (checklist section 8). It is a design gap in
TradeMenu.BuyandCommissionShip, not a verification affordance.CommissionShipstays;TradeMenu.Buyis its consumer. - Tier colours (settings-globals Cut 1). The check is recorded as a known failure (section 5).
- Balance and feel tuning. A check can record a feel call; it does not tune.
1. Body findings
1.1 The new-run path today (read)
| Concern | Anchor | Notes |
|---|---|---|
| Menu | MainMenu.cs:92-184 |
Continue :103-108; New Game :110-176; Settings; Quit. The same component runs in-game (InGame, :96), so the menu reappears on death (ActionGameManager.cs:1171). |
| Clear the run | MainMenu.cs:113 |
RunSave.Clear at click time, before the galaxy is built. |
| Galaxy | MainMenu.cs:124-175 |
TutorialPassed is never set anywhere, so New Game always takes the prelude branch :146-175. The background noise position comes from UnityEngine.Random (:128,153). The seed defaults to the clock (Galaxy.cs:106,158). |
| Handoff | statics ActionGameManager.CurrentGalaxy, IsTutorial (:96-97) |
read by StartGame, SectorMap, and nulled by Die (:1173). |
| Start | StartGame :796-834, called from Start :491 |
New run :803-815: PopulateLevel(Entrance), LoadoutGenerator.GenerateShipLoadout filtered by GameSettings.StartingHullName (GameSettings.cs:13, authored LonginusX), unpack, Zone.Entities.Add, Activate, BindToEntity. Continue :817-833. |
| Zone entry | PopulateLevel :710-744 |
generates the pack lazily (:714-722), moves the pilot (:731-737). |
1.2 What a scenario can reuse (read, one probe)
- Loadout presets already exist.
aetheria.loadout(Loadout.cs:13-20) holds a hull design, per-cell slot designs with rotation, and weapon groups. It is a catalog type (AetheriaStores.cs:9), authored in Studio or by the editor console commandcapturepreset(ActionGameManager.cs:624-662,Loadouts.CommitLoadout.cs:70-87).Loadouts.Materialize(:96-159) builds a liveShipall-or-nothing, listing every failure.- No game path calls
Materialize(LoadoutGenerator.cs:152-153says so; onlyLoadoutTests.cs:310,899call it). - (probe) The live catalog holds no preset: the schema name
aetheria.loadoutdoes not occur inGameData/Aetheria.cc(12,698,108 bytes atdcd7bbc5), while every populated type's name does (for exampleaetheria.hulldata,aetheria.weaponitemdata). Method:grep -a -oover the file. Materializealways builds aShip(:131). A turret hull cannot be materialized.- It resolves every design through a product (
Resolve,:103-115). A design with no product fails.
- No game path calls
- Unbranded lots exist.
ItemManager.CreateLot(design, maker, quality)(ItemManager.cs:161) with no maker is howgivemade its items (ActionGameManager.cs:538,542). Provenance's "no Manufacturer row" check depends on them. - Economy exclusion follows from products. Generation iterates products and requires
product.Manufacturer.IsSet()anddesign.Price > 0(LoadoutGenerator.cs:126-133). A design with no product is never generated, stocked or sold. Loot is what a killed entity carries (EntityInstance.cs:302-321), so it cannot leak through loot either unless a scenario fits it. - Prelude galaxies offer every product.
IsAvailablereturns true whenGalaxy.IsPrelude(LoadoutGenerator.cs:154-156). A preset materialized in a prelude arena never fails on faction availability. - Agents are made only at zone construction.
Zone's constructor gives every packed non-playerShipaMinion(Zone.cs:120-131,CreateAgent:135-142). An entity added later gets none. AMiniontargets the first visible enemy and fights (Minion.cs). - An entity joins a zone in four other places, each by hand:
spawnturret(ActionGameManager.cs:587),PopulateLevel(:735),StartGame(:813) andEntity.TryUndock(Entity.cs:1006). - IFF overrides are entity-local and unsaved.
Entity.SetIff(other, bool?)(Entity.cs:529-533) decidesIsHostileToahead of faction rules (:535-552). AMaterialized ship has no faction, so it is neutral unless overridden. - A target that dies and a target that leaves are the same case in fire control.
targetGone = shot.Target != null && !zone.Entities.Contains(shot.Target)(FireControl.cs:610), and death removes the entity fromZone.Entities(Zone.cs:93). Check A.3 ("the target warps away") is therefore set up by killing a fragile target while a fused round is in flight. No departure machinery is needed. - An orbital entity with no orbit stays where it is put (
OrbitalEntity.csUpdate: position follows the orbit only when one is set). That is howspawnturretplaced turrets (ActionGameManager.cs:583-588). - Headless tests already open the live catalog and the authored settings
(
RestoredHullsTests.cs:AuthoredSettings.Load,OpenReadOnlyRealCatalog). A staging test can generate a prelude galaxy, build the arena zone and stage a scenario with no Unity.
1.3 Debug affordances a scenario replaces (read)
| Affordance | Anchor | Added for |
|---|---|---|
give <name> |
ActionGameManager.cs:515-544 |
ad hoc items; the hull route through CommissionShip was added tonight in f935c29b for step 7 |
spawnturret |
:564-589 |
a hostile turret next to the player |
| Neutral wanderers | ZoneGenerator.cs:342-356, EligibleWandererFactions :361-365, ZoneGenerationSettings.NeutralWandererCount (Settings.cs:156), tests IffAndCombatTests.cs:329-350 |
commit f732cff9 (2026-09-17): "Spawn neutral wanderers so combat has something non-hostile to target". The code comment calls it a "Testing affordance". Every generated zone carries two. |
Staying, because no scenario replaces them: iff (flip a live target's stance mid-play), trackmissile (camera),
revealzones (map), tow (a verb bound to the console), capturepreset (the in-play authoring tool for presets).
1.4 Collisions with open branches (read)
codex/miningrewritesZone.cs(+239/-). Cut 1 edits the constructor loop and adds the admission primitive. Land Cut 1 after mining merges, or re-anchor.codex/moddable-shipseditsAetheriaStores.cs(line 9, the catalog type list). Cuts 1 and 3 edit the same lines.codex/item-provenancecarries the narrative map, which editsMainMenu.cs:132,160and theGalaxyconstructors. Cut 2 edits the same New Game block.
0b. Identity, lifecycle, authority
No cut is mapped against an empty cell.
| Kind | What names it | Lifecycle | Who decides |
|---|---|---|---|
Scenario (aetheria.scenario, catalog) |
CultName Name; key scenario:<Name>, derived like Loadouts.KeyOf |
Authored, then edited in place. A rename is a new record; the old one is deleted, and the checks that pointed at it are repointed in the same commit. A result never points at a scenario (it points at a check), so history survives. A scenario whose presets stop materializing is caught by the staging test (Cut 4), not at play time. | Authored by agents through AetherDb or by the operator in Studio. The operator owns what an arena contains. |
Loadout preset (aetheria.loadout, existing) |
key loadout:<Name> (Loadout.cs:62) |
Unchanged: authored, replaced only explicitly (Commit(..., replace)). |
Unchanged. |
Test design (catalog ItemData, no product) |
name; key minted by Upsert |
Authored with the scenario that needs it. Lives as long as a preset references it. Per Q3. | Operator (Q3) on whether they exist at all. |
Check (aetheria.check, ledger store) |
CultName Slug; key check:<Slug> |
Authored. Its statement is edited in place when a ruling changes (git keeps the history). It is retired, never deleted, once any result references it: Retired plus a reason. |
The rule's owner (a ruling or a map) decides what it says. Agents transcribe it with a source pointer. |
Check result (aetheria.checkresult, ledger store) |
key checkresult:<Slug>:<At as UTC ticks> |
Append-only. Never edited or deleted. A correction is a new result whose note says what it corrects. The latest by At is the current status. Order is carried in the data (At), not in delivery. |
The operator's observation. Who writes the record is Q2. |
| Pending scenario (runtime) | ActionGameManager.PendingScenario, a static |
Written once by the menu's launch, read and nulled once by StartGame's new-run branch. Null for Continue. Never saved. |
Command-only transport. It decides nothing. |
| Staged entities (runtime, then run state) | ordinary entities | Born at staging. From then on they are run state and saved with the zone like any other entity. Two things are not saved: whether a ship was left unpiloted, and scenario IFF overrides. After Continue, an unpiloted scenario ship gets a Minion by the constructor rule and its stance reverts to faction rules (neutral, since presets carry no faction). A scenario run is meant to be played through in one sitting. |
RunStart at birth; the zone afterwards. |
2. Target shape
2.1 Types (names and rules; bodies are Hands')
Assets/Scripts/ServerShared/Scenario.cs, catalog:
Scenario(aetheria.scenariov1):Name(CultName).Brief: the conditions in one or two lines, shown in the menu.Seed(uint): the arena galaxy and the background noise both derive from it, so the arena is the same layout every launch. Combat rolls are already zone-seeded (Zone.cs:84). Lot quality still comes from the clock-seededItemManager.Random(ItemManager.cs:20), so stats vary slightly per launch. Accepted.Ambient(bool): true keeps the arena zone as generated. False keeps planets, orbits and stations but drops generated ships and turrets, so nothing wanders into the conditions.Player(ScenarioShip).Entities(List<ScenarioEntity>).
ScenarioShip:Loadout(CultRecordRef<Loadout>),Position(float2, zone xz),Direction(float2),Cargo(List<CultRecordRef<EquippableItemData>>, stored in the first cargo bay).ScenarioEntity : ScenarioShip:Stance(Neutral | Hostile, applied both ways throughSetIff) andPiloted(bool: aMinion, or no agent at all).- A preset's hull type picks the entity class: a ship hull gives a
Ship; a turret hull gives an orbital entity with no orbit, stationary at its position. A station hull is refused as a staging failure.
Assets/Scripts/ServerShared/Checks.cs, ledger store (GameData/Checks.cc, committed, LFS by the existing
GameData/*.cc rule):
Check(aetheria.checkv1):Slug(CultName),Statement(what to do and what must be observed),Source(the ruling or map pointer,doc:line),Scenario(CultRecordRef<Scenario>, optional),Setup(text, only when there is no scenario: "FieldShieldTest scene", "CultCache Studio", "Addressables play mode Use Existing Build"),RetiredandRetiredReason.CheckResult(aetheria.checkresultv1):Check(ref),Result(Pass | Fail | Partial | Blocked),Revision(the commit the operator played, plus+dirtywhen the tree had changes),At(UTC),Note,Recorder.
Naming: "check", not "behaviour". Behavior is already the gear-behaviour type family
(Assets/Scripts/ServerShared/Behaviors/), and a document named Behaviour beside it reads as a typo.
Where they live, and why:
- Scenarios, presets and test designs are design-side fixtures. They go in the catalog: presets are already catalog data, cross-type refs resolve inside one store, and Studio edits them side by side.
- Checks and results are the verification ledger. They go in their own store. The shipped catalog carries no history, results never trigger a catalog write, and "one commit lands in one store" holds: recording a result touches the ledger only.
2.2 The staging owner
Owner: RunStart, a static class in Assets/Scripts/ServerShared/RunStart.cs. It decides what a new run
starts with: how the arena zone is generated, the player ship, the other entities, their stance, pilot and cargo.
It holds no Unity type.
- Inputs:
ItemManager(and through it the catalog), theGalaxy,ZoneGenerationSettings, the optionalScenario, and, for the absent-scenario case, the default hull filter and factionStartGameuses today (:805-809). - Outputs:
- the arena's
ZonePack, with theAmbientrule applied inside generation (ZoneGenerator.GenerateZonegains the parameter), not generated and then pruned; - the admitted player
Ship, flaggedIsPlayerShip; - the admitted scenario entities;
- a failure list. Staging is all-or-nothing, as
Materializeis.
- the arena's
- A check entry that validates a scenario without a zone (every preset materializes, every cargo design
exists). The menu calls it before
RunSave.Clear; the staging tests call it on every catalog scenario. - Derived state:
ActionGameManager.CurrentEntityis bound to the returned player.PendingScenariois transport only. - Forbidden writers:
StartGamemay not generate, unpack or admit the player ship.MainMenumay not stage. Console commands stay (Q4 ruling), butgiveandspawnturretadmit throughZone.Admit, the same primitive as scenario staging. They get no admission path of their own. Cut 1 repoints them. - Shared paths: New Game and scenario launch both run menu, galaxy,
StartGame,RunStart. Continue does not stage. - Deletion line:
StartGame:805-814(generator, unpack, add, activate). The only surviving line is the bind.
Admission primitive: Zone.Admit(Entity entity, bool piloted). It adds the entity to Entities, activates it,
and, when piloted, gives it the zone's Minion (the CreateAgent rule, Zone.cs:135-142). It is the only writer
of Entities.Add and the only creator of agents.
- The constructor loop (
:120-131) calls it withpiloted: ship && !IsPlayerShip. It keeps its own rule that moves a ship packed at the origin (:129-130); that is generation placement, not admission. PopulateLevel(ActionGameManager.cs:735),Entity.TryUndock(Entity.cs:1006-1007) andRunStartcall it.spawnturret(:587) is deleted.- The
Deathsubscription (Zone.cs:93) stays onObserveAddand still covers every join.
Loadouts.Materialize gains two rules (Loadout.cs:96-159):
- A design with no product at all is built unbranded, through
CreateLot(design, default, quality)at one named quality constant (0.95, the qualitygiveused). A design whose products are all unavailable still fails, as today. The rule turns "no manufacturer makes it" into "outside the economy", which generation already obeys. - The hull type picks the entity class (section 2.1).
- The comment at
LoadoutGenerator.cs:150-153("No game path materializes a preset yet") is rewritten to nameRunStart.
2.3 The Unity lowering
MainMenu.ShowMain(:92-184) gains Scenarios, shown whenDebug.isDebugBuild(editor and development builds). It opens a panel with one button per catalog scenario, ordered by name. Under each button is its brief and its checks, each with its latest result and revision, read from the ledger (display only). A scenario whose check entry fails shows the failures and is disabled.- One launch body is extracted from the New Game lambda (
:110-176). New Game calls it with no scenario; a scenario button calls it with one. With a scenario, it runs the prelude branch (:146-175) withSeed, derives the noise position fromSeedinstead ofUnityEngine.Random, setsPendingScenario, and loadsARPG. The check entry runs first;RunSave.Clear(:113) moves after it, so a refused scenario leaves the saved run intact. StartGame's new-run branch (:803-815) callsRunStart, binds the returned player and nullsPendingScenario. The Continue branch asserts it is null.AetheriaStores.Opengains an optional ledger path. The game attachesGameData/Checks.ccread-only;AetherDbattaches it writable for the record commands (Cut 3).
2.4 Authoring
- Agents author headless through
AetherDb. A seed command (Cut 4) declares the first set in C#. It writes a record only when the key is absent. When the key exists and differs from what it would write, it refuses and reports; that means Studio edited it, and Studio wins. When the key exists and matches, it does nothing. It validates every scenario through theRunStartcheck entry before committing. So the catalog is the authority, and the seed code never clobbers it. - The operator authors in Studio and in play: Studio for scenario fields;
capturepresetfor a ship fitted by hand in play. - Results are recorded per Q2.
3. Cuts
Build budget, every cut:
Aetheria.Shared(netstandard2.1, the Unity-independence check),tests/Aetheria.Shared.Tests,tools/AetherDb(net10.0), Debug.- CultLib is unchanged; the pins are those in
hands-fuse8/run.sh. - No new project, target, package or daemon. One new store file (
GameData/Checks.cc), three new schemas. - Build host: Yggdrasil builds the headless projects (
run.sh <rev>).Assembly-CSharpis proven only by a Unity 6000.3.24f1 batchmode compile on Starfire, with the editor closed by the operator. Never kill Unity. run.shmounts a pinneddata/Aetheria.ccover the tree's LFS file. Any cut that changes the catalog (Cut 4) must refresh that pin, or its content tests read the old catalog. Cut 4's tests that read the ledger mount it the same way. Yggdrasil's disk is saturated: one job per cut,--artifacts-pathin the container.
Order: 1, 2, 3, 4. Cut 0 is cancelled (Q4 ruling). Cuts 1 and 2 branch from master directly. Cuts 1 and 3 are headless. Cut 2
is the only Unity-side cut. Cut 4 is content.
Cut 0. Retire the ad hoc test affordances (Q4). CANCELLED
Cancelled 2026-09-30 by the Q4 ruling ("never toss a single one"). Nothing below runs. It is kept as history.
Cut 1 branches from master.
- Repo/branch: Aetheria,
codex/scenariosfrommasterdcd7bbc5. - Deletes first (per Q4 A; B keeps the item branch of
giveand the wanderers):ActionGameManager.cs:515-544,give(30 lines).ActionGameManager.cs:564-589,spawnturret(26 lines).ZoneGenerator.cs:342-356, neutral wanderers (15 lines), andEligibleWandererFactions:361-365(5).Settings.cs:156,NeutralWandererCount(1). It is absent fromSettings.asset(settings-globals:72), so no asset edit is needed.IffAndCombatTests.cs:329-350, the two wanderer tests (22).
- Keeps:
CommissionShip(:898-903) and its consumerTradeMenu.Buy; the console fixes inf935c29b. - Docs:
docs/merge-to-master-checklist.mdsection 8, thegivebullet: add "retired by the scenarios cut" with the commit.docs/settings-globals-cut.md:72,348get a one-line history note. - Verification:
- builds:
Aetheria.Shared, the test project. Batchmode compile (ActionGameManager.cschanged). - tests: the full suite is green. The locomotion "real enemy-count check" (
95394f78) counts generated ships; if it counted wanderers, fix its expectation and say so. - negative:
rg -n '"give"|"spawnturret"' Assets/Scriptsis empty;rg -n "NeutralWanderer|EligibleWandererFactions" Assets tests toolsis empty. Both patterns were checked to match only these sites today. - operator: none. There is nothing to see until Cut 2.
- builds:
- Ledger: about -99 lines; 0 added.
Cut 1. RunStart, Zone.Admit, and the scenario type (headless)
- Repo/branch: Aetheria
codex/scenarios, frommaster(Cut 0 cancelled). Re-anchorZone.csif mining has merged. - First: with
AetherDb hardpoint-fitand a scratchMaterializeagainst the live catalog, confirm thatDjinni,LonginusXand one turret hull each have a product and materialize bare. If one has no product, stop and report: the Q3 rule would make it unbranded, which may not be intended for a real hull. - Deletes first:
ActionGameManager.cs:805-814, the new-run spawn (it moves intoRunStart; this cut leavesStartGamecallingRunStartwith no scenario, so behaviour is unchanged).- The four hand-written joins:
ActionGameManager.cs:735-736,Entity.cs:1006-1007,Zone.cs:124-125, and the agent add atZone.cs:128. Each becomesAdmit.
- Repoints (Q4 ruling, console commands stay):
give's hull route (ActionGameManager.cs:515-544) andspawnturret(:564-589) admit throughZone.Admit. If either already reaches one of the four joins above, that repoint covers it. A test pins thatgiveof a hull andspawnturretboth land inEntitiesthroughAdmit, and its mutation (restoring a direct add) must fail it. - Adds:
Scenario.cs,RunStart.cs,Zone.Admit, theambientparameter onZoneGenerator.GenerateZone(:38-43; the ship loop:334-340andPlaceTurretscalls:309,331honour it),ScenarioinAetheriaStores.CatalogTypes(:9), and the twoMaterializerules (Loadout.cs:103-115,131). - Authority map: section 2.2. Owner
RunStart; admissionZone.Admit; deletion line as above. - Verification:
- builds:
Aetheria.Shared, tests,AetherDb. Batchmode compile (ActionGameManager.cs,Entity.cschanged). - tests (new
RunStartTests: live catalog, authored settings, a prelude galaxy at a fixed seed). Each rule must fail under its own mutation:- With no scenario, staging yields a player of the
StartingHullNamehull,IsPlayerShip, admitted, at the origin. This pins that New Game is unchanged. - With a scenario, the player is the preset's hull and fit at the authored position and direction, and its cargo holds the listed designs.
- A piloted entity has exactly one agent in
Zone.Agents; an unpiloted one has none and stays put over 5 s ofZone.Update. Hostilemakes bothIsHostileTodirections true.Neutralmakes both false.Ambient: falseleaves no ship or turret in the arena pack that the scenario did not place. Stations remain.- A turret preset stages as a stationary orbital entity. A station preset is a staging failure.
- A scenario naming a missing design fails with that design named. The zone is unchanged, and no
RunSavecall was made (the check entry touches no store). - A product-less design materializes unbranded; a design whose only product is unavailable still fails. This is two tests, because the second case is the existing contract.
Admitis the only agent creator: after a warp (PopulateLevel's join throughAdmit(pilot, false)) the pilot has no agent. The constructor's pack path still gives every non-player ship aMinion.
- With no scenario, staging yields a player of the
- Stryker on
RunStart.cs,Zone.cs(Admitand the constructor loop),Loadout.cs(Materialize), scoped to the diff. Triage survivors by name. - negative:
rg -n "Entities\.Add\(" Assets/Scripts --glob '!**/ZoneGenerator.cs'matches onlyZone.cs(once, insideAdmit),Sensor.cs:169andEntity.cs:242(both areVisibleEntities/pings, not zones).rg -n "new Minion\(" Assets/Scriptsmatches onlyZone.cs. - operator: none beyond the Cut 2 smoke.
- builds:
- Ledger:
Scenario.cs+60;RunStart.cs+110;Zone+12 -8;Loadout.cs+20;ZoneGenerator+4;ActionGameManager-11 +4;Entity±2;AetheriaStores±1; tests +300.
Cut 2. The main menu launches scenarios (Unity)
- Repo/branch: Aetheria
codex/scenarios, on Cut 1. - Deletes first: the duplicated body of the two galaxy branches in the New Game lambda (
MainMenu.cs:124-175) collapses into one launch body. The standard branch (:124-145) is live code only whenTutorialPassedis true, and it is kept. Only the duplication goes. - Adds: the Scenarios panel,
PendingScenario, the seed-derived noise, the check entry beforeRunSave.Clear.StartGamepassesPendingScenariotoRunStart. - Authority map:
- Owner of what the run starts with:
RunStart(unchanged). MainMenuowns which scenario was picked and when the run is cleared.PendingScenariois a command-only transport, consumed once.- Forbidden:
MainMenustages nothing and admits nothing.StartGamereadsPendingScenarioonly in the new-run branch. - Shared paths: both buttons run one launch body. In-game menu (after death) and title menu run the same one.
- Owner of what the run starts with:
- Verification:
- builds: batchmode compile, editor closed.
- negative:
rg -n "RunSave\.Clear" Assets/Scripts/UI/MainMenu.csappears once, after the check entry.rg -n "PendingScenario\s*=" Assets/Scriptsmatches the launch body andStartGame's null only. - operator (Unity, Starfire):
- Title menu: Scenarios is listed. New Game still starts a LonginusX at the entrance with a Zenith station (the Cut 1 test pins the ship; this proves the lowering).
- Launch any scenario. The arena matches its brief; launch it again and the layout is the same.
- Die in a scenario. From the in-game menu, relaunch the same scenario.
- Quit mid-scenario, then Continue. The run continues (the 0b rule: unpiloted ships now patrol).
- Break a scenario on purpose in a scratch catalog (a missing design). It is shown disabled with the failure, and Continue still offers the previous run.
- Ledger:
MainMenu+45 -25;ActionGameManager+4; one static.
Cut 3. The verification ledger (headless; Q2)
- Repo/branch: Aetheria
codex/scenarios, on Cut 1 (independent of Cut 2). - Adds:
Checks.cs(Check,CheckResult);AetheriaStores.LedgerTypesand the optional path;GameData/Checks.cc(empty, committed).- Under Q2 B,
AetherDbgains two commands:checks [scenario]: each check with its latest result, its revision, and "never" when there is none.record <slug> <pass|fail|partial|blocked> --revision <sha> [--note <text>]. It refuses an unknown or retired slug. It opens the ledger writable and the catalog read-only.
- Under Q2 A, the menu panel gains Pass/Fail/Partial and a note field per check. It writes through the same ledger primitive, and the game attaches the ledger writable.
- Under Q2 B,
- Authority map:
- Owner of results: the ledger store. The only writer is the Q2 writer, which calls one append primitive,
Ledger.Record. - Derived, display only: the menu's latest-status line and
checks' output. - Forbidden: nothing edits or deletes a
CheckResult; nothing writes a result into the catalog.
- Owner of results: the ledger store. The only writer is the Q2 writer, which calls one append primitive,
- Verification:
- tests (new
LedgerTests, temp stores):- Recording appends and never replaces: two results for one check both survive a reopen, and the latest is
the one with the later
At, even when written first. - A retired or unknown slug is refused with nothing written.
- The catalog file is byte-identical after
record(a catalog opened read-only cannot be written; this pins that the command opened it that way). - A result carries the revision it was given, and
recordwithout--revisionis a usage error.
- Recording appends and never replaces: two results for one check both survive a reopen, and the latest is
the one with the later
- Stryker on
Checks.csand theAetherDbrecord path. - negative:
rg -n "CheckResult" Assets/Scripts --glob '!**/Checks.cs'matches only the menu's read (and under Q2 A its record call).
- tests (new
- Ledger:
Checks.cs+50;AetheriaStores+4;AetherDb+90 (B) orMainMenu+50 (A); tests +120.
Cut 4. The first scenarios, checks and history (content)
- Repo/branch: Aetheria
codex/scenarios, on Cuts 2 and 3. Hands authors on the branch clone, never inF:\Projects\Aetheria. - First: Self restores the uncommitted smoke weapons in the operator's tree (
git checkout -- GameData/Aetheria.ccinF:\Projects\Aetheria, that file only, per the smoke-weapons ruling) before the operator pulls. This cut recreates them as committed designs. - Adds:
- A transient
AetherDb scenario-seed [apply]command, with the never-clobber rule of section 2.4.- It writes the test designs (section 5.3), the presets (5.4), the scenarios (5.2) and the checks (5.1).
- It imports the 2026-09-30 smoke results (checklist section 8) as
CheckResults at revision113164fa, recorder "operator (play), recorded by agent". - It runs once, the data is committed, and the command is deleted in the same cut. The catalog is the authority from then on.
ScenarioConditionTests(live catalog; pinned catalog refreshed, see the build budget). Per scenario, stage it and assert the conditions its checks need, not the behaviour, which existing suites own. For example:Fused rounds: the bare hull lies within the arc on the player's initial aim line and has no target selected; the close hull is nearer thanSmoke Proximity'sBlastRadius;Smoke RefusedhasRange < BlastRadius; a direct-fire weapon can kill the fragile hull.Duel: the AI is piloted, hostile both ways, and carries a slow missile weapon; the player can see it.Arcs: the three bare hulls lie in the bow, beam and stern sectors of the player's mounts; the turret is hostile.- Every catalog scenario passes the check entry. This is the rot test.
- Every non-retired check with a scenario points at an existing one. Every check without a scenario has a
Setup.
- Docs:
docs/merge-to-master-checklist.mdsections 2 and 8 get one line: steps 7-20 and handoff checks A-F are now checks inGameData/Checks.cc, listed byAetherDb checks. The prose steps stay as history.
- A transient
- Verification:
- tests:
ScenarioConditionTests, with mutation by editing a fixture. Moving the close hull outside the blast radius, dropping the AI's missile weapon, or flipping a stance must each fail its condition. This is a one-off Soul probe, not a committed suite. AetherDb checkslists every check in section 5.1, with the imported results on 3, 4, 5, 6 and "never" elsewhere.- operator: play each scenario and report per check. That report is the first real use of the ledger.
- tests:
- Ledger: seed command +300 then -300; catalog +5 designs, +11 presets, +10 scenarios; ledger +44 checks, +7 results; tests +250; docs ±4.
4. Authority map (whole campaign)
- Owner of a new run's contents:
RunStart. It reads the scenario (or its absence) and writes the arena pack and the admitted entities. - Owner of zone membership and agents:
Zone.Admit. - Owner of fixtures: the catalog (
Scenario,Loadout, test designs), written by the seed, Studio orcapturepreset, and read-only at runtime. - Owner of verification history: the ledger store. It is append-only and written by the Q2 writer.
- Derived: menu status lines;
AetherDb checks;PendingScenario(transport). - Forbidden writers:
StartGamespawning ships;MainMenustaging; console commands admitting entities; anything editing a result; any catalog write at runtime. - Shared paths: New Game and every scenario launch: one launch body,
StartGame,RunStart,Admit. Warp, undock, construction and staging:Admit. - Deletion line:
StartGame:805-814; the four hand-written joins; the duplicated New Game branches; the transient seed command.
5. The first consumers
Sources: docs/merge-to-master-checklist.md section 2 (steps 7-20) and section 8; checks A-F from
F:\Projects\HANDOFF-aetheria-play-smoke-2026-09-29.md section 3. That file sits outside the repo, so the checks
below restate A-F and the ledger becomes their home. The rulings behind A-F are in fire-control-cut.md, in the
block "Ruled (operator, 2026-09-30)" (:2625-2690).
5.1 Checks
| Slug | Statement (abridged; the record carries the full text) | Source | Setup |
|---|---|---|---|
restored-hull-flies |
Thrusters move and turn the Djinni. | step 7; locomotion-cut.md:501 |
Djinni shakedown |
restored-hull-speed-cap |
Take the hull offline with thrusters firing; speed does not run away. | step 7; :377 |
Djinni shakedown |
weak-reactor-starves-legibly |
A reactor too small for the loadout shows starvation, not a cooked reactor. | step 8; stats-and-power-cut.md:705-706 |
Starved reactor |
brownout-degrades |
Brownout reads as degradation, not breakage. | step 9; :774,990 |
Starved reactor |
brownout-refill-stutters |
A weapon refilling under brownout stutters rather than firing at full rate. | step 9; :729-730 |
Starved reactor |
memory-flat-across-zones |
Several zones with kills; memory does not climb. | step 10; :672-673,980-982 |
Long haul |
side-mounts-fire-abeam |
Side-mounted weapons fire at the beam target. | step 11; fire-control-cut.md:397-398 |
Arcs |
turret-tracks-round |
The hostile turret tracks the player all the way round. | step 11 | Arcs |
no-fire-through-hull |
Nothing fires at the stern target through the hull. | step 11 | Arcs |
shots-roll |
Rolled impacts and misses; a miss reads as a near-miss; damage matches the HUD. | step 12; :659-661 |
Duel |
subsystem-aim |
Reveal and selection work; aiming at a revealed subsystem concentrates damage. | step 12 | Duel |
kill-drops-loot |
The kill drops its cargo; pickup stores it; the tractor beam pulls. | step 12; headless-playground-cut.md:789-794 |
Duel |
loot-keeps-brand |
Picked-up loot shows its manufacturer. | step 6; item-provenance-cut.md:528-539 |
Duel |
target-vanish-safe |
The target dies with shots in flight: no crash, no frozen ship. | step 12; fire-control-cut.md:1464-1470 |
Duel |
beam-reads-higher |
The HUD hull factor reads higher from the beam than from the bow. |
step 13; :2380-2383 |
Duel |
armour-face-missile |
Turn the armoured face into the AI's slow missile; it lands there. | step 13 | Duel |
ai-engages |
The AI still engages normally. | D; :2658-2660 |
Duel |
launcher-edge-pulse |
Launcher into the bow, then a flank: the schematic pulses the facing edge only. | step 14; :2519-2520 |
Launcher angles |
guided-explodes-on-detonation |
A guided round's explosion shows only when the simulation detonated. | E | Launcher angles |
guided-target-left-fades |
Kill the target while a guided round flies: the round fades with no explosion. | E | Launcher angles |
guided-hit-at-target |
A guided hit plays its effect at the target. | E | Launcher angles |
fused-clear-line-max-range |
Nothing selected, clear line: the round bursts at max range along the arc-clamped aim. | A.1; :2625,2636-2642 |
Fused rounds |
fused-first-hull-stop |
Nothing selected, aim at the bare hull: the round stops and detonates on it. | A.2; :2632-2634 |
Fused rounds |
fused-target-left-no-burst |
Fire at the fragile target, kill it with the direct weapon before arrival: no burst. | A.3; :2636-2642, FireControl.cs:610 |
Fused rounds |
fused-out-of-arc |
Select the stern target: the round fires and bursts at its range along the arc-clamped aim. | A.4; :2643-2645 |
Fused rounds |
fused-arming-pushout |
Select the close hull: the burst is pushed out to BlastRadius from the ship's centre. |
A.5; :2646-2653 |
Fused rounds |
airburst-at-model |
A proximity airburst beside the LonginusX lands damage where the model shows the blast. | step 15; :3094-3097 |
Fused rounds |
penetrator-reaches-interior |
A delayed penetrator into the LonginusX nose detonates inside. | step 15 | Fused rounds |
hud-forecast-fused |
The HUD shows Direct, Burst at N m or Refused for fused weapons, and a percentage for direct weapons. | C; :2661-2663 |
Fused rounds |
unbranded-no-manufacturer |
An unbranded smoke weapon shows no Manufacturer row. | step 6 | Fused rounds |
refused-shot-free |
A refused shot costs no ammo, energy, heat, sound, visibility or cooldown. | B; :2664-2666 |
Refused rounds |
refused-beam-no-flicker |
A refused beam never starts. | B | Refused rounds |
refused-charge-never-charges |
A refused charged weapon never starts charging. | B | Refused rounds |
instant-and-charged-fire |
Autocannon and ChargeBlast SG fire; effects, particles and action-bar icons show. | step 5; addressables-cut.md:362-366 |
Weapon feel |
burst-cadence |
Burst cadence is unchanged. | F | Weapon feel |
charge-release-press |
A fast release-and-press on a charged weapon starts no second charge. | F | Weapon feel |
existing-build-play |
Under "Use Existing Build", the Weapon feel checks hold (GUID keys in the player catalog). | step 20; addressables-cut.md:367-370 |
Weapon feel, Setup: play mode Use Existing Build |
ai-never-fires-refused |
The hostile AI whose only weapon is refused never fires it. | D | AI fused discipline |
ai-never-bomb-fishes |
The neutral AI carrying a fused weapon never fires it. | D | AI fused discipline |
continue-keeps-items |
Warp, quit, Continue: same items, same tier and brand. | step 16; item-provenance-cut.md:536-537 |
Long haul |
death-ends-run |
Die: Continue is disabled and run.cc holds no ledger. |
step 17; :540-541 |
Long haul |
shield-panel-rig |
The numbered shield-panel look, including the Cut 5 dicing and break-through. | step 18; shield-panel-cut.md:683-700,737-739 |
Setup: FieldShieldTest scene |
studio-* (5 checks) |
The fire-control union renders; the schematic underlay; restored hull records; the fuse fields edit; the provenance ledger renders from a real run.cc. |
step 19 | Setup: CultCache Studio |
addressables-duplicates |
Analyze the duplicate dependencies: 5 known leftovers. | step 20 | Setup: Addressables Analyze |
new-game-start, purchase-keeps-brand, tier-colour |
New Game; buying keeps the brand; tier colours show. | steps 3, 4, 6 | Setup: New Game |
Imported results (checklist section 8, revision 113164fa, 2026-09-30):
new-game-start: pass.purchase-keeps-brand: pass.instant-and-charged-fire: partial ("charged weapon unprovable: 1x2 hardpoint").tier-colour: fail ("known defect,settings-globals-cut.md:150; fixed by settings-globals Cut 1").unbranded-no-manufacturer: blocked ("give Lampnamed a test fixture").loot-keeps-brand: no result ("not yet reported").
5.2 Scenarios
All use Ambient: false except Long haul. Positions are authored in the seed and pinned by the condition tests.
| Scenario | Player | Entities | Checks |
|---|---|---|---|
| Djinni shakedown | Djinni, full fit | none | 2 |
| Starved reactor | a combat hull with an undersized reactor, energy weapons and a shield | 1 unpiloted bare hull ahead, neutral | 3 |
| Arcs | a hull with side mounts | 3 unpiloted bare hulls (bow, beam, stern), neutral; 1 turret preset, hostile | 3 |
| Duel | combat fit | 1 piloted LonginusX, hostile, carrying a slow missile weapon, cargo holding a branded item | 8 |
| Launcher angles | GT 3K or pswarm plus a direct weapon | 1 unpiloted LonginusX, hostile; 1 fragile bare hull | 4 |
| Fused rounds | Smoke Proximity, Smoke Delayed, a direct weapon | unpiloted: LonginusX mid range, hostile; bare hull on the clear bearing; fragile bare hull; close bare hull inside BlastRadius; stern target |
9 |
| Refused rounds | Smoke Refused (instant, beam, charged) plus one working weapon | 1 unpiloted bare hull | 3 |
| Weapon feel | a hull with a 2x2 energy hardpoint: ChargeBlast SG, Autocannon, a burst weapon | 1 unpiloted bare hull | 4 |
| AI fused discipline | a sturdy hull | 1 piloted neutral with a fused weapon; 1 piloted hostile whose only weapon is refused | 2 |
| Long haul | standard combat fit, Ambient: true |
none | 3 |
5.3 Test designs (Q3)
None has a product. They are unbranded and outside the economy.
Smoke Proximity: Proximity, blast 8.Smoke Delayed: Delayed, blast 4, penetration 2 cells.Smoke Refused: Proximity, blast 30, range 20.
These three are the operator's own designs from tonight, Spectra clones, energy, 1x2.
Smoke Refused BeamandSmoke Refused Charge: a constant weapon and a charged weapon with a fuse andRange < BlastRadius.- All must pass
CultRecordRefs.Validate(R-heat, stat modifiers, roles).
5.4 Presets
About eleven, named after their scenarios. The seed author picks designs with AetherDb hardpoint-fit. The only
hard constraints are those the condition tests pin.
6. Subtraction ledger
| Cut | Removed | Added | Targets, schemas, stores |
|---|---|---|---|
| 0 | give 30, spawnturret 26, wanderers 20, setting 1, tests 22 |
0 | no change |
| 1 | StartGame spawn 10, joins 6 |
Scenario.cs 60, RunStart.cs 110, Admit 12, Materialize 20, GenerateZone 4; tests 300 |
+1 schema (aetheria.scenario) |
| 2 | duplicated New Game body about 25 | menu panel and launch body about 45; 1 static | none |
| 3 | none | Checks.cs 50, stores 4, AetherDb 90 (or the menu 50); tests 120 |
+2 schemas, +1 store file |
| 4 | the seed command, once spent (300) | seed 300; condition tests 250; catalog and ledger data | catalog content, ledger content |
Net production code is about +250. That buys:
- the main-menu scenario affordance;
- typed checks with history;
- one staging owner and one admission primitive (from five hand-written joins);
- the first game consumer of the presets that already existed.
It removes three ad hoc test affordances and the wanderers that ran in every zone.
7. Operator questions
Each question stands alone. The first is what the scenario request leaves open.
Q1. Is a scenario arena part of a real run? Your words settle what an arena holds. They leave open what surrounds it. Checks 16 and 17 (warp, save, Continue, death) need a real run. Every other check needs only the arena.
- A. The arena is the entrance zone of a small prelude galaxy built from the scenario's seed. Stations, planets and wormholes are there, and the run saves on warp and quit, continues and ends at death like any run. Launching a scenario replaces your saved run, as New Game does. Scenario-only details (unpiloted ships, forced stances) do not survive a Continue.
- B. A sealed arena: one zone, no galaxy, no saving. Launching never touches your saved run. Checks 16 and 17
go back to plain New Game play. This is a second zone-entry path with no galaxy under it, which
PopulateLeveland the sector map do not support today. - Recommended: A. It is the same New Game path with an input, so there is no second boot path. Every rule under test then runs exactly as it does in a real run. Replacing the saved run seems harmless for a dev affordance.
- Depends on it: Cut 2's launch body; the 0b lifecycle row for staged entities; whether Long haul exists.
Q2. Who records a check's result, and where?
You play and observe. Something has to write "pass at abc123, note ...".
- A. In game. The Scenarios panel gets Pass/Fail/Partial and a note per check. The game writes the ledger file in your working tree, and you commit it with your other work.
- B. The agent. You tell the session what you saw, and it records typed results with
AetherDb recordon a branch and pushes. The menu shows each check's latest result, read-only. - C. No typed results. Checks are typed, and results go in a prose ledger doc, as checklist section 8 does now.
- Recommended: B. It matches how tonight's smoke already worked: you play, the agent records. Nothing in the game writes a file that git tracks, and no agent needs to touch your tree. The menu still shows what is stale. A can be added later on the same append primitive.
- Depends on it: Cut 3's writer and whether the game attaches the ledger writable.
Q3. Where do test-only item designs live? Checks A-C need fused weapons the catalog does not ship. Tonight they were authored in place and left uncommitted.
- A. Committed catalog designs with no product. With no product nothing makes them, so they are never
generated, stocked, sold or looted. A preset builds them unbranded (a new
Materializerule). Studio lists them beside real content. - B. Committed designs with a product and price 0. Generation skips price 0 today, but only through a filter written for another reason, so the exclusion is a coincidence.
- C. A scratch catalog swapped in per session, as checklist step 2a proposed. Scenarios would then depend on which catalog is loaded, and the fixtures would not be retained.
- Recommended: A.
- Depends on it: the
Materializerule in Cut 1; section 5.3.
Q4. Which ad hoc test affordances do scenarios retire?
give got its hull route tonight (your ruling: "mothballed ships do not require docking bays"), and step 7 was its
reason. spawnturret places a turret. Neutral wanderers spawn two non-hostile ships in every generated zone, "so
combat has something non-hostile to target" (f732cff9).
- A. Delete all three. Scenarios cover ships, cargo, turrets and neutral targets.
CommissionShipstays for purchases. - B. Delete the hull route and
spawnturret. Keepgivefor items (ad hoc poking outside scenarios) and keep the wanderers. - C. Keep everything.
- Recommended: A. Each one is a second way to create what scenarios now author, and the wanderers change every
zone of real play for a testing reason.
iff,trackmissile,revealzones,towandcapturepresetstay. - Depends on it: Cut 0.
Defaults Self took, which you may overrule:
- Scenarios show only in editor and development builds.
- The ledger is its own committed store,
GameData/Checks.cc, not the catalog. - "Check", not "behaviour", because
Behavioris the gear type family. - Brokkr is not installed in this campaign (section 0.4).
- The scenario record holds no "start docked" field. No first consumer needs one.
8. Follow-ups outside this campaign
- Re-anchor
docs/headless-playground-cut.md. Its Cut 1 now builds onRunStartandZone.Admit; itsnewcommand stages aScenario. - Brokkr install plus a "launch scenario" intent, if agents should drive Unity play.
- A durable agent authoring command for presets (fit by design name with validation), instead of transient seed commands.
AetherDbcarries spent one-shot migration commands (targeting-catalog-6c,-6d,firing-arc-migrate, and others;Program.csis 1,477 lines). This is a subtraction pass of its own.ItemManager's clock seed (ItemManager.cs:20) makes lot quality vary per scenario launch. It is recorded, not fixed here.
Mining Cut 3: Soul (codex/mining 39cec96a..55ca6d3d, 2026-10-01)
Verdict: hold for one fix batch. It is queued, not dispatched, because the operator ordered a drain.
No correctness bug was found. The suite passes 516/516 with the catalog b698e224 and the settings b2e346f5 that the commit pins. The shared pins/data/Settings.asset (347752dd) is scenarios' file, so any runner that mounts that path by default has been testing mining with the wrong settings.
Findings:
- F1 (medium, operator; fixed on master
df7c44f2by the targeting index, no longer a scan; still O(rocks) per press, deferred tomining-index-treeandmining-belt-cells): picking a rock in a big belt costs one scan of every rock the search circle reaches.Zone.ChunksNear(Zone.cs:333) andVisibleChunksInReach(Entity.cs:378-385) run on every reticle, next or previous key press. Measured cost: 63 ms at 30k rocks, 449 ms at 300k and 4.3 s at 3M. The per-tick cost is fine. - F2 (medium): nothing tests that the shipped catalog has a rock kind. Taking either side of the catalog conflict unchanged ships dark belts silently. Add a test for "at least one rock kind with weight above 0".
- F3: the merge rule is corrected above.
- F4 (medium): nothing tests the ping half of
Sensor.Gain, because the golden fixture saturates at the cap. Two mutants survive (Sensor.cs:168,:189). Soul's sweep fixture kills both and gives the same hash (35DE0E26…) before extraction, at extraction and at the tip. - F5:
FieldKinds.Ensure's explicit save (ZoneData.cs:162) is needed on a clean store. Keep it and commit the probe. - F6/F7/F8 (low): the
SetTargetrefusal is tested only on dark rocks (Entity.cs:345). The outer skip edge ofChunksNearis untested. Rock reach readsEnabled, and nothing tests it. - Deviation (operator): launchers count toward rock reach (Q13), but under Q12 they can't mine. So after Cut 4 a player can pick a rock that no weapon can act on.
- Plausible (operator visual check): the target indicator uses
AsteroidVerticalOffset, but the shader also subtracts the nebula surface height.
Promises that held:
- The
Gainextraction is bit-identical. SetTargetis the only writer.- A dark target is dropped on the tick and on
Activate. - Docking, broken rocks and wormholes are handled.
- The catalog has 206 identical records, plus one kind and 18 weapons now Exact.
- Old saves get a kind on first load.
The fix batch commits Soul's four probes (in the session scratchpad, soul-mc3-run/probes/) and the catalog-kind test.
Rulings (operator, 2026-10-01): "Fix the belt freeze; launchers shouldn't count toward reach".
- F1: fix it before the merge. The cost of a key press must not grow with belt size.
- Rock reach counts only weapons that can mine; under Q12 launchers can't. This supersedes Hands' Q13 reading.
- The fix batch was dispatched with both rulings.
Mining fix batch, Hands (2026-10-01): f88d1fc1, 83d8371e on codex/mining. 523/523; 9 mutants each killed by their own test.
- Launchers:
Weapon.CanMine(virtual, true by default;LockWeaponsets false) is the one predicate. Reach readsCanMine && Active. Cut 4's firing gate (FireControl.cs:175) must read the same predicate. - Soul F2/F4/F5/F6/F7/F8 tests are committed.
- Belt freeze not built; stopped at a fork.
- Each rock has its own angular speed (
Distanceis a continuous float), so no band shares a phase. - At 3M rocks, 633k are in reach and 2,240 are visible.
- Proposed: a derived per-belt index (bands of 256 by distance, sorted by turn, with the query arc widened by rate spread and at most 8 band re-sorts per query); a visibility upper bound that skips dark bands; best-first next/previous and reticle; the rules move from
ActionGameManagertoEntity. - Work in progress (unbuilt) is in the session scratchpad:
hands-mc3fix-index-wip.patch.
- Each rock has its own angular speed (
- Question: today's Previous with no target picks the second-farthest rock (off by one). Hands recommends fixing it.
Belt-freeze rulings (operator, 2026-10-01):
- Structure: "Let's go for it. There's a voice in my head screaming that this is too much code and we should just reduce the whole belt to a single entity, but this stuff will be extra important for when we have EW and much more crowded levels. Just don't specialize the indexing too much towards asteroid belts, because we'll want a bunch of subsystems feeding targeting data. Think the spacebound microfauna from the slime mold experiment, pretty sure there's a campaign for that."
- The index is a general targeting index that many sources feed: belts, entities, later EW and microfauna. Belts are one provider, not the shape of the index.
- The slime-mold experiment is
Assets/Shaders/Compute/Slime/Slime.cs. No campaign doc was found by grep on master docs or by voidbot; ask the operator before assuming one.
- Previous off-by-one: "Fix the off-by-one". With no current target, Previous picks the farthest rock.
- Next step: an Imagination pass (Opus) maps the general index. Inputs: the provider seam, motion models (orbiting rocks with per-rock rates, and moving entities), the brightness/visibility bound as a per-provider bound, and best-first queries. Hands' work in progress (
hands-mc3fix-index-wip.patch) is input, not spec. Then Hands on Sonnet, then Soul.