Operator ruling 2026-09-30: mount points exist only for externally visible hardpoints. No equipment role, no plain mount. Articulation points stay; they are not mounts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
34 KiB
Aetheria Codex assimilation: refreshed cut maps (mining, moddable ships)
Date 2026-09-29. Imagination output for Self. It re-anchors two Codex lanes to the Body as it will stand after
codex/fire-control-12 merges to master. The Codex maps are raw material, not authority. Where this file and
a Codex map disagree, this file is the proposal and the Codex map is the input.
How this was established
-
Branch tips read, 2026-09-29, after
git fetch.origin/master9b85211forigin/codex/fire-control-12b66ba524origin/codex/mining283ce7dcorigin/codex/moddable-ships375d6bd4- Nothing in
F:\Projects\Aetheria's working tree was read or switched. Every read isgit showorgit grepagainst a SHA.
-
The post-fire-control Body is a synthetic merge.
git merge-tree --write-tree origin/master origin/codex/fire-control-12is clean, tree91576243. I wrapped it in an unreferenced local commit,8a2db490(no ref, no push to origin), so it can be built. The fire-control checklist (docs/merge-to-master-checklist.md:7,107onb66ba524) predicts the same clean merge. -
Anchors. Each
file:linenames a real SHA, never the synthetic one:b66ba524for files only fire control touched;283ce7dcfor files the mining branch touched;375d6bd4for files the ships branch touched.
The three file sets are disjoint. I checked with
git diff --name-onlyover each branch's own range, and none of the three pairs shares a file. So each anchored line is byte-identical in the merged tree. -
Tests ran on Yggdrasil, against synthetic merges (section 0). CultLib
45c2f40and CultMath6d5e209are the pins inDirectory.Build.props. The two LFS files the tests read (GameData/Aetheria.cc,Assets/Resources/Settings.asset) were staged beside the job, because the verify mirror carries no LFS. -
Catalog facts come from a probe that opens the merged tree's
GameData/Aetheria.ccthroughAetheriaStores.Open(section 0).
0. Probe results
Test suite on the synthetic merges, Yggdrasil, dotnet test tests/Aetheria.Shared.Tests, CultLib 45c2f40,
CultMath 6d5e209, the dotnet image:
| Tree | Commit (local, unreferenced) | Result |
|---|---|---|
| master + fire-control-12 + mining | 396d19e5 |
379 passed, 0 failed |
| master + fire-control-12 + ships | 0036204f |
364 passed, 0 failed |
| master + fire-control-12 + mining + ships | 474c35c3 |
381 passed, 0 failed |
Fire control closed 12.4 at 362 tests. 379 = 362 + 17 mining tests, 364 = 362 + 2 ship tests, and 381 is both together. Nothing on either branch breaks a fire-control test, and nothing in fire control breaks a branch test. This proves the headless simulation only. No Unity compile ran.
Catalog probe (scratchpad/probe/Program.cs: it opens GameData/Aetheria.cc through AetheriaStores.Open
in the merged tree; the catalog oid 2aa79c0a is the same in all three merges and at b66ba524):
-
124 items: 32
GearData, 18WeaponItemData, 13SimpleCommodityData, 5HullData, 51 compound commodities, 4 cargo bays, 1 docking bay. -
Weapon damage types: Kinetic 9, Electric 3, Optical 3, Thermal 3. There is none of Corrosive or Ionizing, though the
DamageTypeenum has all six. The mining map said Kinetic 10. -
Penetration > 0: only Autocannon, LRMM72 and SRMM72, each at 0.25.
-
DamageSpread > 0: GT 3K 3.25, plight 3.3, pswarm 1.5, scorched void policy 1.5, and SRMM72 0.5. The map listed four and missed SRMM72.
-
No weapon has
BlastRadiusorFuse. Blasts are dormant in shipped content until F12-2 authors them. -
Simple commodities: the 13 the map named. Every one is priced 0 except Ammo, at 1000. The map's "every live simple commodity has Price 0" is off by one.
-
No live item carries a scanner or mining behaviour, on fire control or on mining merged.
-
Pre-existing, on
b66ba524as well as on the merges: fourGearDatarecords (Refinery, Deep Ore Extractor, Assembly Line, Shipyard) hold a null element inBehaviors(index 0 of 1). It does not come from mining's union-26 retirement: the same four are null on fire control alone. This matters to variants C4 (A.6). -
Shipped hull mount ids are not unique:
Hull Hardpoints Distinct TransformsEmpty TransformsDjinni 22 20 3 Longinus 13 12 2 LonginusX 10 9 2 Turret 7 3 5 Zenith 5 1 5 All five hulls use
Prefab. This matters to ship migration (S5).
Shared precondition for both campaigns
Both campaigns wait on one event: codex/fire-control-12 merging into master with --no-ff, which Self does
after the operator signs off (docs/merge-to-master-checklist.md:105-113 on b66ba524). Until then, neither
campaign has a base to merge onto except the fire-control branch itself. After it:
git switch codex/mining && git merge --no-ff master # in a worktree, never the operator's tree
git switch codex/moddable-ships && git merge --no-ff master
Merge, do not rebase. Both maps and their Soul receipts cite landed SHAs: mining cites 11 of its 13 commits, and the ships doc cites its commits' results. A rebase rewrites every one of those SHAs, which strands the maps' evidence. The merge has no conflicts (below), so it costs one merge commit per branch.
Rulings (operator, 2026-09-30)
-
MQ1: a ship
.ccis self-contained. Operator: "The idea is for the new moddable ships to eventually take over from the existing system. A ship .cc should contain everything needed to instantiate and render a ship." The confirmed reading: the hull is its ownHullDatarecord and the visual is its own record, both in the one ship.cc, side by side. The hull is not embedded in the visual. A hull family such as Longinus and LonginusX lives together in one.cc, so variants resolve within one store. S1 follows option B, with both records inside the ship's own store. -
MQ2 A for now. There are no cross-file variants of shipped hulls in this lane. Operator note, to be carried into CultLib's variants target: "we'll want cross-store variant support eventually; modders should be able to add variants of existing items without modifying the original".
-
MQ3 A. The FBX prefab builder is scaffolding. The typed
.cclane replaces the existing system, and the builder dies at S5. -
MQ4 A. Continue refuses loudly and names the missing mod ids.
-
MQ5 A. The game composes the modded catalog at boot, from
GameData/Mods, into a disposable file. -
Soul (2026-09-30) on
375d6bd4found the Blender↔C#.ccround trip real, with no vendored codec. Before any cut builds on it, the S-Soul fix slot must fix:- F1: 8 of 9 validator mutations survive, including the
..path guard and the prefab refusal. - F2:
Compose,ReadPackageandReadNodeIdsare untested. - F3: there are no Python tests; slot numbers and enums are duplicated by position.
- F6:
CatalogTypesincludesShipAuthoring. Check name collisions. - F7: a drive-by
usingremoval. - F5 is settled by MQ1.
- F1: 8 of 9 validator mutations survive, including the
-
Pivots own firing arcs (operator, 2026-09-30, from fire control). A turret's arc belongs to its pivot, not its hardpoints. When this campaign adds pivots to
HullData, the arc moves from the hardpoint to the pivot, and the interim "intersection of hardpoint arcs per pivot" rule in fire control is deleted. Unity'sPivot.<group>.<yawMin>.<yawMax>...node-name parsing (ShipPrefabAuthoring.cs:144) must not survive as a second arc authority.
Part B. Moddable ships
B.1 Status header (verified against git)
Branch codex/moddable-ships, tip 375d6bd4: 4 commits on 4b594e11. 4b594e11 is an ancestor of fire
control, which has since moved 8 commits to b66ba524. Neither side touches the other's files.
| SHA | What landed |
|---|---|
42eb5214 |
ShipAuthoring record and validator; AetherDb ship-authoring create/inspect/validate; Blender LineArt capture; 2 xunit tests |
4ba4bbaf |
ShipModCatalog.Compose (derived catalog) and GLB node-id reader |
24c29640 |
Unity ShipModVisual (glTFast runtime import, line mesh) and Editor preview; com.unity.cloud.gltfast 6.20.0 added to Packages/manifest.json |
375d6bd4 |
Blender layout editing (hull grid, hardpoints) through ship_cc.py |
The delta is +511 C# (including tools/AetherDb), +494 Python, +119 tests, and one new Unity package dependency.
It also carries a stray fix in Assets/Scripts/UI/Menu/TradeMenu.cs (an unused using System.Runtime.Remoting.Contexts).
Self's ruling: it stays on this branch as cleanup. Master compiled clean in Unity with the using present, so master does
not need it.
Test counts. The two ship tests pass on the merged tree: 364/364 (section 0).
S-Soul status: F1, F2, F3 and F6 fixed at fe38f288 (479 tests). F4 has no spec entry. The Python tests
(tools/blender/tests/test_ship_cc.py, 32) have no runner yet; run them by hand.
S0 status (2026-09-30): master 65c63495 (fire control 12, dcd7bbc5) merged into codex/moddable-ships with
--no-ff at 18e12d81, no conflicts. Corrections 1-4 applied to docs/moddable-ship-authoring.md. Yggdrasil full
suite on the merge (dotnet test tests/Aetheria.Shared.Tests, CultLib 45c2f40, CultMath 6d5e209): 615 passed, 0 failed. Self's steps, 2026-09-30 05:15 CEST: the Unity 6000.3.24f1
batchmode compile of 01e9db1c has 0 error CS and 0 warning CS, and ShipModPreview.Smoke passed
(SHIP_MOD_VISUAL_SMOKE anchors=5 points=2 segments=1, its first recorded run). The branch has no committed
fixture package: the smoke ran against one generated from ShipAuthoringTests.Fixture() with a hand-added glTF
scene (the test GLB has nodes but no scene; whether glTFast instantiates a scene-less GLB is unchecked). S1 should
commit a scene-bearing fixture package so the smoke is reproducible.
Anchors re-taken against the merged tree (18e12d81).
- Unchanged:
ShipAuthoring.cs:15(Hull, key 1),:84,:111-136(the hardpoint checks are:112-131),ShipModCatalog.cs:20-21(keys),ItemData.cs:512(HardpointsKey(23)),:515(PrefabKey(24)),Loadout.cs:74,EntityInstance.cs:15-27,ZoneRenderer.cs:289-310(LoadEntity),AetheriaStores.cs:9. - Moved: the second upsert in
ShipModCatalog.Composeis:68-69(was:60-61; the F1-F3 fix added the name-collision checks above it).ship_cc.py: slot constants are:23-25,replace_layoutis:96-124, thebody[HULL_SLOT]reads are:84,101.ActionGameManager.cs: the catalog opens at:55-59(AetheriaStores.Open, not:45-60);capturepresetis:621-662. - S1's key 32 for
HullData.Visualholds:EquippableItemDataowns 30 and 31,HullDatauses 23-29, andWeaponItemData.Fuseat 32 (ItemData.cs:505) is a sibling class, not an ancestor.
Proof-gate status against the doc's own gates (docs/moddable-ship-authoring.md:149-164 @375d6bd4):
| Gate | Doc implies | Verified in git |
|---|---|---|
| 1. C#/Python round trip preserving unknown fields | met | C# only. StandaloneShipRoundTripsWithoutTouchingTheCatalog pins C#. The Python half and "preserving unknown future fields" rest on the Quiet probe, whose output Build/quiet.cc is git-ignored. No repository test exercises ship_cc.py. |
| 2. Blender edits cells and hardpoints | met, except node-id authoring | Code exists, unpinned. ship_cc.replace_layout (ship_cc.py:75-106) has no test. |
| 3. Validator rejects malformed records, changes nothing on failure | met, and compose checks it | Validator pinned. InvalidSchematicOrDanglingMountCannotBePublished covers 5 cases. Compose unpinned: no test covers key collision, the atomic replace, a missing GLB node, or "the previous derived file is unchanged on failure" (ShipModCatalog.cs:23-73). |
| 4. Loads in a built player | outstanding | not started |
| 5. Play checks | outstanding | not started |
Corrections to docs/moddable-ship-authoring.md at 375d6bd4:
:9-16and:132-136report the Quiet probe's numbers as facts. They include 1,040 polylines and "206 shipped records became 208". Nothing in git reproduces them. Label them as an unrecorded probe, or commit a fixture that pins them.- The composed catalog holds two copies of every mod hull.
:114-118says compose preserves one owner per ship.ShipModCatalog.cs:60-61upsertsship.Hullas aHullDataatmod-hull:<id>, and the wholeShipAuthoring, whose key 1 is the sameHullData, atmod-ship:<id>.- Gameplay can read only the first. The second is a dead copy of the same truth inside a derived file. Q1 decides the fix.
AetheriaStores.CatalogTypesnow routesShipAuthoringto the shipped catalog (AetheriaStores.cs:9@375d6bd4), while the doc says the shipped catalog "contains no copy of that ship". Routing a type is not holding a record, so this is harmless today. It stays correct only while nothing writes aShipAuthoringthrough a catalog-writable cache onAetheria.cc. Name that invariant in the doc.:98-106"the same semantic validator" on every path. The Blender side validates nothing semantic: it checks grid bounds and footprint shape (ship_cc.py:83-97). "Shared path" is true for the C# callers only.
B.2 Merge path onto the fire-control-12 lineage
| Probe | Result |
|---|---|
git merge-tree --write-tree origin/codex/fire-control-12 origin/codex/moddable-ships |
clean, tree 97711bcd |
git merge-tree --write-tree 8a2db490 origin/codex/moddable-ships (master+fc12) |
clean, tree 88672977 |
| on top of mining merged into master+fc12 | clean, tree a7410174 |
files shared with fire control 4b594e11..b66ba524 (6 files: Entity.cs, FireControl.cs, two test files, two docs) |
none |
The merged tree passes the full headless suite: 364/364 alone, and 381/381 with mining (section 0).
The only cross-lane seam is Packages/manifest.json (glTFast). It merges textually, but it needs Unity to
resolve the package on first open, like the CultMath tag already does (docs/merge-to-master-checklist.md:14).
B.3 Ordered cuts
| Cut | Nature | Blocked by |
|---|---|---|
| S0 | Placement: merge master, correct the doc | fire-control-12 on master |
| S-Soul | Fix batch from the parallel Soul pass | Soul's report |
| S1 | One hull record, and a hull names its visual | MQ1 |
| S2 | The game composes and loads mod ships (ShipInstance wiring, boot preload) |
S1; MQ4, MQ5 |
| S3 | Gate 4: a built player loads a mod ship | S2; a playable mod ship (content) |
| S4 | Gate 5: play checks | S3 |
| S5 | Migration of shipped hulls, onto variants; FBX builder retirement | S4; MQ2, MQ3; variants C4 |
S0. Placement and doc correction
- Repo/branch: Aetheria,
codex/moddable-ships, in a worktree. - Deletes first: none.
- Keeps/moves/adds:
git merge --no-ff master. Apply corrections 1-4 to the doc. - Verification:
- Yggdrasil full suite on the merge (the M0 command).
- Unity batchmode compile of the merged tree with glTFast resolved. This is Self's step, and it needs network for the package.
- The Editor smoke already in the branch:
-executeMethod ShipModPreview.Smoke -shipModPath <package/ship.cc>(ShipModPreview.cs:8@375d6bd4), run against a committed fixture package. It is the first recorded run of that smoke.
- Subtraction: docs only.
S-Soul. Fix slot for the parallel Soul pass
- Repo/branch:
codex/moddable-ships, after S0. - Content: Soul's findings. Two areas this map expects Soul to own, and does not duplicate:
-
Compose's unpinned failure paths (B.1, gate 3).
-
The hand-copied schema on the Python side:
ship_cc.py:17-21slot constants (HULL_SLOT 1,HULL_SHAPE_SLOT 5,HULL_HARDPOINTS_SLOT 23);ship_cc.py:88-98hardpoint slots 0-6;__init__.py:18-21, a copy of theHardpointTypeandItemRotationenums.
All of these match C# today:
ItemData.cs:287ShapeisKey(5),HullData.HardpointsisKey(23)at:512, andHardpointDatais:553-563, all @b66ba524.Enums.cs:38matches too. The hazard is drift: fire control added slot 6 (FiringArc) toHardpointDatain this same lineage. The memoryhonor-invariants-over-workaroundsnames hand-copied schemas as a known failure. The coherent fix reads the published schema catalog. Soul decides whether it is in scope.
-
S1. One hull record, and a hull names its visual (written for MQ1 = B)
- Repo/branch:
codex/moddable-ships, after S-Soul. - Deletes first:
ShipAuthoring.Hull(ShipAuthoring.cs:15, key 1) becomes a retired-key comment.- The embedded-hull branch of the validator (
ShipAuthoring.cs:84-133) moves; it does not duplicate. - The second upsert in
ShipModCatalog.Compose(ShipModCatalog.cs:60-61) becomes a verbatim copy of the mod store's records. ship_cc.py'sbody[HULL_SLOT]indirection (:63-66,:80-100).- The only existing
.ccwith the old shape is the git-ignoredBuild/quiet.ccprobe. No committed content migrates.
- Adds:
- A mod
.ccholds two records: aHullDataat the deterministic keymod-hull:<id>(ShipModCatalog.cs:20) and theShipAuthoringvisual package (Id,ModelAsset,Anchors,SchematicLines). HullDatagains a nullableCultRecordRef<ShipAuthoring> Visualat the next free key on its ancestry.EquippableItemDataowns keys up to 31 (the comment atItemData.csWeaponItemData.Fuse), so it is 32, unless C4 claims that slot first.- The validator refuses a hull with both
PrefabandVisualset, or neither, when it is a ship. - The validator is one function over (hull, visual). It checks every
Hardpoints[].Transformagainst the visual's anchor ids, the checks that are atShipAuthoring.cs:111-136today.
- A mod
- Authority map:
- Owner: the
HullDatarecord owns hull semantics (cells, hardpoints, stats). TheShipAuthoringrecord owns the visual package (model path, anchor-to-node map, lines). - Inputs: the mod
.cc, and the GLB node extras. - Outputs: records copied verbatim into the derived catalog.
- Demotion:
ShipAuthoringis no longer an owner of hull data.mod-ship:<id>in the derived catalog is the same record as in the mod store. - Forbidden writers: compose transforming records; Blender writing hull slots inside
ShipAuthoring; any lookup from hull to visual by key-prefix convention. The typedVisualref is the only binding. - Shared paths:
ship-authoring validate, compose, the Editor preview and the runtime loader all call the one(hull, visual)validator. - Deletion line: after S1,
grep -n "\.Hull\b" ShipAuthoring.cs ShipModCatalog.csfinds no embedded hull.
- Owner: the
- Verification (Yggdrasil,
--filter FullyQualifiedName~ShipAuthoring, then the full suite):ComposedCatalogHoldsOneHullPerModShip: the negative for correction 2. It fails today.HullNamesExactlyOneVisual: Prefab xor Visual.HardpointsMustResolveToTheVisualsAnchors: moved from the old validator.ComposeRefusesAKeyCollisionAndLeavesThePreviousFileandComposeRefusesAMissingGlbNode: the gate 3 negatives, unless S-Soul already added them.AHullVariantInheritsItsVisual: runs only once Aetheria is on variants C4. It is the reason for B, and it is recorded as pending until then.- A Python check, in the Blender-free
python:3.12-slimimage withpip install msgpack -e <CultLib>/packages/cultcache-py:ship_cc.replace_layouton a C#-written fixture preserves every untouched slot, then C# reopens it and it validates. This pins gate 1's Python half for the first time. - Operator: in Blender, load a layout, change a mount, save, then
ship-authoring validate.
- Subtraction estimate: about 40 removed (the embedded-hull paths and the duplicate upsert), about 35 added
(
Visual, the joint validator's plumbing), and about 25 in the Python path.
S2. The game composes and loads mod ships
- Repo/branch:
codex/moddable-ships, after S1. - Deletes first: none. This cut is additive: it is the construction boundary the doc names at
:141-145. - Adds, per MQ4 and MQ5:
- Compose at boot (MQ5 = A):
ActionGameManageropens its catalog atActionGameManager.cs:45-60(b66ba524). WhenGameData/Modsholds packages, it opens the derived file instead, composed at boot into the persistent data path.capturepresetkeeps writing the shipped catalog (:605-620, and theLoadout.cs:74writable open). - Boot preload: after the catalog opens and before the first zone loads, every hull with
Visualset imports its GLB throughShipModVisual.LoadAsyncinto an inactive prototype keyed by hull key.LoadEntitythen instantiates synchronously, as it does for prefabs. - Assembly: a
ShipInstancetemplate prefab (Addressable) carries the shared effect prototypes: shield, tractor, thruster particles, radiator material. The loader parents the GLB under it and fillsEntityInstance's arrays (EntityInstance.cs:15-27@b66ba524:MapIcon,Shield,HullColliders,EquipmentHardpoints,RadiatorHardpoints,ThrusterHardpoints,WeaponHardpoints,ArticulationPoints, andShipInstance.TractorBeam) from anchors by role. The name matches thatEntityInstance.cs:245,260,271andShipInstance.cs:69-70do againsthp.Transformkeep working, because anchor ids are the mount ids. ZoneRenderer.LoadEntity(ZoneRenderer.cs:289-310@b66ba524) branches onhullData.PrefaborhullData.Visual. That is one branch point, owned by the hull record.
- Compose at boot (MQ5 = A):
- Authority map:
- Owner: the hull's
Visualref decides the visual. The anchor role decides the component slot. - Inputs: the derived catalog snapshot, the mod package directory, and the template prefab.
- Outputs: a
ShipInstanceindistinguishable to the simulation from a prefab one. - Derived state: the prototypes (cache-only, rebuilt each boot) and the derived catalog (disposable).
- Forbidden writers: prefab arrays hand-wired for a mod ship; any async load during
LoadEntity; any gameplay read of a mod package outside the derived catalog. - Shared paths: new game, Continue, zone entry, and wormhole transit all go through
LoadEntity.
- Owner: the hull's
- Verification:
- Yggdrasil:
ComposeAtBootIsDeterministic. The same packages give byte-identical derived records, which pins run-store references across relaunches. RunReferencingAMissingModRefusesContinue, per MQ4.- Unity batchmode (Self): an Editor play-mode smoke loads a fixture mod ship into a zone and asserts
ThrusterHardpoints,WeaponHardpointsand the collider are populated. - Operator: none yet (S3 and S4).
- Yggdrasil:
- Subtraction estimate: 0 removed, about 150 added (loader, template, preload). This is the capability gate 4 buys.
S2 status (2026-09-30, Self): landed on codex/moddable-ships through 48cabc5f. Soul passes on S2 and its
fixes (bad packages excluded and named, never fatal; mount anchors typed by hardpoint; capturepreset refuses mod
designs; explicit null checks at Continue; compose failure falls back to the shipped catalog). Headless 684/684.
Unity 6000.3.24f1: compile clean; ShipModPlaySmoke.Run (edit mode, real preload and Instantiate) prints
SHIP_MOD_PLAY_SMOKE thrusters=1 weapons=1 collider=ok mapicon=ok; ShipModPreview.Smoke -assemble passes against
the generated mesh fixture. Owed: the operator's play-mode checks (menu dialog for excluded mods, New Game/Continue
with a mod, zone entry, wormhole, Continue after removal). Recorded, not fixed: two processes sharing
Aetheria.modded.cc; preload running inside the CultCache getter; a mod updated in place that changes a hull
passes the gate. The interim ruling that a non-thruster/radiator/weapon mount must be an articulation anchor is
superseded by the ruling below.
Mount-point ruling (operator, 2026-09-30): "Regarding mount points, they're only really needed for hardpoints
that are externally visible in some way. That's just weapons, radiators, thrusters right now." So a mount anchor
exists only for the weapon, radiator and thruster roles. There is no equipment role and no plain mount.
Other hardpoints are internal and carry no anchor. Articulation points (turret pivots, ArticulationPoint.Group)
are a separate concept and are not mounts. They stay. Follow-up for S3 prep: find out whether
EquipmentHardpoints (line 314) has a consumer outside anchor wiring. If it has none, delete it, and have the
validator refuse a mount with any other role. Also: the operator said "Missing design refusal on catalog entries
is good": keep it.
S3. Gate 4: a built player
A player build loads a composed mod ship without the Editor or Blender. Only Self (the batchmode build) and the
operator (launch) can do this. It also requires a playable mod ship: a complete package with hardpoints, not the
Quiet probe, which has four anchors and zero hardpoints (docs/moddable-ship-authoring.md:134-136). That is
content, and it is the operator's to author or commission.
S4. Gate 5: play checks
The operator only: spawn, equip, thrust, fire, shield, damage, save and reload, and the schematic UI for the mod ship. It runs on master's post-fire-control rules, so the checklist's steps 11-15 (arcs, rolled impacts, blasts) apply to the mod hull too.
S5. Migration onto variants
After gate 5, and after MQ2 and MQ3.
- Shipped hulls move to
Visualone at a time, and each migration deletes that hull'sPrefab. - Hull families collapse into variants in
Aetheria.cc. The variants scan found Longinus/LonginusX, differing inBehaviors,Hardpointsand two cosmetic members (CultLib docs/document-variants-cut.md§6). - It waits on variants C4 (Aetheria adoption at
caching-unity-v1.5.0). - Mount ids come first. The typed lane requires unique, non-empty mount ids (
ShipAuthoring.cs:115-117@375d6bd4). No shipped hull has them: Djinni has 3 emptyTransforms and one duplicate, and Zenith has 5 hardpoints with one distinctTransform(section 0). Each migration first assigns unique mount ids and repoints the prefab-era name matches.Transformis also not usable as a C2 element id, so C4's minted id and the mount id stay two members: one is the identity for variants, and the other names the model node. - Deletion line: when the last hull migrates,
HullData.Prefab(key 24) retires, andShipPrefabAuthoring.csgoes (MQ3).
B.4 Subtraction ledger (estimate)
| Cut | Removed | Added | Formats, targets, dependencies |
|---|---|---|---|
| landed | 1 | ~511 C#, ~494 Py, 119 test | aetheria.ship_authoring document type; glTFast package |
| S1 | ~40 | ~35 C#, ~25 Py | ShipAuthoring key 1 retired; HullData Visual slot added |
| S2 | 0 | ~150 | one template prefab; no new target |
| S5 | per hull: its prefab; finally ShipPrefabAuthoring.cs (252) plus its smoke (80) and HullData.Prefab |
~0 | FBX builder retired |
The lane is net additive until S5, and S5 is where the old authority dies. Keeping two ship-authoring paths alive indefinitely is the liability MQ3 exists to close.
B.5 Operator questions (moddable ships), one fork each
MQ1. Where does a mod ship's hull live, and how does a hull find its model? (Blocks S1.)
Context.
- Today a mod ship is one
ShipAuthoringrecord with theHullDataembedded inside it. - Compose writes that hull twice into the derived catalog: once as a
HullData, and once inside theShipAuthoringcopy. - The runtime has no binding yet from a hull to its model; the doc lists it as outstanding.
- You want variants to kill duplicate data entry for ships. A variant is a whole record whose base is another record of the same type in the same store.
- "LonginusX is Longinus with another hardpoint layout" is therefore a
HullDatavariant. An embedded hull inside another document cannot be one.
Options.
- A: keep the embedded hull. One record per mod, but a hull family must be full copies, and the derived catalog keeps a dead second copy.
- B: split it. The hull is a plain
HullDatarecord, and it names its visual through aVisualref, the way it names a Unity prefab today.ShipAuthoringbecomes the visual package only. A hull variant inherits its base's visual through normal resolution. - C: split it the other way.
ShipAuthoringreferences its hull. Then a hull variant has no visual unless something reads the variant's stored base key, and no consumer may read deltas (variants target, "Readers see complete documents").
Recommend B. It is the only shape where variants reach ships without either copying or delta-reading. It costs little now, because no committed content uses the old shape.
MQ2. Can a mod ship be a variant of a shipped hull? (Blocks S5's plan for mods; does not block S1-S4.)
Context.
- Your variants ruling 3 says a variant and its base live in the same store. A mod's
.ccis its own store, so "my Longinus with a turret" cannot name the shipped Longinus as its base. - Separately, Blender reads mod stores through the Python runtime, and variants ruling 5 has Python refuse any store holding a variant until Python resolution is built (it is parked).
- So even a family within one mod's store is blocked in Blender today.
Options.
- A: no. A mod ships complete hulls, and families inside a mod wait for Python resolution.
- B: yes, through compose. Compose would resolve a mod-declared base against the shipped catalog, which is a second resolver outside the cache.
- C: yes, by reopening ruling 3 for a mod layer.
Recommend A. B is exactly the bespoke inheritance the variants campaign exists to prevent. C is a CultLib decision bigger than this lane.
MQ3. What is the FBX prefab builder for, now that the typed .cc lane exists? (Blocks S5.)
Context.
ShipPrefabAuthoring.cs(252 lines) and its smoke (80 lines) landed on fire-control-12 at4b594e11, so they reach master in this merge.- They build Unity prefabs from FBX collections by naming convention (
docs/ship-collection-authoring.md). - The typed lane builds the same
ShipInstancefrom a.ccrecord and a GLB. - The ships doc keeps the FBX builder "available to existing content", with retirement "following migration".
- As things stand, the game has two ship-authoring authorities, and no ruling says which one new ships use.
The question. Is the FBX builder a permanent path, for example for artists who will never use Blender with Brokkr? Or is it scaffolding until the typed lane passes gate 5?
Options.
- A: scaffolding. New ships use the typed lane from gate 5 on, and the builder dies at S5.
- B: permanent. Both paths stay, and S2's loader keeps the
Prefabbranch forever. - C: retire it now, and author no new ships until gate 5.
Recommend A. Two permanent authoring authorities for one concept is the split this doctrine cuts. C stalls content for no gain.
MQ4. What happens to a saved run when a mod it used is removed? (Blocks S2's Continue path.)
Context.
- The run store references designs by key, and a mod hull's key is
mod-hull:<id>. - Remove the mod, and those references dangle.
- The fire-control checklist already records that an old run store throws on its first lot lookup
(
docs/merge-to-master-checklist.md:15).
Options.
- A: Continue refuses loudly and names the missing mod ids.
- B: the missing ships are stripped from the run on load.
- C: the run loads with placeholder hulls.
Recommend A. A bad mod is rejected before any scene is built (the doc's own shared-path rule). B silently edits the player's save.
MQ5. Who composes the derived catalog? (Blocks S2.)
Context. The doc's own objective is "a mod package can be installed without opening the Unity Editor". Gate 4
says "in a built Unity player without Unity Editor or Blender". A built player ships no AetherDb, and today
only AetherDb ship-authoring compose composes.
Options.
- A: the game composes at boot, from
GameData/Mods, into a disposable file in the persistent data path. - B: an installer or
AetherDbstep composes, and the game opensAetheria.modded.ccwhen it exists.
Recommend A. Compose already lives in ServerShared (ShipModCatalog.cs), so the game can call it. B makes
"is the derived file stale?" a new failure the player owns.
B.6 Dependencies (moddable ships)
- Fire-control-12 on master: S0.
- Unity package resolution: glTFast 6.20.0 needs network on first open (S0 compile).
- Document variants:
- C2 (element ids): "ids everywhere" puts an element-id member on
ShipAnchor,ShipPolyline,HardpointData,ItemRoleandBehaviorData(every object-list element these records reach). Consequences I read from the code, not ran:- Hardpoint ids survive a Blender layout save while the mount id is unchanged.
ship_cc.py:88-98keeps each hardpoint's slots from 7 on, keyed byTransform, so an id slot at 7 or later rides along. Renaming a mount makes a new element, which is the ruling's own rule ("a moved element is a new element"). A hardpoint created in Blender has no id until the next C# write mints one. - Line ids churn on every capture.
replace_linesreplacesSchematicLineswhole (ship_cc.py:51). This is harmless: a variant would override lines whole anyway (variants Q7). - The Python side writes stores C2's registry will then require ids for. C2 mints them on the first C# write, and Python carries the id as an ordinary member (variants Q3 rollout). So mod stores need no migration step.
- Hardpoint ids survive a Blender layout save while the mount id is unchanged.
- Python refusal: mod stores stay v1 (no variants) while Blender reads them through
cultcache-py(MQ2 = A). - C3 (Studio): it edits hull variants in
Aetheria.cc. It is a prerequisite for S5, not for S1-S4. - C4 (Aetheria adoption,
caching-unity-v1.5.0): S5, and the pending testAHullVariantInheritsItsVisual. Slot coordination: S1 claimsHullDatakey 32 forVisual, and C4's id slots must not collide with it.
- C2 (element ids): "ids everywhere" puts an element-id member on
- Brokkr: the Blender add-on depends on Brokkr's
brokkr_bridgepreferences for the CultLib Python path (__init__.py:182-186). It is an operator-machine dependency with no pin in this repo.