2026-06-13 07:54:30 +01:00
using Aetheria.State ;
using Aetheria.State.Documents ;
2026-06-14 10:46:27 +01:00
using GameCult.Aetheria.State.Unity ;
2026-06-13 07:54:30 +01:00
var root = args . Length > 0 ? Path . GetFullPath ( args [ 0 ] ) : Directory . GetCurrentDirectory ( ) ;
var statePath = args . Length > 1
? Path . GetFullPath ( args [ 1 ] )
: AetheriaStatePaths . ResolveDefaultStatePath ( root ) ;
2026-06-15 20:43:59 +01:00
RequireGameplaySourcePurity ( root ) ;
2026-06-15 20:48:49 +01:00
RequirePackageSerializerBoundary ( root ) ;
2026-06-16 20:16:57 +01:00
RequireSharedEvePackagesImportedFromEveRepo ( root ) ;
2026-06-16 09:27:19 +01:00
RequireTypedPendingCommitKeys ( root ) ;
2026-06-16 10:01:02 +01:00
RequireTypedRuntimeFactionKeys ( root ) ;
2026-06-16 10:20:36 +01:00
RequireTypedGalaxyFactionRelationships ( root ) ;
2026-06-16 10:40:39 +01:00
RequireRuntimeCatalogKeyOnlyLookups ( root ) ;
2026-06-16 11:00:56 +01:00
RequireTypedBehaviorBodyKeys ( root ) ;
2026-06-16 15:54:18 +01:00
RequireTypedOrbitTaskKeys ( root ) ;
2026-06-16 20:51:26 +01:00
RequireTypedAgentTaskKeys ( root ) ;
2026-06-16 16:07:20 +01:00
RequireTypedOrbitalEntityOrbitKeys ( root ) ;
2026-06-16 16:13:54 +01:00
RequireTypedOrbitConsumerKeys ( root ) ;
2026-06-16 16:20:30 +01:00
RequireKeyedOrbitRuntimeWrappers ( root ) ;
2026-06-16 16:27:33 +01:00
RequireNativeZoneKeyResolution ( root ) ;
2026-06-16 16:48:00 +01:00
RequireTypedZoneRuntimeCollections ( root ) ;
2026-06-16 16:57:18 +01:00
RequireTypedZoneConstructionKeys ( root ) ;
2026-06-16 17:03:59 +01:00
RequireTypedZoneStateSnapshotKeys ( root ) ;
2026-06-16 16:32:31 +01:00
RequireTypedAsteroidZoneApi ( root ) ;
2026-06-16 11:22:18 +01:00
RequireTypedFactionShellLinks ( root ) ;
2026-06-16 11:43:37 +01:00
RequireFactionKeyIdentity ( root ) ;
2026-06-16 20:21:24 +01:00
RequireTypedRuntimeBehaviorCoverage ( root ) ;
2026-06-15 21:12:43 +01:00
RequireEveRuntimeBootstrap ( root ) ;
2026-06-15 22:00:25 +01:00
RequireNoRendererLocalConsole ( root ) ;
2026-06-15 22:26:14 +01:00
RequireNoRendererLocalDebugPanels ( root ) ;
2026-06-21 08:56:07 +01:00
RequireMainMenuSettingsCommands ( root ) ;
2026-06-17 04:57:56 +01:00
RequireMainMenuRootUsesEveSurface ( root ) ;
2026-06-16 21:08:20 +01:00
RequireMainMenuSettingsShellUsesEveSurface ( root ) ;
2026-06-17 05:38:58 +01:00
RequireConfirmationDialogOwnsMinimalPromptShell ( root ) ;
2026-06-17 06:10:41 +01:00
RequireMainMenuInputSettingsDelegateToRuntimeScreen ( root ) ;
2026-06-17 08:41:06 +01:00
RequireRuntimeInputScreenUsesEveSurface ( root ) ;
2026-06-17 06:10:41 +01:00
RequireActionGameManagerInputScreenUsesSharedFullscreenPrimitive ( root ) ;
2026-06-16 21:23:05 +01:00
RequireSectorMapZoneDetailsUseEveSurface ( root ) ;
2026-06-16 21:29:12 +01:00
RequireRuntimeMenuTabsUseEveSurface ( root ) ;
2026-06-16 21:35:58 +01:00
RequireInventoryShipSettingsUseEveSurface ( root ) ;
2026-06-16 23:44:14 +01:00
RequireInventoryCargoItemDetailsUseEveSurface ( root ) ;
2026-06-17 01:50:15 +01:00
RequireInventoryEquippedItemDetailsUseEveSurface ( root ) ;
2026-06-16 21:45:56 +01:00
RequireTradeCargoSelectorUseEveSurface ( root ) ;
2026-06-16 22:24:48 +01:00
RequireTradeFilterAndRowActionsUseEveSurface ( root ) ;
2026-06-16 23:09:42 +01:00
RequireTradeItemDetailsUseEveSurface ( root ) ;
2026-06-16 22:08:16 +01:00
RequireInventoryDropdownUseEveSurface ( root ) ;
2026-06-17 03:59:14 +01:00
RequireNoDeadPopupShells ( root ) ;
2026-06-16 18:09:15 +01:00
RequirePlayerSettingsEveSurface ( root ) ;
2026-06-17 08:07:42 +01:00
RequireVerseHostSettingsAuthority ( root ) ;
2026-06-17 10:39:28 +01:00
RequireClientTargetBootAuthority ( root ) ;
2026-06-17 13:51:36 +01:00
RequireVerseReplicaTool ( root ) ;
2026-06-17 11:24:36 +01:00
RequireVerseSettingsShellAndBridge ( root ) ;
2026-06-21 08:56:07 +01:00
RequireTypedStatRecipeOperations ( root ) ;
RequireTypedDaemonCommandPayloads ( root ) ;
RequireUnityPublicRequestVocabulary ( root ) ;
RequireDaemonVersePublication ( root ) ;
RequireTypedEveCommandBodies ( root ) ;
2026-06-17 09:51:49 +01:00
RequireMainMenuVerseHostProjection ( root ) ;
2026-06-16 06:09:46 +01:00
RequireMainMenuContinueRunState ( root ) ;
2026-06-21 08:56:07 +01:00
RequireUnityObserverDoesNotTickLocalSimulation ( root ) ;
RequireUnityDoesNotCallSharedSimulationTicks ( root ) ;
RequireUnityPhysicsIsNotGameplayAuthority ( root ) ;
2026-06-17 07:27:59 +01:00
RequireDeadPropertiesPanelShellDeleted ( root ) ;
2026-06-16 19:55:50 +01:00
RequireTypedBehaviorMetadataCoverage ( root ) ;
2026-06-16 20:26:00 +01:00
RequireNameToolsUsesUiToolkit ( root ) ;
2026-06-17 09:19:11 +01:00
RequireRuntimeStateReaderOwnsUnityStateAcquisition ( root ) ;
2026-06-16 19:19:05 +01:00
RequireNoDeadRuntimeProjectionCaches ( root ) ;
2026-06-16 19:24:01 +01:00
RequireNoDeadInspectorMetadata ( root ) ;
2026-06-21 08:56:07 +01:00
RequireDaemonHostDoesNotDrainRuntimeCommits ( root ) ;
RequireRuntimeSimulationTuningRequests ( root ) ;
RequireHullConductivityRequestAuthority ( root ) ;
RequireInventoryEntityRenameRequestAuthority ( root ) ;
RequireWeaponGroupRequestAuthority ( root ) ;
RequireActionBarBindingRequestAuthority ( root ) ;
RequireInventoryDoubleClickTransferRequestAuthority ( root ) ;
RequireLootPickupRequestAuthority ( root ) ;
RequireEntityDestroyedRequestAuthority ( root ) ;
2026-06-16 05:24:11 +01:00
RequireDroppedPickupCheckpointState ( root ) ;
2026-06-21 08:56:07 +01:00
RequireTradePurchaseRequestAuthority ( root ) ;
RequireInventoryLoadoutSaveRequestAuthority ( root ) ;
RequireInventoryLoadoutRestoreRequestAuthority ( root ) ;
RequireDockedCurrentShipRequestAuthority ( root ) ;
2026-06-15 20:43:59 +01:00
2026-06-13 07:54:30 +01:00
await using var node = await AetheriaStateNode . OpenAsync ( statePath , "aetheria-state-verify" ) ;
var ledger = await node . GetMigrationLedgerAsync ( )
? ? throw new InvalidOperationException ( "Missing typed migration ledger." ) ;
var quarantine = await node . GetLegacyCatalogQuarantineAsync ( )
? ? throw new InvalidOperationException ( "Missing legacy catalog quarantine document." ) ;
2026-06-13 08:37:10 +01:00
var publishedSurface = await node . GetCatalogSurfaceAsync ( )
? ? throw new InvalidOperationException ( "Missing Aetheria catalog Eve surface document." ) ;
2026-06-13 07:54:30 +01:00
var items = node . Cache . GetAll < AetheriaItemDefinition > ( ) . ToArray ( ) ;
var corporations = node . Cache . GetAll < AetheriaCorporation > ( ) . ToArray ( ) ;
var nameFiles = node . Cache . GetAll < AetheriaNameFile > ( ) . ToArray ( ) ;
2026-06-13 08:24:37 +01:00
var catalog = node . ReadCatalogSnapshot ( ) ;
2026-06-13 08:37:10 +01:00
var surface = AetheriaCatalogSurfaceProjector . Build ( catalog , DateTimeOffset . UtcNow . ToString ( "O" ) ) ;
2026-06-13 07:54:30 +01:00
RequireCount ( ledger , "aetheria.item_definition.v1" , items . Length ) ;
2026-06-13 13:18:03 +01:00
RequireCount ( ledger , "aetheria.corporation.v2" , corporations . Length ) ;
2026-06-13 12:46:34 +01:00
RequireCount ( ledger , "aetheria.name_file.v2" , nameFiles . Length ) ;
2026-06-13 07:54:30 +01:00
if ( items . Length = = 0 )
{
throw new InvalidOperationException ( "Typed state has no item definitions." ) ;
}
if ( corporations . Length = = 0 )
{
throw new InvalidOperationException ( "Typed state has no corporations." ) ;
}
if ( nameFiles . Length = = 0 )
{
throw new InvalidOperationException ( "Typed state has no name files." ) ;
}
2026-06-13 12:46:34 +01:00
if ( nameFiles . Any ( nameFile = > nameFile . Names . Length = = 0 | | nameFile . Names . Length ! = nameFile . NameCount ) )
{
throw new InvalidOperationException ( "Typed name files did not import their full name arrays." ) ;
}
2026-06-13 08:09:41 +01:00
var pricedItems = items . Count ( item = > item . Price > 0 ) ;
var manufacturedItems = items . Count ( item = > ! string . IsNullOrWhiteSpace ( item . ManufacturerLegacyId ) ) ;
2026-06-14 04:35:51 +01:00
var specificHeatItems = items . Count ( item = > item . SpecificHeat > 0 ) ;
2026-06-14 05:08:39 +01:00
var conductiveItems = items . Count ( item = > item . Conductivity > 0 ) ;
2026-06-13 08:09:41 +01:00
var shapedItems = items . Count ( item = > item . ShapeWidth > 0 & & item . ShapeHeight > 0 & & item . OccupiedCells > 0 ) ;
2026-06-13 09:32:24 +01:00
var shapedMaskItems = items . Count ( item = > item . ShapeCells . Length > 0 ) ;
2026-06-13 09:47:25 +01:00
var interiorShapeItems = items . Count ( item = > item . InteriorShapeCells . Length > 0 ) ;
var hardpointHostItems = items . Count ( item = > item . Hardpoints . Length > 0 ) ;
var hardpointCount = items . Sum ( item = > item . Hardpoints . Length ) ;
2026-06-13 09:01:13 +01:00
var behaviorItems = items . Count ( item = > item . BehaviorCount > 0 & & item . BehaviorKinds . Length > 0 ) ;
2026-06-13 10:01:20 +01:00
var behaviorPayloadItems = items . Count ( item = > item . BehaviorPayloads . Length > 0 ) ;
var behaviorPayloadCount = items . Sum ( item = > item . BehaviorPayloads . Length ) ;
var behaviorFieldCount = items . SelectMany ( item = > item . BehaviorPayloads ) . Sum ( behavior = > behavior . Fields . Length ) ;
var behaviorLegacyRefCount = items
. SelectMany ( item = > item . BehaviorPayloads )
. SelectMany ( behavior = > behavior . Fields )
. Count ( field = > ContainsBehaviorValueKind ( field . Value , "legacy-id" ) ) ;
2026-06-15 16:10:13 +01:00
var behaviorItemRefsMissingItemKeys = items
. SelectMany ( item = > item . BehaviorPayloads )
. Sum ( CountRequiredBehaviorItemRefsMissingItemKeys ) ;
2026-06-13 09:01:13 +01:00
var hardpointItems = items . Count ( item = > ! string . IsNullOrWhiteSpace ( item . HardpointType ) ) ;
var hullItems = items . Count ( item = > ! string . IsNullOrWhiteSpace ( item . HullType ) ) ;
2026-06-14 03:31:08 +01:00
var hullPrefabItems = items . Count ( item = > ! string . IsNullOrWhiteSpace ( item . HullPrefab ) ) ;
2026-06-14 05:08:39 +01:00
var hullArmorItems = items . Count ( item = > ! string . IsNullOrWhiteSpace ( item . HullType ) & & item . HullArmor > 0 ) ;
var hullPhysicalFacetItems = items . Count ( item = >
! string . IsNullOrWhiteSpace ( item . HullType ) & &
item . HullArmor > 0 & &
item . HullDrag > = 0 & &
! double . IsNaN ( item . HullGridOffset ) ) ;
2026-06-14 03:52:20 +01:00
var simpleCommodityCategoryItems = items . Count ( item = > ! string . IsNullOrWhiteSpace ( item . SimpleCommodityCategory ) ) ;
var compoundCommodityCategoryItems = items . Count ( item = > ! string . IsNullOrWhiteSpace ( item . CompoundCommodityCategory ) ) ;
2026-06-13 09:01:13 +01:00
var weaponItems = items . Count ( item = >
! string . IsNullOrWhiteSpace ( item . WeaponType ) & &
! string . IsNullOrWhiteSpace ( item . WeaponRange ) & &
! string . IsNullOrWhiteSpace ( item . WeaponCaliber ) ) ;
2026-06-14 02:05:26 +01:00
var maxDurabilityItems = items
. Where ( item = > ! string . IsNullOrWhiteSpace ( item . HullType ) | | ! string . IsNullOrWhiteSpace ( item . WeaponType ) )
. ToArray ( ) ;
var durableMaxDurabilityItems = maxDurabilityItems . Count ( item = > item . Durability > 0 ) ;
2026-06-14 03:05:37 +01:00
var importedThermalRangeItems = items . Count ( item = > item . MaximumTemperature > item . MinimumTemperature ) ;
2026-06-14 03:20:13 +01:00
var importedThermalCurveItems = items . Count ( item = > item . ThermalPerformanceCurveKeys . Length > 0 ) ;
2026-06-14 06:57:53 +01:00
var thermalResilienceItems = items . Count ( item = > item . ThermalResilience > 0 ) ;
2026-06-14 07:08:06 +01:00
var audioStatItems = items . Count ( item = > item . AudioStats . Length > 0 ) ;
var audioStatCount = items . Sum ( item = > item . AudioStats . Length ) ;
2026-06-14 10:46:27 +01:00
var consumableItems = items . Count ( item = > item . Category = = AetheriaRuntimeItemCategories . Consumable ) ;
var consumableDurationItems = items . Count ( item = > item . Category = = AetheriaRuntimeItemCategories . Consumable & & item . Duration > 0 ) ;
2026-06-14 07:20:06 +01:00
var consumableEffectivenessItems = items . Count ( item = > item . EffectivenessCurveKeys . Length > 0 ) ;
2026-06-14 06:47:47 +01:00
var actionBarIconItems = items . Count ( item = > ! string . IsNullOrWhiteSpace ( item . ActionBarIcon ) ) ;
2026-06-14 10:46:27 +01:00
var dockingBayItems = items . Count ( item = > item . Category = = AetheriaRuntimeItemCategories . DockingBay ) ;
2026-06-14 05:39:15 +01:00
var dockingBayMaxSizeItems = items . Count ( item = >
2026-06-14 10:46:27 +01:00
item . Category = = AetheriaRuntimeItemCategories . DockingBay & &
2026-06-14 05:39:15 +01:00
item . DockingMaxSizeX > 0 & &
item . DockingMaxSizeY > 0 ) ;
2026-06-13 08:09:41 +01:00
var describedCorporations = corporations . Count ( corporation = > ! string . IsNullOrWhiteSpace ( corporation . Description ) ) ;
var corporationNameLinks = corporations . Count ( corporation = > ! string . IsNullOrWhiteSpace ( corporation . GeonameFileLegacyId ) ) ;
2026-06-13 13:18:03 +01:00
var corporationAllegianceEdges = corporations . Sum ( corporation = > corporation . Allegiances . Length ) ;
2026-06-15 20:33:32 +01:00
var legacyDtoCategories = items
. Where ( item = > item . Category . EndsWith ( "Data" , StringComparison . Ordinal ) )
. Select ( item = > $"{item.Name}:{item.Category}" )
. ToArray ( ) ;
2026-06-13 08:09:41 +01:00
if ( pricedItems = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any prices." ) ;
}
2026-06-15 20:33:32 +01:00
if ( legacyDtoCategories . Length > 0 )
{
throw new InvalidOperationException (
"Typed item categories still contain legacy DTO class names: " +
string . Join ( ", " , legacyDtoCategories . Take ( 5 ) ) ) ;
}
2026-06-13 08:09:41 +01:00
if ( manufacturedItems = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any manufacturer legacy IDs." ) ;
}
2026-06-14 04:35:51 +01:00
if ( specificHeatItems ! = items . Length )
{
throw new InvalidOperationException (
$"Typed item definitions did not import positive specific heat for every item: {specificHeatItems}/{items.Length}." ) ;
}
2026-06-14 05:08:39 +01:00
if ( conductiveItems ! = items . Length )
{
throw new InvalidOperationException (
$"Typed item definitions did not import positive conductivity for every item: {conductiveItems}/{items.Length}." ) ;
}
2026-06-14 06:57:53 +01:00
if ( thermalResilienceItems ! = items . Length )
{
throw new InvalidOperationException (
$"Typed item definitions did not import positive thermal resilience for every item: {thermalResilienceItems}/{items.Length}." ) ;
}
2026-06-13 08:09:41 +01:00
if ( shapedItems = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any shape dimensions." ) ;
}
2026-06-13 09:32:24 +01:00
if ( shapedMaskItems ! = shapedItems )
{
throw new InvalidOperationException (
$"Typed item shape mask import mismatch: masks={shapedMaskItems}, shaped={shapedItems}." ) ;
}
foreach ( var item in items . Where ( item = > item . ShapeCells . Length > 0 ) )
{
if ( item . ShapeCells . Length ! = item . OccupiedCells )
{
throw new InvalidOperationException (
$"Typed item shape mask cell count mismatch for {item.Name}: cells={item.ShapeCells.Length}, occupied={item.OccupiedCells}." ) ;
}
if ( item . ShapeCells . Any ( cell = > cell . X < 0 | | cell . Y < 0 | | cell . X > = item . ShapeWidth | | cell . Y > = item . ShapeHeight ) )
{
throw new InvalidOperationException ( $"Typed item shape mask has out-of-bounds cells for {item.Name}." ) ;
}
}
2026-06-13 09:47:25 +01:00
foreach ( var item in items . Where ( item = > item . InteriorShapeCells . Length > 0 ) )
{
if ( item . InteriorShapeCells . Length ! = item . InteriorOccupiedCells )
{
throw new InvalidOperationException (
$"Typed item interior shape mask count mismatch for {item.Name}: cells={item.InteriorShapeCells.Length}, occupied={item.InteriorOccupiedCells}." ) ;
}
if ( item . InteriorShapeCells . Any ( cell = >
cell . X < 0 | | cell . Y < 0 | | cell . X > = item . InteriorShapeWidth | | cell . Y > = item . InteriorShapeHeight ) )
{
throw new InvalidOperationException ( $"Typed item interior shape mask has out-of-bounds cells for {item.Name}." ) ;
}
}
if ( interiorShapeItems = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any interior shape masks." ) ;
}
if ( hardpointHostItems = = 0 | | hardpointCount = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any hull hardpoints." ) ;
}
2026-06-14 02:05:26 +01:00
var missingMaxDurability = maxDurabilityItems
. Where ( item = > item . Durability < = 0 )
. Select ( item = > item . Name )
. ToArray ( ) ;
if ( missingMaxDurability . Length > 0 )
{
throw new InvalidOperationException (
$"Typed hull/weapon max durability missing for: {string.Join(" , ", missingMaxDurability)}." ) ;
}
2026-06-14 03:05:37 +01:00
if ( importedThermalRangeItems = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any thermal ranges." ) ;
}
2026-06-14 03:20:13 +01:00
if ( importedThermalCurveItems = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any thermal performance curves." ) ;
}
foreach ( var item in items . Where ( item = > item . ThermalPerformanceCurveKeys . Length > 0 ) )
{
if ( item . ThermalPerformanceCurveKeys . Any ( key = > key . Time < 0 | | key . Time > 1 ) )
{
throw new InvalidOperationException ( $"Typed thermal performance curve has out-of-range key time for {item.Name}." ) ;
}
}
2026-06-13 09:47:25 +01:00
foreach ( var item in items . Where ( item = > item . Hardpoints . Length > 0 ) )
{
foreach ( var hardpoint in item . Hardpoints )
{
if ( string . IsNullOrWhiteSpace ( hardpoint . Type ) )
{
throw new InvalidOperationException ( $"Typed item hardpoint has no type for {item.Name}." ) ;
}
if ( hardpoint . ShapeCells . Length ! = hardpoint . OccupiedCells )
{
throw new InvalidOperationException (
$"Typed item hardpoint shape count mismatch for {item.Name}: cells={hardpoint.ShapeCells.Length}, occupied={hardpoint.OccupiedCells}." ) ;
}
if ( hardpoint . ShapeCells . Any ( cell = >
cell . X < 0 | | cell . Y < 0 | | cell . X > = hardpoint . ShapeWidth | | cell . Y > = hardpoint . ShapeHeight ) )
{
throw new InvalidOperationException ( $"Typed item hardpoint shape has out-of-bounds cells for {item.Name}." ) ;
}
// Legacy content includes at least one overhanging hardpoint. Preserve the
// payload faithfully here; content repair belongs to a separate pass.
}
}
2026-06-13 09:01:13 +01:00
if ( behaviorItems = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any behavior fingerprints." ) ;
}
2026-06-13 10:01:20 +01:00
if ( behaviorPayloadItems ! = behaviorItems | | behaviorPayloadCount = = 0 | | behaviorFieldCount = = 0 )
{
throw new InvalidOperationException (
$"Typed behavior payload import mismatch: payloadItems={behaviorPayloadItems}, behaviorItems={behaviorItems}, payloads={behaviorPayloadCount}, fields={behaviorFieldCount}." ) ;
}
2026-06-15 16:10:13 +01:00
if ( behaviorItemRefsMissingItemKeys > 0 )
{
throw new InvalidOperationException (
$"Typed behavior payload item refs missing item-key projections: {behaviorItemRefsMissingItemKeys}." ) ;
}
2026-06-13 10:01:20 +01:00
foreach ( var item in items . Where ( item = > item . BehaviorPayloads . Length > 0 ) )
{
if ( item . BehaviorPayloads . Length ! = item . BehaviorCount )
{
throw new InvalidOperationException (
$"Typed item behavior payload count mismatch for {item.Name}: payloads={item.BehaviorPayloads.Length}, count={item.BehaviorCount}." ) ;
}
foreach ( var behavior in item . BehaviorPayloads )
{
if ( string . IsNullOrWhiteSpace ( behavior . Kind ) | | behavior . UnionKey < 0 | | behavior . Fields . Length = = 0 )
{
throw new InvalidOperationException ( $"Typed item behavior payload is incomplete for {item.Name}." ) ;
}
}
}
2026-06-13 09:01:13 +01:00
if ( hardpointItems = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any hardpoint types." ) ;
}
if ( hullItems = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any hull types." ) ;
}
2026-06-14 03:31:08 +01:00
if ( hullPrefabItems ! = hullItems )
{
throw new InvalidOperationException ( $"Typed hull prefab import mismatch: hulls={hullItems}, prefabs={hullPrefabItems}." ) ;
}
2026-06-14 05:08:39 +01:00
if ( hullArmorItems ! = hullItems | | hullPhysicalFacetItems ! = hullItems )
{
throw new InvalidOperationException (
$"Typed hull physical facet import mismatch: hulls={hullItems}, armor={hullArmorItems}, physical={hullPhysicalFacetItems}." ) ;
}
2026-06-14 03:52:20 +01:00
if ( simpleCommodityCategoryItems = = 0 )
{
throw new InvalidOperationException ( "Typed simple commodity category import produced no categories." ) ;
}
if ( compoundCommodityCategoryItems = = 0 )
{
throw new InvalidOperationException ( "Typed compound commodity category import produced no categories." ) ;
}
2026-06-13 09:01:13 +01:00
if ( weaponItems = = 0 )
{
throw new InvalidOperationException ( "Typed item definitions did not import any weapon facets." ) ;
}
2026-06-14 05:39:15 +01:00
if ( dockingBayItems = = 0 | | dockingBayMaxSizeItems ! = dockingBayItems )
{
throw new InvalidOperationException (
$"Typed docking bay max-size import mismatch: dockingBays={dockingBayItems}, maxSizes={dockingBayMaxSizeItems}." ) ;
}
2026-06-13 08:09:41 +01:00
if ( describedCorporations = = 0 )
{
throw new InvalidOperationException ( "Typed corporations did not import descriptions from legacy key 3." ) ;
}
if ( corporationNameLinks = = 0 )
{
throw new InvalidOperationException ( "Typed corporations did not import geoname file legacy IDs." ) ;
}
2026-06-13 13:18:03 +01:00
if ( corporationAllegianceEdges = = 0 | | corporations . Any ( corporation = > corporation . Allegiances . Length ! = corporation . AllegianceCount ) )
{
throw new InvalidOperationException ( "Typed corporations did not import full allegiance edges." ) ;
}
2026-06-13 08:24:37 +01:00
var tradeItems = catalog . TradeItems . ToArray ( ) ;
if ( tradeItems . Length ! = pricedItems )
{
throw new InvalidOperationException (
$"Typed catalog trade item query mismatch: query={tradeItems.Length}, priced={pricedItems}." ) ;
}
2026-06-13 09:01:13 +01:00
var equipmentItems = catalog . EquipmentItems . ToArray ( ) ;
if ( equipmentItems . Length ! = hardpointItems )
{
throw new InvalidOperationException (
$"Typed catalog equipment item query mismatch: query={equipmentItems.Length}, hardpoint={hardpointItems}." ) ;
}
var behaviorKind = items . SelectMany ( item = > item . BehaviorKinds ) . FirstOrDefault ( )
? ? throw new InvalidOperationException ( "Cannot verify typed catalog behavior query: no behavior kinds." ) ;
if ( ! catalog . FindItemsByBehavior ( behaviorKind ) . Any ( ) )
{
throw new InvalidOperationException ( $"Typed catalog behavior query failed for {behaviorKind}." ) ;
}
var hardpointType = items . FirstOrDefault ( item = > ! string . IsNullOrWhiteSpace ( item . HardpointType ) ) ? . HardpointType
? ? throw new InvalidOperationException ( "Cannot verify typed catalog hardpoint query: no hardpoint types." ) ;
if ( ! catalog . FindItemsByHardpoint ( hardpointType ) . Any ( ) )
{
throw new InvalidOperationException ( $"Typed catalog hardpoint query failed for {hardpointType}." ) ;
}
2026-06-13 08:24:37 +01:00
var manufacturedItem = items . FirstOrDefault ( item = > ! string . IsNullOrWhiteSpace ( item . ManufacturerLegacyId ) )
? ? throw new InvalidOperationException ( "Cannot verify typed catalog manufacturer lookup: no manufactured item." ) ;
2026-06-15 17:25:40 +01:00
if ( string . IsNullOrWhiteSpace ( manufacturedItem . ManufacturerKey ) | |
catalog . FindCorporation ( manufacturedItem . ManufacturerKey ) = = null | |
catalog . GetManufacturer ( manufacturedItem ) = = null )
2026-06-13 08:24:37 +01:00
{
throw new InvalidOperationException (
2026-06-15 17:25:40 +01:00
$"Typed catalog manufacturer-key lookup failed for item {manufacturedItem.Name}." ) ;
2026-06-13 08:24:37 +01:00
}
var corporationWithNames = corporations . FirstOrDefault ( corporation = >
! string . IsNullOrWhiteSpace ( corporation . GeonameFileLegacyId ) )
? ? throw new InvalidOperationException ( "Cannot verify typed catalog name-file lookup: no linked corporation." ) ;
2026-06-15 17:25:40 +01:00
if ( string . IsNullOrWhiteSpace ( corporationWithNames . GeonameFileKey ) | |
catalog . FindNameFile ( corporationWithNames . GeonameFileKey ) = = null | |
catalog . GetNameFile ( corporationWithNames ) = = null )
2026-06-13 08:24:37 +01:00
{
throw new InvalidOperationException (
2026-06-15 17:25:40 +01:00
$"Typed catalog name-file-key lookup failed for corporation {corporationWithNames.Name}." ) ;
2026-06-13 08:24:37 +01:00
}
2026-06-13 08:37:10 +01:00
if ( surface . Schema ! = "gamecult.eve.surface.v1" | |
surface . Surface . Root . Kind ! = "surface" | |
surface . Surface . Root . Children . Length = = 0 )
{
throw new InvalidOperationException ( "Aetheria catalog Eve surface projection is not renderable." ) ;
}
if ( publishedSurface . Schema ! = surface . Schema | |
publishedSurface . Surface . Id ! = AetheriaCatalogSurfaceProjector . SurfaceId | |
publishedSurface . Surface . Root . Kind ! = "surface" )
{
throw new InvalidOperationException ( "Published Aetheria catalog Eve surface is not the expected typed surface." ) ;
}
2026-06-13 07:54:30 +01:00
await RequireLegacyLookupAsync (
items [ 0 ] . LegacyId ,
( ) = > node . GetItemDefinitionByLegacyIdAsync ( items [ 0 ] . LegacyId ) ,
"item definition" ) ;
await RequireLegacyLookupAsync (
corporations [ 0 ] . LegacyId ,
( ) = > node . GetCorporationByLegacyIdAsync ( corporations [ 0 ] . LegacyId ) ,
"corporation" ) ;
await RequireLegacyLookupAsync (
nameFiles [ 0 ] . LegacyId ,
( ) = > node . GetNameFileByLegacyIdAsync ( nameFiles [ 0 ] . LegacyId ) ,
"name file" ) ;
if ( string . IsNullOrWhiteSpace ( quarantine . CatalogFingerprint ) )
{
throw new InvalidOperationException ( "Legacy catalog quarantine has no catalog fingerprint." ) ;
}
Console . WriteLine ( $"Aetheria typed state verify passed: {statePath}" ) ;
Console . WriteLine ( $"Catalog fingerprint: {quarantine.CatalogFingerprint}" ) ;
Console . WriteLine ( $"Item definitions: {items.Length}" ) ;
2026-06-14 05:08:39 +01:00
Console . WriteLine ( $"Priced/manufactured/specific-heat/conductive/shaped items: {pricedItems}/{manufacturedItems}/{specificHeatItems}/{conductiveItems}/{shapedItems}" ) ;
2026-06-14 06:57:53 +01:00
Console . WriteLine ( $"Thermal resilience items: {thermalResilienceItems}" ) ;
2026-06-14 07:08:06 +01:00
Console . WriteLine ( $"Audio stat items/stats: {audioStatItems}/{audioStatCount}" ) ;
2026-06-14 07:20:06 +01:00
Console . WriteLine ( $"Consumable duration/effectiveness items: {consumableDurationItems}/{consumableEffectivenessItems} of {consumableItems}" ) ;
2026-06-13 09:32:24 +01:00
Console . WriteLine ( $"Shape masks: {shapedMaskItems}" ) ;
2026-06-13 09:47:25 +01:00
Console . WriteLine ( $"Interior masks/hardpoint hosts/hardpoints: {interiorShapeItems}/{hardpointHostItems}/{hardpointCount}" ) ;
2026-06-13 10:01:20 +01:00
Console . WriteLine ( $"Behavior payload items/payloads/fields/legacy refs: {behaviorPayloadItems}/{behaviorPayloadCount}/{behaviorFieldCount}/{behaviorLegacyRefCount}" ) ;
2026-06-13 09:01:13 +01:00
Console . WriteLine ( $"Behavior/hardpoint/hull/weapon items: {behaviorItems}/{hardpointItems}/{hullItems}/{weaponItems}" ) ;
2026-06-14 03:31:08 +01:00
Console . WriteLine ( $"Hull prefab items: {hullPrefabItems}" ) ;
2026-06-14 05:08:39 +01:00
Console . WriteLine ( $"Hull armor/physical facet items: {hullArmorItems}/{hullPhysicalFacetItems}" ) ;
2026-06-14 03:52:20 +01:00
Console . WriteLine ( $"Simple/compound commodity category items: {simpleCommodityCategoryItems}/{compoundCommodityCategoryItems}" ) ;
2026-06-14 02:05:26 +01:00
Console . WriteLine ( $"Durable hull/weapon items: {durableMaxDurabilityItems}/{maxDurabilityItems.Length}" ) ;
2026-06-14 03:20:13 +01:00
Console . WriteLine ( $"Thermal range/curve items: {importedThermalRangeItems}/{importedThermalCurveItems}" ) ;
2026-06-14 06:47:47 +01:00
Console . WriteLine ( $"Action-bar icon items: {actionBarIconItems}" ) ;
2026-06-14 05:39:15 +01:00
Console . WriteLine ( $"Docking bay max-size items: {dockingBayMaxSizeItems}/{dockingBayItems}" ) ;
2026-06-13 08:24:37 +01:00
Console . WriteLine ( $"Typed catalog trade items: {tradeItems.Length}" ) ;
2026-06-13 08:37:10 +01:00
Console . WriteLine ( $"Eve catalog surface: {surface.Surface.Id} ({surface.Surface.Root.Children.Length} root children)" ) ;
2026-06-13 07:54:30 +01:00
Console . WriteLine ( $"Corporations: {corporations.Length}" ) ;
2026-06-13 08:09:41 +01:00
Console . WriteLine ( $"Described/geoname-linked corporations: {describedCorporations}/{corporationNameLinks}" ) ;
2026-06-13 13:18:03 +01:00
Console . WriteLine ( $"Corporation allegiance edges: {corporationAllegianceEdges}" ) ;
2026-06-13 07:54:30 +01:00
Console . WriteLine ( $"Name files: {nameFiles.Length}" ) ;
2026-06-15 20:43:59 +01:00
Console . WriteLine ( "Live gameplay source purity: no serializer or legacy database symbols in Assets/Scripts" ) ;
2026-06-15 20:48:49 +01:00
Console . WriteLine ( "Package serializer boundary: MessagePack symbols remain in named CultCache transport files only" ) ;
2026-06-16 20:16:57 +01:00
Console . WriteLine ( "Shared Eve package ownership: generic Unity Eve packages import from the neighboring Eve repo instead of local staged copies" ) ;
2026-06-16 20:21:24 +01:00
Console . WriteLine ( "Typed runtime behavior coverage: live behavior kinds have typed factory plus progress/state restore coverage" ) ;
2026-06-16 20:51:26 +01:00
Console . WriteLine ( "Zone construction/runtime key authority: body/orbit wrappers and task shells retain typed keys instead of GUID sidecars" ) ;
2026-06-21 08:56:07 +01:00
Console . WriteLine ( "Eve runtime bootstrap: daemon-published game surface mounts through UI Toolkit presenter" ) ;
2026-06-15 22:00:25 +01:00
Console . WriteLine ( "Renderer-local console authority: deleted; UI commands flow through Eve command documents" ) ;
2026-06-15 22:26:14 +01:00
Console . WriteLine ( "Renderer-local debug panels: obsolete uGUI field tester authority is deleted" ) ;
2026-06-21 08:56:07 +01:00
Console . WriteLine ( "Main-menu settings authority: player name, gameplay, and graphics settings send typed Eve commands" ) ;
2026-06-17 04:57:56 +01:00
Console . WriteLine ( "Main-menu root shell: root navigation lowers through an Eve UI Toolkit surface instead of the legacy PropertiesPanel/fade shell" ) ;
2026-06-17 06:35:29 +01:00
Console . WriteLine ( "Main-menu settings shell: settings/input subpages lower through Eve UI Toolkit surfaces, and the fake audio page is deleted until a typed audio owner exists" ) ;
2026-06-17 05:38:58 +01:00
Console . WriteLine ( "Confirmation dialog shell: runtime prompts no longer inherit the generic PropertiesPanel machinery" ) ;
2026-06-17 06:10:41 +01:00
Console . WriteLine ( "Main-menu input shell: the Eve input page delegates to the live runtime remap screen when that owner exists" ) ;
2026-06-17 08:41:06 +01:00
Console . WriteLine ( "Runtime input screen shell: input rebinding lowers through an Eve UI Toolkit surface instead of the old drag/drop uGUI screen" ) ;
2026-06-17 06:10:41 +01:00
Console . WriteLine ( "Runtime input-screen authority: hotkey and menu handoff share the same fullscreen-menu primitive" ) ;
2026-06-21 08:56:07 +01:00
Console . WriteLine ( "Sector-map zone details shell: shared zone detail surface lowers selected zone state through Eve UI Toolkit" ) ;
Console . WriteLine ( "Runtime menu tab shell: shared runtime tab surface lowers MenuPanel tab metadata through Eve UI Toolkit" ) ;
Console . WriteLine ( "Inventory ship-settings shell: shared ship settings surface lowers selected ship state through Eve UI Toolkit" ) ;
Console . WriteLine ( "Inventory cargo-item shell: shared cargo detail surface lowers selected item state through Eve UI Toolkit" ) ;
Console . WriteLine ( "Inventory equipped-item shell: shared equipped item surface lowers selected equipment state and controls through Eve UI Toolkit" ) ;
Console . WriteLine ( "Trade cargo-selector shell: shared cargo selector surface lowers target cargo options through Eve UI Toolkit" ) ;
Console . WriteLine ( "Trade filter and row-action shells: shared trade interaction surfaces lower filter and row actions through Eve UI Toolkit" ) ;
Console . WriteLine ( "Trade item-details shell: shared trade item surface lowers selected market item state through Eve UI Toolkit" ) ;
Console . WriteLine ( "Inventory dropdown shell: shared inventory dropdown surface lowers entity and loadout navigation through Eve UI Toolkit" ) ;
2026-06-16 06:09:46 +01:00
Console . WriteLine ( "Main-menu Continue authority: Continue selects typed run state instead of a null button" ) ;
2026-06-17 07:27:59 +01:00
Console . WriteLine ( "Generic popup inspector shell: PropertiesPanel, PropertiesList, and DropdownMenu are deleted from source and serialized assets" ) ;
2026-06-16 19:55:50 +01:00
Console . WriteLine ( "Typed behavior metadata authority: live heat/mining/thermotoggle payload kinds stay owned by package metadata" ) ;
2026-06-16 20:26:00 +01:00
Console . WriteLine ( "NameTools editor shell: the remaining name helper window lowers through UI Toolkit instead of IMGUI" ) ;
2026-06-17 09:19:11 +01:00
Console . WriteLine ( "Runtime state reader authority: Unity gameplay/UI read typed state through a shared runtime reader instead of direct store spelunking" ) ;
2026-06-17 12:46:11 +01:00
Console . WriteLine ( "Verse host authority: daemon-owned typed verse host settings now drive provider advertisement, operations telemetry, and served Verse discovery" ) ;
2026-06-17 10:39:28 +01:00
Console . WriteLine ( "Client target boot authority: Unity boot resolves the active Verse through a typed client target instead of local path folklore" ) ;
2026-06-17 13:51:36 +01:00
Console . WriteLine ( "Verse replica authority: remote client targets resolve to cache-only replica .cc files fed from the daemon" ) ;
2026-06-17 11:24:36 +01:00
Console . WriteLine ( "Verse settings shell: client target edits and Verse-host visibility commands lower through typed Eve surfaces" ) ;
2026-06-17 09:51:49 +01:00
Console . WriteLine ( "Main-menu Verse projection: the Unity Eve shell lowers daemon-owned verse identity instead of ad-libbing local menu copy" ) ;
2026-06-21 08:56:07 +01:00
Console . WriteLine ( "Unity observer authority: gameplay scenes restore daemon frames and do not tick local simulation" ) ;
Console . WriteLine ( "Shared simulation authority: Zone/Entity/Agent ticks stay out of Unity gameplay callers" ) ;
Console . WriteLine ( "Ymir physics authority: gameplay code uses Ymir queries and has no Unity collision callback fallback" ) ;
Console . WriteLine ( "Runtime simulation tuning authority: UI sends daemon operations instead of local simulation mutation" ) ;
Console . WriteLine ( "Hull conductivity authority: inventory UI sends daemon operations for toggles instead of checkpoint rewrites" ) ;
Console . WriteLine ( "Inventory entity rename authority: UI sends daemon operations for renames instead of local graph edits" ) ;
Console . WriteLine ( "Weapon group authority: UI sends daemon operations for assignments instead of checkpoint rewrites" ) ;
Console . WriteLine ( "Action-bar binding authority: drag/drop sends daemon operations and restores from daemon frames" ) ;
Console . WriteLine ( "Inventory transfer authority: UI transfer and drag/drop send daemon operations" ) ;
Console . WriteLine ( "Loot pickup authority: collision pickup sends daemon operations instead of local pickup disposal" ) ;
Console . WriteLine ( "Entity destruction authority: hull-death observers send daemon operations instead of local graph deletion" ) ;
Console . WriteLine ( "Dropped pickup state: daemon frames carry typed dropped-pickup snapshots and keyed live lowering" ) ;
Console . WriteLine ( "Trade purchase authority: UI buy requests send daemon operations instead of checkpoint rewrites" ) ;
Console . WriteLine ( "Inventory loadout restore authority: UI restore requests send daemon operations instead of local mutation" ) ;
Console . WriteLine ( "Docked current-ship authority: UI selection requests send daemon operations instead of checkpoint rewrites" ) ;
2026-06-13 07:54:30 +01:00
static void RequireCount ( AetheriaMigrationLedger ledger , string documentType , int actual )
{
var expected = ledger . Counts . FirstOrDefault ( count = > count . DocumentType = = documentType ) ? . Count ;
if ( expected = = null )
{
throw new InvalidOperationException ( $"Migration ledger is missing count for {documentType}." ) ;
}
if ( expected . Value ! = actual )
{
throw new InvalidOperationException (
$"Migration ledger count mismatch for {documentType}: ledger={expected.Value}, actual={actual}." ) ;
}
}
2026-06-15 20:43:59 +01:00
static void RequireGameplaySourcePurity ( string root )
{
var gameplayRoot = Path . Combine ( root , "Assets" , "Scripts" ) ;
if ( ! Directory . Exists ( gameplayRoot ) )
{
throw new InvalidOperationException ( $"Cannot verify live gameplay source purity; missing path: {gameplayRoot}" ) ;
}
var forbiddenSymbols = new [ ]
{
"MessagePack" ,
"MessagePackObject" ,
"MessagePackSerializer" ,
"MessagePackReader" ,
"IMessagePackFormatter" ,
"JsonConvert" ,
"Newtonsoft" ,
"JsonKnownTypes" ,
"RethinkDB" ,
"DatabaseEntry" ,
"DatabaseLink" ,
"[Union(" ,
"[Key("
} ;
var hits = Directory . EnumerateFiles ( gameplayRoot , "*.cs" , SearchOption . AllDirectories )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. Take ( 10 )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Live gameplay source still contains serializer or legacy database symbols: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-16 20:16:57 +01:00
static void RequireSharedEvePackagesImportedFromEveRepo ( string root )
{
var manifestPath = Path . Combine ( root , "Packages" , "manifest.json" ) ;
var lockPath = Path . Combine ( root , "Packages" , "packages-lock.json" ) ;
2026-06-21 08:56:07 +01:00
var unityGeneratedProjectPath = Path . Combine ( root , "GameCult.Aetheria.State.Unity.csproj" ) ;
var unityAsmdefPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"GameCult.Aetheria.State.Unity.asmdef" ) ;
2026-06-16 20:16:57 +01:00
var localSurfacePath = Path . Combine ( root , "Packages" , "org.gamecult.eve.surface" ) ;
var localUnityUiToolkitPath = Path . Combine ( root , "Packages" , "org.gamecult.eve.unity-uitoolkit" ) ;
var evePackagesRoot = Path . GetFullPath ( Path . Combine ( root , ".." , "Eve" , "packages" ) ) ;
var upstreamSurfacePath = Path . Combine ( evePackagesRoot , "org.gamecult.eve.surface" , "package.json" ) ;
var upstreamUnityUiToolkitPath = Path . Combine ( evePackagesRoot , "org.gamecult.eve.unity-uitoolkit" , "package.json" ) ;
var manifest = File . Exists ( manifestPath )
? File . ReadAllText ( manifestPath )
: throw new InvalidOperationException ( "Cannot verify shared Eve package ownership; manifest.json is missing." ) ;
var packageLock = File . Exists ( lockPath )
? File . ReadAllText ( lockPath )
: throw new InvalidOperationException ( "Cannot verify shared Eve package ownership; packages-lock.json is missing." ) ;
2026-06-21 08:56:07 +01:00
var unityGeneratedProject = File . Exists ( unityGeneratedProjectPath )
? File . ReadAllText ( unityGeneratedProjectPath )
: throw new InvalidOperationException ( "Cannot verify shared Eve package ownership; GameCult.Aetheria.State.Unity.csproj is missing." ) ;
var unityAsmdef = File . Exists ( unityAsmdefPath )
? File . ReadAllText ( unityAsmdefPath )
: throw new InvalidOperationException ( "Cannot verify shared Eve package ownership; GameCult.Aetheria.State.Unity.asmdef is missing." ) ;
2026-06-16 20:16:57 +01:00
var requiredManifestSymbols = new [ ]
{
"\"org.gamecult.eve.surface\": \"file:../../Eve/packages/org.gamecult.eve.surface\"" ,
"\"org.gamecult.eve.unity-uitoolkit\": \"file:../../Eve/packages/org.gamecult.eve.unity-uitoolkit\""
} ;
var missingManifestSymbols = requiredManifestSymbols
. Where ( symbol = > ! manifest . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingManifestSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria manifest no longer imports the shared Eve Unity packages from the neighboring Eve repo: " +
string . Join ( ", " , missingManifestSymbols ) ) ;
}
var requiredLockSymbols = new [ ]
{
"\"version\": \"file:../../Eve/packages/org.gamecult.eve.surface\"" ,
"\"version\": \"file:../../Eve/packages/org.gamecult.eve.unity-uitoolkit\"" ,
"\"source\": \"local\""
} ;
var missingLockSymbols = requiredLockSymbols
. Where ( symbol = > ! packageLock . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingLockSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Unity lockfile no longer records the shared Eve packages as sibling-repo local imports: " +
string . Join ( ", " , missingLockSymbols ) ) ;
}
if ( Directory . Exists ( localSurfacePath ) | | Directory . Exists ( localUnityUiToolkitPath ) )
{
throw new InvalidOperationException (
"Aetheria should not keep local staged copies of the shared Eve Unity packages under Packages/." ) ;
}
if ( ! File . Exists ( upstreamSurfacePath ) | | ! File . Exists ( upstreamUnityUiToolkitPath ) )
{
throw new InvalidOperationException (
"The neighboring Eve repo is missing the shared Unity package roots Aetheria imports." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! unityGeneratedProject . Contains ( "GameCult.Eve.Surface.csproj" , StringComparison . Ordinal ) | |
! unityGeneratedProject . Contains ( "<Name>GameCult.Eve.Surface</Name>" , StringComparison . Ordinal ) | |
! unityAsmdef . Contains ( "\"GameCult.Eve.Surface\"" , StringComparison . Ordinal ) )
2026-06-16 20:16:57 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Aetheria.State.Unity no longer references the shared Eve surface package assembly." ) ;
2026-06-16 20:16:57 +01:00
}
}
2026-06-15 20:48:49 +01:00
static void RequirePackageSerializerBoundary ( string root )
{
var packageRuntimeRoot = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" ) ;
if ( ! Directory . Exists ( packageRuntimeRoot ) )
{
throw new InvalidOperationException ( $"Cannot verify package serializer boundary; missing path: {packageRuntimeRoot}" ) ;
}
var allowedFiles = new HashSet < string > ( StringComparer . Ordinal )
{
"AetheriaRuntimeCatalogStore.cs" ,
2026-06-17 10:39:28 +01:00
"AetheriaRuntimeClientTargetStore.cs" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeCultCacheDocumentStore.cs" ,
"AetheriaRuntimeCommandPort.cs" ,
"AetheriaRuntimeSnapshotDocuments.cs" ,
"AetheriaRuntimeEveCommandDocument.cs" ,
"AetheriaRuntimeDaemonDocuments.cs" ,
"AetheriaRuntimeDaemonSoaDocuments.cs"
2026-06-15 20:48:49 +01:00
} ;
var serializerSymbols = new [ ]
{
"MessagePack" ,
"MessagePackObject" ,
"MessagePackSerializer" ,
"MessagePackReader" ,
"MessagePackWriter" ,
"IMessagePackFormatter" ,
"[Union(" ,
"[Key("
} ;
var hits = Directory . EnumerateFiles ( packageRuntimeRoot , "*.cs" , SearchOption . AllDirectories )
. Where ( path = > ! allowedFiles . Contains ( Path . GetFileName ( path ) ) )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > serializerSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. Take ( 10 )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Package serializer symbols escaped the named CultCache transport boundary: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-16 09:27:19 +01:00
static void RequireTypedPendingCommitKeys ( string root )
{
var checkedFiles = new [ ]
{
2026-06-21 08:56:07 +01:00
Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeSnapshotDocuments.cs" )
2026-06-16 09:27:19 +01:00
} ;
var forbiddenSymbols = new [ ]
{
"CorporationLegacyId" ,
"OrbitLegacyId" ,
"ParentLegacyId" ,
"BodyLegacyId" ,
"ResourceScannerTargetBodyId { get; set; }" ,
"MiningToolAsteroidBeltId { get; set; }" ,
"ReferenceKey(entity.FactionKey" ,
"ReferenceKey(orbit.OrbitKey" ,
"ReferenceKey(body.BodyKey" ,
"ReferenceKey(relationship.FactionKey"
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Pending runtime commit authority still accepts legacy ID fallback fields: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-16 10:01:02 +01:00
static void RequireTypedRuntimeFactionKeys ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "EntityConstructionBlueprintProjector.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" )
} ;
var forbiddenSymbols = new [ ]
{
"public Guid Faction" ,
"blueprint.Faction =" ,
"blueprint.Faction)" ,
"blueprint.Faction," ,
"blueprint.Faction;" ,
"ResolveFaction(blueprint.Faction" ,
"LegacyId(blueprint.Faction" ,
"ReferenceKey(blueprint.FactionKey" ,
"CorporationKey(entity.Faction?.ID" ,
"CorporationKey(pair.Key.ID" ,
"private static string CorporationKey"
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Runtime faction commits must use typed FactionKey authority, not legacy GUID fallback: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-16 10:20:36 +01:00
static void RequireTypedGalaxyFactionRelationships ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Corporations.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Galaxy.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "LoadoutGenerator.cs" )
} ;
var forbiddenSymbols = new [ ]
{
"Dictionary<Guid, float> Allegiance" ,
".Allegiance[" ,
"ContainsFaction(Guid" ,
"ResolveFaction(Guid" ,
"CorporationLegacyId" ,
"factionsByLegacyId" ,
"_containedFactions"
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Galaxy faction relationships must use typed corporation keys, not legacy GUID allegiance state: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-16 10:40:39 +01:00
static void RequireRuntimeCatalogKeyOnlyLookups ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeCatalogSnapshot.cs" ) ,
2026-06-16 19:14:35 +01:00
Path . Combine ( root , "Aetheria.State.Unity.Smoke" , "Program.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "ItemManager.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" )
2026-06-16 10:40:39 +01:00
} ;
var forbiddenSymbols = new [ ]
{
2026-06-16 19:14:35 +01:00
"IRuntimeItemCatalogReader" ,
"AetheriaRuntimeItemCatalog" ,
2026-06-16 10:40:39 +01:00
"_itemsByLegacyId" ,
"_corporationsByLegacyId" ,
"_nameFilesByLegacyId" ,
"FindItemByLegacyId" ,
"FindCorporationByLegacyId" ,
2026-06-16 12:59:33 +01:00
"FindNameFileByLegacyId" ,
"public string LegacyId { get; }" ,
"public string ManufacturerLegacyId" ,
"public string GeonameFileLegacyId" ,
"public string BossHullLegacyId" ,
"public string CorporationLegacyId" ,
".ManufacturerLegacyId" ,
".GeonameFileLegacyId" ,
".BossHullLegacyId" ,
".CorporationLegacyId"
2026-06-16 10:40:39 +01:00
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
2026-06-16 19:14:35 +01:00
"Unity runtime catalog lookup authority must stay on the typed snapshot; legacy-ID indexes and redundant catalog adapters belong to migration boundaries: " +
2026-06-16 10:40:39 +01:00
string . Join ( "; " , hits ) ) ;
}
}
2026-06-16 11:00:56 +01:00
static void RequireTypedBehaviorBodyKeys ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Aetheria.State" , "Documents" , "AetheriaRuntimeStateDocuments.cs" ) ,
Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeCatalogSnapshot.cs" ) ,
Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeCatalogStore.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ,
Path . Combine ( root , "Aetheria.State.Smoke" , "Program.cs" ) ,
Path . Combine ( root , "Aetheria.State.Unity.Smoke" , "Program.cs" )
} ;
var forbiddenSymbols = new [ ]
{
"ResourceScannerTargetBodyId" ,
"MiningToolAsteroidBeltId" ,
"ParseLegacyGuidFromReferenceKey" ,
2026-06-16 14:22:52 +01:00
"ParseLegacyIdFromReferenceKey" ,
2026-06-16 15:08:00 +01:00
"ParseBodyGuidFromKey(" ,
2026-06-16 14:22:52 +01:00
"private static string OrbitKey(Guid" ,
"private static string BodyKey(Guid" ,
"$\"aetheria.orbit:legacy:" ,
2026-06-16 15:08:00 +01:00
"$\"aetheria.body:legacy:" ,
"public Guid ScanTarget" ,
"public Guid AsteroidBelt" ,
"RestoreRuntimeState(Guid scanTarget" ,
"RestoreRuntimeState(Guid asteroidBelt"
2026-06-16 11:00:56 +01:00
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
2026-06-16 15:08:00 +01:00
"Runtime behavior body references must stay on typed BodyKey surfaces, with Zone owning key resolution and behaviors no longer storing raw GUID body ids: " +
2026-06-16 11:00:56 +01:00
string . Join ( "; " , hits ) ) ;
}
2026-06-16 14:22:52 +01:00
var zoneSourcePath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" ) ;
var zoneSource = File . Exists ( zoneSourcePath )
? File . ReadAllText ( zoneSourcePath )
: throw new InvalidOperationException ( "Cannot verify body/orbit key ownership; Zone source is missing." ) ;
var requiredZoneSymbols = new [ ]
{
"public const string OrbitKeyPrefix" ,
"public const string BodyKeyPrefix" ,
"public string BodyKey { get; }" ,
"public string OrbitKey { get; }" ,
"public string ParentOrbitKey { get; }" ,
2026-06-16 15:08:00 +01:00
"public bool TryGetPlanet(string bodyKey, out Planet planet)" ,
"public bool TryGetAsteroidBelt(string bodyKey, out AsteroidBelt belt)"
2026-06-16 14:22:52 +01:00
} ;
var missingZoneSymbols = requiredZoneSymbols
. Where ( symbol = > ! zoneSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingZoneSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Zone runtime body/orbit objects must own typed projection key surfaces: " +
string . Join ( ", " , missingZoneSymbols ) ) ;
}
2026-06-16 15:08:00 +01:00
var requiredBehaviorSymbols = new Dictionary < string , string [ ] >
{
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Behaviors", "ResourceScanner.cs")] = new [ ]
{
"private string _scanTargetBodyKey = \"\";" ,
"public string ScanTargetBodyKey" ,
"Entity.Zone.TryGetAsteroidBelt(ScanTargetBodyKey, out var belt)" ,
"Entity.Zone.TryGetPlanet(ScanTargetBodyKey, out var planet)" ,
"RestoreRuntimeState(" ,
"string scanTargetBodyKey"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Behaviors", "MiningTool.cs")] = new [ ]
{
"public string AsteroidBeltBodyKey = \"\";" ,
"Entity.Zone.TryGetAsteroidBelt(AsteroidBeltBodyKey, out var belt)" ,
"Entity.Zone.AsteroidExists(AsteroidBeltBodyKey, Asteroid)" ,
"Entity.Zone.MineAsteroid(" ,
"AsteroidBeltBodyKey," ,
"string asteroidBeltBodyKey"
}
} ;
var missingBehaviorSymbols = requiredBehaviorSymbols
. Where ( pair = > ! File . Exists ( pair . Key ) | | pair . Value . Any ( symbol = > ! File . ReadAllText ( pair . Key ) . Contains ( symbol , StringComparison . Ordinal ) ) )
. SelectMany ( pair = >
{
var text = File . Exists ( pair . Key ) ? File . ReadAllText ( pair . Key ) : "" ;
return pair . Value
. Where ( symbol = > ! text . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, pair.Key)}: missing {symbol}" ) ;
} )
. ToArray ( ) ;
if ( missingBehaviorSymbols . Length > 0 )
{
throw new InvalidOperationException (
"ResourceScanner and MiningTool must own typed body-key runtime references rather than raw GUID fields: " +
string . Join ( "; " , missingBehaviorSymbols ) ) ;
}
2026-06-16 11:00:56 +01:00
}
2026-06-16 19:19:05 +01:00
static void RequireNoDeadRuntimeProjectionCaches ( string root )
{
var path = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "RuntimeProjection" , "ReflectionExtensions.cs" ) ;
if ( ! File . Exists ( path ) )
return ;
var forbiddenSymbols = new [ ]
{
"GetAllInterfaceClasses(" ,
"GetParentTypes(" ,
"GetAllChildClasses(" ,
"GetAllGenericChildClasses(" ,
"IsAssignableToGenericType(" ,
"GetFullName(" ,
"InterfaceClasses" ,
"ParentTypes" ,
"ChildClasses" ,
"GenericChildClasses" ,
"AppDomain.CurrentDomain.GetAssemblies()"
} ;
var hits = File . ReadLines ( path )
. Select ( ( line , index ) = > new { Line = line , LineNumber = index + 1 } )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"Assets/Scripts/ServerShared/RuntimeProjection/ReflectionExtensions.cs:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Dead runtime-projection reflection caches should stay deleted; keep only the live string-formatting helpers: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-16 19:24:01 +01:00
static void RequireNoDeadInspectorMetadata ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "FieldDriver.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Corporations.cs" ) ,
2026-06-16 19:38:02 +01:00
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Behaviors" , "AetherDrive.cs" ) ,
2026-06-16 19:24:01 +01:00
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Behaviors" , "StatModifier.cs" ) ,
2026-06-16 19:38:02 +01:00
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Behaviors" , "VelocityLimit.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "ExponentialCurve.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "RuntimeGeometry.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Properties Panel" , "PropertiesPanel.cs" )
2026-06-16 19:24:01 +01:00
} ;
var forbiddenSymbols = new [ ]
{
2026-06-16 19:38:02 +01:00
"InspectableAttribute" ,
2026-06-16 19:24:01 +01:00
"PreferredInspectorAttribute" ,
"InspectableTextAttribute" ,
"InspectablePrefabAttribute" ,
"InspectableTextureAttribute" ,
"InspectableTextAssetAttribute" ,
"InspectableTemperatureAttribute" ,
"InspectableAnimationCurveAttribute" ,
"InspectableColorAttribute" ,
"InspectableSoundBankAttribute" ,
"InspectableAudioParameterAttribute" ,
"InspectableSchematicShapeAttribute" ,
"InspectableEnumValuesAttribute" ,
"InspectableRangedFloatAttribute" ,
"InspectableRangedIntAttribute" ,
"OrderAttribute" ,
2026-06-16 19:38:02 +01:00
"EntityTypeRestrictionAttribute" ,
2026-06-16 19:24:01 +01:00
"InspectorHeaderAttribute" ,
2026-06-16 19:38:02 +01:00
"[Inspectable" ,
2026-06-16 19:24:01 +01:00
"[InspectableText" ,
"[InspectablePrefab" ,
"[InspectableTexture" ,
"[InspectableTextAsset" ,
"[InspectableTemperature" ,
"[InspectableAnimationCurve" ,
"[InspectableColor" ,
"[InspectableSoundBank" ,
"[InspectableAudioParameter" ,
"[InspectableSchematicShape" ,
"[InspectableEnumValues" ,
"[InspectableRangedFloat" ,
"[InspectableRangedInt" ,
"[Order(" ,
2026-06-16 19:38:02 +01:00
"EntityTypeRestriction(" ,
2026-06-16 19:24:01 +01:00
"[InspectorHeader("
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
2026-06-16 19:38:02 +01:00
"Dead inspector-specialization metadata should stay deleted; no generic Inspectable attribute path should remain in live runtime code: " +
2026-06-16 19:24:01 +01:00
string . Join ( "; " , hits ) ) ;
}
2026-06-16 19:38:02 +01:00
var deletedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "RuntimeProjection" , "Attributes.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Attributes.cs" )
} ;
var stillPresent = deletedFiles . Where ( File . Exists ) . Select ( path = > Path . GetRelativePath ( root , path ) ) . ToArray ( ) ;
if ( stillPresent . Length > 0 )
{
throw new InvalidOperationException (
"Dead inspector attribute definition files should be deleted once the generic reflection inspector path is gone: " +
string . Join ( ", " , stillPresent ) ) ;
}
2026-06-16 19:24:01 +01:00
}
2026-06-16 15:54:18 +01:00
static void RequireTypedOrbitTaskKeys ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Agents" , "States" , "MoveTo.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Agents" , "Tasks" , "PatrolOrbits.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Agents" , "Tasks" , "StationTowing.cs" )
} ;
var forbiddenSymbols = new [ ]
{
"public Guid Orbit { get; set; }" ,
"Guid[] Circuit" ,
"public Guid OrbitParent" ,
"Select(x => x.Key)"
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Orbit-targeting agent tasks and move states must stay on typed OrbitKey surfaces, with Zone owning orbit-key resolution: " +
string . Join ( "; " , hits ) ) ;
}
var zoneSourcePath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" ) ;
var zoneSource = File . Exists ( zoneSourcePath )
? File . ReadAllText ( zoneSourcePath )
: throw new InvalidOperationException ( "Cannot verify orbit-key task ownership; Zone source is missing." ) ;
var requiredZoneSymbols = new [ ]
{
"public bool TryGetOrbit(string orbitKey, out Orbit orbit)" ,
"public float2 GetOrbitPosition(string orbitKey)"
} ;
var missingZoneSymbols = requiredZoneSymbols
. Where ( symbol = > ! zoneSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingZoneSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Zone must own typed orbit-key parsing and resolution for agent/runtime orbit movement: " +
string . Join ( ", " , missingZoneSymbols ) ) ;
}
var requiredTaskSymbols = new Dictionary < string , string [ ] >
{
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "States", "MoveTo.cs")] = new [ ]
{
"public string OrbitKey { get; set; } = \"\";" ,
"_agent.Ship.Zone.GetOrbitPosition(OrbitKey)"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "PatrolOrbits.cs")] = new [ ]
{
"public string[] Circuit = Array.Empty<string>();" ,
"public string CurrentTarget" ,
"patrolMoveState.OrbitKey = CurrentTarget"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "StationTowing.cs")] = new [ ]
{
"public string OrbitParentKey = \"\";"
}
} ;
var missingTaskSymbols = requiredTaskSymbols
. Where ( pair = > ! File . Exists ( pair . Key ) | | pair . Value . Any ( symbol = > ! File . ReadAllText ( pair . Key ) . Contains ( symbol , StringComparison . Ordinal ) ) )
. SelectMany ( pair = >
{
var text = File . Exists ( pair . Key ) ? File . ReadAllText ( pair . Key ) : "" ;
return pair . Value
. Where ( symbol = > ! text . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, pair.Key)}: missing {symbol}" ) ;
} )
. ToArray ( ) ;
if ( missingTaskSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Orbit-targeting agent tasks must retain typed orbit keys rather than raw GUIDs: " +
string . Join ( "; " , missingTaskSymbols ) ) ;
}
}
2026-06-16 20:51:26 +01:00
static void RequireTypedAgentTaskKeys ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Agents" , "Tasks" , "AgentTask.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Agents" , "Tasks" , "Survey.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Agents" , "Tasks" , "Mining.cs" )
} ;
var forbiddenSymbols = new [ ]
{
"public Guid Zone" ,
"List<Guid> Planets" ,
"public Guid Asteroids"
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Agent task shells must retain typed zone/body keys rather than raw GUID fields: " +
string . Join ( "; " , hits ) ) ;
}
var requiredSymbols = new Dictionary < string , string [ ] >
{
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "AgentTask.cs")] = new [ ]
{
"public string ZoneKey = \"\";"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "Survey.cs")] = new [ ]
{
"public List<string> PlanetBodyKeys;"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "Mining.cs")] = new [ ]
{
"public string AsteroidBeltBodyKey = \"\";"
}
} ;
var missing = requiredSymbols
. Where ( entry = > ! File . Exists ( entry . Key ) | |
entry . Value . Any ( symbol = > ! File . ReadAllText ( entry . Key ) . Contains ( symbol , StringComparison . Ordinal ) ) )
. SelectMany ( entry = > entry . Value
. Where ( symbol = > ! File . Exists ( entry . Key ) | | ! File . ReadAllText ( entry . Key ) . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, entry.Key)}: missing {symbol}" ) )
. ToArray ( ) ;
if ( missing . Length > 0 )
{
throw new InvalidOperationException (
"Agent task shells must keep typed key placeholders on zone/body references: " +
string . Join ( "; " , missing ) ) ;
}
}
2026-06-16 16:07:20 +01:00
static void RequireTypedOrbitalEntityOrbitKeys ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "OrbitalEntity.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "EntityConstructionBlueprintProjector.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "LoadoutGenerator.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "ZoneGenerator.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" )
} ;
var forbiddenSymbols = new [ ]
{
"public Guid OrbitId;" ,
"public Guid Orbit;" ,
"Orbit = orbital.OrbitId" ,
"blueprint.Orbit," ,
"Zone.Orbits[orbital.OrbitId]" ,
"GetOrbitPosition(OrbitId)" ,
"GetOrbitVelocity(OrbitId)" ,
"new OrbitalEntity(ItemManager, null, hull, Guid.Empty"
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Orbital runtime entities and construction blueprints must retain typed OrbitKey state, with Zone owning orbit-key resolution: " +
string . Join ( "; " , hits ) ) ;
}
var requiredSymbols = new Dictionary < string , string [ ] >
{
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "OrbitalEntity.cs")] = new [ ]
{
"public string OrbitKey = \"\";" ,
"OrbitKey = orbitKey ?? \"\";" ,
"Zone.GetOrbitPosition(OrbitKey)" ,
"Zone.GetOrbitVelocity(OrbitKey)"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "EntityConstructionBlueprintProjector.cs")] = new [ ]
{
"OrbitKey = orbital.OrbitKey," ,
"blueprint.OrbitKey" ,
"public string OrbitKey = \"\";"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "LoadoutGenerator.cs")] = new [ ]
{
"new OrbitalEntity(ItemManager, null, hull, \"\", ItemManager.GameplaySettings.DefaultEntitySettings)"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "ZoneGenerator.cs")] = new [ ]
{
2026-06-16 16:57:18 +01:00
"turret.OrbitKey = turretOrbit.OrbitKey;" ,
"station.OrbitKey = lagrangeOrbit.OrbitKey;"
2026-06-16 16:07:20 +01:00
} ,
[Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionGameManager.cs")] = new [ ]
{
"Zone.TryGetOrbit(orbital.OrbitKey, out var orbit)" ,
"planet => planet.OrbitKey == parentOrbitKey"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Zone.cs")] = new [ ]
{
"public float2 GetOrbitVelocity(string orbitKey)"
}
} ;
var missingSymbols = requiredSymbols
. Where ( pair = > ! File . Exists ( pair . Key ) | | pair . Value . Any ( symbol = > ! File . ReadAllText ( pair . Key ) . Contains ( symbol , StringComparison . Ordinal ) ) )
. SelectMany ( pair = >
{
var text = File . Exists ( pair . Key ) ? File . ReadAllText ( pair . Key ) : "" ;
return pair . Value
. Where ( symbol = > ! text . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, pair.Key)}: missing {symbol}" ) ;
} )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Orbital runtime seam must stay on typed orbit keys through the entity owner, blueprint projector, and docking path: " +
string . Join ( "; " , missingSymbols ) ) ;
}
}
2026-06-16 16:13:54 +01:00
static void RequireTypedOrbitConsumerKeys ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "Zone Display" , "ZoneRenderer.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Behaviors" , "ResourceScanner.cs" )
} ;
var forbiddenSymbols = new [ ]
{
"planet => planet.OrbitId == followOrbit" ,
"planet => planet.OrbitId == rootOrbit" ,
"body => body.OrbitId == orbit.ID" ,
"body => body.Orbit == orbit.ID" ,
"GetOrbitPosition(planetInstance.OrbitId)" ,
"Zone.Orbits[planetInstance.OrbitId].Parent" ,
"zone.Orbits[belt.Orbit]" ,
"GetOrbitPosition(planet.OrbitId)"
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Orbit consumer edges must read typed OrbitKey surfaces rather than wrapper GUID fields in gameplay, renderer, and sensor code: " +
string . Join ( "; " , hits ) ) ;
}
var requiredSymbols = new Dictionary < string , string [ ] >
{
[Path.Combine(root, "Assets", "Scripts", "Zone Display", "ZoneRenderer.cs")] = new [ ]
{
"body => body.OrbitKey == orbit.OrbitKey" ,
"Zone.GetOrbitPosition(planetInstance.OrbitKey)" ,
"Zone.GetOrbitPosition(planetInstance.Orbit.ParentOrbitKey)"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Behaviors", "ResourceScanner.cs")] = new [ ]
{
"Entity.Zone.GetOrbitPosition(planet.OrbitKey)"
}
} ;
var missingSymbols = requiredSymbols
. Where ( pair = > ! File . Exists ( pair . Key ) | | pair . Value . Any ( symbol = > ! File . ReadAllText ( pair . Key ) . Contains ( symbol , StringComparison . Ordinal ) ) )
. SelectMany ( pair = >
{
var text = File . Exists ( pair . Key ) ? File . ReadAllText ( pair . Key ) : "" ;
return pair . Value
. Where ( symbol = > ! text . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, pair.Key)}: missing {symbol}" ) ;
} )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Orbit consumer edges must stay keyed at the public runtime wrapper surface: " +
string . Join ( "; " , missingSymbols ) ) ;
}
}
2026-06-16 16:20:30 +01:00
static void RequireKeyedOrbitRuntimeWrappers ( string root )
{
var zoneSourcePath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" ) ;
var zoneSource = File . Exists ( zoneSourcePath )
? File . ReadAllText ( zoneSourcePath )
: throw new InvalidOperationException ( "Cannot verify keyed orbit runtime wrappers; Zone source is missing." ) ;
var forbiddenSymbols = new [ ]
{
"public Guid OrbitId { get; }" ,
"public Guid Orbit { get; }" ,
2026-06-16 20:51:26 +01:00
"public Guid ID { get; }" ,
2026-06-16 16:20:30 +01:00
"OrbitId = data.Orbit;" ,
"Orbit = data.Orbit;" ,
"AsteroidBelts[belt.ID] = new AsteroidBelt(belt);" ,
"var orbit = Orbits[belt.Orbit];"
} ;
var hits = forbiddenSymbols
. Where ( symbol = > zoneSource . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"Assets/Scripts/ServerShared/Zone.cs: contains {symbol}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Planet and asteroid-belt runtime wrappers must not publish wrapper orbit GUID fields once keyed/object orbit surfaces exist: " +
string . Join ( "; " , hits ) ) ;
}
var requiredSymbols = new [ ]
{
"public Orbit Orbit;" ,
"public Orbit Orbit { get; }" ,
2026-06-16 16:57:18 +01:00
"var runtimeBelt = new AsteroidBelt(belt, Orbits[belt.OrbitKey]);" ,
2026-06-16 16:48:00 +01:00
"AsteroidBelts[runtimeBelt.BodyKey] = runtimeBelt;" ,
2026-06-16 16:20:30 +01:00
"belt.NewOrbitPosition = GetOrbitPosition(belt.Orbit.ParentOrbitKey);" ,
"public AsteroidBelt(AsteroidBeltConstructionData data, Orbit orbit)" ,
"Orbit = orbit;"
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! zoneSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Planet and asteroid-belt runtime wrappers must keep orbit authority on Orbit objects plus OrbitKey surfaces: " +
string . Join ( ", " , missingSymbols ) ) ;
}
}
2026-06-16 11:22:18 +01:00
static void RequireTypedFactionShellLinks ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Corporations.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Galaxy.cs" )
} ;
var forbiddenSymbols = new [ ]
{
"Guid GeonameFile" ,
"Guid BossHull" ,
"GeonameFile =" ,
"BossHull =" ,
"ParseOptionalLegacyId" ,
"GeonameFileLegacyId" ,
"BossHullLegacyId" ,
2026-06-16 12:31:48 +01:00
".BossHull != Guid.Empty" ,
"public Guid ID" ,
"ID = ParseLegacyId" ,
"ParseLegacyId(corporation.LegacyId"
2026-06-16 11:22:18 +01:00
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Temporary Faction shell links must use typed key fields, not geoname/boss-hull legacy GUID projections: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-16 16:27:33 +01:00
static void RequireNativeZoneKeyResolution ( string root )
{
var zoneSourcePath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" ) ;
var zoneSource = File . Exists ( zoneSourcePath )
? File . ReadAllText ( zoneSourcePath )
: throw new InvalidOperationException ( "Cannot verify native Zone key resolution; Zone source is missing." ) ;
var forbiddenSymbols = new [ ]
{
"public static Guid ParseOrbitGuid(string orbitKey)" ,
"public static Guid ParseBodyGuid(string bodyKey)" ,
"PlanetInstances.TryGetValue(ParseBodyGuid(bodyKey), out planet)" ,
"AsteroidBelts.TryGetValue(ParseBodyGuid(bodyKey), out belt)" ,
"Orbits.TryGetValue(ParseOrbitGuid(orbitKey), out orbit)" ,
"return GetOrbitPosition(ParseOrbitGuid(orbitKey));" ,
2026-06-16 16:48:00 +01:00
"return GetOrbitVelocity(ParseOrbitGuid(orbitKey));" ,
"public float2 GetOrbitPosition(Guid orbitID)" ,
2026-06-16 20:51:26 +01:00
"public float2 GetOrbitVelocity(Guid orbit)" ,
"public string GetBodyKey(Guid bodyId)" ,
"private readonly Dictionary<Guid, Planet> _planetsById = new Dictionary<Guid, Planet>();" ,
"private readonly Dictionary<Guid, AsteroidBelt> _asteroidBeltsById = new Dictionary<Guid, AsteroidBelt>();"
2026-06-16 16:27:33 +01:00
} ;
var hits = forbiddenSymbols
. Where ( symbol = > zoneSource . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"Assets/Scripts/ServerShared/Zone.cs: contains {symbol}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Zone key-facing APIs must resolve typed body/orbit keys natively instead of reparsing GUIDs from those keys: " +
string . Join ( "; " , hits ) ) ;
}
var requiredSymbols = new [ ]
{
2026-06-16 16:48:00 +01:00
"public Dictionary<string, Planet> PlanetInstances = new Dictionary<string, Planet>(StringComparer.Ordinal);" ,
"public Dictionary<string, Orbit> Orbits = new Dictionary<string, Orbit>(StringComparer.Ordinal);" ,
"public Dictionary<string, AsteroidBelt> AsteroidBelts = new Dictionary<string, AsteroidBelt>(StringComparer.Ordinal);" ,
"PlanetInstances[runtimeSun.BodyKey] = runtimeSun;" ,
"PlanetInstances[runtimeGas.BodyKey] = runtimeGas;" ,
"PlanetInstances[runtimePlanet.BodyKey] = runtimePlanet;" ,
"AsteroidBelts[runtimeBelt.BodyKey] = runtimeBelt;" ,
"Orbits[runtimeOrbit.OrbitKey] = runtimeOrbit;" ,
"return PlanetInstances.TryGetValue(bodyKey ?? \"\", out planet);" ,
"return AsteroidBelts.TryGetValue(bodyKey ?? \"\", out belt);" ,
"return Orbits.TryGetValue(orbitKey ?? \"\", out orbit);" ,
"var parentPosition = string.IsNullOrWhiteSpace(orbit.ParentOrbitKey)" ,
": GetOrbitPosition(orbit.ParentOrbitKey);" ,
2026-06-16 16:57:18 +01:00
"return TryGetOrbit(orbitKey, out var orbit) ? orbit.Velocity : float2.zero;" ,
"var runtimeBelt = new AsteroidBelt(belt, Orbits[belt.OrbitKey]);" ,
"var runtimeSun = new Sun(settings, sun, Orbits[planet.OrbitKey]);" ,
"var runtimeGas = new GasGiant(settings, gas, Orbits[planet.OrbitKey]);" ,
"var runtimePlanet = new Planet(settings, planet, Orbits[planet.OrbitKey]);" ,
"BodyKey = data.BodyKey ?? \"\";" ,
"OrbitKey = data.OrbitKey ?? \"\";" ,
"ParentOrbitKey = data.ParentOrbitKey ?? \"\";"
2026-06-16 16:27:33 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! zoneSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Zone must keep native key-indexed runtime lookup tables for body and orbit resolution: " +
string . Join ( ", " , missingSymbols ) ) ;
}
}
2026-06-16 16:48:00 +01:00
static void RequireTypedZoneRuntimeCollections ( string root )
{
var zoneSourcePath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" ) ;
var zoneRendererPath = Path . Combine ( root , "Assets" , "Scripts" , "Zone Display" , "ZoneRenderer.cs" ) ;
var zoneSource = File . Exists ( zoneSourcePath )
? File . ReadAllText ( zoneSourcePath )
: throw new InvalidOperationException ( "Cannot verify typed Zone runtime collections; Zone source is missing." ) ;
var zoneRenderer = File . Exists ( zoneRendererPath )
? File . ReadAllText ( zoneRendererPath )
: throw new InvalidOperationException ( "Cannot verify typed Zone runtime collections; ZoneRenderer source is missing." ) ;
var forbiddenZoneSymbols = new [ ]
{
"public Dictionary<Guid, Planet> PlanetInstances" ,
"public Dictionary<Guid, Orbit> Orbits" ,
"public Dictionary<Guid, AsteroidBelt> AsteroidBelts" ,
"private HashSet<Guid> _updatedOrbits" ,
"orbit.Value.Position = GetOrbitPosition(orbit.Key);"
} ;
var zoneHits = forbiddenZoneSymbols
. Where ( symbol = > zoneSource . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"Assets/Scripts/ServerShared/Zone.cs: contains {symbol}" ) ;
var forbiddenRendererSymbols = new [ ]
{
"public Dictionary<Guid, PlanetObject> Planets" ,
"private Dictionary<Guid, AsteroidBeltUI> _beltObjects" ,
"private Dictionary<Guid, InstancedMesh[]> _beltMeshes" ,
"private Dictionary<Guid, Matrix4x4[][]> _beltMatrices" ,
"_beltMeshes[runtimeBelt.ID] =" ,
"_beltObjects[runtimeBelt.ID] =" ,
"Planets[planetInstance.ID] = planet;"
} ;
var rendererHits = forbiddenRendererSymbols
. Where ( symbol = > zoneRenderer . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"Assets/Scripts/Zone Display/ZoneRenderer.cs: contains {symbol}" ) ;
var hits = zoneHits . Concat ( rendererHits ) . ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Zone runtime collections and renderer caches must keep typed OrbitKey/BodyKey ownership rather than Guid dictionary keys: " +
string . Join ( "; " , hits ) ) ;
}
var requiredZoneSymbols = new [ ]
{
"public Dictionary<string, Planet> PlanetInstances = new Dictionary<string, Planet>(StringComparer.Ordinal);" ,
"public Dictionary<string, Orbit> Orbits = new Dictionary<string, Orbit>(StringComparer.Ordinal);" ,
"public Dictionary<string, AsteroidBelt> AsteroidBelts = new Dictionary<string, AsteroidBelt>(StringComparer.Ordinal);" ,
"private HashSet<string> _updatedOrbits = new HashSet<string>(StringComparer.Ordinal);" ,
"foreach (var orbit in Orbits.Values)" ,
"orbit.Position = GetOrbitPosition(orbit.OrbitKey);"
} ;
var missingZoneSymbols = requiredZoneSymbols
. Where ( symbol = > ! zoneSource . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"Assets/Scripts/ServerShared/Zone.cs: missing {symbol}" ) ;
var requiredRendererSymbols = new [ ]
{
"public Dictionary<string, PlanetObject> Planets = new Dictionary<string, PlanetObject>(StringComparer.Ordinal);" ,
"private Dictionary<string, AsteroidBeltUI> _beltObjects = new Dictionary<string, AsteroidBeltUI>(StringComparer.Ordinal);" ,
"private Dictionary<string, InstancedMesh[]> _beltMeshes = new Dictionary<string, InstancedMesh[]>(StringComparer.Ordinal);" ,
"private Dictionary<string, Matrix4x4[][]> _beltMatrices = new Dictionary<string, Matrix4x4[][]>(StringComparer.Ordinal);" ,
"_beltMeshes[runtimeBelt.BodyKey] = meshes.ToArray();" ,
"_beltObjects[runtimeBelt.BodyKey] = belt;" ,
"Planets[planetInstance.BodyKey] = planet;"
} ;
var missingRendererSymbols = requiredRendererSymbols
. Where ( symbol = > ! zoneRenderer . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"Assets/Scripts/Zone Display/ZoneRenderer.cs: missing {symbol}" ) ;
var missing = missingZoneSymbols . Concat ( missingRendererSymbols ) . ToArray ( ) ;
if ( missing . Length > 0 )
{
throw new InvalidOperationException (
"Zone runtime collections and renderer caches must expose typed keyed ownership end to end: " +
string . Join ( "; " , missing ) ) ;
}
}
2026-06-16 16:57:18 +01:00
static void RequireTypedZoneConstructionKeys ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "ZoneConstructionData.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "ZoneGenerator.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" )
} ;
var forbiddenSymbols = new [ ]
{
"var runtimeBelt = new AsteroidBelt(belt, Orbits[OrbitKey(belt.Orbit)]);" ,
"var runtimeSun = new Sun(settings, sun, Orbits[OrbitKey(planet.Orbit)]);" ,
"var runtimeGas = new GasGiant(settings, gas, Orbits[OrbitKey(planet.Orbit)]);" ,
"var runtimePlanet = new Planet(settings, planet, Orbits[OrbitKey(planet.Orbit)]);" ,
"turret.OrbitKey = Zone.OrbitKey(turretOrbit.ID);" ,
2026-06-16 20:51:26 +01:00
"station.OrbitKey = Zone.OrbitKey(lagrangeOrbit.ID);" ,
"public Guid ID = Guid.NewGuid();" ,
"public Guid Orbit;" ,
"public Guid Parent;" ,
"data.OrbitKey = Zone.OrbitKey(data.ID);" ,
"planetData.BodyKey = Zone.BodyKey(planetData.ID);" ,
"lagrangeOrbit.OrbitKey = Zone.OrbitKey(lagrangeOrbit.ID);" ,
"turretOrbit.OrbitKey = Zone.OrbitKey(turretOrbit.ID);"
2026-06-16 16:57:18 +01:00
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Zone construction and generation must hand orbit/body identity through typed key fields instead of recomputing runtime authority from legacy GUID refs: " +
string . Join ( "; " , hits ) ) ;
}
var requiredSymbols = new Dictionary < string , string [ ] >
{
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "ZoneConstructionData.cs")] = new [ ]
{
"public string BodyKey = \"\";" ,
"public string OrbitKey = \"\";" ,
"public string ParentOrbitKey = \"\";"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "ZoneGenerator.cs")] = new [ ]
{
2026-06-16 20:51:26 +01:00
"OrbitKey = Zone.OrbitKey(Guid.NewGuid())," ,
2026-06-16 16:57:18 +01:00
"data.ParentOrbitKey = orbitInverseMap[data].Parent != null" ,
2026-06-16 20:51:26 +01:00
"planetData.BodyKey = Zone.BodyKey(Guid.NewGuid());" ,
2026-06-16 16:57:18 +01:00
"planetData.OrbitKey = orbitMap[planet].OrbitKey;" ,
2026-06-16 20:51:26 +01:00
"planetData.Name = bodyName.Substring(0, Math.Min(8, bodyName.Length));" ,
"var bodyName = planetData.BodyKey.Substring(planetData.BodyKey.LastIndexOf(':') + 1);" ,
"OrbitKey = Zone.OrbitKey(Guid.NewGuid())," ,
2026-06-16 16:57:18 +01:00
"ParentOrbitKey = baseOrbit.ParentOrbitKey," ,
"ParentOrbitKey = orbit.ParentOrbitKey," ,
"turret.OrbitKey = turretOrbit.OrbitKey;" ,
"station.OrbitKey = lagrangeOrbit.OrbitKey;"
} ,
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Zone.cs")] = new [ ]
{
"var runtimeBelt = new AsteroidBelt(belt, Orbits[belt.OrbitKey]);" ,
"var runtimeSun = new Sun(settings, sun, Orbits[planet.OrbitKey]);" ,
"var runtimeGas = new GasGiant(settings, gas, Orbits[planet.OrbitKey]);" ,
"var runtimePlanet = new Planet(settings, planet, Orbits[planet.OrbitKey]);" ,
"BodyKey = data.BodyKey ?? \"\";" ,
"OrbitKey = data.OrbitKey ?? \"\";" ,
"ParentOrbitKey = data.ParentOrbitKey ?? \"\";"
}
} ;
var missingSymbols = requiredSymbols
. Where ( pair = > ! File . Exists ( pair . Key ) | | pair . Value . Any ( symbol = > ! File . ReadAllText ( pair . Key ) . Contains ( symbol , StringComparison . Ordinal ) ) )
. SelectMany ( pair = >
{
var text = File . Exists ( pair . Key ) ? File . ReadAllText ( pair . Key ) : "" ;
return pair . Value
. Where ( symbol = > ! text . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, pair.Key)}: missing {symbol}" ) ;
} )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Zone construction inputs and generator seams must retain typed orbit/body keys as the handoff authority: " +
string . Join ( "; " , missingSymbols ) ) ;
}
}
2026-06-16 17:03:59 +01:00
static void RequireTypedZoneStateSnapshotKeys ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Aetheria.State" , "Documents" , "AetheriaRuntimeStateDocuments.cs" ) ,
Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeCatalogSnapshot.cs" ) ,
Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeCatalogStore.cs" )
} ;
var forbiddenSymbols = new [ ]
{
"public string OrbitId { get; set; } = \"\";" ,
"public string ParentId { get; set; } = \"\";" ,
"public string BodyId { get; set; } = \"\";" ,
"public AetheriaRuntimeOrbitSnapshot(string orbitId, string parentId" ,
"string bodyId," ,
"string orbitId," ,
"OrbitId = orbit.OrbitKey ?? \"\"," ,
"ParentId = orbit.ParentOrbitKey ?? \"\"," ,
"BodyId = body.BodyKey ?? \"\"," ,
"var orbitId = ReadFieldString(ref reader, orbitFields, 0);" ,
"var parentId = ReadFieldString(ref reader, orbitFields, 1);" ,
"var bodyId = ReadFieldString(ref reader, bodyFields, 0);" ,
"var orbitId = ReadFieldString(ref reader, bodyFields, 3);"
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Typed zone-state documents and runtime snapshots must name orbit/body identity as keys, not legacy ids: " +
string . Join ( "; " , hits ) ) ;
}
var requiredSymbols = new Dictionary < string , string [ ] >
{
[Path.Combine(root, "Aetheria.State", "Documents", "AetheriaRuntimeStateDocuments.cs")] = new [ ]
{
"public string OrbitKey { get; set; } = \"\";" ,
"public string ParentOrbitKey { get; set; } = \"\";" ,
"public string BodyKey { get; set; } = \"\";"
} ,
[Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeCatalogSnapshot.cs")] = new [ ]
{
"public AetheriaRuntimeOrbitSnapshot(string orbitKey, string parentOrbitKey" ,
"OrbitKey = orbitKey;" ,
"ParentOrbitKey = parentOrbitKey;" ,
"public string OrbitKey { get; }" ,
"public string ParentOrbitKey { get; }" ,
"BodyKey = bodyKey;" ,
"OrbitKey = orbitKey;" ,
"public string BodyKey { get; }" ,
"public string OrbitKey { get; }"
} ,
[Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeCatalogStore.cs")] = new [ ]
{
"var orbitKey = ReadFieldString(ref reader, orbitFields, 0);" ,
"var parentOrbitKey = ReadFieldString(ref reader, orbitFields, 1);" ,
"var bodyKey = ReadFieldString(ref reader, bodyFields, 0);" ,
"var orbitKey = ReadFieldString(ref reader, bodyFields, 3);" ,
"new AetheriaRuntimeOrbitSnapshot(" ,
"new AetheriaRuntimeBodySnapshot("
}
} ;
var missingSymbols = requiredSymbols
. Where ( pair = > ! File . Exists ( pair . Key ) | | pair . Value . Any ( symbol = > ! File . ReadAllText ( pair . Key ) . Contains ( symbol , StringComparison . Ordinal ) ) )
. SelectMany ( pair = >
{
var text = File . Exists ( pair . Key ) ? File . ReadAllText ( pair . Key ) : "" ;
return pair . Value
. Where ( symbol = > ! text . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, pair.Key)}: missing {symbol}" ) ;
} )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Typed zone-state documents and runtime catalog readers must expose orbit/body key ownership explicitly: " +
string . Join ( "; " , missingSymbols ) ) ;
}
}
2026-06-16 16:32:31 +01:00
static void RequireTypedAsteroidZoneApi ( string root )
{
var zoneSourcePath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" ) ;
var zoneSource = File . Exists ( zoneSourcePath )
? File . ReadAllText ( zoneSourcePath )
: throw new InvalidOperationException ( "Cannot verify typed asteroid Zone API; Zone source is missing." ) ;
var forbiddenSymbols = new [ ]
{
"public int NearestAsteroid(Guid planetDataID, float2 position)" ,
"public bool AsteroidExists(Guid planetDataID, int asteroid)" ,
"public void MineAsteroid(Entity miner, Guid asteroidBelt, int asteroid, float damage, float efficiency, float penetration)" ,
"BeltUpdates.Add(Task.Run(() => UpdateAsteroidTransforms(belt.Key)));" ,
"private void UpdateAsteroidTransforms(Guid planetDataID)" ,
"MineAsteroid(miner, belt.ID, asteroid, damage, efficiency, penetration);"
} ;
var hits = forbiddenSymbols
. Where ( symbol = > zoneSource . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"Assets/Scripts/ServerShared/Zone.cs: contains {symbol}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Zone asteroid helpers must keep typed body-key or runtime-object authority rather than Guid overloads: " +
string . Join ( "; " , hits ) ) ;
}
var requiredSymbols = new [ ]
{
"foreach (var belt in AsteroidBelts.Values)" ,
"BeltUpdates.Add(Task.Run(() => UpdateAsteroidTransforms(belt)));" ,
"public int NearestAsteroid(string asteroidBeltKey, float2 position)" ,
"if (!TryGetAsteroidBelt(asteroidBeltKey, out var belt))" ,
"public bool AsteroidExists(string asteroidBeltKey, int asteroid)" ,
"private void UpdateAsteroidTransforms(AsteroidBelt belt)" ,
"public void MineAsteroid(Entity miner, string asteroidBeltKey, int asteroid, float damage, float efficiency, float penetration)"
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! zoneSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Zone asteroid helpers must route through typed body keys or runtime belt objects: " +
string . Join ( ", " , missingSymbols ) ) ;
}
}
2026-06-16 11:43:37 +01:00
static void RequireFactionKeyIdentity ( string root )
{
var checkedFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Corporations.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Entity.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Galaxy.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Narrative" , "ZoneConstraints.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ,
2026-06-16 12:00:34 +01:00
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "SectorRenderer.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "SectorMap.cs" )
2026-06-16 11:43:37 +01:00
} ;
var forbiddenSymbols = new [ ]
{
"ID.GetHashCode" ,
"faction.ID == ID" ,
"Faction.ID ==" ,
"faction.ID ==" ,
".ID == TargetFaction" ,
".ID == GalaxyZone.Owner" ,
".ID != zone.Owner" ,
"pair.Key.ID" ,
"f.ID != zone.Owner" ,
2026-06-16 12:00:34 +01:00
"Owner.ID ==" ,
"zone.Owner == adjacentZone.Owner" ,
"zone.Owner != mega"
2026-06-16 11:43:37 +01:00
} ;
var hits = checkedFiles
. Where ( File . Exists )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Faction identity must compare and order by FactionKey; Faction.ID is temporary projection residue only: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-16 20:21:24 +01:00
static void RequireTypedRuntimeBehaviorCoverage ( string root )
{
var itemManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "ItemManager.cs" ) ;
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var itemManager = File . Exists ( itemManagerPath )
? File . ReadAllText ( itemManagerPath )
: throw new InvalidOperationException ( "Cannot verify typed runtime behavior coverage; ItemManager.cs is missing." ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify typed runtime behavior coverage; ActionGameManager.cs is missing." ) ;
var requiredFactoryMappings = new [ ]
{
"case \"AetherDrive\": return new AetherDrive(definition, item);" ,
"case \"AutoWeapon\": return new AutoWeapon(definition, item);" ,
"case \"Capacitor\": return new Capacitor(definition, item);" ,
"case \"ChargedWeapon\": return new ChargedWeapon(definition, item);" ,
"case \"Cockpit\": return new Cockpit(definition, item);" ,
"case \"Cooldown\": return new Cooldown(definition, item);" ,
"case \"ConstantWeapon\": return new ConstantWeapon(definition, item);" ,
"case \"EnergyDraw\": return new EnergyDraw(definition, item);" ,
"case \"GuidedWeapon\": return new InstantWeapon(definition, item);" ,
"case \"Heat\": return new Heat(definition, item);" ,
"case \"HeatStorage\": return new HeatStorage(definition, item);" ,
"case \"InstantWeapon\": return new InstantWeapon(definition, item);" ,
"case \"ItemUsage\": return new ItemUsage(definition, item);" ,
"case \"Launcher\": return new LockWeapon(definition, item);" ,
"case \"LockWeapon\": return new LockWeapon(definition, item);" ,
"case \"MiningTool\": return new MiningTool(definition, item);" ,
"case \"Radiator\": return new Radiator(definition, item);" ,
"case \"Reactor\": return new Reactor(definition, item);" ,
"case \"Reflector\": return new Reflector(definition, item);" ,
"case \"ResourceScanner\": return new ResourceScanner(definition, item);" ,
"case \"Sensor\": return new Sensor(definition, item);" ,
"case \"Shield\": return new Shield(definition, item);" ,
"case \"StatModifier\": return new StatModifier(definition, item);" ,
"case \"Switch\": return new Switch(definition, item);" ,
"case \"Thermotoggle\": return new Thermotoggle(definition, item);" ,
"case \"Thruster\": return new Thruster(definition, item);" ,
"case \"Trigger\": return new Trigger(definition, item);" ,
"case \"TurretController\": return new TurretController(definition, item);" ,
"case \"VelocityConversion\": return new VelocityConversion(definition, item);" ,
"case \"VelocityLimit\": return new VelocityLimit(definition, item);" ,
"case \"Visibility\": return new Visibility(definition, item);" ,
"case \"Wear\": return new Wear(definition, item);" ,
"case \"AetherDrive\": return new AetherDrive(definition, effect);" ,
"case \"AutoWeapon\": return new InstantWeapon(definition, effect);" ,
"case \"Capacitor\": return new Capacitor(definition, effect);" ,
"case \"ChargedWeapon\": return new ChargedWeapon(definition, effect);" ,
"case \"Cockpit\": return new Cockpit(definition, effect);" ,
"case \"Cooldown\": return new Cooldown(definition, effect);" ,
"case \"ConstantWeapon\": return new ConstantWeapon(definition, effect);" ,
"case \"EnergyDraw\": return new EnergyDraw(definition, effect);" ,
"case \"GuidedWeapon\": return new InstantWeapon(definition, effect);" ,
"case \"Heat\": return new Heat(definition, effect);" ,
"case \"HeatStorage\": return new HeatStorage(definition, effect);" ,
"case \"InstantWeapon\": return new InstantWeapon(definition, effect);" ,
"case \"ItemUsage\": return new ItemUsage(definition, effect);" ,
"case \"Launcher\": return new LockWeapon(definition, effect);" ,
"case \"LockWeapon\": return new LockWeapon(definition, effect);" ,
"case \"MiningTool\": return new MiningTool(definition, effect);" ,
"case \"Radiator\": return new Radiator(definition, effect);" ,
"case \"Reactor\": return new Reactor(definition, effect);" ,
"case \"Reflector\": return new Reflector(definition, effect);" ,
"case \"ResourceScanner\": return new ResourceScanner(definition, effect);" ,
"case \"Sensor\": return new Sensor(definition, effect);" ,
"case \"Shield\": return new Shield(definition, effect);" ,
"case \"StatModifier\": return new StatModifier(definition, effect);" ,
"case \"Switch\": return new Switch(definition, effect);" ,
"case \"Thermotoggle\": return new Thermotoggle(definition, effect);" ,
"case \"Thruster\": return new Thruster(definition, effect);" ,
"case \"Trigger\": return new Trigger(definition, effect);" ,
"case \"TurretController\": return new TurretController(definition, effect);" ,
"case \"VelocityConversion\": return new VelocityConversion(definition, effect);" ,
"case \"VelocityLimit\": return new VelocityLimit(definition, effect);" ,
"case \"Visibility\": return new Visibility(definition, effect);" ,
"case \"Wear\": return new Wear(definition, effect);"
} ;
var missingFactoryMappings = requiredFactoryMappings
. Where ( symbol = > ! itemManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingFactoryMappings . Length > 0 )
{
throw new InvalidOperationException (
"Typed runtime behavior factory no longer covers the live behavior payload kinds: " +
string . Join ( ", " , missingFactoryMappings ) ) ;
}
var requiredWeaponStateCoverage = new [ ]
{
"if (weapon is LockWeapon lockWeapon)" ,
"else if (weapon is ChargedWeapon chargedWeapon)" ,
"else if (weapon is ConstantWeapon constantWeapon)" ,
"else if (weapon is InstantWeapon instantWeapon)"
} ;
var missingWeaponStateCoverage = requiredWeaponStateCoverage
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingWeaponStateCoverage . Length > 0 )
{
throw new InvalidOperationException (
"Typed weapon snapshot coverage no longer restores the live articulated weapon families: " +
string . Join ( ", " , missingWeaponStateCoverage ) ) ;
}
var requiredBehaviorStateCoverage = new [ ]
{
"case Sensor sensor:" ,
"case Radiator radiator:" ,
"case Reactor reactor:" ,
"case Capacitor capacitor:" ,
"case AetherDrive drive:" ,
"case ResourceScanner resourceScanner:" ,
"case MiningTool miningTool:" ,
"case Thruster thruster:" ,
"case Shield shield:" ,
"case VelocityLimit velocityLimit:" ,
"case Thermotoggle thermotoggle:" ,
"case Switch switchBehavior:" ,
"case Trigger trigger:" ,
"case StatModifier statModifier:" ,
"case TurretController turretController:"
} ;
var missingBehaviorStateCoverage = requiredBehaviorStateCoverage
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBehaviorStateCoverage . Length > 0 )
{
throw new InvalidOperationException (
"Typed runtime behavior snapshot coverage no longer owns the mutable non-weapon behavior families: " +
string . Join ( ", " , missingBehaviorStateCoverage ) ) ;
}
}
2026-06-15 21:12:43 +01:00
static void RequireEveRuntimeBootstrap ( string root )
{
var bootstrapPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.eve-runtime" ,
"Runtime" ,
"AetheriaEveRuntimeBootstrap.cs" ) ;
var presenterPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.eve-runtime" ,
"Runtime" ,
"AetheriaEveSurfacePresenter.cs" ) ;
2026-06-21 08:56:07 +01:00
var daemonSurfaceCommandsPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeDaemonSurfaceCommands.cs" ) ;
2026-06-15 21:12:43 +01:00
if ( ! File . Exists ( bootstrapPath ) )
{
throw new InvalidOperationException ( "Aetheria Eve runtime bootstrap is missing." ) ;
}
if ( ! File . Exists ( presenterPath ) )
{
throw new InvalidOperationException ( "Aetheria Eve surface presenter is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( daemonSurfaceCommandsPath ) )
{
throw new InvalidOperationException ( "Aetheria daemon surface command router is missing." ) ;
}
2026-06-15 21:12:43 +01:00
var bootstrap = File . ReadAllText ( bootstrapPath ) ;
if ( ! bootstrap . Contains ( "RuntimeInitializeOnLoadMethod" , StringComparison . Ordinal ) | |
2026-06-21 08:56:07 +01:00
! bootstrap . Contains ( "DefaultSurfaceId = AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId" , StringComparison . Ordinal ) | |
2026-06-15 21:12:43 +01:00
! bootstrap . Contains ( "AetheriaEveSurfacePresenter" , StringComparison . Ordinal ) | |
! bootstrap . Contains ( "UIDocument" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Aetheria Eve runtime bootstrap no longer mounts the daemon-published game surface through the UI Toolkit presenter." ) ;
2026-06-15 21:12:43 +01:00
}
var presenter = File . ReadAllText ( presenterPath ) ;
2026-06-21 08:56:07 +01:00
var daemonSurfaceCommands = File . ReadAllText ( daemonSurfaceCommandsPath ) ;
var requiredPresenterSymbols = new [ ]
{
"private string surfaceId = AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId" ,
"AetheriaRuntimeStateReader.ReadEveSurface(statePath, surfaceId)" ,
"AetheriaRuntimeStateReader.ReadEveSurface(stateBoot.StateFilePath, surfaceId)" ,
"private bool ShouldMountSurface(" ,
"_mountedSurfaceVersion != surface.Version" ,
"private static readonly AetheriaEveUnitySurfaceChrome RootOnlyChrome" ,
"UseShell = false" ,
"MountSurface(string statePath, EveSurfaceDocument surface)" ,
"AetheriaEveUnitySurfaceHost.Render(" ,
"AetheriaRuntimeDaemonSurfaceCommands.TrySubmit(statePath, request, out var daemonEnvelope)" ,
"AetheriaRuntimeEveCommands.TrySendKnownSurfaceCommand("
} ;
var missingPresenterSymbols = requiredPresenterSymbols
. Where ( symbol = > ! presenter . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingPresenterSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria Eve presenter no longer lowers refreshed daemon/CultMesh surfaces through typed Eve commands: " +
string . Join ( ", " , missingPresenterSymbols ) ) ;
}
if ( presenter . Contains ( "new EveUiToolkitSurfaceLowerer" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria Eve presenter still lowers daemon-published surfaces directly instead of delegating to the shared Unity Eve surface host." ) ;
}
var daemonSubmitIndex = presenter . IndexOf (
"AetheriaRuntimeDaemonSurfaceCommands.TrySubmit(statePath, request, out var daemonEnvelope)" ,
StringComparison . Ordinal ) ;
var eveSubmitIndex = presenter . IndexOf (
"AetheriaRuntimeEveCommands.TrySendKnownSurfaceCommand(" ,
StringComparison . Ordinal ) ;
if ( daemonSubmitIndex < 0 | | eveSubmitIndex < 0 | | daemonSubmitIndex > eveSubmitIndex )
{
throw new InvalidOperationException (
"Aetheria Eve presenter must route daemon-published surface commands to the typed daemon boundary before falling back to Eve requests." ) ;
}
var requiredDaemonSurfaceCommandSymbols = new [ ]
{
"public static class AetheriaRuntimeDaemonSurfaceCommands" ,
"private const string CommandPrefix = \"aetheria.daemon.commands.\"" ,
"EveSurfaceCommandRequest request" ,
"request.ProviderId, \"aetheria.daemon\"" ,
"AetheriaRuntimeStateReader.TryReadObservedDaemonState(stateFilePath, out var observed)" ,
"new AetheriaRuntimeDaemonOperationClient(" ,
"var command = client.Create(kind, observed)" ,
"client.TrySend(command, out envelope, out _)"
} ;
var missingDaemonSurfaceCommandSymbols = requiredDaemonSurfaceCommandSymbols
. Where ( symbol = > ! daemonSurfaceCommands . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDaemonSurfaceCommandSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon Eve surface command routing no longer submits typed daemon operations: " +
string . Join ( ", " , missingDaemonSurfaceCommandSymbols ) ) ;
}
var forbiddenDaemonSurfaceCommandSymbols = new [ ]
{
"request.Payload.TryGetValue" ,
"ApplyPayload(" ,
"ReadInt(" ,
"ReadDouble(" ,
"ReadString(" ,
"\"commandKind\""
} ;
var daemonSurfaceCommandHits = forbiddenDaemonSurfaceCommandSymbols
. Where ( symbol = > daemonSurfaceCommands . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( daemonSurfaceCommandHits . Length > 0 )
2026-06-15 21:12:43 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Daemon Eve surface command routing must lower command ids to typed daemon documents, not decode string payload maps: " +
string . Join ( ", " , daemonSurfaceCommandHits ) ) ;
}
if ( daemonSurfaceCommands . Contains ( "AetheriaRuntimeCommandSubmitter.TrySubmitDaemonCommand(" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Daemon Eve surface command routing still calls the generic daemon submitter instead of the typed daemon operation client." ) ;
2026-06-15 21:12:43 +01:00
}
}
2026-06-15 22:00:25 +01:00
static void RequireNoRendererLocalConsole ( string root )
{
var consoleFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "ConsoleController.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "ConsoleView.cs" )
} ;
var existingConsoleFiles = consoleFiles
. Where ( File . Exists )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( existingConsoleFiles . Length > 0 )
{
throw new InvalidOperationException (
"Renderer-local console source files still exist: " +
string . Join ( ", " , existingConsoleFiles ) ) ;
}
var forbiddenSymbols = new [ ]
{
"ConsoleController" ,
"ConsoleView" ,
"AddCommand("
} ;
var hits = Directory . EnumerateFiles ( Path . Combine ( root , "Assets" ) , "*.*" , SearchOption . AllDirectories )
. Where ( path = >
path . EndsWith ( ".cs" , StringComparison . OrdinalIgnoreCase ) | |
path . EndsWith ( ".unity" , StringComparison . OrdinalIgnoreCase ) | |
path . EndsWith ( ".prefab" , StringComparison . OrdinalIgnoreCase ) )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. Take ( 10 )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Renderer-local console authority is still wired in Assets: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-15 22:26:14 +01:00
static void RequireNoRendererLocalDebugPanels ( string root )
{
var forbiddenFiles = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "FieldTester.cs" )
} ;
var existingFiles = forbiddenFiles
. Where ( File . Exists )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( existingFiles . Length > 0 )
{
throw new InvalidOperationException (
"Renderer-local debug panel source files still exist: " +
string . Join ( ", " , existingFiles ) ) ;
}
var hits = Directory . EnumerateFiles ( Path . Combine ( root , "Assets" ) , "*.*" , SearchOption . AllDirectories )
. Where ( path = >
path . EndsWith ( ".cs" , StringComparison . OrdinalIgnoreCase ) | |
path . EndsWith ( ".unity" , StringComparison . OrdinalIgnoreCase ) | |
path . EndsWith ( ".prefab" , StringComparison . OrdinalIgnoreCase ) )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > line . Line . Contains ( "FieldTester" , StringComparison . Ordinal ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. Take ( 10 )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Renderer-local FieldTester authority is still wired in Assets: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-21 08:56:07 +01:00
static void RequireMainMenuSettingsCommands ( string root )
2026-06-15 22:47:15 +01:00
{
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
2026-06-16 18:32:19 +01:00
var sharedCommandsPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimePlayerSettingsCommands.cs" ) ;
2026-06-16 19:05:00 +01:00
var surfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimePlayerSettingsSurfaceBuilder.cs" ) ;
2026-06-15 22:47:15 +01:00
if ( ! File . Exists ( mainMenuPath ) )
{
2026-06-21 08:56:07 +01:00
throw new InvalidOperationException ( "Cannot verify main-menu settings command path; MainMenu.cs is missing." ) ;
2026-06-15 22:47:15 +01:00
}
2026-06-16 18:32:19 +01:00
if ( ! File . Exists ( sharedCommandsPath ) )
{
throw new InvalidOperationException (
"Cannot verify main-menu settings command contract; AetheriaRuntimePlayerSettingsCommands.cs is missing." ) ;
}
2026-06-16 19:05:00 +01:00
if ( ! File . Exists ( surfaceBuilderPath ) )
{
throw new InvalidOperationException (
"Cannot verify main-menu settings surface contract; AetheriaRuntimePlayerSettingsSurfaceBuilder.cs is missing." ) ;
}
2026-06-15 22:47:15 +01:00
var source = File . ReadAllText ( mainMenuPath ) ;
2026-06-16 18:32:19 +01:00
var sharedCommands = File . ReadAllText ( sharedCommandsPath ) ;
2026-06-16 19:05:00 +01:00
var surfaceBuilder = File . ReadAllText ( surfaceBuilderPath ) ;
2026-06-16 04:04:59 +01:00
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify main-menu settings authority; ActionGameManager.cs is missing." ) ;
var forbiddenSymbols = new [ ]
{
"ActionGameManager.RuntimePlayerSettings.Name =" ,
"ActionGameManager.RuntimePlayerSettings.GameplaySettings.TemperatureUnit =" ,
"ActionGameManager.RuntimePlayerSettings.GameplaySettings.SignificantDigits =" ,
"ActionGameManager.RuntimePlayerSettings.GraphicsSettings.NebulaQuality =" ,
2026-06-21 08:56:07 +01:00
"ActionGameManager.RuntimePlayerSettings.GraphicsSettings.ShowAsteroidsInMinimap =" ,
"ActionGameManager.RequestRuntimePlayerSettingsCommand" ,
"ActionGameManager.RequestRuntimePlayerName" ,
"ActionGameManager.RequestRuntimeTemperatureUnit" ,
"ActionGameManager.RequestRuntimeSignificantDigits" ,
"ActionGameManager.RequestRuntimeNebulaQuality" ,
"ActionGameManager.RequestRuntimeShowAsteroidsInMinimap"
} ;
var actionGameManagerForbiddenSymbols = new [ ]
{
"RequestRuntimePlayerSettingsCommand" ,
"RequestRuntimePlayerName" ,
"RequestRuntimeTemperatureUnit" ,
"RequestRuntimeSignificantDigits" ,
"RequestRuntimeNebulaQuality" ,
"RequestRuntimeShowAsteroidsInMinimap"
2026-06-16 04:04:59 +01:00
} ;
var hits = File . ReadLines ( mainMenuPath )
2026-06-21 08:56:07 +01:00
. Select ( ( line , index ) = > new { Path = mainMenuPath , LineNumber = index + 1 , Line = line } )
2026-06-16 04:04:59 +01:00
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
2026-06-21 08:56:07 +01:00
. Concat ( File . ReadLines ( actionGameManagerPath )
. Select ( ( line , index ) = > new { Path = actionGameManagerPath , LineNumber = index + 1 , Line = line } )
. Where ( line = > actionGameManagerForbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
2026-06-16 04:04:59 +01:00
. ToArray ( ) ;
if ( hits . Length > 0 )
2026-06-15 22:47:15 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity still owns direct player-settings mutation instead of sending Eve commands: " +
2026-06-16 04:04:59 +01:00
string . Join ( "; " , hits ) ) ;
2026-06-15 22:47:15 +01:00
}
2026-06-16 19:05:00 +01:00
var requiredMainMenuAuthoritySymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"TrySendPlayerSettingsCommand(request, command.Command)" ,
"AetheriaRuntimeMainMenuCommandKind.PlayerSettingsCommand" ,
"AetheriaRuntimeEveCommands.TrySendPlayerSettingsCommand(" ,
"stateBoot.StateFilePath" ,
"\"unity-main-menu\""
2026-06-16 19:05:00 +01:00
} ;
var missingUiCalls = requiredMainMenuAuthoritySymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-16 04:04:59 +01:00
. ToArray ( ) ;
if ( missingUiCalls . Length > 0 )
2026-06-15 22:47:15 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"MainMenu no longer routes settings changes through the Aetheria Eve command boundary: " +
2026-06-16 04:04:59 +01:00
string . Join ( ", " , missingUiCalls ) ) ;
2026-06-15 22:47:15 +01:00
}
2026-06-16 18:32:19 +01:00
2026-06-21 08:56:07 +01:00
if ( source . Contains ( "TrySendPlayerSettingsCommand(request.Command, request.Payload)" , StringComparison . Ordinal ) | |
source . Contains ( "new AetheriaRuntimePlayerSettingsCommandBody" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"MainMenu still decodes player-settings Eve payloads locally instead of delegating to the typed command client." ) ;
}
2026-06-16 18:32:19 +01:00
var requiredSharedCommands = new [ ]
{
2026-06-16 19:45:39 +01:00
"SetPlayerName" ,
2026-06-16 18:32:19 +01:00
"CycleTemperatureUnit" ,
"DecrementSignificantDigits" ,
"IncrementSignificantDigits" ,
"CycleNebulaQuality" ,
"ToggleShowAsteroidsInMinimap"
} ;
var missingSharedCommands = requiredSharedCommands
. Where ( symbol = > ! sharedCommands . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSharedCommands . Length > 0 )
{
throw new InvalidOperationException (
"Shared player-settings Eve command contract is incomplete: " +
string . Join ( ", " , missingSharedCommands ) ) ;
}
2026-06-16 19:05:00 +01:00
var requiredSurfaceBuilderSymbols = new [ ]
{
"AetheriaRuntimePlayerSettingsSurfaceBuilder" ,
2026-06-16 19:45:39 +01:00
"AetheriaRuntimePlayerSettingsCommands.SetPlayerName" ,
"\"control.text\"" ,
2026-06-16 19:05:00 +01:00
"AetheriaRuntimePlayerSettingsCommands.CycleTemperatureUnit" ,
"AetheriaRuntimePlayerSettingsCommands.DecrementSignificantDigits" ,
"AetheriaRuntimePlayerSettingsCommands.IncrementSignificantDigits" ,
"AetheriaRuntimePlayerSettingsCommands.CycleNebulaQuality" ,
"AetheriaRuntimePlayerSettingsCommands.ToggleShowAsteroidsInMinimap"
} ;
var missingSurfaceBuilderSymbols = requiredSurfaceBuilderSymbols
. Where ( symbol = > ! surfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSurfaceBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared player-settings Eve surface builder is incomplete: " +
string . Join ( ", " , missingSurfaceBuilderSymbols ) ) ;
}
2026-06-16 18:32:19 +01:00
var requiredMainMenuSymbols = new [ ]
{
2026-06-16 19:05:00 +01:00
"AetheriaRuntimePlayerSettingsSurfaceBuilder.Build" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaRuntimeMainMenuCommandKind.PlayerSettingsCommand" ,
"TrySendPlayerSettingsCommand(request, command.Command)"
2026-06-16 18:32:19 +01:00
} ;
var missingMainMenuSymbols = requiredMainMenuSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingMainMenuSymbols . Length > 0 )
{
throw new InvalidOperationException (
2026-06-16 19:05:00 +01:00
"MainMenu no longer lowers the shared player-settings Eve surface contract: " +
2026-06-16 18:32:19 +01:00
string . Join ( ", " , missingMainMenuSymbols ) ) ;
}
2026-06-16 19:45:39 +01:00
var forbiddenMainMenuSymbols = new [ ]
{
"new TextField(\"Name\")" ,
2026-06-21 08:56:07 +01:00
"ActionGameManager.RequestRuntimePlayerName(evt.newValue)" ,
"TryQueuePlayerSettingsCommand(" ,
"TryQueueAetheriaEveCommand(" ,
"AetheriaRuntimeEveCommandLog.QueueCommand(" ,
"ResolveMenuSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer" ,
"host.AddComponent<UIDocument>" ,
"new EveSurfaceCommandRequest("
2026-06-16 19:45:39 +01:00
} ;
var mainMenuHits = forbiddenMainMenuSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( mainMenuHits . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu should not keep renderer-local player-name input authority outside the shared Eve surface: " +
string . Join ( ", " , mainMenuHits ) ) ;
}
2026-06-16 18:32:19 +01:00
var forbiddenUiFieldSymbols = new [ ]
{
"AddField(\"Temperature Unit\"" ,
"AddField(\"Significant Digits\"" ,
"AddField(\"Nebula Quality\"" ,
2026-06-16 19:05:00 +01:00
"AddField(\"Show Asteroids in Minimap\"" ,
"ShowGameplaySettings()" ,
"ShowGraphicsSettings()"
2026-06-16 18:32:19 +01:00
} ;
var uiFieldHits = File . ReadLines ( mainMenuPath )
. Select ( ( line , index ) = > new { LineNumber = index + 1 , Line = line } )
. Where ( line = > forbiddenUiFieldSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, mainMenuPath)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( uiFieldHits . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu still uses legacy field widgets for Eve-owned gameplay/graphics settings: " +
string . Join ( "; " , uiFieldHits ) ) ;
}
2026-06-15 22:47:15 +01:00
}
2026-06-16 21:08:20 +01:00
static void RequireMainMenuSettingsShellUsesEveSurface ( string root )
{
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
2026-06-21 08:56:07 +01:00
var mainMenuSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeMainMenuSurfaceBuilder.cs" ) ;
2026-06-16 21:08:20 +01:00
if ( ! File . Exists ( mainMenuPath ) )
{
throw new InvalidOperationException ( "Cannot verify main-menu settings shell; MainMenu.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( mainMenuSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify main-menu settings shell; AetheriaRuntimeMainMenuSurfaceBuilder.cs is missing." ) ;
}
2026-06-16 21:08:20 +01:00
var source = File . ReadAllText ( mainMenuPath ) ;
2026-06-21 08:56:07 +01:00
var mainMenuSurfaceBuilder = File . ReadAllText ( mainMenuSurfaceBuilderPath ) ;
2026-06-16 21:08:20 +01:00
var requiredSymbols = new [ ]
{
"RenderMenuSurface(" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeMainMenuSurfaceBuilder.BuildSettings(" ,
2026-06-17 11:24:36 +01:00
"BuildVerseSettingsSurfaceDefinition()" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeMainMenuSurfaceBuilder.BuildInputSettings(" ,
2026-06-16 21:08:20 +01:00
"HandleSettingsSurfaceCommand(" ,
2026-06-17 11:24:36 +01:00
"HandleVerseSettingsSurfaceCommand(" ,
2026-06-16 21:08:20 +01:00
"HandleInputSettingsSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeMainMenuSurfaceBuilder.WithBackAction(" ,
"AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeMainMenuCommandKind.ShowPlayerSettings" ,
"AetheriaRuntimeMainMenuCommandKind.ShowVerseSettings" ,
"AetheriaRuntimeMainMenuCommandKind.ShowInputSettings" ,
"AetheriaRuntimeMainMenuCommandKind.BackToMain" ,
"AetheriaRuntimeMainMenuCommandKind.BackToSettings" ,
"AetheriaRuntimeMainMenuCommandKind.OpenRuntimeInputScreen" ,
"AetheriaRuntimeMainMenuCommands.BackToSettings" ,
"ProjectMainMenuSurfaceState(" ,
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_menuSurfaceDocument)"
2026-06-16 21:08:20 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu no longer lowers the settings shell through Eve surfaces: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"BuildSettingsSurfaceDefinition(" ,
"BuildInputSettingsSurfaceDefinition(" ,
"private static EveSurfaceComponent Button(" ,
"private static EveSurfaceComponent Card(" ,
2026-06-16 21:08:20 +01:00
"_nextMenu.panel.AddButton(\"Player Settings\"" ,
2026-06-17 11:24:36 +01:00
"_nextMenu.panel.AddButton(\"Verse\"" ,
2026-06-16 21:08:20 +01:00
"_nextMenu.panel.AddButton(\"Input\"" ,
"_nextMenu.panel.AddButton(\"Audio\"" ,
2026-06-17 06:35:29 +01:00
"BuildAudioSettingsSurfaceDefinition(" ,
"HandleAudioSettingsSurfaceCommand(" ,
"ShowAudioSettingsCommand" ,
2026-06-21 08:56:07 +01:00
"ResolveMenuSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer" ,
"host.AddComponent<UIDocument>" ,
"switch (request.Command)" ,
"string.Equals(request.Command, AetheriaRuntimeMainMenuCommands.BackToSettings" ,
"string.Equals(request.Command, AetheriaRuntimeMainMenuCommands.OpenRuntimeInputScreen" ,
"TrySendVerseHostCommand(request.Command)" ,
"AetheriaRuntimeClientTargetCommands.IsKnown(request.Command" ,
2026-06-16 21:08:20 +01:00
"_nextMenu.panel.Title.text = \"settings\"" ,
"_nextMenu.panel.Title.text = TitleSubtitle(\"input\", \"settings\")" ,
"_nextMenu.panel.Title.text = TitleSubtitle(\"audio\", \"settings\")"
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
2026-06-17 06:35:29 +01:00
"MainMenu still keeps a fake audio/settings shell or old PropertiesPanel settings shell alive: " +
2026-06-16 21:08:20 +01:00
string . Join ( ", " , hits ) ) ;
2026-06-16 21:23:05 +01:00
}
2026-06-21 08:56:07 +01:00
var requiredBuilderSymbols = new [ ]
{
"public static class AetheriaRuntimeMainMenuSurfaceBuilder" ,
"public static class AetheriaRuntimeMainMenuCommands" ,
"BuildSettings(" ,
"BuildInputSettings(" ,
"WithBackAction(" ,
"public enum AetheriaRuntimeMainMenuCommandKind" ,
"public readonly struct AetheriaRuntimeMainMenuCommand" ,
"public static class AetheriaRuntimeMainMenuSurfaceCommands" ,
"public static bool TryRead(" ,
"AetheriaRuntimeMainMenuCommands.ShowPlayerSettings" ,
"AetheriaRuntimeMainMenuCommands.ShowVerseSettings" ,
"AetheriaRuntimeMainMenuCommands.ShowInputSettings" ,
"AetheriaRuntimeMainMenuCommands.BackToSettings"
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! mainMenuSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared main-menu surface builder no longer owns the settings/input shell contract: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-16 21:23:05 +01:00
}
2026-06-17 04:57:56 +01:00
static void RequireMainMenuRootUsesEveSurface ( string root )
{
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
2026-06-21 08:56:07 +01:00
var mainMenuSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeMainMenuSurfaceBuilder.cs" ) ;
2026-06-17 04:57:56 +01:00
if ( ! File . Exists ( mainMenuPath ) )
{
throw new InvalidOperationException ( "Cannot verify main-menu root shell; MainMenu.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( mainMenuSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify main-menu root shell; AetheriaRuntimeMainMenuSurfaceBuilder.cs is missing." ) ;
}
2026-06-17 04:57:56 +01:00
var source = File . ReadAllText ( mainMenuPath ) ;
2026-06-21 08:56:07 +01:00
var mainMenuSurfaceBuilder = File . ReadAllText ( mainMenuSurfaceBuilderPath ) ;
2026-06-17 04:57:56 +01:00
var requiredSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot(" ,
"ProjectMainMenuSurfaceState(" ,
"AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeMainMenuCommandKind.ContinueRun" ,
"AetheriaRuntimeMainMenuCommandKind.NewGame" ,
"AetheriaRuntimeMainMenuCommandKind.ShowSettings" ,
"AetheriaRuntimeMainMenuCommandKind.Quit" ,
2026-06-17 04:57:56 +01:00
"HandleMainSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"LatestDaemonFrame(stateBoot)" ,
2026-06-17 10:39:28 +01:00
"LatestVerseHostSettings(stateBoot)" ,
2026-06-17 04:57:56 +01:00
"HideMenuSurface();"
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu no longer lowers the root shell through Eve surfaces: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
"PanelPrototype" ,
"FadeTime" ,
"FadeDistance" ,
"FadeAlphaExponent" ,
"FadePositionExponent" ,
"_currentMenu" ,
"_nextMenu" ,
"_fadeFromRight" ,
"_fadeLerp" ,
"_fading" ,
"_panelPosition" ,
"TitleSubtitle(" ,
"IsMenuSurfaceVisible(" ,
"_nextMenu.panel.AddButton(\"Continue\"" ,
"_nextMenu.panel.AddButton(\"New Game\"" ,
"_nextMenu.panel.AddButton(\"Settings\"" ,
"_nextMenu.panel.AddButton(\"Quit\"" ,
2026-06-21 08:56:07 +01:00
"BuildMainSurfaceDefinition(" ,
"private static EveSurfaceDocument BuildMenuSurfaceDocument(" ,
"switch (request.Command)" ,
2026-06-17 04:57:56 +01:00
"Fade(true)" ,
"Fade(false)"
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu still owns root navigation through the legacy PropertiesPanel/fade shell: " +
string . Join ( ", " , hits ) ) ;
}
var prefabPath = Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Main Menu Canvas.prefab" ) ;
if ( ! File . Exists ( prefabPath ) )
{
throw new InvalidOperationException ( "Cannot verify main-menu prefab shell; Main Menu Canvas.prefab is missing." ) ;
}
var prefab = File . ReadAllText ( prefabPath ) ;
var forbiddenPrefabSymbols = new [ ]
{
"PanelPrototype:" ,
"FadeTime:" ,
"FadeDistance:" ,
"FadeAlphaExponent:" ,
"FadePositionExponent:"
} ;
var prefabHits = forbiddenPrefabSymbols
. Where ( symbol = > prefab . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( prefabHits . Length > 0 )
{
throw new InvalidOperationException (
"Main-menu prefab still serializes the dead root shell: " +
string . Join ( ", " , prefabHits ) ) ;
}
2026-06-21 08:56:07 +01:00
var requiredBuilderSymbols = new [ ]
{
"BuildRoot(" ,
"AetheriaRuntimeMainMenuCommands.RootSurfaceId" ,
"AetheriaRuntimeMainMenuCommands.ContinueRun" ,
"AetheriaRuntimeMainMenuCommands.NewGame" ,
"AetheriaRuntimeMainMenuCommands.ShowSettings" ,
"AetheriaRuntimeMainMenuCommands.Quit" ,
"AetheriaRuntimeMainMenuSurfaceState" ,
"public enum AetheriaRuntimeMainMenuCommandKind" ,
"public readonly struct AetheriaRuntimeMainMenuCommand" ,
"public static class AetheriaRuntimeMainMenuSurfaceCommands" ,
"public static bool TryRead("
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! mainMenuSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared main-menu surface builder no longer owns the root shell contract: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-17 04:57:56 +01:00
}
2026-06-17 05:38:58 +01:00
static void RequireConfirmationDialogOwnsMinimalPromptShell ( string root )
{
var dialogPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "ConfirmationDialog.cs" ) ;
if ( ! File . Exists ( dialogPath ) )
{
throw new InvalidOperationException ( "Cannot verify confirmation dialog shell; ConfirmationDialog.cs is missing." ) ;
}
var source = File . ReadAllText ( dialogPath ) ;
var requiredSymbols = new [ ]
{
"public class ConfirmationDialog : MonoBehaviour" ,
"public TextMeshProUGUI Title;" ,
"public RectTransform Content;" ,
"public Property Property;" ,
"public InputField InputField;" ,
"public void Clear()" ,
"public Property AddProperty(Func<string> read)" ,
"public void AddField(string name, Func<string> read, Action<string> write)" ,
"public void AddField(string name, Func<int> read, Action<int> write)"
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"ConfirmationDialog no longer owns the minimal prompt shell contract: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
": PropertiesPanel" ,
"OnPropertyAdded" ,
"RefreshPropertyValues" ,
"PropertyLabel" ,
"Dropdown" ,
"RangedFloatField" ,
"BoolField" ,
"EnumField" ,
"StatSheet"
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"ConfirmationDialog still drags generic PropertiesPanel machinery into the prompt shell: " +
string . Join ( ", " , hits ) ) ;
}
var prefabBlock = ReadSerializedMonoBehaviourBlock (
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Main Menu Canvas.prefab" ) ,
"83488dc4b5e58cd40b4d1c4ee98207b5" ) ;
var sceneBlock = ReadSerializedMonoBehaviourBlock (
Path . Combine ( root , "Assets" , "Scenes" , "ARPG.unity" ) ,
"83488dc4b5e58cd40b4d1c4ee98207b5" ) ;
var forbiddenSerializedFields = new [ ]
{
"Spacer:" ,
"Dropdown:" ,
"Section:" ,
"List:" ,
"PropertyLabel:" ,
"Attribute:" ,
"RangedFloatField:" ,
"ProgressField:" ,
"EnumField:" ,
"BoolField:" ,
"PropertyButton:" ,
"ButtonField:" ,
"IncrementField:" ,
"StatSheet:" ,
"CurveField:" ,
"SelectedChild:" ,
"GameManager:"
} ;
foreach ( var ( label , block ) in new [ ] { ( "Main Menu Canvas.prefab" , prefabBlock ) , ( "ARPG.unity" , sceneBlock ) } )
{
var blockHits = forbiddenSerializedFields
. Where ( symbol = > block . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( blockHits . Length > 0 )
{
throw new InvalidOperationException (
$"ConfirmationDialog still serializes dead PropertiesPanel fields in {label}: " +
string . Join ( ", " , blockHits ) ) ;
}
}
}
static string ReadSerializedMonoBehaviourBlock ( string path , string scriptGuid )
{
if ( ! File . Exists ( path ) )
{
throw new InvalidOperationException ( $"Cannot inspect serialized MonoBehaviour block; file is missing: {path}" ) ;
}
var lines = File . ReadAllLines ( path ) ;
var scriptLine = $"guid: {scriptGuid}" ;
for ( var index = 0 ; index < lines . Length ; index + + )
{
if ( ! lines [ index ] . Contains ( scriptLine , StringComparison . Ordinal ) )
{
continue ;
}
var start = index ;
while ( start > = 0 & & ! lines [ start ] . StartsWith ( "--- !u!114 " , StringComparison . Ordinal ) )
{
start - - ;
}
if ( start < 0 )
{
break ;
}
var end = start + 1 ;
while ( end < lines . Length & & ! lines [ end ] . StartsWith ( "--- !u!" , StringComparison . Ordinal ) )
{
end + + ;
}
return string . Join ( Environment . NewLine , lines . Skip ( start ) . Take ( end - start ) ) ;
}
throw new InvalidOperationException (
$"Cannot inspect serialized MonoBehaviour block for script guid {scriptGuid} in {path}." ) ;
}
2026-06-17 06:10:41 +01:00
static void RequireMainMenuInputSettingsDelegateToRuntimeScreen ( string root )
{
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
2026-06-21 08:56:07 +01:00
var mainMenuSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeMainMenuSurfaceBuilder.cs" ) ;
2026-06-17 06:10:41 +01:00
if ( ! File . Exists ( mainMenuPath ) )
{
throw new InvalidOperationException ( "Cannot verify main-menu input delegation; MainMenu.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( mainMenuSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify main-menu input delegation; AetheriaRuntimeMainMenuSurfaceBuilder.cs is missing." ) ;
}
2026-06-17 06:10:41 +01:00
var source = File . ReadAllText ( mainMenuPath ) ;
2026-06-21 08:56:07 +01:00
var mainMenuSurfaceBuilder = File . ReadAllText ( mainMenuSurfaceBuilderPath ) ;
2026-06-17 06:10:41 +01:00
var requiredSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeMainMenuCommandKind.OpenRuntimeInputScreen" ,
2026-06-17 06:10:41 +01:00
"CanOpenRuntimeInputScreen()" ,
"TryOpenRuntimeInputScreen()" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeMainMenuSurfaceBuilder.BuildInputSettings(" ,
"ProjectMainMenuSurfaceState(" ,
2026-06-17 06:10:41 +01:00
"ActionGameManager.Instance.ShowInputScreenFromMenu();"
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu input page no longer delegates to the live runtime remap screen: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
"Typed input rebinding controls are not lowered through Eve yet." ,
"The live remapping screen still owns drag/drop rebinding and low-level InputSystem edits."
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu input page still reports the live remap owner as future work instead of delegating to it: " +
string . Join ( ", " , hits ) ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! mainMenuSurfaceBuilder . Contains ( "AetheriaRuntimeMainMenuCommands.OpenRuntimeInputScreen" , StringComparison . Ordinal ) | |
! mainMenuSurfaceBuilder . Contains ( "CanOpenRuntimeInputScreen" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Shared main-menu input surface no longer exposes the runtime input-screen handoff." ) ;
}
2026-06-17 06:10:41 +01:00
}
2026-06-17 08:41:06 +01:00
static void RequireRuntimeInputScreenUsesEveSurface ( string root )
{
var inputScreenPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "InputScreen" , "InputDisplayLayout.cs" ) ;
var commandsPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeInputSettingsCommands.cs" ) ;
var builderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeInputSettingsSurfaceBuilder.cs" ) ;
if ( ! File . Exists ( inputScreenPath ) )
{
throw new InvalidOperationException ( "Cannot verify runtime input-screen Eve lowering; InputDisplayLayout.cs is missing." ) ;
}
if ( ! File . Exists ( commandsPath ) | | ! File . Exists ( builderPath ) )
{
throw new InvalidOperationException (
"Cannot verify runtime input-screen Eve lowering; the shared input-settings Eve contract is missing." ) ;
}
var inputScreen = File . ReadAllText ( inputScreenPath ) ;
var commands = File . ReadAllText ( commandsPath ) ;
var builder = File . ReadAllText ( builderPath ) ;
var requiredInputScreenSymbols = new [ ]
{
"AetheriaRuntimeInputSettingsSurfaceBuilder.Build(" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeInputSettingsSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeInputSettingsCommandKind.BeginCapture" ,
"AetheriaRuntimeInputSettingsCommandKind.ToggleActionBar" ,
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_surfaceDocument)" ,
2026-06-17 08:41:06 +01:00
"UIDocument" ,
2026-06-21 08:56:07 +01:00
"ActionGameManager.RequestRuntimeInputBindingOverride" ,
"ActionGameManager.RequestRuntimeActionBarInput" ,
2026-06-17 08:41:06 +01:00
"action.ApplyBindingOverride" ,
"new InputAction(\"Aetheria Input Capture\")" ,
"HideLegacyChildren()"
} ;
var missingInputScreenSymbols = requiredInputScreenSymbols
. Where ( symbol = > ! inputScreen . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingInputScreenSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Runtime input screen no longer lowers through the Eve UI Toolkit surface contract: " +
string . Join ( ", " , missingInputScreenSymbols ) ) ;
}
var forbiddenInputScreenSymbols = new [ ]
{
"OnPointerClickAsObservable" ,
"BeginDragTrigger" ,
"EndDragTrigger" ,
"VerticalLayoutGroup" ,
"LayoutRebuilder" ,
"UILineRenderer" ,
"Observable.NextFrame()" ,
2026-06-21 08:56:07 +01:00
"DisplayLayout(_inputLayout)" ,
"EnsureSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer()" ,
"host.AddComponent<UIDocument>" ,
"AetheriaRuntimeStateCommitLog.QueuePlayerSettings" ,
"ActionGameManager.CommitRuntimeInputBindingOverride" ,
"ActionGameManager.CommitRuntimeActionBarInput" ,
"BeginCapture(request.Payload)" ,
"ToggleActionBarInput(request.Payload)" ,
"private void BeginCapture(IReadOnlyDictionary<string, string> payload)" ,
"private void ToggleActionBarInput(IReadOnlyDictionary<string, string> payload)" ,
"request.Payload"
2026-06-17 08:41:06 +01:00
} ;
var survivingLegacySymbols = forbiddenInputScreenSymbols
. Where ( symbol = > inputScreen . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( survivingLegacySymbols . Length > 0 )
{
throw new InvalidOperationException (
"Runtime input screen still carries the old drag/drop uGUI authority path: " +
string . Join ( ", " , survivingLegacySymbols ) ) ;
}
if ( ! commands . Contains ( "SurfaceId = \"aetheria.input_settings\"" , StringComparison . Ordinal ) | |
! commands . Contains ( "BeginCapture" , StringComparison . Ordinal ) | |
! commands . Contains ( "ToggleActionBar" , StringComparison . Ordinal ) | |
2026-06-21 08:56:07 +01:00
! commands . Contains ( "SetBindingOverride" , StringComparison . Ordinal ) | |
! commands . Contains ( "SetActionBarEnabled" , StringComparison . Ordinal ) | |
2026-06-17 08:41:06 +01:00
! commands . Contains ( "public static bool IsKnown" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Shared input-settings command contract is incomplete." ) ;
}
var requiredBuilderSymbols = new [ ]
{
"public sealed class AetheriaRuntimeInputSettingsSurfaceState" ,
"public sealed class AetheriaRuntimeInputBindingSurfaceState" ,
"public sealed class AetheriaRuntimeActionBarInputSurfaceState" ,
2026-06-21 08:56:07 +01:00
"public enum AetheriaRuntimeInputSettingsCommandKind" ,
"public readonly struct AetheriaRuntimeInputSettingsSurfaceCommand" ,
"public static class AetheriaRuntimeInputSettingsSurfaceCommands" ,
"public static bool TryRead(" ,
2026-06-17 08:41:06 +01:00
"AetheriaRuntimeInputSettingsCommands.BeginCapture" ,
"AetheriaRuntimeInputSettingsCommands.ToggleActionBar" ,
2026-06-21 08:56:07 +01:00
"ReadString(request, \"actionName\")" ,
"ReadInt(request, \"bindingIndex\", -1)" ,
"ReadString(request, \"inputPath\")" ,
"ReadBool(request, \"enabled\", false)" ,
"\"Low-level InputSystem edits flow through this Eve surface as typed input-setting requests.\""
2026-06-17 08:41:06 +01:00
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! builder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared input-settings Eve surface builder is incomplete: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-21 08:56:07 +01:00
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify runtime input-screen authority; ActionGameManager.cs is missing." ) ;
var requiredActionGameManagerSymbols = new [ ]
{
"AetheriaRuntimeEveCommands.TrySendInputSettingsCommand" ,
"AetheriaRuntimeInputSettingsCommands.SetBindingOverride" ,
"AetheriaRuntimeInputSettingsCommands.SetActionBarEnabled"
} ;
var missingActionGameManagerSymbols = requiredActionGameManagerSymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingActionGameManagerSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Runtime input writes no longer send explicit Eve input-setting commands: " +
string . Join ( ", " , missingActionGameManagerSymbols ) ) ;
}
var forbiddenActionGameManagerSymbols = new [ ]
{
"public static void CommitRuntimeInputBindingOverride" ,
"public static void CommitRuntimeActionBarInput" ,
"RuntimePlayerSettings.InputSettings.SetBindingOverride(actionName, bindingIndex, inputSystemPath)" ,
"RuntimePlayerSettings.InputSettings.SetActionBarInputEnabled(inputSystemPath, enabled)"
} ;
var survivingActionGameManagerSymbols = forbiddenActionGameManagerSymbols
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( survivingActionGameManagerSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Runtime input settings are still using Unity-local mutation authority instead of typed Eve requests: " +
string . Join ( ", " , survivingActionGameManagerSymbols ) ) ;
}
2026-06-17 08:41:06 +01:00
}
2026-06-17 06:10:41 +01:00
static void RequireActionGameManagerInputScreenUsesSharedFullscreenPrimitive ( string root )
{
var managerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
if ( ! File . Exists ( managerPath ) )
{
throw new InvalidOperationException ( "Cannot verify ActionGameManager input-screen delegation; ActionGameManager.cs is missing." ) ;
}
var source = File . ReadAllText ( managerPath ) ;
var requiredSymbols = new [ ]
{
"public bool CanShowInputScreenFromMenu()" ,
"public void ShowInputScreenFromMenu()" ,
"ShowFullscreenMenu(HelpScreen);" ,
"private void ShowFullscreenMenu(GameObject menu)" ,
"private void HideFullscreenMenu(GameObject menu)" ,
"Input.Global.InputScreen.performed += context => ToggleFullscreenMenu(HelpScreen);"
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"ActionGameManager no longer exposes the shared fullscreen primitive for the live input-screen owner: " +
string . Join ( ", " , missingSymbols ) ) ;
}
}
2026-06-16 21:23:05 +01:00
static void RequireSectorMapZoneDetailsUseEveSurface ( string root )
{
var sectorRendererPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "SectorRenderer.cs" ) ;
2026-06-21 08:56:07 +01:00
var zoneDetailsSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeZoneDetailsSurfaceBuilder.cs" ) ;
2026-06-16 21:23:05 +01:00
if ( ! File . Exists ( sectorRendererPath ) )
{
throw new InvalidOperationException ( "Cannot verify sector-map zone details shell; SectorRenderer.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( zoneDetailsSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify sector-map zone details shell; shared runtime zone details surface builder is missing." ) ;
}
2026-06-16 21:23:05 +01:00
var source = File . ReadAllText ( sectorRendererPath ) ;
2026-06-21 08:56:07 +01:00
var zoneDetailsSurfaceBuilder = File . ReadAllText ( zoneDetailsSurfaceBuilderPath ) ;
2026-06-16 21:23:05 +01:00
var requiredSymbols = new [ ]
{
"RenderZoneDetailsSurface(" ,
"HandleZoneDetailsSurfaceCommand(" ,
"HideZoneDetailsSurface(" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_zoneDetailsSurfaceDocument)" ,
"AetheriaRuntimeZoneDetailsSurfaceBuilder.Build(ProjectZoneDetailsSurfaceState(" ,
"ProjectZoneDetailsSurfaceState(" ,
"AetheriaRuntimeZoneDetailsSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeZoneDetailsCommandKind.Close"
2026-06-16 21:23:05 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"SectorRenderer no longer lowers zone details through an Eve surface shell: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"BuildZoneDetailsSurfaceDefinition(" ,
"ZoneDetailsSurfaceId" ,
"CloseZoneDetailsCommand" ,
"private const string ZoneDetailsSurfaceType" ,
"ResolveZoneDetailsSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer()" ,
2026-06-16 21:23:05 +01:00
"Properties.Clear();" ,
"Properties.Title.text = zone.Name;" ,
"Properties.AddProperty(\"Owner\"" ,
"Properties.AddProperty(\"Mass\"" ,
"Properties.AddProperty(\"Radius\"" ,
"Properties.AddProperty(\"Planets\"" ,
"Properties.AddProperty(\"Asteroid Belts\"" ,
"Properties.AddProperty(\"Gas Giants\"" ,
"Properties.AddProperty(\"Stars\"" ,
"Properties.AddProperty(\"Stations\"" ,
"Properties.AddProperty(\"Turrets\"" ,
2026-06-21 08:56:07 +01:00
"Properties.AddProperty(\"Ships\"" ,
"string.Equals(request.Command, AetheriaRuntimeZoneDetailsSurfaceBuilder.Close"
2026-06-16 21:23:05 +01:00
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"SectorRenderer still owns zone details through the old PropertiesPanel path: " +
string . Join ( ", " , hits ) ) ;
2026-06-16 21:29:12 +01:00
}
2026-06-21 08:56:07 +01:00
var requiredBuilderSymbols = new [ ]
{
"public static class AetheriaRuntimeZoneDetailsSurfaceBuilder" ,
"public const string SurfaceId = \"aetheria.sector_map.zone_details\"" ,
"public const string Close = \"aetheria.sector_map.zone_details.close\"" ,
"public enum AetheriaRuntimeZoneDetailsCommandKind" ,
"public readonly struct AetheriaRuntimeZoneDetailsCommand" ,
"public static class AetheriaRuntimeZoneDetailsSurfaceCommands" ,
"public static bool TryRead(" ,
"AetheriaRuntimeZoneDetailsSurfaceState" ,
"public static AetheriaRuntimeSurfaceDocument Build(" ,
"providerKind: \"sector.map\"" ,
"Factions Present" ,
"Has not been visited." ,
"Asteroid Belts"
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! zoneDetailsSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared runtime zone details surface builder no longer owns the sector-map zone shell contract: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-16 21:29:12 +01:00
}
static void RequireRuntimeMenuTabsUseEveSurface ( string root )
{
var menuPanelPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "MenuPanel.cs" ) ;
2026-06-21 08:56:07 +01:00
var menuTabsSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeMenuTabsSurfaceBuilder.cs" ) ;
2026-06-16 21:29:12 +01:00
if ( ! File . Exists ( menuPanelPath ) )
{
throw new InvalidOperationException ( "Cannot verify runtime menu tab shell; MenuPanel.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( menuTabsSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify runtime menu tab shell; AetheriaRuntimeMenuTabsSurfaceBuilder.cs is missing." ) ;
}
2026-06-16 21:29:12 +01:00
2026-06-17 00:43:42 +01:00
var legacyTabButtonPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "MenuTabButton.cs" ) ;
if ( File . Exists ( legacyTabButtonPath ) )
{
throw new InvalidOperationException ( "MenuPanel tab metadata still has a surviving MenuTabButton component shell." ) ;
}
2026-06-16 21:29:12 +01:00
var source = File . ReadAllText ( menuPanelPath ) ;
2026-06-21 08:56:07 +01:00
var surfaceHostPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.eve-runtime" , "Runtime" , "AetheriaEveUnitySurfaceHost.cs" ) ;
if ( ! File . Exists ( surfaceHostPath ) )
{
throw new InvalidOperationException ( "Cannot verify runtime menu tab shell; AetheriaEveUnitySurfaceHost.cs is missing." ) ;
}
var menuTabsSurfaceBuilder = File . ReadAllText ( menuTabsSurfaceBuilderPath ) ;
var surfaceHost = File . ReadAllText ( surfaceHostPath ) ;
2026-06-16 21:29:12 +01:00
var requiredSymbols = new [ ]
{
2026-06-17 00:43:42 +01:00
"MenuTabBinding" ,
"TabBindings = Array.Empty<MenuTabBinding>();" ,
2026-06-16 21:29:12 +01:00
"RenderTabSurface(" ,
"HandleTabSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_tabSurfaceDocument)" ,
"AetheriaRuntimeMenuTabsSurfaceBuilder.Build(ProjectTabSurfaceState())" ,
"ProjectTabSurfaceState(" ,
2026-06-16 21:29:12 +01:00
"ResolveVisibleTabs(" ,
2026-06-17 00:43:42 +01:00
"GetTabLabel(" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeMenuTabsSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeMenuTabCommandKind.SelectTab" ,
"string.Equals(command.TabKey, TabKey(tab), StringComparison.Ordinal)" ,
2026-06-16 21:29:12 +01:00
"TabButtons.gameObject.SetActive(false)"
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"MenuPanel no longer lowers the runtime tab shell through Eve surfaces: " +
string . Join ( ", " , missingSymbols ) ) ;
}
2026-06-21 08:56:07 +01:00
var requiredHostSymbols = new [ ]
{
"public static class AetheriaEveUnitySurfaceHost" ,
"public static UIDocument Render(" ,
"new EveUiToolkitSurfaceLowerer()" ,
"shell.Add(lowerer.Lower(surface, commandHandler))"
} ;
var missingHostSymbols = requiredHostSymbols
. Where ( symbol = > ! surfaceHost . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingHostSymbols . Length > 0 )
{
throw new InvalidOperationException (
"AetheriaEveUnitySurfaceHost no longer owns the shared Unity Eve lowering path: " +
string . Join ( ", " , missingHostSymbols ) ) ;
}
2026-06-16 21:29:12 +01:00
var forbiddenSymbols = new [ ]
{
2026-06-17 00:43:42 +01:00
"MenuTabButton" ,
2026-06-16 21:29:12 +01:00
"tabButton.Button.onClick.AddListener(" ,
"tabButton.gameObject.SetActive(!tabButton.RequireDock || GameManager.DockedEntity != null);" ,
2026-06-21 08:56:07 +01:00
"_tabs[MenuTab.Local].gameObject.SetActive(" ,
"BuildTabSurfaceDefinition(" ,
"private static EveSurfaceComponent Button(" ,
"private static EveSurfaceComponent Text(" ,
"private static EveSurfaceComponent Node(" ,
"ResolveTabSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer()" ,
"string.Equals(request.Command, AetheriaRuntimeMenuTabsSurfaceBuilder.CommandFor("
2026-06-16 21:29:12 +01:00
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"MenuPanel still owns tab-shell behavior through the old MenuTabButton path: " +
string . Join ( ", " , hits ) ) ;
2026-06-16 21:35:58 +01:00
}
2026-06-21 08:56:07 +01:00
var requiredBuilderSymbols = new [ ]
{
"public static class AetheriaRuntimeMenuTabsSurfaceBuilder" ,
"public const string SurfaceId = \"aetheria.runtime_menu.tabs\"" ,
"CommandFor(string tabKey)" ,
"public enum AetheriaRuntimeMenuTabCommandKind" ,
"public readonly struct AetheriaRuntimeMenuTabCommand" ,
"public static class AetheriaRuntimeMenuTabsSurfaceCommands" ,
"public static bool TryRead(" ,
"public static AetheriaRuntimeSurfaceDocument Build(" ,
"AetheriaRuntimeMenuTabsSurfaceState" ,
"AetheriaRuntimeMenuTabSurfaceEntry"
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! menuTabsSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared runtime menu tab surface builder no longer owns the tab shell contract: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-16 21:35:58 +01:00
}
static void RequireInventoryShipSettingsUseEveSurface ( string root )
{
var inventoryMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "InventoryMenu.cs" ) ;
2026-06-21 08:56:07 +01:00
var shipSettingsSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeShipSettingsSurfaceBuilder.cs" ) ;
2026-06-16 21:35:58 +01:00
if ( ! File . Exists ( inventoryMenuPath ) )
{
throw new InvalidOperationException ( "Cannot verify inventory ship-settings shell; InventoryMenu.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( shipSettingsSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify inventory ship-settings shell; shared runtime ship settings surface builder is missing." ) ;
}
2026-06-16 21:35:58 +01:00
var source = File . ReadAllText ( inventoryMenuPath ) ;
2026-06-21 08:56:07 +01:00
var shipSettingsSurfaceBuilder = File . ReadAllText ( shipSettingsSurfaceBuilderPath ) ;
2026-06-16 21:35:58 +01:00
var requiredSymbols = new [ ]
{
"RenderCurrentShipSettingsSurface(" ,
"HandleCurrentShipSettingsSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_shipSettingsSurfaceDocument)" ,
"AetheriaRuntimeShipSettingsSurfaceBuilder.Build(ProjectCurrentShipSettingsSurfaceState(" ,
"ProjectCurrentShipSettingsSurfaceState(" ,
"AetheriaRuntimeShipSettingsSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeShipSettingsCommandKind.DecrementShutdownThreshold" ,
"AetheriaRuntimeShipSettingsCommandKind.IncrementShutdownThreshold" ,
"AetheriaRuntimeShipSettingsCommandKind.ResetShutdownThreshold" ,
"AetheriaRuntimeShipSettingsCommandKind.Close"
2026-06-16 21:35:58 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"InventoryMenu no longer lowers current ship settings through an Eve surface shell: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"BuildCurrentShipSettingsSurfaceDefinition(" ,
"ShipSettingsSurfaceId" ,
"DecrementShutdownThresholdCommand" ,
"IncrementShutdownThresholdCommand" ,
"ResetShutdownThresholdCommand" ,
"CloseShipSettingsCommand" ,
"private const string ShipSettingsSurfaceType" ,
"ResolveShipSettingsSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer()" ,
"host.AddComponent<UIDocument>" ,
2026-06-16 21:35:58 +01:00
"PropertiesPanel.AddField(\"Shutdown Threshold\"" ,
"() => GameManager.CurrentEntity.Settings.ShutdownPerformance" ,
2026-06-21 08:56:07 +01:00
"f => GameManager.RequestEntityShutdownPerformance(GameManager.CurrentEntity, f)" ,
"switch (request.Command)"
2026-06-16 21:35:58 +01:00
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"InventoryMenu still owns ship settings through the old PropertiesPanel field path: " +
string . Join ( ", " , hits ) ) ;
2026-06-16 21:45:56 +01:00
}
2026-06-21 08:56:07 +01:00
var requiredBuilderSymbols = new [ ]
{
"public static class AetheriaRuntimeShipSettingsSurfaceBuilder" ,
"public const string SurfaceId = \"aetheria.inventory.current_ship_settings\"" ,
"public const string DecrementShutdownThreshold" ,
"public const string IncrementShutdownThreshold" ,
"public const string ResetShutdownThreshold" ,
"public const string Close" ,
"public enum AetheriaRuntimeShipSettingsCommandKind" ,
"public readonly struct AetheriaRuntimeShipSettingsCommand" ,
"public static class AetheriaRuntimeShipSettingsSurfaceCommands" ,
"public static bool TryRead(" ,
"AetheriaRuntimeShipSettingsSurfaceState" ,
"public static AetheriaRuntimeSurfaceDocument Build("
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! shipSettingsSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared runtime ship settings surface builder no longer owns the ship-settings shell contract: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-16 21:45:56 +01:00
}
2026-06-16 23:44:14 +01:00
static void RequireInventoryCargoItemDetailsUseEveSurface ( string root )
{
var inventoryMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "InventoryMenu.cs" ) ;
2026-06-21 08:56:07 +01:00
var cargoItemSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeCargoItemDetailsSurfaceBuilder.cs" ) ;
2026-06-16 23:44:14 +01:00
if ( ! File . Exists ( inventoryMenuPath ) )
{
throw new InvalidOperationException ( "Cannot verify inventory cargo-item shell; InventoryMenu.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( cargoItemSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify inventory cargo-item shell; AetheriaRuntimeCargoItemDetailsSurfaceBuilder.cs is missing." ) ;
}
2026-06-16 23:44:14 +01:00
var source = File . ReadAllText ( inventoryMenuPath ) ;
2026-06-21 08:56:07 +01:00
var cargoItemSurfaceBuilder = File . ReadAllText ( cargoItemSurfaceBuilderPath ) ;
2026-06-16 23:44:14 +01:00
var requiredSymbols = new [ ]
{
"RenderCargoItemDetailsSurface(" ,
"HandleCargoItemDetailsSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_cargoItemDetailsSurfaceDocument)" ,
"AetheriaRuntimeCargoItemDetailsSurfaceBuilder.Build(ProjectCargoItemDetailsSurfaceState(" ,
"AetheriaRuntimeCargoItemDetailsSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeCargoItemDetailsCommandKind.Close" ,
"ProjectCargoItemDetailsSurfaceState(" ,
"ProjectCargoItemBehaviorSections(" ,
"ProjectCargoItemBehaviorMetric("
2026-06-16 23:44:14 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"InventoryMenu no longer lowers cargo-item inspection through an Eve surface shell: " +
string . Join ( ", " , missingSymbols ) ) ;
}
if ( ! source . Contains ( "RenderCargoItemDetailsSurface(item);" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException ( "InventoryMenu cargo click path no longer routes item inspection through the Eve surface." ) ;
}
2026-06-21 08:56:07 +01:00
var forbiddenSymbols = new [ ]
{
"BuildCargoItemDetailsSurfaceDefinition(" ,
"BuildCargoItemBehaviorCards(" ,
"BuildCargoItemBehaviorMetric(" ,
"CargoItemDetailsSurfaceId" ,
"CloseCargoItemDetailsCommand" ,
"ResolveCargoItemDetailsSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer()" ,
"host.AddComponent<UIDocument>" ,
"string.Equals(request.Command, AetheriaRuntimeCargoItemDetailsSurfaceBuilder.Close"
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"InventoryMenu still owns cargo-item inspection surface construction instead of projecting shared runtime state: " +
string . Join ( ", " , hits ) ) ;
}
var requiredBuilderSymbols = new [ ]
{
"public static class AetheriaRuntimeCargoItemDetailsSurfaceBuilder" ,
"public const string SurfaceId = \"aetheria.inventory.cargo_item_details\"" ,
"public const string Close = \"aetheria.inventory.cargo_item_details.close\"" ,
"public enum AetheriaRuntimeCargoItemDetailsCommandKind" ,
"public readonly struct AetheriaRuntimeCargoItemDetailsCommand" ,
"public static class AetheriaRuntimeCargoItemDetailsSurfaceCommands" ,
"public static bool TryRead(" ,
"AetheriaRuntimeCargoItemDetailsSurfaceState" ,
"AetheriaRuntimeCargoItemSection" ,
"AetheriaRuntimeCargoItemMetric" ,
"public static AetheriaRuntimeSurfaceDocument Build("
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! cargoItemSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared cargo-item detail surface builder no longer owns the cargo inspection contract: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-17 01:50:15 +01:00
}
static void RequireInventoryEquippedItemDetailsUseEveSurface ( string root )
{
var inventoryMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "InventoryMenu.cs" ) ;
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
2026-06-21 08:56:07 +01:00
var equippedItemSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeEquippedItemDetailsSurfaceBuilder.cs" ) ;
var stateProjectPath = Path . Combine ( root , "Aetheria.State" , "Aetheria.State.csproj" ) ;
var unityProjectPath = Path . Combine ( root , "GameCult.Aetheria.State.Unity.csproj" ) ;
var requiredFiles = new [ ]
2026-06-17 01:50:15 +01:00
{
2026-06-21 08:56:07 +01:00
inventoryMenuPath ,
actionGameManagerPath ,
equippedItemSurfaceBuilderPath ,
stateProjectPath ,
unityProjectPath
} ;
var missingFiles = requiredFiles
. Where ( path = > ! File . Exists ( path ) )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( missingFiles . Length > 0 )
2026-06-17 01:50:15 +01:00
{
2026-06-21 08:56:07 +01:00
throw new InvalidOperationException (
"Cannot verify inventory equipped-item shell; required files are missing: " +
string . Join ( ", " , missingFiles ) ) ;
2026-06-17 01:50:15 +01:00
}
var source = File . ReadAllText ( inventoryMenuPath ) ;
var requiredSymbols = new [ ]
{
"RenderEquippedItemDetailsSurface(" ,
"HandleEquippedItemDetailsSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_equippedItemDetailsSurfaceDocument)" ,
"AetheriaRuntimeEquippedItemDetailsSurfaceBuilder.Build(ProjectEquippedItemDetailsSurfaceState(" ,
"ProjectEquippedItemDetailsSurfaceState(" ,
"ProjectEquippedItemTemperatureControls(" ,
"ProjectEquippedItemBehaviorSections(" ,
"ProjectEquippedItemWeaponGroupControls(" ,
"ProjectEquippedItemActionBarSlots(" ,
"AetheriaRuntimeEquippedItemDetailsSurfaceCommands.TryRead(request, out var command)" ,
"switch (command.Kind)" ,
"AetheriaRuntimeEquippedItemDetailsCommandKind.Close" ,
"AetheriaRuntimeEquippedItemDetailsCommandKind.ToggleOverrideShutdown" ,
"AetheriaRuntimeEquippedItemDetailsCommandKind.SetTargetTemperature" ,
"AetheriaRuntimeEquippedItemDetailsCommandKind.ToggleWeaponGroup" ,
"AetheriaRuntimeEquippedItemDetailsCommandKind.BindWeaponGroup" ,
"AetheriaRuntimeEquippedItemDetailsCommandKind.ClearActionBarBinding"
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"InventoryMenu no longer lowers equipped-item inspection through an Eve surface shell: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
"EquippedItemDetailsSurfaceId" ,
2026-06-17 01:50:15 +01:00
"BuildEquippedItemDetailsSurfaceDefinition(" ,
"BuildEquippedItemControlCard(" ,
"BuildEquippedItemWeaponGroupCard(" ,
"BuildEquippedItemActionBarCards(" ,
"BuildItemBehaviorCards(" ,
2026-06-21 08:56:07 +01:00
"BuildItemBehaviorMetric(" ,
2026-06-17 01:50:15 +01:00
"CloseEquippedItemDetailsCommand" ,
"ToggleEquippedItemOverrideShutdownCommand" ,
"SetEquippedItemTargetTemperatureCommand" ,
"ToggleEquippedItemWeaponGroupCommand" ,
"BindEquippedItemWeaponGroupCommand" ,
"ClearEquippedItemActionBarBindingCommand" ,
2026-06-21 08:56:07 +01:00
"ResolveEquippedItemDetailsSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer()" ,
"host.AddComponent<UIDocument>" ,
"private static EveSurfaceComponent Card(" ,
"private static EveSurfaceComponent Metric(" ,
"private static EveSurfaceComponent Text(" ,
"private static EveSurfaceComponent Button(" ,
"private static EveSurfaceComponent CommandButton(" ,
"private static EveSurfaceComponent TextField(" ,
"private static EveSurfaceComponent ButtonRow(" ,
"private static EveSurfaceComponent Node(" ,
"TryReadPayloadInt(" ,
"TryReadPayloadFloat(" ,
"request.Payload"
} ;
var forbiddenHits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-17 01:50:15 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( forbiddenHits . Length > 0 )
2026-06-16 23:44:14 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"InventoryMenu still owns the equipped-item surface contract locally: " +
string . Join ( ", " , forbiddenHits ) ) ;
2026-06-16 23:44:14 +01:00
}
2026-06-17 01:50:15 +01:00
if ( ! source . Contains ( "RenderEquippedItemDetailsSurface(item);" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException ( "InventoryMenu equipped-item click path no longer routes inspection through the Eve surface." ) ;
}
2026-06-21 08:56:07 +01:00
var legacyShellSymbols = new [ ]
2026-06-17 01:50:15 +01:00
{
"public PropertiesPanel PropertiesPanel;" ,
"PropertiesPanel.GameManager = GameManager;" ,
"PropertiesPanel.gameObject.SetActive(true);" ,
"PropertiesPanel.Inspect(item);"
} ;
2026-06-21 08:56:07 +01:00
var hits = legacyShellSymbols
2026-06-17 01:50:15 +01:00
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"InventoryMenu still owns equipped-item inspection through the old PropertiesPanel shell: " +
string . Join ( ", " , hits ) ) ;
}
2026-06-21 08:56:07 +01:00
var equippedItemSurfaceBuilder = File . ReadAllText ( equippedItemSurfaceBuilderPath ) ;
var stateProject = File . ReadAllText ( stateProjectPath ) ;
var unityProject = File . ReadAllText ( unityProjectPath ) ;
var requiredBuilderSymbols = new [ ]
{
"public sealed class AetheriaRuntimeEquippedItemDetailsSurfaceState" ,
"public sealed class AetheriaRuntimeEquippedItemSection" ,
"public sealed class AetheriaRuntimeEquippedItemMetric" ,
"public sealed class AetheriaRuntimeEquippedItemControl" ,
"public sealed class AetheriaRuntimeEquippedItemTemperatureControl" ,
"public sealed class AetheriaRuntimeEquippedItemActionBarSlot" ,
"public enum AetheriaRuntimeEquippedItemDetailsCommandKind" ,
"public readonly struct AetheriaRuntimeEquippedItemDetailsCommand" ,
"public static class AetheriaRuntimeEquippedItemDetailsSurfaceCommands" ,
"public static bool TryRead(" ,
"public const string SurfaceId = \"aetheria.inventory.equipped_item_details\"" ,
"public const string Close = \"aetheria.inventory.equipped_item_details.close\"" ,
"public const string ToggleOverrideShutdown = \"aetheria.inventory.equipped_item_details.override_shutdown.toggle\"" ,
"public const string SetTargetTemperature = \"aetheria.inventory.equipped_item_details.target_temperature.set\"" ,
"public const string ToggleWeaponGroup = \"aetheria.inventory.equipped_item_details.weapon_group.toggle\"" ,
"public const string BindWeaponGroup = \"aetheria.inventory.equipped_item_details.weapon_group.bind\"" ,
"public const string ClearActionBarBinding = \"aetheria.inventory.equipped_item_details.action_bar.clear\"" ,
"public static AetheriaRuntimeSurfaceDocument Build(" ,
"new AetheriaRuntimeSurfaceCommandTemplate(Close" ,
"new AetheriaRuntimeSurfaceCommandTemplate(ToggleOverrideShutdown" ,
"new AetheriaRuntimeSurfaceCommandTemplate(SetTargetTemperature" ,
"new AetheriaRuntimeSurfaceCommandTemplate(ToggleWeaponGroup" ,
"new AetheriaRuntimeSurfaceCommandTemplate(BindWeaponGroup" ,
"new AetheriaRuntimeSurfaceCommandTemplate(ClearActionBarBinding" ,
"ReadInt(request, \"behaviorIndex\", -1)" ,
"ReadFloat(request, \"value\", 0f)" ,
"ReadInt(request, \"group\", -1)" ,
"ReadInt(request, \"slot\", -1)" ,
"\"control.text\"" ,
"\"inventory.menu\""
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! equippedItemSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared equipped-item detail surface builder no longer owns the equipped-item inspection contract: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
if ( ! stateProject . Contains ( "AetheriaRuntimeEquippedItemDetailsSurfaceBuilder.cs" , StringComparison . Ordinal ) | |
! unityProject . Contains ( "AetheriaRuntimeEquippedItemDetailsSurfaceBuilder.cs" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Shared equipped-item surface builder is not included in both runtime project surfaces." ) ;
}
2026-06-17 01:50:15 +01:00
var actionGameManagerSource = File . ReadAllText ( actionGameManagerPath ) ;
var requiredActionBarSymbols = new [ ]
{
"GetActionBarSlotCount(" ,
"GetActionBarSlotLabel(" ,
"GetActionBarBindingLabel(" ,
2026-06-21 08:56:07 +01:00
"RequestWeaponGroupActionBarBinding(" ,
"RequestClearActionBarBinding(" ,
"TryRequestDaemonActionBarBinding(" ,
"TryRequestDaemonActionBarBindingClear("
2026-06-17 01:50:15 +01:00
} ;
var missingActionBarSymbols = requiredActionBarSymbols
. Where ( symbol = > ! actionGameManagerSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingActionBarSymbols . Length > 0 )
2026-06-16 23:44:14 +01:00
{
throw new InvalidOperationException (
2026-06-17 01:50:15 +01:00
"ActionGameManager no longer exposes the equipped-item action-bar binding authority surface: " +
string . Join ( ", " , missingActionBarSymbols ) ) ;
2026-06-16 23:44:14 +01:00
}
}
2026-06-16 21:45:56 +01:00
static void RequireTradeCargoSelectorUseEveSurface ( string root )
{
var tradeMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "TradeMenu.cs" ) ;
2026-06-21 08:56:07 +01:00
var tradeCargoSelectorSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeTradeCargoSelectorSurfaceBuilder.cs" ) ;
2026-06-16 21:45:56 +01:00
if ( ! File . Exists ( tradeMenuPath ) )
{
throw new InvalidOperationException ( "Cannot verify trade cargo-selector shell; TradeMenu.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( tradeCargoSelectorSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify trade cargo-selector shell; shared runtime trade cargo selector surface builder is missing." ) ;
}
2026-06-16 21:45:56 +01:00
var source = File . ReadAllText ( tradeMenuPath ) ;
2026-06-21 08:56:07 +01:00
var tradeCargoSelectorSurfaceBuilder = File . ReadAllText ( tradeCargoSelectorSurfaceBuilderPath ) ;
2026-06-16 21:45:56 +01:00
var requiredSymbols = new [ ]
{
"RenderCargoSelectorSurface(" ,
"BuildCargoSelectionCommands(" ,
"HandleCargoSelectorSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_cargoSelectorSurfaceDocument)" ,
"AetheriaRuntimeTradeCargoSelectorSurfaceBuilder.Build(ProjectTradeCargoSelectorSurfaceState(" ,
"ProjectTradeCargoSelectorSurfaceState(" ,
"AetheriaRuntimeTradeCargoSelectorSurfaceBuilder.DockingBay" ,
"AetheriaRuntimeTradeCargoSelectorSurfaceBuilder.ShipBayCommand(" ,
"AetheriaRuntimeTradeCargoSelectorSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeTradeCargoSelectorCommandKind.Close" ,
"AetheriaRuntimeTradeCargoSelectorCommandKind.Select"
2026-06-16 21:45:56 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"TradeMenu no longer lowers the cargo selector through an Eve surface shell: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"private const string CargoSelectorSurfaceType" ,
"private const string CargoSelectorSurfaceId" ,
"CloseCargoSelectorCommand" ,
"BuildCargoSelectorSurfaceDefinition(" ,
"ResolveCargoSelectorSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer()" ,
"host.AddComponent<UIDocument>" ,
2026-06-16 21:45:56 +01:00
"ContextMenu.AddOption(\"Docking Bay\"" ,
2026-06-21 08:56:07 +01:00
"ContextMenu.AddOption($\"{ship.Name} Bay {bay.index+1}\"" ,
"string.Equals(request.Command, AetheriaRuntimeTradeCargoSelectorSurfaceBuilder.Close" ,
"_cargoSelectionCommands.TryGetValue(request.Command"
2026-06-16 21:45:56 +01:00
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"TradeMenu still owns cargo selection through the old context-menu option path: " +
string . Join ( ", " , hits ) ) ;
2026-06-16 22:08:16 +01:00
}
2026-06-21 08:56:07 +01:00
var requiredBuilderSymbols = new [ ]
{
"public static class AetheriaRuntimeTradeCargoSelectorSurfaceBuilder" ,
"public const string SurfaceId = \"aetheria.trade.target_cargo_selector\"" ,
"public const string Close = \"aetheria.trade.target_cargo_selector.close\"" ,
"public const string DockingBay = \"aetheria.trade.target_cargo_selector.docking_bay\"" ,
"public enum AetheriaRuntimeTradeCargoSelectorCommandKind" ,
"public readonly struct AetheriaRuntimeTradeCargoSelectorCommand" ,
"public static class AetheriaRuntimeTradeCargoSelectorSurfaceCommands" ,
"public static bool TryRead(" ,
"AetheriaRuntimeTradeCargoSelectorSurfaceState" ,
"AetheriaRuntimeTradeCargoTargetOption" ,
"public static string ShipBayCommand(" ,
"public static AetheriaRuntimeSurfaceDocument Build(" ,
"providerKind: \"trade.menu\"" ,
"Unity projects available cargo targets; the shared runtime surface owns the cargo selector contract."
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! tradeCargoSelectorSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared runtime trade cargo selector surface builder no longer owns the trade cargo-selector shell contract: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-16 22:08:16 +01:00
}
2026-06-16 22:24:48 +01:00
static void RequireTradeFilterAndRowActionsUseEveSurface ( string root )
{
var tradeMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "TradeMenu.cs" ) ;
2026-06-21 08:56:07 +01:00
var tradeInteractionSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeTradeInteractionSurfaceBuilder.cs" ) ;
2026-06-16 22:24:48 +01:00
if ( ! File . Exists ( tradeMenuPath ) )
{
throw new InvalidOperationException ( "Cannot verify trade filter and row-action shells; TradeMenu.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( tradeInteractionSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify trade filter and row-action shells; shared runtime trade interaction surface builder is missing." ) ;
}
2026-06-16 22:24:48 +01:00
var source = File . ReadAllText ( tradeMenuPath ) ;
2026-06-21 08:56:07 +01:00
var tradeInteractionSurfaceBuilder = File . ReadAllText ( tradeInteractionSurfaceBuilderPath ) ;
2026-06-16 22:24:48 +01:00
var requiredSymbols = new [ ]
{
"RenderFilterSurface(" ,
"BuildFilterSurfaceCommands(" ,
"HandleFilterSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_filterSurfaceDocument)" ,
"AetheriaRuntimeTradeInteractionSurfaceBuilder.BuildFilter(ProjectTradeFilterSurfaceState(" ,
"ProjectTradeFilterSurfaceState(" ,
2026-06-16 22:24:48 +01:00
"RenderRowActionSurface(" ,
"BuildRowActionSurfaceCommands(" ,
"HandleRowActionSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.Hide(_rowActionSurfaceDocument)" ,
"AetheriaRuntimeTradeInteractionSurfaceBuilder.BuildRowActions(ProjectTradeRowActionSurfaceState(" ,
"ProjectTradeRowActionSurfaceState(" ,
2026-06-16 22:24:48 +01:00
"ShowBuyQuantityDialog(" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeTradeInteractionSurfaceCommands.TryReadFilter(request, out var command)" ,
"AetheriaRuntimeTradeInteractionSurfaceCommands.TryReadRowAction(request, out var command)" ,
"AetheriaRuntimeTradeInteractionCommandKind.Close" ,
"AetheriaRuntimeTradeInteractionCommandKind.Select"
2026-06-16 22:24:48 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"TradeMenu no longer lowers filter and row-action shells through Eve surfaces: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"private const string FilterSurfaceId" ,
"private const string RowActionSurfaceId" ,
"CloseFilterSurfaceCommand" ,
"CloseRowActionSurfaceCommand" ,
"BuildFilterSurfaceDefinition(" ,
"BuildRowActionSurfaceDefinition(" ,
"ResolveFilterSurfaceDocument(" ,
"ResolveRowActionSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer()" ,
"host.AddComponent<UIDocument>" ,
2026-06-16 22:24:48 +01:00
"public ContextMenu ContextMenu;" ,
"ContextMenu.Clear();" ,
"ContextMenu.AddDropdown(" ,
"ContextMenu.AddOption(" ,
2026-06-21 08:56:07 +01:00
"ContextMenu.Show();" ,
"string.Equals(request.Command, AetheriaRuntimeTradeInteractionSurfaceBuilder.CloseFilter" ,
"string.Equals(request.Command, AetheriaRuntimeTradeInteractionSurfaceBuilder.CloseRowAction" ,
"_filterSurfaceCommands.TryGetValue(request.Command" ,
"_rowActionSurfaceCommands.TryGetValue(request.Command"
2026-06-16 22:24:48 +01:00
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"TradeMenu still owns filter or row-action behavior through the old context-menu path: " +
string . Join ( ", " , hits ) ) ;
}
2026-06-21 08:56:07 +01:00
var requiredBuilderSymbols = new [ ]
{
"public static class AetheriaRuntimeTradeInteractionSurfaceBuilder" ,
"public const string FilterSurfaceId = \"aetheria.trade.filter_selector\"" ,
"public const string CloseFilter = \"aetheria.trade.filter_selector.close\"" ,
"public const string RowActionSurfaceId = \"aetheria.trade.row_actions\"" ,
"public const string CloseRowAction = \"aetheria.trade.row_actions.close\"" ,
"public enum AetheriaRuntimeTradeInteractionCommandKind" ,
"public readonly struct AetheriaRuntimeTradeInteractionCommand" ,
"public static class AetheriaRuntimeTradeInteractionSurfaceCommands" ,
"public static bool TryReadFilter(" ,
"public static bool TryReadRowAction(" ,
"AetheriaRuntimeTradeFilterSurfaceState" ,
"AetheriaRuntimeTradeRowActionSurfaceState" ,
"AetheriaRuntimeTradeSurfaceGroup" ,
"AetheriaRuntimeTradeSurfaceOption" ,
"public static string HardpointFilterCommand(" ,
"public static string RowActionCommand(" ,
"public static AetheriaRuntimeSurfaceDocument BuildFilter(" ,
"public static AetheriaRuntimeSurfaceDocument BuildRowActions(" ,
"providerKind: \"trade.menu\""
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! tradeInteractionSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared runtime trade interaction surface builder no longer owns the trade filter/row-action shell contracts: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-16 22:24:48 +01:00
}
2026-06-16 23:09:42 +01:00
static void RequireTradeItemDetailsUseEveSurface ( string root )
{
var tradeMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "TradeMenu.cs" ) ;
2026-06-21 08:56:07 +01:00
var tradeItemDetailsSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeTradeItemDetailsSurfaceBuilder.cs" ) ;
2026-06-16 23:09:42 +01:00
if ( ! File . Exists ( tradeMenuPath ) )
{
throw new InvalidOperationException ( "Cannot verify trade item-details shell; TradeMenu.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( tradeItemDetailsSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify trade item-details shell; shared runtime trade item details surface builder is missing." ) ;
}
2026-06-16 23:09:42 +01:00
var source = File . ReadAllText ( tradeMenuPath ) ;
2026-06-21 08:56:07 +01:00
var tradeItemDetailsSurfaceBuilder = File . ReadAllText ( tradeItemDetailsSurfaceBuilderPath ) ;
2026-06-16 23:09:42 +01:00
var requiredSymbols = new [ ]
{
"RenderTradeItemDetailsSurface(" ,
"HandleTradeItemDetailsSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_tradeItemSurfaceDocument)" ,
"AetheriaRuntimeTradeItemDetailsSurfaceBuilder.Build(ProjectTradeItemDetailsSurfaceState(" ,
"ProjectTradeItemDetailsSurfaceState(" ,
"ProjectTradeItemBehaviorSections(" ,
"ProjectTradeItemBehaviorMetric(" ,
"AetheriaRuntimeTradeItemDetailsSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeTradeItemDetailsCommandKind.Close"
2026-06-16 23:09:42 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"TradeMenu no longer lowers typed item inspection through an Eve surface: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"TradeItemSurfaceId" ,
"CloseTradeItemDetailsCommand" ,
"BuildTradeItemDetailsSurfaceDefinition(" ,
"BuildTradeItemBehaviorCards(" ,
"BuildTradeItemBehaviorMetric(" ,
"ResolveTradeItemDetailsSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer()" ,
"host.AddComponent<UIDocument>" ,
2026-06-16 23:09:42 +01:00
"public PropertiesPanel Properties;" ,
2026-06-21 08:56:07 +01:00
"OnClick = () => Properties.Inspect(i.TypedItem)" ,
"string.Equals(request.Command, AetheriaRuntimeTradeItemDetailsSurfaceBuilder.Close"
2026-06-16 23:09:42 +01:00
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"TradeMenu still owns typed item inspection through the old PropertiesPanel path: " +
string . Join ( ", " , hits ) ) ;
}
2026-06-21 08:56:07 +01:00
var requiredBuilderSymbols = new [ ]
{
"public static class AetheriaRuntimeTradeItemDetailsSurfaceBuilder" ,
"public const string SurfaceId = \"aetheria.trade.item_details\"" ,
"public const string Close = \"aetheria.trade.item_details.close\"" ,
"public enum AetheriaRuntimeTradeItemDetailsCommandKind" ,
"public readonly struct AetheriaRuntimeTradeItemDetailsCommand" ,
"public static class AetheriaRuntimeTradeItemDetailsSurfaceCommands" ,
"public static bool TryRead(" ,
"AetheriaRuntimeTradeItemDetailsSurfaceState" ,
"AetheriaRuntimeTradeItemSection" ,
"AetheriaRuntimeTradeItemMetric" ,
"public static AetheriaRuntimeSurfaceDocument Build(" ,
"providerKind: \"trade.menu\"" ,
"Unity supplies the selected market row; the shared runtime surface owns trade item inspection layout."
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! tradeItemDetailsSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared runtime trade item details surface builder no longer owns the trade item-details shell contract: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-16 23:09:42 +01:00
}
2026-06-16 22:08:16 +01:00
static void RequireInventoryDropdownUseEveSurface ( string root )
{
var inventoryPanelPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "InventoryPanel.cs" ) ;
2026-06-21 08:56:07 +01:00
var inventoryDropdownSurfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeInventoryDropdownSurfaceBuilder.cs" ) ;
2026-06-16 22:08:16 +01:00
if ( ! File . Exists ( inventoryPanelPath ) )
{
throw new InvalidOperationException ( "Cannot verify inventory dropdown shell; InventoryPanel.cs is missing." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( inventoryDropdownSurfaceBuilderPath ) )
{
throw new InvalidOperationException ( "Cannot verify inventory dropdown shell; shared runtime inventory dropdown surface builder is missing." ) ;
}
2026-06-16 22:08:16 +01:00
var source = File . ReadAllText ( inventoryPanelPath ) ;
2026-06-21 08:56:07 +01:00
var inventoryDropdownSurfaceBuilder = File . ReadAllText ( inventoryDropdownSurfaceBuilderPath ) ;
2026-06-16 22:08:16 +01:00
var requiredSymbols = new [ ]
{
"RenderDropdownSurface(" ,
"BuildDropdownCommands(" ,
"HandleDropdownSurfaceCommand(" ,
2026-06-21 08:56:07 +01:00
"AetheriaEveUnitySurfaceHost.RenderRuntime(" ,
"AetheriaEveUnitySurfaceHost.Hide(_dropdownSurfaceDocument)" ,
"AetheriaRuntimeInventoryDropdownSurfaceBuilder.Build(ProjectDropdownSurfaceState(" ,
"ProjectDropdownSurfaceState(" ,
"AetheriaRuntimeInventoryDropdownSurfaceBuilder.SaveLoadout" ,
"AetheriaRuntimeInventoryDropdownSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeInventoryDropdownCommandKind.Close" ,
"AetheriaRuntimeInventoryDropdownCommandKind.Select" ,
"AetheriaRuntimeInventoryDropdownSurfaceBuilder.EntityEquipmentCommand(" ,
"AetheriaRuntimeInventoryDropdownSurfaceBuilder.LoadoutCommand("
2026-06-16 22:08:16 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"InventoryPanel no longer lowers the dropdown shell through an Eve surface: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"DropdownSurfaceId" ,
"CloseDropdownSurfaceCommand" ,
"SaveLoadoutCommand" ,
"BuildDropdownSurfaceDefinition(" ,
"private const string DropdownSurfaceType" ,
"ResolveDropdownSurfaceDocument(" ,
"new EveUiToolkitSurfaceLowerer()" ,
"host.AddComponent<UIE.UIDocument>" ,
2026-06-16 22:08:16 +01:00
"ContextMenu.AddDropdown(entity.Name" ,
"ContextMenu.AddOption(GameManager.DockingBay.Name" ,
"ContextMenu.AddOption(\"Save Loadout\"" ,
"ContextMenu.AddDropdown(\"Restore Loadout\"" ,
2026-06-21 08:56:07 +01:00
"ContextMenu.Show();" ,
"string.Equals(request.Command, AetheriaRuntimeInventoryDropdownSurfaceBuilder.Close" ,
"_dropdownCommands.TryGetValue(request.Command"
2026-06-16 22:08:16 +01:00
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"InventoryPanel still owns dropdown behavior through the old ContextMenu path: " +
string . Join ( ", " , hits ) ) ;
2026-06-16 21:08:20 +01:00
}
2026-06-21 08:56:07 +01:00
var requiredBuilderSymbols = new [ ]
{
"public static class AetheriaRuntimeInventoryDropdownSurfaceBuilder" ,
"public const string SurfaceId = \"aetheria.inventory.panel.dropdown\"" ,
"public const string Close = \"aetheria.inventory.panel.dropdown.close\"" ,
"public const string SaveLoadout = \"aetheria.inventory.panel.dropdown.save_loadout\"" ,
"public const string DockingBay = \"aetheria.inventory.panel.dropdown.docking_bay\"" ,
"public enum AetheriaRuntimeInventoryDropdownCommandKind" ,
"public readonly struct AetheriaRuntimeInventoryDropdownCommand" ,
"public static class AetheriaRuntimeInventoryDropdownSurfaceCommands" ,
"public static bool TryRead(" ,
"AetheriaRuntimeInventoryDropdownSurfaceState" ,
"AetheriaRuntimeInventoryDropdownGroup" ,
"AetheriaRuntimeInventoryDropdownOption" ,
"public static string EntityEquipmentCommand(" ,
"public static string EntityBayCommand(" ,
"public static string EntityCommand(" ,
"public static string LoadoutCommand(" ,
"public static AetheriaRuntimeSurfaceDocument Build(" ,
"providerKind: \"inventory.panel\"" ,
"Unity projects available inventory navigation; the shared runtime surface owns the dropdown contract."
} ;
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! inventoryDropdownSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared runtime inventory dropdown surface builder no longer owns the inventory dropdown shell contract: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
2026-06-16 21:08:20 +01:00
}
2026-06-17 03:59:14 +01:00
static void RequireNoDeadPopupShells ( string root )
{
var deletedShellPaths = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "ContextMenu.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "ContextMenu.cs.meta" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "ContextMenuOption.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "ContextMenuOption.cs.meta" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Context Menu.prefab" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Context Menu.prefab.meta" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Context Menu Option.prefab" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Context Menu Option.prefab.meta" )
} ;
var survivingShells = deletedShellPaths
. Where ( File . Exists )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( survivingShells . Length > 0 )
{
throw new InvalidOperationException (
"The dead ContextMenu popup shell still survives in source or prefab assets: " +
string . Join ( ", " , survivingShells ) ) ;
}
var sourceChecks = new Dictionary < string , string [ ] >
{
[Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "InventoryPanel.cs")] = new [ ]
{
"public ContextMenu ContextMenu;"
} ,
[Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionGameManager.cs")] = new [ ]
{
"public ContextMenu Context;" ,
"public DropdownMenu Dropdown;"
} ,
[Path.Combine(root, "Assets", "Scripts", "UI", "Menu", "SectorRenderer.cs")] = new [ ]
{
"public PropertiesPanel Properties;" ,
"Properties.gameObject.SetActive(false);"
}
} ;
var survivingSymbols = sourceChecks
. SelectMany ( entry = >
{
var text = File . Exists ( entry . Key ) ? File . ReadAllText ( entry . Key ) : "" ;
return entry . Value
. Where ( symbol = > text . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, entry.Key)}: {symbol}" ) ;
} )
. ToArray ( ) ;
if ( survivingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Renderer-local popup shell authority still survives in live source: " +
string . Join ( "; " , survivingSymbols ) ) ;
}
var serializedChecks = new Dictionary < string , string [ ] >
{
[Path.Combine(root, "Assets", "Scenes", "ARPG.unity")] = new [ ]
{
"ContextMenu: {" ,
"propertyPath: ContextMenu" ,
"Context: {fileID:" ,
"Properties: {fileID:"
} ,
[Path.Combine(root, "Assets", "Prefabs", "UI", "Inventory.prefab")] = new [ ]
{
"ContextMenu: {"
}
} ;
var survivingSerializedShells = serializedChecks
. SelectMany ( entry = >
{
var text = File . Exists ( entry . Key ) ? File . ReadAllText ( entry . Key ) : "" ;
return entry . Value
. Where ( symbol = > text . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, entry.Key)}: {symbol}" ) ;
} )
. ToArray ( ) ;
if ( survivingSerializedShells . Length > 0 )
{
throw new InvalidOperationException (
"Scene or prefab YAML still serializes the deleted popup shell or orphan PropertiesPanel links: " +
string . Join ( "; " , survivingSerializedShells ) ) ;
}
}
2026-06-16 18:09:15 +01:00
static void RequirePlayerSettingsEveSurface ( string root )
{
var projectorPath = Path . Combine ( root , "Aetheria.State" , "AetheriaPlayerSettingsSurfaceProjector.cs" ) ;
var bridgePath = Path . Combine ( root , "Aetheria.State" , "AetheriaEveCommandBridge.cs" ) ;
var providerPath = Path . Combine ( root , "Aetheria.State" , "AetheriaProviderAdvertisementProjector.cs" ) ;
2026-06-21 08:56:07 +01:00
var daemonHostPath = Path . Combine ( root , "Aetheria.State.Daemon" , "Program.cs" ) ;
2026-06-16 18:32:19 +01:00
var sharedCommandsPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimePlayerSettingsCommands.cs" ) ;
2026-06-16 19:05:00 +01:00
var surfaceBuilderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimePlayerSettingsSurfaceBuilder.cs" ) ;
2026-06-16 18:09:15 +01:00
if ( ! File . Exists ( projectorPath ) )
{
throw new InvalidOperationException ( "Player settings Eve surface projector is missing." ) ;
}
var projector = File . ReadAllText ( projectorPath ) ;
var requiredProjectorSymbols = new [ ]
{
2026-06-16 18:32:19 +01:00
"AetheriaRuntimePlayerSettingsCommands.SurfaceId" ,
2026-06-16 19:05:00 +01:00
"AetheriaRuntimePlayerSettingsSurfaceBuilder.Build" ,
"AetheriaRuntimePlayerSettingsSurfaceState" ,
"settings.ActiveRunKey"
2026-06-16 18:09:15 +01:00
} ;
var missingProjectorSymbols = requiredProjectorSymbols
. Where ( symbol = > ! projector . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingProjectorSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Player settings Eve surface projector is missing required typed controls: " +
string . Join ( ", " , missingProjectorSymbols ) ) ;
}
var bridge = File . Exists ( bridgePath )
? File . ReadAllText ( bridgePath )
: throw new InvalidOperationException ( "Player settings Eve command bridge is missing." ) ;
var provider = File . Exists ( providerPath )
? File . ReadAllText ( providerPath )
: throw new InvalidOperationException ( "Player settings provider advertisement projector is missing." ) ;
2026-06-21 08:56:07 +01:00
var daemonHost = File . Exists ( daemonHostPath )
? File . ReadAllText ( daemonHostPath )
: throw new InvalidOperationException ( "Aetheria.State.Daemon program is missing." ) ;
2026-06-16 18:32:19 +01:00
var sharedCommands = File . Exists ( sharedCommandsPath )
? File . ReadAllText ( sharedCommandsPath )
: throw new InvalidOperationException ( "Shared player-settings Eve command contract is missing." ) ;
2026-06-16 19:05:00 +01:00
var surfaceBuilder = File . Exists ( surfaceBuilderPath )
? File . ReadAllText ( surfaceBuilderPath )
: throw new InvalidOperationException ( "Shared player-settings Eve surface builder is missing." ) ;
2026-06-16 18:09:15 +01:00
2026-06-21 08:56:07 +01:00
if ( ! bridge . Contains ( "AcceptedPlayerSettingsCommands" , StringComparison . Ordinal ) | |
! bridge . Contains ( "ExecutePlayerSettingsCommandAsync" , StringComparison . Ordinal ) | |
2026-06-16 19:45:39 +01:00
! bridge . Contains ( "PutPlayerSettingsSurfaceAsync" , StringComparison . Ordinal ) | |
! bridge . Contains ( "SetPlayerName" , StringComparison . Ordinal ) | |
2026-06-21 08:56:07 +01:00
! bridge . Contains ( "command.PlayerSettings.PlayerName" , StringComparison . Ordinal ) )
2026-06-16 18:09:15 +01:00
{
throw new InvalidOperationException (
"Eve command bridge no longer owns the typed player-settings surface mutation path." ) ;
}
if ( ! provider . Contains ( "AetheriaPlayerSettingsSurfaceProjector.SurfaceId" , StringComparison . Ordinal ) | |
2026-06-16 18:32:19 +01:00
! provider . Contains ( "AetheriaRuntimePlayerSettingsCommands.Refresh" , StringComparison . Ordinal ) )
2026-06-16 18:09:15 +01:00
{
throw new InvalidOperationException (
"Provider advertisement no longer publishes the player-settings Eve surface and commands." ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! daemonHost . Contains ( "PutPlayerSettingsSurfaceAsync" , StringComparison . Ordinal ) | |
! daemonHost . Contains ( "AetheriaPlayerSettingsSurfaceProjector.Build" , StringComparison . Ordinal ) )
2026-06-16 18:09:15 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Aetheria.State.Daemon no longer republishes the provider-owned player-settings Eve surface." ) ;
2026-06-16 18:09:15 +01:00
}
2026-06-16 18:32:19 +01:00
if ( ! sharedCommands . Contains ( "SurfaceId = \"aetheria.player_settings\"" , StringComparison . Ordinal ) | |
2026-06-16 19:45:39 +01:00
! sharedCommands . Contains ( "public static bool IsKnown" , StringComparison . Ordinal ) | |
! sharedCommands . Contains ( "SetPlayerName" , StringComparison . Ordinal ) )
2026-06-16 18:32:19 +01:00
{
throw new InvalidOperationException (
"Shared player-settings Eve command contract is missing the surface id or command registry helper." ) ;
}
2026-06-16 19:05:00 +01:00
2026-06-16 19:45:39 +01:00
if ( ! surfaceBuilder . Contains ( "public static class AetheriaRuntimePlayerSettingsSurfaceBuilder" , StringComparison . Ordinal ) | |
! surfaceBuilder . Contains ( "\"control.text\"" , StringComparison . Ordinal ) | |
! surfaceBuilder . Contains ( "SetPlayerName" , StringComparison . Ordinal ) )
2026-06-16 19:05:00 +01:00
{
throw new InvalidOperationException (
"Shared player-settings Eve surface builder no longer owns the portable settings surface contract." ) ;
}
2026-06-16 18:09:15 +01:00
}
2026-06-21 08:56:07 +01:00
static void RequireDaemonHostDoesNotDrainRuntimeCommits ( string root )
2026-06-17 08:07:42 +01:00
{
2026-06-21 08:56:07 +01:00
var economyServerPath = Path . Combine ( root , "Economy" + ".Server" , "Program.cs" ) ;
var economyServerProjectPath = Path . Combine ( root , "Economy" + ".Server" , "Economy" + ".Server.csproj" ) ;
var daemonHostPath = Path . Combine ( root , "Aetheria.State.Daemon" , "Program.cs" ) ;
var drainCommandsPath = Path . Combine ( root , "Aetheria.State.DrainCommands" , "Program.cs" ) ;
var packageCommitLogPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeStateCommitLog.cs" ) ;
var generatedUnityProjectPath = Path . Combine ( root , "GameCult.Aetheria.State.Unity.csproj" ) ;
if ( File . Exists ( economyServerPath ) | | File . Exists ( economyServerProjectPath ) )
{
throw new InvalidOperationException ( "Economy" + ".Server still exists beside the first-class Aetheria.State.Daemon host." ) ;
}
2026-06-17 08:07:42 +01:00
2026-06-21 08:56:07 +01:00
var daemonHost = File . Exists ( daemonHostPath )
? File . ReadAllText ( daemonHostPath )
: throw new InvalidOperationException ( "Cannot verify daemon host command authority; Aetheria.State.Daemon program is missing." ) ;
var generatedUnityProject = File . Exists ( generatedUnityProjectPath )
? File . ReadAllText ( generatedUnityProjectPath )
: throw new InvalidOperationException ( "Cannot verify daemon host command authority; GameCult.Aetheria.State.Unity.csproj is missing." ) ;
if ( File . Exists ( drainCommandsPath ) | |
File . Exists ( Path . Combine ( root , "Aetheria.State.DrainCommands" , "Aetheria.State.DrainCommands.csproj" ) ) )
2026-06-17 08:07:42 +01:00
{
2026-06-21 08:56:07 +01:00
throw new InvalidOperationException (
"Aetheria.State.DrainCommands still exists as a sidecar command applicator; command acceptance belongs inside the Verse daemon." ) ;
}
2026-06-17 08:07:42 +01:00
2026-06-21 08:56:07 +01:00
if ( File . Exists ( packageCommitLogPath ) | |
generatedUnityProject . Contains ( "AetheriaRuntimeStateCommitLog.cs" , StringComparison . Ordinal ) )
2026-06-17 08:07:42 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity package still exposes the legacy runtime state commit log instead of typed Eve/daemon requests." ) ;
2026-06-17 08:07:42 +01:00
}
2026-06-21 08:56:07 +01:00
var forbiddenSymbols = new [ ]
{
"AetheriaLegacyRuntimeSnapshotImporter.ImportPendingAsync" ,
"DrainCommandsRuntimeCommitsAsync" ,
"CountPendingRuntimeCommits" ,
"Applied pending Aetheria runtime commits" ,
"AetheriaRuntimeCommitDrainStatus" ,
"PutRuntimeCommitDrainStatusAsync" ,
"GetRuntimeCommitDrainStatusAsync" ,
"PublishRuntimeCommitDrainDisabledAsync" ,
"disabled-daemon-authority"
} ;
var hits = forbiddenSymbols
. SelectMany ( symbol = > new [ ]
{
( Path : daemonHostPath , Symbol : symbol , Hit : daemonHost . Contains ( symbol , StringComparison . Ordinal ) )
} )
. Where ( entry = > entry . Hit )
. Select ( entry = > $"{Path.GetRelativePath(root, entry.Path)}: {entry.Symbol}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"First-party daemon drains still apply legacy runtime commits instead of daemon/Eve command authority: " +
string . Join ( ", " , hits ) ) ;
}
var requiredSymbols = new [ ]
{
"AetheriaEveCommandBridge.AcceptObservedAsync" ,
"AppliedInputSettingsCommands = report.AcceptedInputSettingsCommands" ,
"AppliedLoadoutTemplateCommands = report.AcceptedLoadoutTemplateCommands"
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! daemonHost . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria.State.Daemon no longer advertises daemon authority while accepting observed Eve command documents: " +
string . Join ( ", " , missingSymbols ) ) ;
}
}
static void RequireVerseHostSettingsAuthority ( string root )
{
var settingsPath = Path . Combine ( root , "Aetheria.State" , "Documents" , "AetheriaVerseHostSettings.cs" ) ;
var normalizerPath = Path . Combine ( root , "Aetheria.State" , "AetheriaVerseHostSettingsNormalizer.cs" ) ;
var verseCatalogProjectorPath = Path . Combine ( root , "Aetheria.State" , "AetheriaVerseCatalogProjector.cs" ) ;
var verseDiscoveryHostPath = Path . Combine ( root , "Aetheria.State" , "AetheriaVerseDiscoveryHost.cs" ) ;
var registryPath = Path . Combine ( root , "Aetheria.State" , "AetheriaDocumentRegistry.cs" ) ;
var nodePath = Path . Combine ( root , "Aetheria.State" , "AetheriaStateNode.cs" ) ;
var providerPath = Path . Combine ( root , "Aetheria.State" , "AetheriaProviderAdvertisementProjector.cs" ) ;
var operationsPath = Path . Combine ( root , "Aetheria.State" , "AetheriaOperationsSurfaceProjector.cs" ) ;
var daemonHostPath = Path . Combine ( root , "Aetheria.State.Daemon" , "Program.cs" ) ;
var requiredFiles = new [ ]
{
settingsPath ,
normalizerPath ,
verseCatalogProjectorPath ,
verseDiscoveryHostPath ,
registryPath ,
nodePath ,
providerPath ,
operationsPath ,
daemonHostPath
} ;
var missingFiles = requiredFiles
. Where ( path = > ! File . Exists ( path ) )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( missingFiles . Length > 0 )
{
throw new InvalidOperationException (
"Verse host authority cut is incomplete; missing files: " + string . Join ( ", " , missingFiles ) ) ;
}
var settings = File . ReadAllText ( settingsPath ) ;
2026-06-17 08:07:42 +01:00
var normalizer = File . ReadAllText ( normalizerPath ) ;
2026-06-17 12:46:11 +01:00
var verseCatalogProjector = File . ReadAllText ( verseCatalogProjectorPath ) ;
var verseDiscoveryHost = File . ReadAllText ( verseDiscoveryHostPath ) ;
2026-06-17 08:07:42 +01:00
var registry = File . ReadAllText ( registryPath ) ;
var node = File . ReadAllText ( nodePath ) ;
var provider = File . ReadAllText ( providerPath ) ;
var operations = File . ReadAllText ( operationsPath ) ;
2026-06-21 08:56:07 +01:00
var daemonHost = File . ReadAllText ( daemonHostPath ) ;
2026-06-17 08:07:42 +01:00
var requiredSettingsSymbols = new [ ]
{
"[CultDocument(\"aetheria.verse_host_settings\", \"aetheria.verse_host_settings.v1\")]" ,
"public sealed class AetheriaVerseHostSettings" ,
"public string VerseId" ,
"public string CultMeshAddress" ,
"public string Visibility"
} ;
var missingSettingsSymbols = requiredSettingsSymbols
. Where ( symbol = > ! settings . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSettingsSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Typed verse-host settings document is missing required authority fields: " +
string . Join ( ", " , missingSettingsSymbols ) ) ;
}
if ( ! normalizer . Contains ( "public static AetheriaVerseHostSettings Normalize" , StringComparison . Ordinal ) | |
! normalizer . Contains ( "public static bool Equivalent" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Verse-host settings normalizer is missing normalize/equivalence ownership." ) ;
}
2026-06-17 12:46:11 +01:00
var requiredVerseCatalogProjectorSymbols = new [ ]
{
"public static class AetheriaVerseCatalogProjector" ,
"CultMeshVerseDescriptor" ,
"BuildDiscoveryEndpoint" ,
"CultMeshVerseDescriptor.ComputeRulesHash" ,
"cultnet://"
} ;
var missingVerseCatalogProjectorSymbols = requiredVerseCatalogProjectorSymbols
. Where ( symbol = > ! verseCatalogProjector . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingVerseCatalogProjectorSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Verse catalog projector no longer derives public CultMesh discovery from typed host settings: " +
string . Join ( ", " , missingVerseCatalogProjectorSymbols ) ) ;
}
var requiredVerseDiscoveryHostSymbols = new [ ]
{
"public sealed class AetheriaVerseDiscoveryHost" ,
"AetheriaVerseCatalogProjector.Build(normalized)" ,
"CultMesh.CreateVerseCatalog()" ,
"CultMesh.ServeVerseCatalog(_node.MeshNode, _catalog)" ,
"normalized.Visibility"
} ;
var missingVerseDiscoveryHostSymbols = requiredVerseDiscoveryHostSymbols
. Where ( symbol = > ! verseDiscoveryHost . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingVerseDiscoveryHostSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Verse discovery host no longer gates served Verse catalogs through the typed host settings: " +
string . Join ( ", " , missingVerseDiscoveryHostSymbols ) ) ;
}
2026-06-17 08:07:42 +01:00
if ( ! registry . Contains ( "CultNetDocumentBinding.ForDocument<AetheriaVerseHostSettings>" , StringComparison . Ordinal ) | |
! registry . Contains ( "typeof(AetheriaVerseHostSettings)" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria document registry does not register typed verse-host settings." ) ;
}
if ( ! node . Contains ( "PutVerseHostSettingsAsync" , StringComparison . Ordinal ) | |
! node . Contains ( "GetVerseHostSettingsAsync" , StringComparison . Ordinal ) | |
2026-06-17 12:46:11 +01:00
! node . Contains ( "public CultMeshNode MeshNode => _node;" , StringComparison . Ordinal ) | |
2026-06-17 08:07:42 +01:00
! node . Contains ( "global:aetheria.verse_host_settings.v1" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria state node does not expose typed verse-host settings get/put ports." ) ;
}
var forbiddenHardcodedProviderSymbols = new [ ]
{
"VerseId = \"aetheria.local\"" ,
"RootVerse = \"asgard\"" ,
"CanonicalService = \"asgard.aetheria\"" ,
"LocatedService = \"asgard.local.aetheria\"" ,
"CultMeshAddress = \"asgard.local.aetheria/eve\""
} ;
var survivingHardcodedProviderSymbols = forbiddenHardcodedProviderSymbols
. Where ( symbol = > provider . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( survivingHardcodedProviderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Provider advertisement still hardcodes daemon verse identity: " +
string . Join ( ", " , survivingHardcodedProviderSymbols ) ) ;
}
if ( ! provider . Contains ( "AetheriaVerseHostSettings settings" , StringComparison . Ordinal ) | |
! provider . Contains ( "AetheriaVerseHostSettingsNormalizer.Normalize(settings)" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Provider advertisement no longer derives Verse identity from typed daemon settings." ) ;
}
if ( ! operations . Contains ( "AetheriaVerseHostSettings? verseHostSettings = null" , StringComparison . Ordinal ) | |
! operations . Contains ( "AetheriaVerseHostSettingsNormalizer.Normalize(verseHostSettings)" , StringComparison . Ordinal ) | |
! operations . Contains ( "\"aetheria.operations.verseHost\"" , StringComparison . Ordinal ) | |
! operations . Contains ( "Metric(\"verseHost.visibility\"" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Operations surface does not expose the typed daemon Verse-host telemetry card." ) ;
}
2026-06-21 08:56:07 +01:00
var requiredDaemonHostSymbols = new [ ]
2026-06-17 08:07:42 +01:00
{
"EnsureVerseHostSettingsAsync" ,
2026-06-17 12:46:11 +01:00
"AetheriaVerseDiscoveryHost" ,
2026-06-21 08:56:07 +01:00
"discoveryHost.Update" ,
"ReadOption(args, \"--verse-id\")" ,
"ReadOption(args, \"--cultmesh-address\")" ,
2026-06-17 08:07:42 +01:00
"GetVerseHostSettingsAsync" ,
"PutVerseHostSettingsAsync" ,
2026-06-21 08:56:07 +01:00
"AetheriaProviderAdvertisementProjector.Build(verseHost, node.StatePath, updatedAtUtc)"
2026-06-17 08:07:42 +01:00
} ;
2026-06-21 08:56:07 +01:00
var missingDaemonHostSymbols = requiredDaemonHostSymbols
. Where ( symbol = > ! daemonHost . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-17 08:07:42 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( missingDaemonHostSymbols . Length > 0 )
2026-06-17 08:07:42 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Aetheria.State.Daemon is missing typed verse-host startup authority: " +
string . Join ( ", " , missingDaemonHostSymbols ) ) ;
2026-06-17 08:07:42 +01:00
}
}
2026-06-17 10:39:28 +01:00
static void RequireClientTargetBootAuthority ( string root )
{
2026-06-21 08:56:07 +01:00
var unityPackageProjectPath = Path . Combine ( root , "GameCult.Aetheria.State.Unity.csproj" ) ;
2026-06-17 10:39:28 +01:00
var boundaryPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeStateBoundary.cs" ) ;
var bootPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeStateBoot.cs" ) ;
var clientTargetStorePath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeClientTargetStore.cs" ) ;
2026-06-21 09:37:54 +01:00
var clientTargetCommandsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeClientTargetCommands.cs" ) ;
2026-06-21 08:56:07 +01:00
var aetheriaStatePath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaState.cs" ) ;
2026-06-17 12:14:16 +01:00
var verseDiscoveryPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeVerseDiscovery.cs" ) ;
2026-06-17 14:29:41 +01:00
var replicaBridgePath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeVerseReplicaBridge.cs" ) ;
2026-06-21 08:56:07 +01:00
var mainMenuSurfaceBuilderPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeMainMenuSurfaceBuilder.cs" ) ;
2026-06-17 10:39:28 +01:00
var bootstrapPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.eve-runtime" , "Runtime" , "AetheriaEveRuntimeBootstrap.cs" ) ;
var presenterPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.eve-runtime" , "Runtime" , "AetheriaEveSurfacePresenter.cs" ) ;
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
var requiredFiles = new [ ]
{
2026-06-21 08:56:07 +01:00
unityPackageProjectPath ,
2026-06-17 10:39:28 +01:00
boundaryPath ,
bootPath ,
clientTargetStorePath ,
2026-06-21 09:37:54 +01:00
clientTargetCommandsPath ,
2026-06-21 08:56:07 +01:00
aetheriaStatePath ,
2026-06-17 12:14:16 +01:00
verseDiscoveryPath ,
2026-06-17 14:29:41 +01:00
replicaBridgePath ,
2026-06-21 08:56:07 +01:00
mainMenuSurfaceBuilderPath ,
2026-06-17 10:39:28 +01:00
bootstrapPath ,
presenterPath ,
actionGameManagerPath ,
mainMenuPath
} ;
var missingFiles = requiredFiles
. Where ( path = > ! File . Exists ( path ) )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( missingFiles . Length > 0 )
{
throw new InvalidOperationException (
"Client target boot authority cannot be verified because required files are missing: " +
string . Join ( ", " , missingFiles ) ) ;
}
2026-06-21 08:56:07 +01:00
var unityPackageProject = File . ReadAllText ( unityPackageProjectPath ) ;
2026-06-17 10:39:28 +01:00
var boundary = File . ReadAllText ( boundaryPath ) ;
var boot = File . ReadAllText ( bootPath ) ;
var clientTargetStore = File . ReadAllText ( clientTargetStorePath ) ;
2026-06-21 09:37:54 +01:00
var clientTargetCommands = File . ReadAllText ( clientTargetCommandsPath ) ;
2026-06-21 08:56:07 +01:00
var aetheriaState = File . ReadAllText ( aetheriaStatePath ) ;
2026-06-17 12:14:16 +01:00
var verseDiscovery = File . ReadAllText ( verseDiscoveryPath ) ;
2026-06-17 14:29:41 +01:00
var replicaBridge = File . ReadAllText ( replicaBridgePath ) ;
2026-06-21 08:56:07 +01:00
var mainMenuSurfaceBuilder = File . ReadAllText ( mainMenuSurfaceBuilderPath ) ;
2026-06-17 10:39:28 +01:00
var bootstrap = File . ReadAllText ( bootstrapPath ) ;
var presenter = File . ReadAllText ( presenterPath ) ;
var actionGameManager = File . ReadAllText ( actionGameManagerPath ) ;
var mainMenu = File . ReadAllText ( mainMenuPath ) ;
2026-06-21 08:56:07 +01:00
var requiredUnityPackageProjectSymbols = new [ ]
2026-06-17 10:39:28 +01:00
{
"AetheriaRuntimeClientTargetStore.cs" ,
2026-06-21 08:56:07 +01:00
"AetheriaState.cs" ,
2026-06-17 12:14:16 +01:00
"AetheriaRuntimeVerseDiscovery.cs" ,
2026-06-17 10:39:28 +01:00
"AetheriaRuntimeStateBoundary.cs" ,
2026-06-17 14:29:41 +01:00
"AetheriaRuntimeStateBoot.cs" ,
"AetheriaRuntimeVerseReplicaBridge.cs"
2026-06-17 10:39:28 +01:00
} ;
2026-06-21 08:56:07 +01:00
var missingUnityPackageProjectSymbols = requiredUnityPackageProjectSymbols
. Where ( symbol = > ! unityPackageProject . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-17 10:39:28 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( missingUnityPackageProjectSymbols . Length > 0 )
2026-06-17 10:39:28 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"GameCult.Aetheria.State.Unity.csproj no longer compiles the shared client-target boot files: " +
string . Join ( ", " , missingUnityPackageProjectSymbols ) ) ;
2026-06-17 10:39:28 +01:00
}
var requiredBoundarySymbols = new [ ]
{
"RuntimeClientTargetFileName = \"aetheria-client.cc\"" ,
2026-06-17 13:51:36 +01:00
"RuntimeReplicaDirectoryName = \"Verses\"" ,
2026-06-17 10:39:28 +01:00
"RuntimeStatePathOverrideEnvironmentVariable = \"AETHERIA_STATE_PATH\"" ,
"LegacyRuntimeStatePathOverrideEnvironmentVariable = \"AETHERIA_EVE_STATE_PATH\"" ,
"GetClientTargetPath" ,
2026-06-17 13:51:36 +01:00
"GetReplicaStateFilePath" ,
2026-06-17 10:39:28 +01:00
"ResolveStatePathOverride"
} ;
var missingBoundarySymbols = requiredBoundarySymbols
. Where ( symbol = > ! boundary . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBoundarySymbols . Length > 0 )
{
throw new InvalidOperationException (
"Runtime state boundary no longer owns the client target file and override resolution: " +
string . Join ( ", " , missingBoundarySymbols ) ) ;
}
var requiredClientTargetSymbols = new [ ]
{
"public static class AetheriaRuntimeClientTargetKinds" ,
"public sealed class AetheriaRuntimeClientTargetDocument" ,
"SchemaId = \"gamecult.aetheria.runtime_client_target.v1\"" ,
"TargetKind" ,
"StateFilePath" ,
"CultMeshAddress" ,
2026-06-17 12:14:16 +01:00
"DiscoveryEndpoints" ,
"DiscoveredVerses" ,
"LastDiscoveryAtUtc" ,
"LastDiscoveryError" ,
2026-06-17 13:51:36 +01:00
"ReplicaStateFilePath" ,
2026-06-17 14:29:41 +01:00
"LastReplicaSyncAtUtc" ,
"LastReplicaSyncError" ,
2026-06-17 10:39:28 +01:00
"ReadOrInitialize" ,
"CreateDefault" ,
"Write(string clientTargetPath, AetheriaRuntimeClientTargetDocument document)" ,
"Read(string clientTargetPath)"
} ;
var missingClientTargetSymbols = requiredClientTargetSymbols
. Where ( symbol = > ! clientTargetStore . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingClientTargetSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Typed client target store is incomplete: " +
string . Join ( ", " , missingClientTargetSymbols ) ) ;
}
var requiredBootSymbols = new [ ]
{
"ClientTargetPath" ,
"TargetKind" ,
"TargetSource" ,
"SupportsLocalStateFileRead" ,
"FailureMessage" ,
2026-06-17 12:14:16 +01:00
"DiscoveryEndpoints" ,
"DiscoveredVerses" ,
"LastDiscoveryAtUtc" ,
"LastDiscoveryError" ,
2026-06-17 13:51:36 +01:00
"ReplicaStateFilePath" ,
2026-06-17 14:29:41 +01:00
"LastReplicaSyncAtUtc" ,
"LastReplicaSyncError" ,
2026-06-17 10:39:28 +01:00
"TargetLabel" ,
"AetheriaRuntimeClientTargetStore.ReadOrInitialize" ,
"AetheriaRuntimeStateBoundary.ResolveStatePathOverride()" ,
"AetheriaRuntimeClientTargetKinds.CultMeshVerse" ,
2026-06-17 13:51:36 +01:00
"GetReplicaStateFilePath" ,
"Sync the local replica" ,
2026-06-17 10:39:28 +01:00
"state-path-override" ,
"client-target"
} ;
var missingBootSymbols = requiredBootSymbols
. Where ( symbol = > ! boot . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBootSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Runtime boot no longer resolves the active Verse through the client target owner: " +
string . Join ( ", " , missingBootSymbols ) ) ;
}
2026-06-17 12:14:16 +01:00
var requiredVerseDiscoverySymbols = new [ ]
{
"CultMesh.CreateVerseCatalog()" ,
"CultMesh.CreateVerseDiscoveryClient()" ,
"DiscoverAsync(catalog, endpoints)" ,
"DiscoveredVerses" ,
"LastDiscoveryAtUtc" ,
"LastDiscoveryError"
} ;
var missingVerseDiscoverySymbols = requiredVerseDiscoverySymbols
. Where ( symbol = > ! verseDiscovery . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingVerseDiscoverySymbols . Length > 0 )
{
throw new InvalidOperationException (
"Runtime Verse discovery helper no longer refreshes the typed client-target catalog through CultMesh discovery: " +
string . Join ( ", " , missingVerseDiscoverySymbols ) ) ;
}
2026-06-21 08:56:07 +01:00
var requiredAetheriaStateSymbols = new [ ]
{
"public readonly struct AetheriaState" ,
"public static AetheriaState At(DirectoryInfo gameDataDirectory)" ,
"public AetheriaClientTarget ClientTarget" ,
"public readonly struct AetheriaClientTarget" ,
"public AetheriaRuntimeClientTargetDocument Refresh()" ,
"public AetheriaRuntimeClientTargetDocument DiscoverVerses()" ,
"public AetheriaRuntimeClientTargetDocument SyncReplica()" ,
"public AetheriaRuntimeClientTargetDocument CycleTransport()" ,
"public AetheriaRuntimeClientTargetDocument RequestTitle(string title)" ,
"public AetheriaRuntimeClientTargetDocument RequestVerseId(string verseId)" ,
"public AetheriaRuntimeClientTargetDocument RequestCultMeshAddress(string cultMeshAddress)" ,
"public AetheriaRuntimeClientTargetDocument RequestStateFilePath(string stateFilePath)" ,
"public AetheriaRuntimeClientTargetDocument RequestDiscoveryEndpoints(IEnumerable<string>? discoveryEndpoints)" ,
"public AetheriaRuntimeClientTargetDocument SelectDiscoveredVerse(" ,
"AetheriaRuntimeStateBoundary.GetClientTargetPath(_gameDataDirectory)" ,
"AetheriaRuntimeStateBoundary.GetStateFilePath(_gameDataDirectory)" ,
"AetheriaRuntimeVerseDiscovery.RefreshClientTarget(" ,
"AetheriaRuntimeVerseReplicaBridge.Sync(" ,
"AetheriaRuntimeClientTargetStore.Update(" ,
"NormalizeDiscoveryEndpoints(" ,
"GetReplicaStateFilePath(gameDataDirectory, document.VerseId)"
} ;
var missingAetheriaStateSymbols = requiredAetheriaStateSymbols
. Where ( symbol = > ! aetheriaState . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingAetheriaStateSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria state sugar no longer owns typed client-target mutation: " +
string . Join ( ", " , missingAetheriaStateSymbols ) ) ;
}
if ( aetheriaState . Contains ( "public bool TryApply(AetheriaClientTargetOperation" , StringComparison . Ordinal ) | |
aetheriaState . Contains ( "public AetheriaRuntimeClientTargetDocument Apply(AetheriaClientTargetOperation" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria state sugar still exposes Apply vocabulary for client-target edits; Unity should request typed target changes." ) ;
}
2026-06-21 09:37:54 +01:00
if ( aetheriaState . Contains ( "public bool TryRequest(AetheriaClientTargetOperation" , StringComparison . Ordinal ) | |
aetheriaState . Contains ( "public AetheriaRuntimeClientTargetDocument Request(AetheriaClientTargetOperation" , StringComparison . Ordinal ) | |
clientTargetCommands . Contains ( "public readonly struct AetheriaClientTargetOperation" , StringComparison . Ordinal ) | |
clientTargetCommands . Contains ( "public enum AetheriaClientTargetOperationKind" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria client-target sugar still exposes generic operation-bag dispatch instead of native request methods plus a surface adapter." ) ;
}
2026-06-21 08:56:07 +01:00
var forbiddenClientTargetSetters = new [ ]
{
"public AetheriaRuntimeClientTargetDocument SetTitle(" ,
"public AetheriaRuntimeClientTargetDocument SetVerseId(" ,
"public AetheriaRuntimeClientTargetDocument SetCultMeshAddress(" ,
"public AetheriaRuntimeClientTargetDocument SetStateFilePath(" ,
"public AetheriaRuntimeClientTargetDocument SetDiscoveryEndpoints("
} ;
var clientTargetSetterHits = forbiddenClientTargetSetters
. Where ( symbol = > aetheriaState . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( clientTargetSetterHits . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria state sugar still exposes Set vocabulary for client-target edits; Unity should request typed target changes: " +
string . Join ( ", " , clientTargetSetterHits ) ) ;
}
if ( aetheriaState . Contains ( "DaemonOperations" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria state sugar regressed to command-applier naming instead of native state handles." ) ;
}
if ( aetheriaState . Contains ( "IReadOnlyDictionary<string, string>" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria state sugar exposes generic dictionary payload plumbing instead of typed client-target operations." ) ;
}
if ( aetheriaState . Contains ( "EveSurfaceCommandRequest" , StringComparison . Ordinal ) | |
aetheriaState . Contains ( "ReadPayloadValue(" , StringComparison . Ordinal ) | |
aetheriaState . Contains ( "request.Payload" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria state sugar still exposes Eve request payload plumbing instead of typed client-target operations." ) ;
}
2026-06-17 14:29:41 +01:00
var requiredReplicaBridgeSymbols = new [ ]
{
"public static class AetheriaRuntimeVerseReplicaBridge" ,
"AetheriaRuntimeVerseReplicaSyncResult" ,
"Aetheria.State.Replica" ,
"Sync(" ,
"ProcessStartInfo" ,
"AetheriaRuntimeStateBoundary.GetReplicaStateFilePath"
} ;
var missingReplicaBridgeSymbols = requiredReplicaBridgeSymbols
. Where ( symbol = > ! replicaBridge . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingReplicaBridgeSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Runtime Verse replica bridge is incomplete: " +
string . Join ( ", " , missingReplicaBridgeSymbols ) ) ;
}
2026-06-17 10:39:28 +01:00
var requiredActionGameManagerSymbols = new [ ]
{
"AetheriaRuntimeStateBoot.Inspect(GameDataDirectory)" ,
"Aetheria runtime target: {stateBoot.TargetLabel} via {stateBoot.TargetKind} ({stateBoot.TargetSource})" ,
"!stateBoot.SupportsLocalStateFileRead"
} ;
var missingActionGameManagerSymbols = requiredActionGameManagerSymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingActionGameManagerSymbols . Length > 0 )
{
throw new InvalidOperationException (
"ActionGameManager no longer boots gameplay through the shared client-target report: " +
string . Join ( ", " , missingActionGameManagerSymbols ) ) ;
}
var requiredPresenterSymbols = new [ ]
{
"AetheriaRuntimeStateBoot.Inspect(gameDataDirectory, stateFilePathOverride)" ,
"!stateBoot.SupportsLocalStateFileRead" ,
"stateBoot.StateFileExists"
} ;
var missingPresenterSymbols = requiredPresenterSymbols
. Where ( symbol = > ! presenter . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingPresenterSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria Eve presenter no longer mounts through the shared client-target boot report: " +
string . Join ( ", " , missingPresenterSymbols ) ) ;
}
if ( bootstrap . Contains ( "StatePathEnvironmentVariable" , StringComparison . Ordinal ) | |
bootstrap . Contains ( "presenter.StateFilePathOverride =" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria Eve runtime bootstrap still tries to own state-path override resolution." ) ;
}
var requiredMainMenuSymbols = new [ ]
{
"CurrentStateBoot()" ,
2026-06-21 08:56:07 +01:00
"LatestDaemonFrame(AetheriaRuntimeStateBootReport stateBoot)" ,
2026-06-17 10:39:28 +01:00
"LatestVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)" ,
"stateBoot.FailureMessage" ,
2026-06-17 12:14:16 +01:00
"stateBoot.DiscoveryEndpoints" ,
"stateBoot.DiscoveredVerses" ,
"stateBoot.LastDiscoveryAtUtc" ,
"stateBoot.LastDiscoveryError" ,
2026-06-17 13:51:36 +01:00
"stateBoot.ReplicaStateFilePath" ,
2026-06-17 14:29:41 +01:00
"stateBoot.LastReplicaSyncAtUtc" ,
"stateBoot.LastReplicaSyncError" ,
2026-06-21 08:56:07 +01:00
"AetheriaState.At(ActionGameManager.GameDataDirectory)" ,
".ClientTarget" ,
"TryRequestClientTargetCommand(request)" ,
2026-06-21 09:37:54 +01:00
"AetheriaRuntimeClientTargetSurfaceCommands.TryRequest(" ,
2026-06-21 08:56:07 +01:00
"ProjectMainMenuSurfaceState(" ,
"AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot(" ,
"AetheriaRuntimeMainMenuSurfaceBuilder.BuildSettings(" ,
"AetheriaRuntimeMainMenuSurfaceBuilder.BuildInputSettings("
} ;
var requiredMainMenuBuilderSymbols = new [ ]
{
"AetheriaRuntimeMainMenuSurfaceState" ,
2026-06-17 10:39:28 +01:00
"\"Client Target\"" ,
"\"Transport\"" ,
"\"Target Source\""
} ;
var missingMainMenuSymbols = requiredMainMenuSymbols
. Where ( symbol = > ! mainMenu . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingMainMenuSymbols . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu no longer lowers the shared client-target boot state through Eve: " +
string . Join ( ", " , missingMainMenuSymbols ) ) ;
}
2026-06-21 08:56:07 +01:00
var missingMainMenuBuilderSymbols = requiredMainMenuBuilderSymbols
. Where ( symbol = > ! mainMenuSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingMainMenuBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared main-menu surface builder no longer lowers the client-target boot state through Eve: " +
string . Join ( ", " , missingMainMenuBuilderSymbols ) ) ;
}
2026-06-17 10:39:28 +01:00
var forbiddenDirectPathSymbols = new Dictionary < string , string [ ] >
{
[actionGameManagerPath] = new [ ]
{
"AetheriaRuntimeStateBoundary.GetStateFilePath(GameDataDirectory)"
} ,
[presenterPath] = new [ ]
{
"AetheriaRuntimeStateBoundary.GetStateFilePath(gameDataDirectory)"
} ,
[bootstrapPath] = new [ ]
{
"StatePathOverride()"
2026-06-21 08:56:07 +01:00
} ,
[mainMenuPath] = new [ ]
{
"AetheriaRuntimeStateBoundary." ,
"AetheriaRuntimeClientTargetStore." ,
"AetheriaRuntimeVerseDiscovery.RefreshClientTarget(" ,
"AetheriaRuntimeVerseReplicaBridge.Sync(" ,
"AetheriaRuntimeClientTarget." ,
"DaemonOperations" ,
2026-06-17 10:39:28 +01:00
}
} ;
var hits = forbiddenDirectPathSymbols
. SelectMany ( entry = >
{
var source = File . ReadAllText ( entry . Key ) ;
return entry . Value
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, entry.Key)} -> {symbol}" ) ;
} )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Unity boot still contains direct local state-path owners instead of the shared client target: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-17 13:51:36 +01:00
static void RequireVerseReplicaTool ( string root )
{
var replicaToolProjectPath = Path . Combine ( root , "Aetheria.State.Replica" , "Aetheria.State.Replica.csproj" ) ;
var replicaToolProgramPath = Path . Combine ( root , "Aetheria.State.Replica" , "Program.cs" ) ;
var replicaHostPath = Path . Combine ( root , "Aetheria.State" , "AetheriaVerseReplica.cs" ) ;
var stateReadmePath = Path . Combine ( root , "Aetheria.State" , "README.md" ) ;
var requiredFiles = new [ ]
{
replicaToolProjectPath ,
replicaToolProgramPath ,
replicaHostPath ,
stateReadmePath
} ;
var missingFiles = requiredFiles
. Where ( path = > ! File . Exists ( path ) )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( missingFiles . Length > 0 )
{
throw new InvalidOperationException (
"Verse replica tool cannot be verified because required files are missing: " +
string . Join ( ", " , missingFiles ) ) ;
}
var replicaToolProject = File . ReadAllText ( replicaToolProjectPath ) ;
var replicaToolProgram = File . ReadAllText ( replicaToolProgramPath ) ;
var replicaHost = File . ReadAllText ( replicaHostPath ) ;
var stateReadme = File . ReadAllText ( stateReadmePath ) ;
var requiredProjectSymbols = new [ ]
{
"<ProjectReference Include=\"..\\Aetheria.State\\Aetheria.State.csproj\" />" ,
"<ProjectReference Include=\"..\\Aetheria.State.Unity\\Aetheria.State.Unity.csproj\" />"
} ;
var missingProjectSymbols = requiredProjectSymbols
. Where ( symbol = > ! replicaToolProject . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingProjectSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Verse replica tool project is incomplete: " +
string . Join ( ", " , missingProjectSymbols ) ) ;
}
var requiredProgramSymbols = new [ ]
{
"sync" ,
"follow" ,
"AetheriaVerseReplica" ,
"GetReplicaStateFilePath" ,
"--endpoint" ,
"--replica"
} ;
var missingProgramSymbols = requiredProgramSymbols
. Where ( symbol = > ! replicaToolProgram . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingProgramSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Verse replica tool program is incomplete: " +
string . Join ( ", " , missingProgramSymbols ) ) ;
}
var requiredReplicaHostSymbols = new [ ]
{
"public static class AetheriaVerseReplica" ,
"SyncSnapshotAsync" ,
"RunReplicaAsync" ,
"CultNetSchemaShardSnapshotFetcher" ,
"CultNetSchemaShardLogFetcher" ,
"CultNetShardReplicator" ,
"ApplyShardSnapshotResponseAsync"
} ;
var missingReplicaHostSymbols = requiredReplicaHostSymbols
. Where ( symbol = > ! replicaHost . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingReplicaHostSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Verse replica host is incomplete: " +
string . Join ( ", " , missingReplicaHostSymbols ) ) ;
}
if ( ! stateReadme . Contains ( "Aetheria.State.Replica" , StringComparison . Ordinal ) | |
! stateReadme . Contains ( "GameData\\Verses\\<verse>.cc" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria.State README does not describe the cache-only Verse replica workflow." ) ;
}
}
2026-06-17 11:24:36 +01:00
static void RequireVerseSettingsShellAndBridge ( string root )
{
2026-06-21 08:56:07 +01:00
var unityPackageProjectPath = Path . Combine ( root , "GameCult.Aetheria.State.Unity.csproj" ) ;
2026-06-17 11:24:36 +01:00
var stateProjectPath = Path . Combine ( root , "Aetheria.State" , "Aetheria.State.csproj" ) ;
var clientTargetCommandsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeClientTargetCommands.cs" ) ;
2026-06-21 08:56:07 +01:00
var aetheriaStatePath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaState.cs" ) ;
2026-06-17 11:24:36 +01:00
var clientTargetSurfaceBuilderPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeClientTargetSurfaceBuilder.cs" ) ;
var verseHostCommandsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeVerseHostCommands.cs" ) ;
var clientTargetStorePath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeClientTargetStore.cs" ) ;
2026-06-17 12:14:16 +01:00
var verseDiscoveryPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeVerseDiscovery.cs" ) ;
2026-06-17 14:29:41 +01:00
var replicaBridgePath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeVerseReplicaBridge.cs" ) ;
2026-06-17 11:24:36 +01:00
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
var commandBridgePath = Path . Combine ( root , "Aetheria.State" , "AetheriaEveCommandBridge.cs" ) ;
2026-06-21 08:56:07 +01:00
var acceptanceStatusPath = Path . Combine ( root , "Aetheria.State" , "Documents" , "AetheriaEveCommandAcceptanceStatus.cs" ) ;
2026-06-17 11:24:36 +01:00
var operationsProjectorPath = Path . Combine ( root , "Aetheria.State" , "AetheriaOperationsSurfaceProjector.cs" ) ;
2026-06-21 08:56:07 +01:00
var daemonHostPath = Path . Combine ( root , "Aetheria.State.Daemon" , "Program.cs" ) ;
2026-06-17 11:24:36 +01:00
var requiredFiles = new [ ]
{
2026-06-21 08:56:07 +01:00
unityPackageProjectPath ,
2026-06-17 11:24:36 +01:00
stateProjectPath ,
clientTargetCommandsPath ,
2026-06-21 08:56:07 +01:00
aetheriaStatePath ,
2026-06-17 11:24:36 +01:00
clientTargetSurfaceBuilderPath ,
verseHostCommandsPath ,
clientTargetStorePath ,
2026-06-17 12:14:16 +01:00
verseDiscoveryPath ,
2026-06-17 14:29:41 +01:00
replicaBridgePath ,
2026-06-17 11:24:36 +01:00
mainMenuPath ,
commandBridgePath ,
2026-06-21 08:56:07 +01:00
acceptanceStatusPath ,
2026-06-17 11:24:36 +01:00
operationsProjectorPath ,
2026-06-21 08:56:07 +01:00
daemonHostPath
2026-06-17 11:24:36 +01:00
} ;
var missingFiles = requiredFiles
. Where ( path = > ! File . Exists ( path ) )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( missingFiles . Length > 0 )
{
throw new InvalidOperationException (
"Verse settings shell/bridge cannot be verified because required files are missing: " +
string . Join ( ", " , missingFiles ) ) ;
}
2026-06-21 08:56:07 +01:00
var unityPackageProject = File . ReadAllText ( unityPackageProjectPath ) ;
2026-06-17 11:24:36 +01:00
var stateProject = File . ReadAllText ( stateProjectPath ) ;
var clientTargetCommands = File . ReadAllText ( clientTargetCommandsPath ) ;
2026-06-21 08:56:07 +01:00
var aetheriaState = File . ReadAllText ( aetheriaStatePath ) ;
2026-06-17 11:24:36 +01:00
var clientTargetSurfaceBuilder = File . ReadAllText ( clientTargetSurfaceBuilderPath ) ;
var verseHostCommands = File . ReadAllText ( verseHostCommandsPath ) ;
var clientTargetStore = File . ReadAllText ( clientTargetStorePath ) ;
2026-06-17 12:14:16 +01:00
var verseDiscovery = File . ReadAllText ( verseDiscoveryPath ) ;
2026-06-17 14:29:41 +01:00
var replicaBridge = File . ReadAllText ( replicaBridgePath ) ;
2026-06-17 11:24:36 +01:00
var mainMenu = File . ReadAllText ( mainMenuPath ) ;
var commandBridge = File . ReadAllText ( commandBridgePath ) ;
2026-06-21 08:56:07 +01:00
var acceptanceStatus = File . ReadAllText ( acceptanceStatusPath ) ;
2026-06-17 11:24:36 +01:00
var operationsProjector = File . ReadAllText ( operationsProjectorPath ) ;
2026-06-21 08:56:07 +01:00
var daemonHost = File . ReadAllText ( daemonHostPath ) ;
2026-06-17 11:24:36 +01:00
2026-06-21 08:56:07 +01:00
var requiredUnityPackageProjectSymbols = new [ ]
2026-06-17 11:24:36 +01:00
{
"AetheriaRuntimeClientTargetCommands.cs" ,
2026-06-21 08:56:07 +01:00
"AetheriaState.cs" ,
2026-06-17 12:14:16 +01:00
"AetheriaRuntimeClientTargetSurfaceBuilder.cs" ,
2026-06-17 14:29:41 +01:00
"AetheriaRuntimeVerseDiscovery.cs" ,
"AetheriaRuntimeVerseReplicaBridge.cs"
2026-06-17 11:24:36 +01:00
} ;
2026-06-21 08:56:07 +01:00
var missingUnityPackageProjectSymbols = requiredUnityPackageProjectSymbols
. Where ( symbol = > ! unityPackageProject . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-17 11:24:36 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( missingUnityPackageProjectSymbols . Length > 0 )
2026-06-17 11:24:36 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"GameCult.Aetheria.State.Unity.csproj does not compile the Verse settings shell files: " +
string . Join ( ", " , missingUnityPackageProjectSymbols ) ) ;
2026-06-17 11:24:36 +01:00
}
if ( ! stateProject . Contains ( "AetheriaRuntimeVerseHostCommands.cs" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria.State.csproj does not compile the Verse-host Eve command contract." ) ;
}
var requiredClientTargetCommandSymbols = new [ ]
{
"SurfaceId = \"aetheria.client_target\"" ,
"CycleTargetKind" ,
"SetTitle" ,
"SetVerseId" ,
"SetCultMeshAddress" ,
"SetStateFilePath" ,
2026-06-17 12:14:16 +01:00
"SetDiscoveryEndpoints" ,
"DiscoverVerses" ,
"SelectDiscoveredVerse" ,
2026-06-17 14:29:41 +01:00
"SyncReplica" ,
2026-06-17 11:24:36 +01:00
"IsKnown"
} ;
var missingClientTargetCommandSymbols = requiredClientTargetCommandSymbols
. Where ( symbol = > ! clientTargetCommands . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingClientTargetCommandSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Client-target command contract is incomplete: " +
string . Join ( ", " , missingClientTargetCommandSymbols ) ) ;
}
var requiredVerseHostCommandSymbols = new [ ]
{
"SurfaceId = \"aetheria.verse_host_settings\"" ,
"CycleVisibility" ,
"Refresh" ,
"IsKnown"
} ;
var missingVerseHostCommandSymbols = requiredVerseHostCommandSymbols
. Where ( symbol = > ! verseHostCommands . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingVerseHostCommandSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Verse-host command contract is incomplete: " +
string . Join ( ", " , missingVerseHostCommandSymbols ) ) ;
}
var requiredSurfaceBuilderSymbols = new [ ]
{
"public sealed class AetheriaRuntimeClientTargetSurfaceState" ,
"\"Client Target\"" ,
"\"Target Fields\"" ,
2026-06-17 12:14:16 +01:00
"\"Verse Discovery\"" ,
2026-06-17 11:24:36 +01:00
"\"Daemon Verse Host\"" ,
2026-06-17 13:51:36 +01:00
"\"Replica State File\"" ,
2026-06-17 14:29:41 +01:00
"\"Replica Sync\"" ,
2026-06-17 11:24:36 +01:00
"AetheriaRuntimeClientTargetCommands.SetStateFilePath" ,
2026-06-17 12:14:16 +01:00
"AetheriaRuntimeClientTargetCommands.SetDiscoveryEndpoints" ,
"AetheriaRuntimeClientTargetCommands.DiscoverVerses" ,
"AetheriaRuntimeClientTargetCommands.SelectDiscoveredVerse" ,
2026-06-17 14:29:41 +01:00
"AetheriaRuntimeClientTargetCommands.SyncReplica" ,
2026-06-17 11:24:36 +01:00
"AetheriaRuntimeVerseHostCommands.CycleVisibility" ,
"AetheriaRuntimeVerseHostCommands.Refresh"
} ;
var missingSurfaceBuilderSymbols = requiredSurfaceBuilderSymbols
. Where ( symbol = > ! clientTargetSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSurfaceBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Client-target Eve surface builder is incomplete: " +
string . Join ( ", " , missingSurfaceBuilderSymbols ) ) ;
}
if ( ! clientTargetStore . Contains ( "Update(" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Client-target store no longer exposes typed update ownership for local target edits." ) ;
}
2026-06-21 08:56:07 +01:00
var requiredAetheriaStateSymbols = new [ ]
{
"public readonly struct AetheriaState" ,
"public AetheriaClientTarget ClientTarget" ,
"public readonly struct AetheriaClientTarget" ,
"public AetheriaRuntimeClientTargetDocument Refresh()" ,
"public AetheriaRuntimeClientTargetDocument DiscoverVerses()" ,
"public AetheriaRuntimeClientTargetDocument SyncReplica()" ,
"public AetheriaRuntimeClientTargetDocument CycleTransport()" ,
"public AetheriaRuntimeClientTargetDocument RequestTitle(string title)" ,
"public AetheriaRuntimeClientTargetDocument RequestVerseId(string verseId)" ,
"public AetheriaRuntimeClientTargetDocument RequestCultMeshAddress(string cultMeshAddress)" ,
"public AetheriaRuntimeClientTargetDocument RequestStateFilePath(string stateFilePath)" ,
"public AetheriaRuntimeClientTargetDocument RequestDiscoveryEndpoints(IEnumerable<string>? discoveryEndpoints)" ,
"public AetheriaRuntimeClientTargetDocument SelectDiscoveredVerse(" ,
"AetheriaRuntimeClientTargetStore.Update(" ,
"AetheriaRuntimeVerseDiscovery.RefreshClientTarget(" ,
"AetheriaRuntimeVerseReplicaBridge.Sync(" ,
"NormalizeDiscoveryEndpoints("
} ;
var missingAetheriaStateSymbols = requiredAetheriaStateSymbols
. Where ( symbol = > ! aetheriaState . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingAetheriaStateSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria state sugar no longer owns the Verse settings mutation bridge: " +
string . Join ( ", " , missingAetheriaStateSymbols ) ) ;
}
if ( aetheriaState . Contains ( "public bool TryApply(AetheriaClientTargetOperation" , StringComparison . Ordinal ) | |
aetheriaState . Contains ( "public AetheriaRuntimeClientTargetDocument Apply(AetheriaClientTargetOperation" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria state sugar still exposes Apply vocabulary for Verse settings edits; Unity should request typed target changes." ) ;
}
2026-06-21 09:37:54 +01:00
if ( aetheriaState . Contains ( "public bool TryRequest(AetheriaClientTargetOperation" , StringComparison . Ordinal ) | |
aetheriaState . Contains ( "public AetheriaRuntimeClientTargetDocument Request(AetheriaClientTargetOperation" , StringComparison . Ordinal ) | |
clientTargetCommands . Contains ( "public readonly struct AetheriaClientTargetOperation" , StringComparison . Ordinal ) | |
clientTargetCommands . Contains ( "public enum AetheriaClientTargetOperationKind" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Verse settings sugar still exposes generic operation-bag dispatch instead of native request methods plus a surface adapter." ) ;
}
2026-06-21 08:56:07 +01:00
var forbiddenClientTargetSetters = new [ ]
{
"public AetheriaRuntimeClientTargetDocument SetTitle(" ,
"public AetheriaRuntimeClientTargetDocument SetVerseId(" ,
"public AetheriaRuntimeClientTargetDocument SetCultMeshAddress(" ,
"public AetheriaRuntimeClientTargetDocument SetStateFilePath(" ,
"public AetheriaRuntimeClientTargetDocument SetDiscoveryEndpoints("
} ;
var clientTargetSetterHits = forbiddenClientTargetSetters
. Where ( symbol = > aetheriaState . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( clientTargetSetterHits . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria state sugar still exposes Set vocabulary for Verse settings edits; Unity should request typed target changes: " +
string . Join ( ", " , clientTargetSetterHits ) ) ;
}
if ( aetheriaState . Contains ( "DaemonOperations" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria state sugar regressed to command-applier naming instead of native state handles." ) ;
}
if ( aetheriaState . Contains ( "IReadOnlyDictionary<string, string>" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria state sugar exposes generic dictionary payload plumbing instead of typed client-target operations." ) ;
}
if ( aetheriaState . Contains ( "EveSurfaceCommandRequest" , StringComparison . Ordinal ) | |
aetheriaState . Contains ( "ReadPayloadValue(" , StringComparison . Ordinal ) | |
aetheriaState . Contains ( "request.Payload" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria state sugar still exposes Eve request payload plumbing instead of typed client-target operations." ) ;
}
2026-06-17 12:14:16 +01:00
var requiredVerseDiscoverySymbols = new [ ]
{
"CultMesh.CreateVerseCatalog()" ,
"CultMesh.CreateVerseDiscoveryClient()" ,
"DiscoverAsync(catalog, endpoints)"
} ;
var missingVerseDiscoverySymbols = requiredVerseDiscoverySymbols
. Where ( symbol = > ! verseDiscovery . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingVerseDiscoverySymbols . Length > 0 )
{
throw new InvalidOperationException (
"Verse discovery helper no longer routes scans through the CultMesh catalog client: " +
string . Join ( ", " , missingVerseDiscoverySymbols ) ) ;
}
2026-06-17 14:29:41 +01:00
var requiredReplicaBridgeSymbols = new [ ]
{
"public static class AetheriaRuntimeVerseReplicaBridge" ,
"ProcessStartInfo" ,
"Aetheria.State.Replica" ,
"Sync("
} ;
var missingReplicaBridgeSymbols = requiredReplicaBridgeSymbols
. Where ( symbol = > ! replicaBridge . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingReplicaBridgeSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Verse replica bridge no longer exposes the sync transport used by the Unity shell: " +
string . Join ( ", " , missingReplicaBridgeSymbols ) ) ;
}
2026-06-17 11:24:36 +01:00
var requiredMainMenuSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeMainMenuCommands.VerseSettingsShellSurfaceId" ,
"AetheriaRuntimeMainMenuCommandKind.ShowVerseSettings" ,
2026-06-17 11:24:36 +01:00
"ShowVerseSettingsSurface()" ,
"HandleVerseSettingsSurfaceCommand(EveSurfaceCommandRequest request)" ,
"BuildVerseSettingsSurfaceDefinition()" ,
"AetheriaRuntimeClientTargetSurfaceBuilder.Build(" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeMainMenuSurfaceCommands.TryRead(request, out var command)" ,
"AetheriaRuntimeMainMenuCommandKind.ClientTargetCommand" ,
"AetheriaRuntimeMainMenuCommandKind.VerseHostCommand" ,
"TryRequestClientTargetCommand(request)" ,
"TrySendVerseHostCommand(command.Command)" ,
"AetheriaRuntimeEveCommands.TrySendVerseHostCommand(" ,
"AetheriaState.At(ActionGameManager.GameDataDirectory)" ,
".ClientTarget" ,
2026-06-21 09:37:54 +01:00
"AetheriaRuntimeClientTargetSurfaceCommands.TryRequest(" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeEveCommands.TrySendVerseHostCommand("
2026-06-17 11:24:36 +01:00
} ;
var missingMainMenuSymbols = requiredMainMenuSymbols
. Where ( symbol = > ! mainMenu . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingMainMenuSymbols . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu no longer owns the typed Verse settings shell handoff: " +
string . Join ( ", " , missingMainMenuSymbols ) ) ;
}
2026-06-21 08:56:07 +01:00
var forbiddenMainMenuMutationSymbols = new [ ]
{
"AetheriaRuntimeClientTargetStore." ,
"AetheriaRuntimeVerseDiscovery.RefreshClientTarget(" ,
"AetheriaRuntimeVerseReplicaBridge.Sync(" ,
"AetheriaRuntimeStateBoundary." ,
"AetheriaRuntimeClientTarget." ,
"DaemonOperations" ,
"ReadPayloadValue(" ,
"ParseDiscoveryEndpoints(" ,
"request.Payload" ,
"IReadOnlyDictionary<string, string>" ,
"clientTarget.SetTitle(" ,
"clientTarget.SetVerseId(" ,
"clientTarget.SetCultMeshAddress(" ,
"clientTarget.SetStateFilePath(" ,
"clientTarget.SetDiscoveryEndpoints(" ,
"clientTarget.DiscoverVerses()" ,
"clientTarget.SelectDiscoveredVerse(" ,
"clientTarget.SyncReplica()" ,
"TryQueueVerseHostCommand(" ,
"TrySendVerseHostCommand(request.Command)" ,
"AetheriaRuntimeClientTargetCommands.IsKnown(request.Command" ,
"AetheriaRuntimeVerseHostCommands.IsKnown(request.Command" ,
"new EveSurfaceCommandRequest(" ,
"AetheriaRuntimeEveCommandLog.Queue" ,
} ;
var mainMenuMutationHits = forbiddenMainMenuMutationSymbols
. Where ( symbol = > mainMenu . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( mainMenuMutationHits . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu still mutates the client target directly instead of dispatching through the runtime daemon operations: " +
string . Join ( ", " , mainMenuMutationHits ) ) ;
}
2026-06-17 11:24:36 +01:00
var requiredBridgeSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"AcceptedVerseHostCommands" ,
2026-06-17 11:24:36 +01:00
"AetheriaRuntimeVerseHostCommands.Refresh" ,
"AetheriaRuntimeVerseHostCommands.CycleVisibility" ,
2026-06-21 08:56:07 +01:00
"ExecuteVerseHostCommandAsync" ,
2026-06-17 11:24:36 +01:00
"PutVerseHostSettingsAsync" ,
"PutProviderAdvertisementAsync" ,
"AetheriaOperationsSurfaceProjector.Build(" ,
"AetheriaRuntimeVerseHostCommands.IsKnown(command)"
} ;
var missingBridgeSymbols = requiredBridgeSymbols
. Where ( symbol = > ! commandBridge . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBridgeSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria Eve command bridge no longer routes Verse-host commands through the provider owner: " +
string . Join ( ", " , missingBridgeSymbols ) ) ;
}
2026-06-21 08:56:07 +01:00
var requiredAcceptanceSymbols = new [ ]
2026-06-17 11:24:36 +01:00
{
2026-06-21 08:56:07 +01:00
"AetheriaEveCommandAcceptanceStatus" ,
"aetheria.eve_command_acceptance_status.v1" ,
"ObservedBeforeAccept" ,
2026-06-17 11:24:36 +01:00
"AppliedVerseHostCommands"
} ;
2026-06-21 08:56:07 +01:00
var missingAcceptanceSymbols = requiredAcceptanceSymbols
. Where ( symbol = > ! acceptanceStatus . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-17 11:24:36 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( missingAcceptanceSymbols . Length > 0 )
2026-06-17 11:24:36 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Aetheria Eve command acceptance status no longer records Verse-host command counts: " +
string . Join ( ", " , missingAcceptanceSymbols ) ) ;
2026-06-17 11:24:36 +01:00
}
2026-06-21 08:56:07 +01:00
if ( File . Exists ( Path . Combine ( root , "Aetheria.State" , "Documents" , "AetheriaEveCommandDrainStatus.cs" ) ) | |
acceptanceStatus . Contains ( "AetheriaEveCommandDrainStatus" , StringComparison . Ordinal ) | |
acceptanceStatus . Contains ( "aetheria.eve_command_drain_status" , StringComparison . Ordinal ) | |
daemonHost . Contains ( "DrainEveCommandsAsync" , StringComparison . Ordinal ) | |
daemonHost . Contains ( "PutEveCommandDrainStatusAsync" , StringComparison . Ordinal ) | |
daemonHost . Contains ( "GetEveCommandDrainStatusAsync" , StringComparison . Ordinal ) )
2026-06-17 11:24:36 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Eve command acceptance regressed to drain/queue vocabulary; the daemon should accept observed typed command records." ) ;
}
if ( acceptanceStatus . Contains ( "public int PendingBeforeApply" , StringComparison . Ordinal ) & &
! acceptanceStatus . Contains ( "[IgnoreMember]" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Eve command acceptance exposes the legacy PendingBeforeApply schema shim as serialized command state." ) ;
}
var pendingBeforeApplyUseSources = new Dictionary < string , string >
{
["Aetheria daemon host"] = daemonHost ,
["Aetheria Eve command bridge"] = commandBridge ,
["operations projector"] = operationsProjector
} ;
var pendingBeforeApplyUses = pendingBeforeApplyUseSources
. Where ( source = > source . Value . Contains ( "PendingBeforeApply" , StringComparison . Ordinal ) )
. Select ( source = > source . Key )
. ToArray ( ) ;
if ( pendingBeforeApplyUses . Length > 0 )
{
throw new InvalidOperationException (
"Live Eve command acceptance code still uses the legacy PendingBeforeApply shim instead of ObservedBeforeAccept: " +
string . Join ( ", " , pendingBeforeApplyUses ) ) ;
2026-06-17 11:24:36 +01:00
}
2026-06-21 08:56:07 +01:00
if ( ! operationsProjector . Contains ( "Verse Host Commands" , StringComparison . Ordinal ) | |
! operationsProjector . Contains ( "AppliedVerseHostCommands" , StringComparison . Ordinal ) )
2026-06-17 11:24:36 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Operations surface no longer projects Verse-host Eve command counts." ) ;
2026-06-17 11:24:36 +01:00
}
2026-06-21 08:56:07 +01:00
if ( ! commandBridge . Contains ( "report.AcceptedVerseHostCommands++" , StringComparison . Ordinal ) | |
! daemonHost . Contains ( "AppliedVerseHostCommands = report.AcceptedVerseHostCommands" , StringComparison . Ordinal ) )
2026-06-17 11:24:36 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Daemon command acceptance no longer publishes Verse-host Eve command totals." ) ;
2026-06-17 11:24:36 +01:00
}
}
2026-06-21 08:56:07 +01:00
static void RequireTypedStatRecipeOperations ( string root )
2026-06-17 09:51:49 +01:00
{
2026-06-21 08:56:07 +01:00
var statRecipesPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeStatRecipes.cs" ) ;
var legacyApplierPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeStatRecipeCommandApplier.cs" ) ;
var catalogStorePath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeCatalogStore.cs" ) ;
var testsPath = Path . Combine ( root , "Assets" , "Scripts" , "Tests" , "StatRecipeSurfaceCommandTests.cs" ) ;
2026-06-17 09:51:49 +01:00
2026-06-21 08:56:07 +01:00
if ( ! File . Exists ( statRecipesPath ) )
throw new InvalidOperationException ( "Typed stat recipe operations are missing." ) ;
if ( File . Exists ( legacyApplierPath ) )
throw new InvalidOperationException ( "Legacy stat recipe command applier still exists." ) ;
if ( ! File . Exists ( catalogStorePath ) | | ! File . Exists ( testsPath ) )
throw new InvalidOperationException ( "Cannot verify typed stat recipe operations; required files are missing." ) ;
var statRecipes = File . ReadAllText ( statRecipesPath ) ;
var catalogStore = File . ReadAllText ( catalogStorePath ) ;
var tests = File . ReadAllText ( testsPath ) ;
var requiredTypedSymbols = new [ ]
2026-06-17 09:51:49 +01:00
{
2026-06-21 08:56:07 +01:00
"public static class AetheriaRuntimeStatRecipes" ,
"public static AetheriaRuntimeStatRecipeSurfaceState Refresh(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState SelectStat(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState AddStat(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState RemoveStat(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState SetStatName(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState SetBaseValue(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState SetConditionEnabled(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState ToggleCondition(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState CycleInfluenceOperation(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState SetInfluenceAmount(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState SetInfluenceCurve(" ,
"public static AetheriaRuntimeStatRecipeSurfaceState SetPreviewCondition("
2026-06-17 09:51:49 +01:00
} ;
2026-06-21 08:56:07 +01:00
var missingTypedSymbols = requiredTypedSymbols
. Where ( symbol = > ! statRecipes . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-17 09:51:49 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( missingTypedSymbols . Length > 0 )
2026-06-17 09:51:49 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Typed stat recipe operations are incomplete: " +
string . Join ( ", " , missingTypedSymbols ) ) ;
2026-06-17 09:51:49 +01:00
}
2026-06-21 08:56:07 +01:00
var forbiddenPublicStringlySymbols = new [ ]
2026-06-17 09:51:49 +01:00
{
2026-06-21 08:56:07 +01:00
"public static AetheriaRuntimeStatRecipeSurfaceState Apply" ,
"public static AetheriaRuntimeStatRecipeSurfaceState ApplyCommand" ,
"public static class AetheriaRuntimeStatRecipeDaemonOperations" ,
"public static AetheriaRuntimeStatRecipeSurfaceState" ,
"IReadOnlyDictionary<string, string>? payload"
2026-06-17 09:51:49 +01:00
} ;
2026-06-21 08:56:07 +01:00
var publicStringlyHits = forbiddenPublicStringlySymbols
. Where ( symbol = >
symbol = = "public static AetheriaRuntimeStatRecipeSurfaceState"
? statRecipes . Contains ( "public static AetheriaRuntimeStatRecipeSurfaceState Apply(" , StringComparison . Ordinal )
: statRecipes . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-17 09:51:49 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( publicStringlyHits . Length > 0 )
2026-06-17 09:51:49 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Stat recipe client API still exposes stringly command/payload operations: " +
string . Join ( ", " , publicStringlyHits ) ) ;
2026-06-17 09:51:49 +01:00
}
2026-06-21 08:56:07 +01:00
var forbiddenCatalogDecoderSymbols = new [ ]
2026-06-17 09:51:49 +01:00
{
2026-06-21 08:56:07 +01:00
"DrainCommandsStatRecipeCommand(" ,
"AetheriaRuntimeStatRecipes.ApplyCommand(" ,
"ReadPayloadDouble(" ,
"ReadPayloadBool("
2026-06-17 09:51:49 +01:00
} ;
2026-06-21 08:56:07 +01:00
var catalogDecoderHits = forbiddenCatalogDecoderSymbols
. Where ( symbol = > catalogStore . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-17 09:51:49 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( catalogDecoderHits . Length > 0 )
2026-06-17 09:51:49 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Catalog store still replays stringly stat recipe command payloads: " +
string . Join ( ", " , catalogDecoderHits ) ) ;
2026-06-17 09:51:49 +01:00
}
2026-06-21 08:56:07 +01:00
if ( tests . Contains ( "AetheriaRuntimeStatRecipeDaemonOperations" , StringComparison . Ordinal ) | |
tests . Contains ( "AetheriaRuntimeStatRecipeCommands." , StringComparison . Ordinal ) )
2026-06-17 09:51:49 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Stat recipe tests still exercise string command constants instead of typed operations." ) ;
2026-06-17 09:51:49 +01:00
}
2026-06-21 08:56:07 +01:00
}
2026-06-17 09:51:49 +01:00
2026-06-21 08:56:07 +01:00
static void RequireTypedDaemonCommandPayloads ( string root )
{
var daemonDocumentsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonDocuments.cs" ) ;
var daemonClientPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonOperationClient.cs" ) ;
var daemonRuntimeOperationsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonOperations.cs" ) ;
var legacyDaemonApplierPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonCommandApplier.cs" ) ;
var stateNodePath = Path . Combine ( root , "Aetheria.State" , "AetheriaStateNode.cs" ) ;
var observerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "AetheriaDaemonObserver.cs" ) ;
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var testsPath = Path . Combine ( root , "Assets" , "Scripts" , "Tests" , "DaemonRuntimeDocumentTests.cs" ) ;
var daemonSurfaceCommandsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonSurfaceCommands.cs" ) ;
if ( File . Exists ( legacyDaemonApplierPath ) )
throw new InvalidOperationException ( "Legacy daemon command applier still exists; daemon operations must own execution." ) ;
var requiredFiles = new [ ] { daemonDocumentsPath , daemonClientPath , daemonRuntimeOperationsPath , stateNodePath , observerPath , actionGameManagerPath , testsPath , daemonSurfaceCommandsPath } ;
var missingFiles = requiredFiles
. Where ( path = > ! File . Exists ( path ) )
. Select ( path = > Path . GetRelativePath ( root , path ) )
2026-06-17 09:51:49 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( missingFiles . Length > 0 )
2026-06-17 09:51:49 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Typed daemon command payloads cannot be verified because required files are missing: " +
string . Join ( ", " , missingFiles ) ) ;
2026-06-17 09:51:49 +01:00
}
2026-06-21 08:56:07 +01:00
var daemonDocuments = File . ReadAllText ( daemonDocumentsPath ) ;
var daemonClient = File . ReadAllText ( daemonClientPath ) ;
var daemonOperationsSource = File . ReadAllText ( daemonRuntimeOperationsPath ) ;
var stateNode = File . ReadAllText ( stateNodePath ) ;
var observer = File . ReadAllText ( observerPath ) ;
var actionGameManager = File . ReadAllText ( actionGameManagerPath ) ;
var tests = File . ReadAllText ( testsPath ) ;
var daemonSurfaceCommands = File . ReadAllText ( daemonSurfaceCommandsPath ) ;
var requiredPayloadTypes = new [ ]
{
"public enum AetheriaRuntimeDaemonCommandKinds" ,
"public sealed class AetheriaRuntimeActionBarBindingCommand" ,
"public sealed class AetheriaRuntimeCargoTransferCommand" ,
"public sealed class AetheriaRuntimeTradePurchaseCommand" ,
"public sealed class AetheriaRuntimeLootPickupCommand" ,
"public sealed class AetheriaRuntimeLoadoutRestoreCommand" ,
"public sealed class AetheriaRuntimeEquipmentTransferCommand" ,
"public sealed class AetheriaRuntimeStoreItemCommand"
} ;
var missingPayloadTypes = requiredPayloadTypes
. Where ( symbol = > ! daemonDocuments . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingPayloadTypes . Length > 0 )
{
throw new InvalidOperationException (
"Daemon command document is missing typed command payloads: " +
string . Join ( ", " , missingPayloadTypes ) ) ;
}
var liveSources = new Dictionary < string , string >
{
["daemon documents"] = daemonDocuments ,
["daemon operation client"] = daemonClient ,
["daemon operations"] = daemonOperationsSource ,
["state node"] = stateNode ,
["Unity daemon observer"] = observer ,
["ActionGameManager"] = actionGameManager ,
["daemon command tests"] = tests ,
["daemon surface commands"] = daemonSurfaceCommands
} ;
foreach ( var source in liveSources )
{
var forbidden = new [ ]
{
"Dictionary<string, string> Payload" ,
".Payload[" ,
"ReadPayloadInt(" ,
"ReadPayloadDouble(" ,
"ReadPayloadBool(" ,
"Payload(command,"
}
. Where ( symbol = > source . Value . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( forbidden . Length > 0 )
{
throw new InvalidOperationException (
$"{source.Key} still uses stringly daemon command payloads: " +
string . Join ( ", " , forbidden ) ) ;
}
}
var forbiddenDaemonKindSymbols = new [ ]
{
"public static class AetheriaRuntimeDaemonCommandKinds" ,
"public const string SetTarget" ,
"Queue(\r\n string kind" ,
"Create(\r\n string kind"
} ;
var daemonKindSources = new Dictionary < string , string >
{
["daemon documents"] = daemonDocuments ,
["daemon operation client"] = daemonClient ,
["Unity daemon observer"] = observer
} ;
foreach ( var source in daemonKindSources )
{
var forbidden = forbiddenDaemonKindSymbols
. Where ( symbol = > source . Value . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( forbidden . Length > 0 )
{
throw new InvalidOperationException (
$"{source.Key} still exposes daemon command kind as strings: " +
string . Join ( ", " , forbidden ) ) ;
}
}
if ( ! daemonDocuments . Contains ( "public AetheriaRuntimeDaemonCommandKinds Kind { get; set; }" , StringComparison . Ordinal ) | |
! daemonDocuments . Contains ( "public AetheriaRuntimeDaemonCommandKinds Kind { get; }" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Daemon command document/envelope must expose typed enum command kinds." ) ;
}
var requiredClientSymbols = new [ ]
{
"public AetheriaRuntimeDaemonCommandEnvelope Send(" ,
"public bool TrySend(" ,
"ReadObservedDaemonCommands()" ,
"_operationClient.Create(" ,
"_operationClient.TrySend(command, out var envelope, out var error)" ,
"command.ActionBarBinding.Kind" ,
"command.CargoTransfer.OriginEntityKey" ,
"command.TradePurchase.TotalPrice" ,
"command.LootPickup.ItemKey" ,
"command.LoadoutRestore.TemplateName" ,
"command.EquipmentTransfer.SourceKind" ,
"command.StoreItem.SourceEquipmentIndex"
} ;
var clientAndObserver = daemonClient + "\n" + observer + "\n" + stateNode ;
var missingClientSymbols = requiredClientSymbols
. Where ( symbol = > ! clientAndObserver . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingClientSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon command producers are not filling typed payloads: " +
string . Join ( ", " , missingClientSymbols ) ) ;
}
var forbiddenQueueSymbols = new [ ]
{
"public static class AetheriaRuntimeDaemonCommandLog" ,
"internal static class AetheriaRuntimeDaemonCommandLog" ,
"public AetheriaRuntimeDaemonCommandEnvelope Queue(" ,
"public static AetheriaRuntimeDaemonCommandEnvelope QueueCommand(" ,
"AetheriaRuntimeDaemonCommandLog.QueueCommand(" ,
"AetheriaRuntimeDaemonCommandLog." ,
"AetheriaRuntimeDaemonCommandInbox" ,
"ReadPending(" ,
".daemon.pending" ,
"stateFilePath + \".daemon.commands\"" ,
"_operationClient.Queue("
} ;
var daemonQueueSources = new Dictionary < string , string >
{
["daemon operation client"] = daemonClient ,
["state node"] = stateNode ,
["Unity daemon observer"] = observer ,
["ActionGameManager"] = actionGameManager ,
["daemon command tests"] = tests
} ;
var survivingQueueSymbols = daemonQueueSources
. SelectMany ( source = > forbiddenQueueSymbols
. Where ( symbol = > source . Value . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{source.Key}: {symbol}" ) )
. ToArray ( ) ;
if ( survivingQueueSymbols . Length > 0 | |
observer . Contains ( "_operationClient.Queue(" , StringComparison . Ordinal ) | |
daemonClient . Contains ( "AetheriaRuntimeDaemonCommandLog." , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Unity-facing daemon operation clients still expose queue semantics instead of sending typed daemon operations: " +
string . Join ( ", " , survivingQueueSymbols ) ) ;
}
if ( tests . Contains ( "AetheriaRuntimeDaemonCommandLog." , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Daemon command tests still reach through the old command log instead of the typed command client/inbox." ) ;
}
if ( actionGameManager . Contains ( "local mutation suppressed" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"ActionGameManager still treats failed daemon submission as a local-mutation fallback." ) ;
}
var failedSubmitSuccessHits = System . Text . RegularExpressions . Regex
. Matches (
actionGameManager ,
"Failed to send Aetheria daemon[\\s\\S]*?return true;" ,
System . Text . RegularExpressions . RegexOptions . Multiline )
. Cast < System . Text . RegularExpressions . Match > ( )
. Where ( match = > ! match . Value . Contains ( "return false;" , StringComparison . Ordinal ) )
. Select ( match = > match . Value . Split ( '\n' ) . FirstOrDefault ( ) ? . Trim ( ) ? ? "daemon submit catch" )
. Take ( 5 )
. ToArray ( ) ;
if ( failedSubmitSuccessHits . Length > 0 )
{
throw new InvalidOperationException (
"ActionGameManager reports failed daemon submissions as successful commands: " +
string . Join ( "; " , failedSubmitSuccessHits ) ) ;
}
}
static void RequireUnityPublicRequestVocabulary ( string root )
{
var checkedRoots = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" ) ,
Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" )
} ;
var commitPattern = new System . Text . RegularExpressions . Regex (
"\\bCommit[A-Z][A-Za-z0-9_]*\\b" ,
System . Text . RegularExpressions . RegexOptions . Compiled ) ;
var hits = checkedRoots
. Where ( Directory . Exists )
. SelectMany ( path = > Directory . EnumerateFiles ( path , "*.cs" , SearchOption . AllDirectories ) )
. Where ( path = > ! path . Contains ( Path . Combine ( "ServerShared" , "NIH" ) , StringComparison . Ordinal ) )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. SelectMany ( line = > commitPattern . Matches ( line . Line )
. Cast < System . Text . RegularExpressions . Match > ( )
. Select ( match = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {match.Value}" ) )
. Take ( 20 )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Unity-facing Aetheria APIs must say Request/Submit, not Commit; Unity is an input provider, not state authority: " +
string . Join ( "; " , hits ) ) ;
}
}
static void RequireDaemonVersePublication ( string root )
{
var daemonDocumentsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonDocuments.cs" ) ;
var daemonTickRunnerPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonTickRunner.cs" ) ;
var daemonPublicationStorePath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonPublicationStore.cs" ) ;
var daemonSoaDocumentsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonSoaDocuments.cs" ) ;
var daemonGameSurfaceBuilderPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonGameSurfaceBuilder.cs" ) ;
var daemonEditorSurfaceBuilderPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonEditorSurfaceBuilder.cs" ) ;
var daemonHostProjectPath = Path . Combine ( root , "Aetheria.State.Daemon" , "Aetheria.State.Daemon.csproj" ) ;
var daemonHostProgramPath = Path . Combine ( root , "Aetheria.State.Daemon" , "Program.cs" ) ;
var documentRegistryPath = Path . Combine ( root , "Aetheria.State" , "AetheriaDocumentRegistry.cs" ) ;
var stateNodePath = Path . Combine ( root , "Aetheria.State" , "AetheriaStateNode.cs" ) ;
var daemonSurfaceProjectorPath = Path . Combine ( root , "Aetheria.State" , "AetheriaRuntimeEveSurfaceStateProjector.cs" ) ;
var providerAdvertisementPath = Path . Combine ( root , "Aetheria.State" , "AetheriaProviderAdvertisementProjector.cs" ) ;
var documentStorePath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeCultCacheDocumentStore.cs" ) ;
var boundaryPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeStateBoundary.cs" ) ;
var testsPath = Path . Combine ( root , "Assets" , "Scripts" , "Tests" , "DaemonRuntimeDocumentTests.cs" ) ;
var smokePath = Path . Combine ( root , "Aetheria.State.Smoke" , "Program.cs" ) ;
var notePath = Path . Combine ( root , "Aetheria.State" , "docs" , "verse-daemon-shape.md" ) ;
var requiredFiles = new [ ]
{
daemonDocumentsPath ,
daemonTickRunnerPath ,
daemonPublicationStorePath ,
daemonSoaDocumentsPath ,
daemonGameSurfaceBuilderPath ,
daemonEditorSurfaceBuilderPath ,
daemonHostProjectPath ,
daemonHostProgramPath ,
documentRegistryPath ,
stateNodePath ,
daemonSurfaceProjectorPath ,
providerAdvertisementPath ,
documentStorePath ,
boundaryPath ,
testsPath ,
smokePath ,
notePath
} ;
var missingFiles = requiredFiles
. Where ( path = > ! File . Exists ( path ) )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( missingFiles . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria daemon Verse publication cannot be verified because required files are missing: " +
string . Join ( ", " , missingFiles ) ) ;
}
var daemonDocuments = File . ReadAllText ( daemonDocumentsPath ) ;
var daemonTickRunner = File . ReadAllText ( daemonTickRunnerPath ) ;
var daemonPublicationStore = File . ReadAllText ( daemonPublicationStorePath ) ;
var daemonSoaDocuments = File . ReadAllText ( daemonSoaDocumentsPath ) ;
var daemonGameSurfaceBuilder = File . ReadAllText ( daemonGameSurfaceBuilderPath ) ;
var daemonEditorSurfaceBuilder = File . ReadAllText ( daemonEditorSurfaceBuilderPath ) ;
var daemonHostProject = File . ReadAllText ( daemonHostProjectPath ) ;
var daemonHostProgram = File . ReadAllText ( daemonHostProgramPath ) ;
var documentRegistry = File . ReadAllText ( documentRegistryPath ) ;
var stateNode = File . ReadAllText ( stateNodePath ) ;
var daemonSurfaceProjector = File . ReadAllText ( daemonSurfaceProjectorPath ) ;
var providerAdvertisement = File . ReadAllText ( providerAdvertisementPath ) ;
var documentStore = File . ReadAllText ( documentStorePath ) ;
var boundary = File . ReadAllText ( boundaryPath ) ;
var tests = File . ReadAllText ( testsPath ) ;
var smoke = File . ReadAllText ( smokePath ) ;
var note = File . ReadAllText ( notePath ) ;
var requiredDocumentSymbols = new [ ]
{
"public const string ProviderAdvertisement = \"gamecult.aetheria.daemon_provider_advertisement.v1\"" ,
"public const string Health = \"gamecult.aetheria.daemon_health.v1\"" ,
"public const string CommandBoundary = \"gamecult.aetheria.daemon_command_boundary.v1\"" ,
"public const string GameSurface = \"gamecult.aetheria.daemon_game_surface.v1\"" ,
"public const string EditorSurface = \"gamecult.aetheria.daemon_editor_surface.v1\"" ,
"public sealed class AetheriaRuntimeDaemonProviderAdvertisementDocument" ,
"public sealed class AetheriaRuntimeDaemonHealthDocument" ,
"public sealed class AetheriaRuntimeDaemonCommandBoundaryDocument" ,
"public sealed class AetheriaRuntimeDaemonCommandBoundaryEntry" ,
"AetheriaRuntimeDaemonCommandKinds Kind" ,
"AetheriaRuntimeCargoTransferCommand" ,
"[CultDocument(\"gamecult.aetheria.daemon_provider_advertisement\", \"gamecult.aetheria.daemon_provider_advertisement.v1\")]" ,
"[CultDocument(\"gamecult.aetheria.daemon_health\", \"gamecult.aetheria.daemon_health.v1\")]" ,
"[CultDocument(\"gamecult.aetheria.daemon_command_boundary\", \"gamecult.aetheria.daemon_command_boundary.v1\")]" ,
"[CultDocument(\"gamecult.aetheria.daemon_frame\", \"gamecult.aetheria.daemon_frame.v1\")]" ,
"[CultDocument(\"gamecult.aetheria.daemon_command\", \"gamecult.aetheria.daemon_command.v1\")]" ,
"EveGuiSurfaceWitnessPath" ,
"EveTuiSurfaceWitnessPath" ,
"EditorGuiSurfaceWitnessPath" ,
"EditorTuiSurfaceWitnessPath"
} ;
var missingDocumentSymbols = requiredDocumentSymbols
. Where ( symbol = > ! daemonDocuments . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDocumentSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon Verse publication documents are incomplete: " +
string . Join ( ", " , missingDocumentSymbols ) ) ;
}
if ( ! daemonSoaDocuments . Contains ( "[CultDocument(\"gamecult.aetheria.daemon_soa_view\", \"gamecult.aetheria.daemon_soa_view.v1\")]" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException ( "Daemon SoA view is no longer a registered CultCache/CultNet document." ) ;
}
var requiredBoundarySymbols = new [ ]
{
"RuntimeDaemonProviderFileSuffix" ,
"RuntimeDaemonHealthFileSuffix" ,
"RuntimeDaemonCommandBoundaryFileSuffix" ,
"RuntimeDaemonGameSurfaceFileSuffix" ,
"RuntimeDaemonGameTuiSurfaceFileSuffix" ,
"RuntimeDaemonEditorSurfaceFileSuffix" ,
"RuntimeDaemonEditorTuiSurfaceFileSuffix" ,
"GetDaemonProviderPath(" ,
"GetDaemonHealthPath(" ,
"GetDaemonCommandBoundaryPath(" ,
"GetDaemonGameSurfacePath(" ,
"GetDaemonGameTuiSurfacePath(" ,
"GetDaemonEditorSurfacePath(" ,
"GetDaemonEditorTuiSurfacePath("
} ;
var missingBoundarySymbols = requiredBoundarySymbols
. Where ( symbol = > ! boundary . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBoundarySymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon Verse publication paths are incomplete: " +
string . Join ( ", " , missingBoundarySymbols ) ) ;
}
var requiredStoreSymbols = new [ ]
{
"WriteDaemonProviderAdvertisement(" ,
"ReadDaemonProviderAdvertisement(" ,
"WriteDaemonHealth(" ,
"ReadDaemonHealth(" ,
"WriteDaemonCommandBoundary(" ,
"ReadDaemonCommandBoundary(" ,
"WriteDaemonGameSurface(" ,
"ReadDaemonGameSurface(" ,
"WriteDaemonEditorSurface(" ,
"ReadDaemonEditorSurface(" ,
"PublishProviderAdvertisement(" ,
"PublishHealth(" ,
"PublishCommandBoundary(" ,
"PublishGameSurface(" ,
"PublishGameTuiSurface(" ,
"PublishEditorSurface(" ,
"PublishEditorTuiSurface(" ,
"TryReadProviderAdvertisement(" ,
"TryReadHealth(" ,
"TryReadCommandBoundary(" ,
"TryReadGameSurface(" ,
"TryReadGameTuiSurface(" ,
"TryReadEditorSurface(" ,
"TryReadEditorTuiSurface("
} ;
var storeSource = documentStore + "\n" + daemonPublicationStore ;
var missingStoreSymbols = requiredStoreSymbols
. Where ( symbol = > ! storeSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingStoreSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon Verse publication store is incomplete: " +
string . Join ( ", " , missingStoreSymbols ) ) ;
}
var requiredDaemonRegistrySymbols = new [ ]
{
"CultNetDocumentBinding.ForDocument<AetheriaRuntimeDaemonProviderAdvertisementDocument>(registry)" ,
"CultNetDocumentBinding.ForDocument<AetheriaRuntimeDaemonHealthDocument>(registry)" ,
"CultNetDocumentBinding.ForDocument<AetheriaRuntimeDaemonCommandBoundaryDocument>(registry)" ,
"CultNetDocumentBinding.ForDocument<AetheriaRuntimeDaemonFrameDocument>(registry)" ,
"CultNetDocumentBinding.ForDocument<AetheriaRuntimeDaemonSoaViewDocument>(registry)" ,
"CultNetDocumentBinding.ForDocument<AetheriaRuntimeDaemonCommandDocument>(registry)" ,
"typeof(AetheriaRuntimeDaemonProviderAdvertisementDocument)" ,
"typeof(AetheriaRuntimeDaemonHealthDocument)" ,
"typeof(AetheriaRuntimeDaemonCommandBoundaryDocument)" ,
"typeof(AetheriaRuntimeDaemonFrameDocument)" ,
"typeof(AetheriaRuntimeDaemonSoaViewDocument)" ,
"typeof(AetheriaRuntimeDaemonCommandDocument)"
} ;
var missingDaemonRegistrySymbols = requiredDaemonRegistrySymbols
. Where ( symbol = > ! documentRegistry . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDaemonRegistrySymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon API documents are not registered as CultCache/CultNet records: " +
string . Join ( ", " , missingDaemonRegistrySymbols ) ) ;
}
var requiredDaemonNodeSymbols = new [ ]
{
"PutDaemonProviderAdvertisementAsync(" ,
"GetDaemonProviderAdvertisementAsync(" ,
"PutDaemonHealthAsync(" ,
"GetDaemonHealthAsync(" ,
"PutDaemonCommandBoundaryAsync(" ,
"GetDaemonCommandBoundaryAsync(" ,
"PutDaemonFrameAsync(" ,
"GetDaemonFrameAsync(" ,
"PutDaemonSoaViewAsync(" ,
"GetDaemonSoaViewAsync(" ,
"PutDaemonGameSurfaceAsync(" ,
"GetDaemonGameSurfaceAsync(" ,
"PutDaemonGameTuiSurfaceAsync(" ,
"GetDaemonGameTuiSurfaceAsync(" ,
"PutDaemonEditorSurfaceAsync(" ,
"GetDaemonEditorSurfaceAsync(" ,
"PutDaemonEditorTuiSurfaceAsync(" ,
"GetDaemonEditorTuiSurfaceAsync("
} ;
var missingDaemonNodeSymbols = requiredDaemonNodeSymbols
. Where ( symbol = > ! stateNode . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDaemonNodeSymbols . Length > 0 )
{
throw new InvalidOperationException (
"AetheriaStateNode no longer exposes daemon Verse API records ergonomically: " +
string . Join ( ", " , missingDaemonNodeSymbols ) ) ;
}
if ( ! daemonSurfaceProjector . Contains ( "public static EveSurfaceState ToState(AetheriaRuntimeSurfaceDocument document)" , StringComparison . Ordinal ) | |
! daemonSurfaceProjector . Contains ( "EveCommandTemplate" , StringComparison . Ordinal ) | |
! daemonSurfaceProjector . Contains ( "EveSurfaceComponent" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException ( "Daemon Eve surface projector no longer lowers daemon surfaces into registered Eve surface state." ) ;
}
if ( ! daemonPublicationStore . Contains ( "GetCommandBoundaryPath(" , StringComparison . Ordinal ) | |
! daemonDocuments . Contains ( "AetheriaRuntimeDaemonCommandKinds" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Daemon command boundary no longer exposes typed daemon command metadata." ) ;
}
var forbiddenDaemonQueueSymbols = new [ ]
{
"public static AetheriaRuntimeDaemonCommandEnvelope QueueCommand(" ,
"AetheriaRuntimeDaemonCommandLog." ,
"AetheriaRuntimeDaemonCommandLog.QueueCommand(" ,
"var envelope = AetheriaRuntimeDaemonCommandLog.QueueCommand" ,
".daemon.pending" ,
"ReadPending("
} ;
var daemonQueueSource = daemonDocuments + "\n" + daemonTickRunner + "\n" + daemonPublicationStore + "\n" + tests ;
var survivingDaemonQueueSymbols = forbiddenDaemonQueueSymbols
. Where ( symbol = > daemonQueueSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( survivingDaemonQueueSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon command boundary still exposes mailbox queue semantics: " +
string . Join ( ", " , survivingDaemonQueueSymbols ) ) ;
}
2026-06-21 09:37:54 +01:00
if ( daemonDocuments . Contains ( "PendingBeforeApply" , StringComparison . Ordinal ) )
2026-06-21 08:56:07 +01:00
{
throw new InvalidOperationException (
2026-06-21 09:37:54 +01:00
"Daemon health still exposes the legacy PendingBeforeApply queue vocabulary instead of observed Verse command documents." ) ;
2026-06-21 08:56:07 +01:00
}
var requiredProviderAdvertisementSymbols = new [ ]
{
"private const string DaemonCommandBoundaryId = \"aetheria.daemon.commands\"" ,
"private const string DaemonWitnessTransport = \"cultcache-witness\"" ,
"AetheriaRuntimeDaemonSchemas.ProviderAdvertisement" ,
"AetheriaRuntimeDaemonSchemas.Frame" ,
"AetheriaRuntimeDaemonSchemas.SoaView" ,
"AetheriaRuntimeDaemonSchemas.Health" ,
"AetheriaRuntimeDaemonSchemas.CommandBoundary" ,
"AetheriaRuntimeDaemonSchemas.GameSurface" ,
"AetheriaRuntimeDaemonSchemas.EditorSurface" ,
"AetheriaRuntimeDaemonSchemas.Command" ,
"AetheriaRuntimeStateBoundary.GetDaemonProviderPath(statePath)" ,
"AetheriaRuntimeStateBoundary.GetDaemonFramePath(statePath)" ,
"AetheriaRuntimeStateBoundary.GetDaemonSoaViewPath(statePath)" ,
"AetheriaRuntimeStateBoundary.GetDaemonHealthPath(statePath)" ,
"AetheriaRuntimeStateBoundary.GetDaemonCommandBoundaryPath(statePath)" ,
"AetheriaRuntimeStateBoundary.GetDaemonGameSurfacePath(statePath)" ,
"AetheriaRuntimeStateBoundary.GetDaemonGameTuiSurfacePath(statePath)" ,
"AetheriaRuntimeStateBoundary.GetDaemonEditorSurfacePath(statePath)" ,
"AetheriaRuntimeStateBoundary.GetDaemonEditorTuiSurfacePath(statePath)" ,
"public const string DaemonGameSurfaceKey = \"eve:surface:aetheria.daemon.game\"" ,
"public const string DaemonGameTuiSurfaceKey = \"eve:surface:aetheria.daemon.game.tui\"" ,
"public const string DaemonEditorSurfaceKey = \"eve:surface:aetheria.daemon.editor\"" ,
"public const string DaemonEditorTuiSurfaceKey = \"eve:surface:aetheria.daemon.editor.tui\"" ,
"AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId" ,
"AetheriaRuntimeDaemonGameSurfaceBuilder.TuiSurfaceId" ,
"AetheriaRuntimeDaemonEditorSurfaceBuilder.SurfaceId" ,
"AetheriaRuntimeDaemonEditorSurfaceBuilder.TuiSurfaceId"
} ;
var missingProviderAdvertisementSymbols = requiredProviderAdvertisementSymbols
. Where ( symbol = > ! providerAdvertisement . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingProviderAdvertisementSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Odin-visible provider advertisement no longer points at daemon-owned Verse witnesses: " +
string . Join ( ", " , missingProviderAdvertisementSymbols ) ) ;
}
var requiredTickSymbols = new [ ]
{
"VerseId" ,
"CultMeshAddress" ,
"AetheriaRuntimeDaemonCommandBoundaryDocument.Create" ,
"AetheriaRuntimeDaemonProviderAdvertisementDocument.Create" ,
"AetheriaRuntimeDaemonPublicationStore.PublishCommandBoundary" ,
"AetheriaRuntimeDaemonPublicationStore.PublishProviderAdvertisement" ,
"AetheriaRuntimeDaemonPublicationStore.PublishHealth" ,
"AetheriaRuntimeDaemonGameSurfaceBuilder.Build" ,
"AetheriaRuntimeDaemonPublicationStore.PublishGameSurface" ,
"AetheriaRuntimeDaemonPublicationStore.PublishGameTuiSurface" ,
"AetheriaRuntimeDaemonEditorSurfaceBuilder.Build" ,
"AetheriaRuntimeDaemonPublicationStore.PublishEditorSurface" ,
"AetheriaRuntimeDaemonPublicationStore.PublishEditorTuiSurface" ,
"ObservedCommands" ,
2026-06-21 09:37:54 +01:00
"AccountedCommandIds" ,
"frame.AccountedCommandIds = accountedBeforeTick" ,
"ObservedCommandCount = observedCommands.Length" ,
2026-06-21 08:56:07 +01:00
"PublicationSource = \"daemon-published\""
} ;
var missingTickSymbols = requiredTickSymbols
. Where ( symbol = > ! daemonTickRunner . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingTickSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon tick does not publish Verse-facing provider/health/command-boundary records: " +
string . Join ( ", " , missingTickSymbols ) ) ;
}
var requiredDaemonHostSymbols = new [ ]
{
"<OutputType>Exe</OutputType>" ,
"ProjectReference Include=\"..\\Aetheria.State\\Aetheria.State.csproj\"" ,
"AetheriaStateNode.OpenAsync(" ,
"startServer: true" ,
"new AetheriaVerseDiscoveryHost(node)" ,
"discoveryHost.Update(" ,
"PublishDaemonApiDocumentsAsync(node, result)" ,
"PutDaemonFrameAsync(result.Frame)" ,
"PutDaemonGameSurfaceAsync(AetheriaRuntimeEveSurfaceStateProjector.ToState(gameSurface))" ,
"PutDaemonEditorTuiSurfaceAsync(AetheriaRuntimeEveSurfaceStateProjector.ToState(editorTuiSurface))" ,
"AetheriaEveCommandBridge.AcceptObservedAsync(" ,
"ReadObservedDaemonCommands()" ,
2026-06-21 09:37:54 +01:00
"AccountedCommandIds = currentFrame?.AccountedCommandIds ?? Array.Empty<string>()" ,
2026-06-21 08:56:07 +01:00
"AetheriaRuntimeDaemonTickRunner.Tick(" ,
"AetheriaProviderAdvertisementProjector.Build(verseHost, node.StatePath, updatedAtUtc)" ,
"AetheriaOperationsSurfaceProjector.Build(eveStatus, verseHost, runtimeSession)" ,
"AetheriaPlayerSettingsSurfaceProjector.Build(playerSettings, playerSettingsUpdatedAt)" ,
"Role = \"verse-daemon\"" ,
"Console.CancelKeyPress" ,
"Aetheria Verse daemon is running"
} ;
var daemonHostSource = daemonHostProject + "\n" + daemonHostProgram ;
var missingDaemonHostSymbols = requiredDaemonHostSymbols
. Where ( symbol = > ! daemonHostSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDaemonHostSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria.State.Daemon no longer has Odin/VoidBot-shaped Verse daemon host authority: " +
string . Join ( ", " , missingDaemonHostSymbols ) ) ;
}
var forbiddenDaemonHostSymbols = new [ ]
{
"AetheriaRuntimeDaemonCommandLog." ,
"DeleteProcessedCommands" ,
"new HttpListener" ,
"UseUrls(" ,
"MapGet(" ,
"JsonSerializer.Serialize"
} ;
2026-06-21 09:37:54 +01:00
if ( daemonHostSource . Contains ( "KeepAccountedCommandRecords" , StringComparison . Ordinal ) | |
daemonHostSource . Contains ( "--keep-accounted-command-records" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria.State.Daemon still exposes command-retention toggles after adopting receipt-ledger command accounting." ) ;
}
if ( stateNode . Contains ( "DeleteDaemonCommandAsync(" , StringComparison . Ordinal ) | |
daemonHostSource . Contains ( "DeleteAccountedDaemonCommandsAsync" , StringComparison . Ordinal ) | |
daemonHostSource . Contains ( "DeleteDaemonCommandAsync(" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Daemon command documents are still consumed by deletion instead of accounted by frame receipts." ) ;
}
2026-06-21 08:56:07 +01:00
var survivingDaemonHostSymbols = forbiddenDaemonHostSymbols
. Where ( symbol = > daemonHostSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( survivingDaemonHostSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Aetheria.State.Daemon has drifted into private command-log/dashboard ownership instead of CultMesh Verse publication: " +
string . Join ( ", " , survivingDaemonHostSymbols ) ) ;
}
var requiredGameSurfaceSymbols = new [ ]
{
"public const string SurfaceId = \"aetheria.game\"" ,
"public const string TuiSurfaceId = \"aetheria.game.tui\"" ,
"AetheriaRuntimeDaemonFrameDocument frame" ,
"AetheriaRuntimeDaemonHealthDocument health" ,
"AetheriaRuntimeDaemonCommandBoundaryDocument commandBoundary" ,
"\"game.daemon\"" ,
"\"Typed Command Boundary\"" ,
"\"commandBody\"" ,
"nameof(AetheriaRuntimeDaemonCommandDocument)" ,
"AetheriaRuntimeDaemonCommandKinds.SetMoveVector"
} ;
var missingGameSurfaceSymbols = requiredGameSurfaceSymbols
. Where ( symbol = > ! daemonGameSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingGameSurfaceSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon game Eve surface builder is incomplete: " +
string . Join ( ", " , missingGameSurfaceSymbols ) ) ;
}
var requiredEditorSurfaceSymbols = new [ ]
{
"public const string SurfaceId = \"aetheria.daemon.editor\"" ,
"public const string TuiSurfaceId = \"aetheria.daemon.editor.tui\"" ,
"AetheriaRuntimeDaemonProviderAdvertisementDocument provider" ,
"AetheriaRuntimeDaemonHealthDocument health" ,
"AetheriaRuntimeDaemonCommandBoundaryDocument commandBoundary" ,
"\"editor.daemon\"" ,
"\"Verse Provider\"" ,
"\"Witnesses\"" ,
"\"Typed Commands\"" ,
"AetheriaRuntimeDaemonCommandBoundaryEntry"
} ;
var missingEditorSurfaceSymbols = requiredEditorSurfaceSymbols
. Where ( symbol = > ! daemonEditorSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingEditorSurfaceSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon editor Eve surface builder is incomplete: " +
string . Join ( ", " , missingEditorSurfaceSymbols ) ) ;
}
var requiredTestSymbols = new [ ]
{
"TryReadProviderAdvertisement(" ,
"TryReadHealth(" ,
"TryReadCommandBoundary(" ,
"TryReadGameSurface(" ,
"TryReadEditorSurface(" ,
"TryReadGameTuiSurface(" ,
"TryReadEditorTuiSurface(" ,
"AetheriaRuntimeDaemonSchemas.ProviderAdvertisement" ,
"AetheriaRuntimeDaemonSchemas.CommandBoundary" ,
"AetheriaRuntimeDaemonSchemas.Health" ,
"AetheriaRuntimeDaemonSchemas.GameSurface" ,
"AetheriaRuntimeDaemonSchemas.EditorSurface" ,
"AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId" ,
"AetheriaRuntimeDaemonEditorSurfaceBuilder.SurfaceId" ,
"AetheriaRuntimeDaemonCommandKinds.TransferCargoItem" ,
"nameof(AetheriaRuntimeCargoTransferCommand)"
} ;
var missingTestSymbols = requiredTestSymbols
. Where ( symbol = > ! tests . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingTestSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon Verse publication tests are incomplete: " +
string . Join ( ", " , missingTestSymbols ) ) ;
}
var requiredSmokeSymbols = new [ ]
{
"PutDaemonProviderAdvertisementAsync(daemonProvider)" ,
"PutDaemonHealthAsync(daemonHealth)" ,
"PutDaemonCommandBoundaryAsync(daemonCommandBoundary)" ,
"AetheriaCommandPort.OpenAsync(" ,
"daemonCommandPort.SubmitDaemonCommandAsync(" ,
"eveCommandPort.SubmitEveCommandAsync(" ,
"PutDaemonFrameAsync(daemonFrame)" ,
"PutDaemonGameSurfaceAsync(AetheriaRuntimeEveSurfaceStateProjector.ToState(daemonGameSurface))" ,
"GetDaemonProviderAdvertisementAsync()" ,
"GetDaemonHealthAsync()" ,
"GetDaemonCommandBoundaryAsync()" ,
"GetDaemonFrameAsync()" ,
"GetDaemonGameSurfaceAsync()" ,
"DaemonGameSurfaceKey" ,
"DaemonGameTuiSurfaceKey" ,
"DaemonEditorSurfaceKey" ,
"DaemonEditorTuiSurfaceKey"
} ;
var missingSmokeSymbols = requiredSmokeSymbols
. Where ( symbol = > ! smoke . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSmokeSymbols . Length > 0 )
{
throw new InvalidOperationException (
"State smoke no longer proves daemon Verse API records round-trip through CultCache: " +
string . Join ( ", " , missingSmokeSymbols ) ) ;
}
var requiredNoteSymbols = new [ ]
{
"Odin is the all-seer, not the owner." ,
"Idunn keeps the daemon alive from daemon-published health and command-boundary records." ,
"Bifrost hosts the MCP crossing for Codex and other xeno agents." ,
"The Aetheria daemon remains the side-effect owner." ,
"gamecult.aetheria.daemon_provider_advertisement.v1" ,
"gamecult.aetheria.daemon_health.v1" ,
"gamecult.aetheria.daemon_command_boundary.v1" ,
"gamecult.aetheria.daemon_frame.v1" ,
"gamecult.aetheria.daemon_soa_view.v1" ,
"gamecult.aetheria.daemon_game_surface.v1" ,
"gamecult.aetheria.daemon_editor_surface.v1" ,
"gamecult.eve.provider_advertisement.v1` is the Odin-visible Eve provider card" ,
"gamecult.aetheria.daemon_provider_advertisement.v1` is the daemon-owned Aetheria runtime contract" ,
"The bridge layer must point at daemon-owned witnesses instead of becoming a second source of truth." ,
"Long term, Odin should discover the daemon-owned provider advertisement and interface bindings directly" ,
"Queues are an implementation detail." ,
"Eve commands and daemon commands are typed `gamecult.eve.command.v1` and `gamecult.aetheria.daemon_command.v1` records in the Aetheria state graph" ,
"`AetheriaCommandPort` is the neutral command submission implementation for typed command records." ,
"Unity-side command lowerers may use `AetheriaRuntimeCommandPort` as a facade over that same port." ,
"`CommandLog`, `Inbox`, `mailbox`, `.eve.commands`, `.daemon.commands`, `.cc.pending`, or `" + "Pending" + "CultCacheStore` in Unity-facing daemon/Eve command code" ,
"Do not add a private HTTP dashboard, Unity-only inspector, JSON status blob, or agent-specific daemon wrapper as canonical truth."
} ;
var missingNoteSymbols = requiredNoteSymbols
. Where ( symbol = > ! note . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingNoteSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon Verse shape note no longer records the Odin/VoidBot daemon authority contract: " +
string . Join ( ", " , missingNoteSymbols ) ) ;
}
}
static void RequireTypedEveCommandBodies ( string root )
{
var eveCommandDocumentPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeEveCommandDocument.cs" ) ;
var eveCommandClientPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeEveCommandClient.cs" ) ;
var runtimeCommandPortPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeCommandPort.cs" ) ;
var eveCommandBridgePath = Path . Combine ( root , "Aetheria.State" , "AetheriaEveCommandBridge.cs" ) ;
var stateNodePath = Path . Combine ( root , "Aetheria.State" , "AetheriaStateNode.cs" ) ;
var documentRegistryPath = Path . Combine ( root , "Aetheria.State" , "AetheriaDocumentRegistry.cs" ) ;
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
var evePresenterPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.eve-runtime" , "Runtime" , "AetheriaEveSurfacePresenter.cs" ) ;
var requiredFiles = new [ ] { eveCommandDocumentPath , eveCommandClientPath , runtimeCommandPortPath , eveCommandBridgePath , stateNodePath , documentRegistryPath , actionGameManagerPath , mainMenuPath , evePresenterPath } ;
var missingFiles = requiredFiles
. Where ( path = > ! File . Exists ( path ) )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( missingFiles . Length > 0 )
{
throw new InvalidOperationException (
"Typed Eve command bodies cannot be verified because required files are missing: " +
string . Join ( ", " , missingFiles ) ) ;
}
var eveCommandDocument = File . ReadAllText ( eveCommandDocumentPath ) ;
var eveCommandClient = File . ReadAllText ( eveCommandClientPath ) ;
var runtimeCommandPort = File . ReadAllText ( runtimeCommandPortPath ) ;
var eveCommandBridge = File . ReadAllText ( eveCommandBridgePath ) ;
var stateNode = File . ReadAllText ( stateNodePath ) ;
var documentRegistry = File . ReadAllText ( documentRegistryPath ) ;
var actionGameManager = File . ReadAllText ( actionGameManagerPath ) ;
var evePresenter = File . ReadAllText ( evePresenterPath ) ;
var requiredDocumentSymbols = new [ ]
{
"public sealed class AetheriaRuntimePlayerSettingsCommandBody" ,
"public sealed class AetheriaRuntimeInputSettingsCommandBody" ,
"public AetheriaRuntimePlayerSettingsCommandBody PlayerSettings" ,
"public AetheriaRuntimeInputSettingsCommandBody InputSettings" ,
"public AetheriaRuntimeLoadoutTemplateCommit? LoadoutTemplate" ,
"[CultDocument(\"gamecult.eve.command\", \"gamecult.eve.command.v1\")]"
} ;
var missingDocumentSymbols = requiredDocumentSymbols
. Where ( symbol = > ! eveCommandDocument . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDocumentSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Eve submitted command document is missing typed command bodies: " +
string . Join ( ", " , missingDocumentSymbols ) ) ;
}
var forbiddenDocumentSymbols = new [ ]
{
"IReadOnlyDictionary<string, string> Payload" ,
"Dictionary<string, string> Payload" ,
"SubmitSurfaceRequest(" ,
"CreateSurfaceRequestCommand("
} ;
var forbiddenDocumentHits = forbiddenDocumentSymbols
. Where ( symbol = > eveCommandDocument . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( forbiddenDocumentHits . Length > 0 )
{
throw new InvalidOperationException (
"Eve submitted command document still persists string payload maps: " +
string . Join ( ", " , forbiddenDocumentHits ) ) ;
}
var requiredTypedCommandSymbols = new [ ]
{
"public static class AetheriaRuntimeEveCommands" ,
"public static class AetheriaRuntimeEveCommandClient" ,
"public sealed class AetheriaRuntimeCommandPort" ,
"public sealed class AetheriaCommandPort" ,
"namespace Aetheria.State" ,
"namespace GameCult.Aetheria.State.Unity" ,
"global::Aetheria.State.AetheriaCommandPort" ,
"public static class AetheriaRuntimeCommandSubmitter" ,
"TrySubmitEveCommand(" ,
"TrySubmitDaemonCommand(" ,
"CreatePlayerSettingsCommand(" ,
"CreateInputSettingsCommand(" ,
"CreateCatalogCommand(" ,
"CreateOperationsCommand(" ,
"CreateVerseHostCommand(" ,
"CreateLoadoutTemplateCommand(" ,
"TrySendPlayerSettingsCommand(" ,
"TrySendInputSettingsCommand(" ,
"TrySendVerseHostCommand(" ,
"TrySendLoadoutTemplateCommand(" ,
"TrySendKnownSurfaceCommand(" ,
"TryCreateKnownSurfaceCommand(" ,
"ToDocument(" ,
"AcceptObservedAsync(" ,
"node.ReadObservedEveCommands()" ,
2026-06-21 09:37:54 +01:00
"AccountedCommandIds" ,
2026-06-21 08:56:07 +01:00
"SubmitEveCommandAsync(" ,
"ReadObservedEveCommands(" ,
"EveCommandKey(" ,
"CultNetDocumentBinding.ForDocument<AetheriaRuntimeEveCommandDocument>" ,
"typeof(AetheriaRuntimeEveCommandDocument)" ,
"SubmitPlayerSettingsCommand(" ,
"command.PlayerSettings.PlayerName" ,
"command.InputSettings.ActionName" ,
"command.InputSettings.InputSystemPath" ,
"AetheriaRuntimeEveCommands.TrySendInputSettingsCommand(" ,
"AetheriaRuntimeEveCommands.TrySendVerseHostCommand(" ,
"AetheriaRuntimeEveCommands.TrySendLoadoutTemplateCommand(" ,
"AetheriaRuntimeEveCommands.TrySendKnownSurfaceCommand("
} ;
var mainMenu = File . ReadAllText ( mainMenuPath ) ;
var typedCommandSources = eveCommandClient + "\n" + runtimeCommandPort + "\n" + eveCommandBridge + "\n" + stateNode + "\n" + documentRegistry + "\n" + actionGameManager + "\n" + mainMenu + "\n" + evePresenter ;
2026-06-21 09:37:54 +01:00
if ( typedCommandSources . Contains ( "DeleteEveCommandAsync(" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Eve command documents are still consumed by deletion instead of accounted by acceptance receipts." ) ;
}
if ( eveCommandBridge . Contains ( "AcceptedPaths" , StringComparison . Ordinal ) | |
eveCommandBridge . Contains ( "RejectedPaths" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Eve command acceptance report still exposes path vocabulary instead of command-id receipts." ) ;
}
2026-06-21 08:56:07 +01:00
var missingTypedCommandSymbols = requiredTypedCommandSymbols
. Where ( symbol = > ! typedCommandSources . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingTypedCommandSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Eve command append/apply path is missing typed command body usage: " +
string . Join ( ", " , missingTypedCommandSymbols ) ) ;
}
var forbiddenUnitySubmitterSymbols = new [ ]
{
"AetheriaRuntimeCommandSubmitter.TrySubmitEveCommand(" ,
"AetheriaRuntimeCommandSubmitter.TrySubmitDaemonCommand(" ,
"AetheriaRuntimeEveCommandClient.ToDocument("
} ;
var unitySubmitterSources = new Dictionary < string , string >
{
[Path.GetRelativePath(root, actionGameManagerPath)] = actionGameManager ,
[Path.GetRelativePath(root, mainMenuPath)] = mainMenu ,
[Path.GetRelativePath(root, evePresenterPath)] = evePresenter
} ;
var unitySubmitterHits = unitySubmitterSources
. SelectMany ( pair = > forbiddenUnitySubmitterSymbols
. Where ( symbol = > pair . Value . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{pair.Key}: {symbol}" ) )
. ToArray ( ) ;
if ( unitySubmitterHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity-facing Eve command code still assembles transport documents instead of using typed send helpers: " +
string . Join ( ", " , unitySubmitterHits ) ) ;
}
var forbiddenGenericSurfaceCommandSymbols = new [ ]
{
"SubmitSurfaceRequest(" ,
"CreateSurfaceRequestCommand("
} ;
var genericSurfaceCommandHits = forbiddenGenericSurfaceCommandSymbols
. Where ( symbol = > typedCommandSources . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( genericSurfaceCommandHits . Length > 0 )
{
throw new InvalidOperationException (
"Eve command edge still exposes a generic surface-command submission back door: " +
string . Join ( ", " , genericSurfaceCommandHits ) ) ;
}
var forbiddenQueueSymbols = new [ ]
{
"public static class AetheriaRuntimeEveCommandLog" ,
"internal static class AetheriaRuntimeEveCommandLog" ,
"public static AetheriaRuntimeEveCommandEnvelope QueueCommand(" ,
"public static AetheriaRuntimeEveCommandEnvelope QueuePlayerSettingsCommand(" ,
"public static AetheriaRuntimeEveCommandEnvelope QueueInputSettingsCommand(" ,
"public static AetheriaRuntimeEveCommandEnvelope QueueCatalogCommand(" ,
"public static AetheriaRuntimeEveCommandEnvelope QueueOperationsCommand(" ,
"public static AetheriaRuntimeEveCommandEnvelope QueueVerseHostCommand(" ,
"public static AetheriaRuntimeEveCommandEnvelope QueueLoadoutTemplateCommand(" ,
"private static AetheriaRuntimeEveCommandEnvelope QueueTypedCommand(" ,
"AetheriaRuntimeEveCommandLog." ,
"AetheriaRuntimeEveCommandLog.Queue" ,
"AetheriaRuntimeEveCommandInbox" ,
"GetInboxDirectory(" ,
"Read" + "Submitted(" ,
".eve.commands" ,
"ReadPending(" ,
".eve.pending"
} ;
var forbiddenQueueSources = new Dictionary < string , string >
{
["Eve command client"] = eveCommandClient ,
["Eve command bridge"] = eveCommandBridge ,
["Aetheria state node"] = stateNode ,
["ActionGameManager"] = actionGameManager ,
["MainMenu"] = mainMenu ,
["Eve presenter"] = evePresenter
} ;
var survivingQueueSymbols = forbiddenQueueSources
. SelectMany ( source = > forbiddenQueueSymbols
. Where ( symbol = > source . Value . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{source.Key}: {symbol}" ) )
. ToArray ( ) ;
if ( survivingQueueSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Eve command edge still exposes queue-shaped public API: " +
string . Join ( ", " , survivingQueueSymbols ) ) ;
}
var rendererLogSources = new Dictionary < string , string >
{
["ActionGameManager"] = actionGameManager ,
["MainMenu"] = mainMenu ,
["Eve presenter"] = evePresenter
} ;
var rendererLogHits = rendererLogSources
. Where ( source = > source . Value . Contains ( "AetheriaRuntimeEveCommandLog." , StringComparison . Ordinal ) )
. Select ( source = > $"{source.Key}: AetheriaRuntimeEveCommandLog." )
. ToArray ( ) ;
if ( rendererLogHits . Length > 0 )
{
throw new InvalidOperationException (
"Renderer code still speaks to the Eve mailbox log instead of the typed command port: " +
string . Join ( ", " , rendererLogHits ) ) ;
}
if ( actionGameManager . Contains ( "TryQueueRuntimeEveCommand(" , StringComparison . Ordinal ) | |
actionGameManager . Contains ( "new EveSurfaceCommandRequest(" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"ActionGameManager still manufactures generic Eve surface command payloads instead of typed Eve command bodies." ) ;
}
}
static void RequireMainMenuVerseHostProjection ( string root )
{
var packageSnapshotPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeCatalogSnapshot.cs" ) ;
var packageStorePath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeCatalogStore.cs" ) ;
var runtimeStateReaderPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeStateReader.cs" ) ;
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
var mainMenuSurfaceBuilderPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeMainMenuSurfaceBuilder.cs" ) ;
var requiredFiles = new [ ]
{
packageSnapshotPath ,
packageStorePath ,
runtimeStateReaderPath ,
mainMenuPath ,
mainMenuSurfaceBuilderPath
} ;
var missingFiles = requiredFiles
. Where ( path = > ! File . Exists ( path ) )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( missingFiles . Length > 0 )
{
throw new InvalidOperationException (
"Main-menu Verse projection cannot be verified because required files are missing: " +
string . Join ( ", " , missingFiles ) ) ;
}
var packageSnapshot = File . ReadAllText ( packageSnapshotPath ) ;
var packageStore = File . ReadAllText ( packageStorePath ) ;
var runtimeStateReader = File . ReadAllText ( runtimeStateReaderPath ) ;
var mainMenu = File . ReadAllText ( mainMenuPath ) ;
var mainMenuSurfaceBuilder = File . ReadAllText ( mainMenuSurfaceBuilderPath ) ;
var requiredSnapshotSymbols = new [ ]
{
"public sealed class AetheriaRuntimeVerseHostSettingsSnapshot" ,
"public string VerseId" ,
"public string CultMeshAddress" ,
"public string Visibility"
} ;
var missingSnapshotSymbols = requiredSnapshotSymbols
. Where ( symbol = > ! packageSnapshot . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSnapshotSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Unity runtime snapshot does not expose typed verse-host settings: " +
string . Join ( ", " , missingSnapshotSymbols ) ) ;
}
var requiredStoreSymbols = new [ ]
{
"VerseHostSettingsSchema = \"aetheria.verse_host_settings\"" ,
"VerseHostSettingsKey = \"global:aetheria.verse_host_settings.v1\"" ,
"ReadVerseHostSettings(string stateFilePath)" ,
"ReadVerseHostSettingsPayload" ,
"AetheriaRuntimeVerseHostSettingsSnapshot"
} ;
var missingStoreSymbols = requiredStoreSymbols
. Where ( symbol = > ! packageStore . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingStoreSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Unity runtime store does not preserve typed verse-host read authority: " +
string . Join ( ", " , missingStoreSymbols ) ) ;
}
if ( ! runtimeStateReader . Contains ( "ReadVerseHostSettings" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Shared runtime state reader no longer exposes typed verse-host settings." ) ;
}
var requiredMainMenuSymbols = new [ ]
{
"LatestVerseHostSettings(AetheriaRuntimeStateBootReport stateBoot)" ,
"AetheriaRuntimeStateReader.ReadVerseHostSettings(stateBoot.StateFilePath)" ,
"ProjectMainMenuSurfaceState(" ,
"AetheriaRuntimeMainMenuSurfaceBuilder.BuildRoot("
} ;
var requiredBuilderSymbols = new [ ]
{
"\"Client Target\"" ,
"\"Transport\"" ,
"\"Target Source\"" ,
"\"Verse\"" ,
"\"Visibility\"" ,
"\"CultMesh\"" ,
"The client target chooses which Verse it follows; game truth belongs to the daemon serving"
} ;
var missingMainMenuSymbols = requiredMainMenuSymbols
. Where ( symbol = > ! mainMenu . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingMainMenuSymbols . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu no longer lowers daemon-owned Verse identity through its Eve shell: " +
string . Join ( ", " , missingMainMenuSymbols ) ) ;
}
var missingBuilderSymbols = requiredBuilderSymbols
. Where ( symbol = > ! mainMenuSurfaceBuilder . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBuilderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared main-menu surface builder no longer lowers daemon-owned Verse identity through its Eve shell: " +
string . Join ( ", " , missingBuilderSymbols ) ) ;
}
}
2026-06-17 09:51:49 +01:00
2026-06-16 06:09:46 +01:00
static void RequireMainMenuContinueRunState ( string root )
{
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
var mainMenu = File . Exists ( mainMenuPath )
? File . ReadAllText ( mainMenuPath )
: throw new InvalidOperationException ( "Cannot verify main-menu Continue path; MainMenu.cs is missing." ) ;
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
2026-06-21 08:56:07 +01:00
: throw new InvalidOperationException ( "Cannot verify Continue run authority; ActionGameManager.cs is missing." ) ;
var packageSnapshotPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeCatalogSnapshot.cs" ) ;
var packageSnapshot = File . Exists ( packageSnapshotPath )
? File . ReadAllText ( packageSnapshotPath )
: throw new InvalidOperationException ( "Cannot verify Continue entity identity; package runtime snapshots are missing." ) ;
var runtimeStateReaderPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeStateReader.cs" ) ;
var runtimeStateReader = File . Exists ( runtimeStateReaderPath )
? File . ReadAllText ( runtimeStateReaderPath )
: throw new InvalidOperationException ( "Cannot verify Continue entity readback; shared runtime state reader is missing." ) ;
var packageStorePath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeCatalogStore.cs" ) ;
var packageStore = File . Exists ( packageStorePath )
? File . ReadAllText ( packageStorePath )
: throw new InvalidOperationException ( "Cannot verify Continue entity payload readback; package runtime store is missing." ) ;
var requiredMenuSymbols = new [ ]
{
"LatestDaemonFrame" ,
"AetheriaRuntimeStateReader" ,
"TryReadDaemonFrame(stateBoot.StateFilePath, out var frame)" ,
"frame.IsAuthoritative" ,
"ContinueGame()" ,
"ActionGameManager.ObservedGalaxy = Galaxy.ProjectObservedDaemonRun(" ,
"SceneManager.LoadScene(\"ARPG\")"
} ;
var missingMenuSymbols = requiredMenuSymbols
. Where ( symbol = > ! mainMenu . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingMenuSymbols . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu Continue no longer selects an authoritative daemon frame: " +
string . Join ( ", " , missingMenuSymbols ) ) ;
}
if ( mainMenu . Contains ( "AddButton(\"Continue\", null)" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException ( "MainMenu Continue regressed to a null button." ) ;
}
var requiredGameplaySymbols = new [ ]
{
"ApplyLatestAuthoritativeDaemonFrame()" ,
"TryRestoreEntityGraphFromDaemonRun(observed.Run)" ,
"if (string.IsNullOrWhiteSpace(run.RunId))" ,
"Authoritative daemon frame does not identify a run id." ,
"Authoritative daemon frame has no zone snapshot" ,
"entity.RecordKey" ,
"run.CurrentEntityKey" ,
"CreateDaemonEntitySnapshots(runId, daemonZone)" ,
"CanApplyDaemonEntitySnapshotsInPlace" ,
"ApplyDaemonEntitySnapshotsInPlace" ,
"ReplaceZoneEntitiesFromTypedSnapshots" ,
"RestoreChildAndDockingRelationships" ,
"RestoreCurrentEntityBinding" ,
"RestoreEntityContactsFromTypedSnapshot" ,
"entity.Target.Value = target" ,
"RestoreCurrentEntityBinding(currentEntity, actionBarBindings)" ,
"RestoreActiveConsumablesFromTypedEntitySnapshot(entity, entitySnapshot)" ,
"RestoreRuntimeBehaviorStateFromTypedSnapshot(entity, entitySnapshot, restoredEntities)" ,
"ResolveRuntimeBehavior(entity, weaponState.OwnerKind, weaponState.OwnerIndex, weaponState.BehaviorIndex)" ,
"lockWeapon.RestoreRuntimeState(" ,
"drive.RestoreRuntimeState(" ,
"resourceScanner.RestoreRuntimeState(" ,
"entity.RestoreStatGrids(entitySnapshot.StatGrids)" ,
"RestoreThermalExposure((float)entitySnapshot.Heatstroke, (float)entitySnapshot.Hypothermia)" ,
"entity.HeatsinksEnabled = entitySnapshot.HeatsinksEnabled" ,
"RestoreDroppedPickupsFromDaemonZoneState"
} ;
var missingGameplaySymbols = requiredGameplaySymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingGameplaySymbols . Length > 0 )
{
throw new InvalidOperationException (
"ActionGameManager no longer has the daemon-frame Continue boot path: " +
string . Join ( ", " , missingGameplaySymbols ) ) ;
}
var forbiddenGameplaySymbols = new [ ]
{
"if (run == null ||\r\n run.CurrentZoneIndex < 0 ||\r\n run.CurrentZoneEntityIndex < 0)" ,
"var currentEntityKey = $\"{zoneEntityKeyPrefix}{run.CurrentZoneEntityIndex}.v1\""
} ;
var forbiddenGameplayHits = forbiddenGameplaySymbols
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( forbiddenGameplayHits . Length > 0 )
{
throw new InvalidOperationException (
"ActionGameManager still reconstructs Continue current-entity ownership from integer slot state: " +
string . Join ( ", " , forbiddenGameplayHits ) ) ;
}
var canonicalRunStatePath = Path . Combine ( root , "Aetheria.State" , "Documents" , "AetheriaRuntimeStateDocuments.cs" ) ;
var canonicalRunState = File . Exists ( canonicalRunStatePath )
? File . ReadAllText ( canonicalRunStatePath )
: throw new InvalidOperationException ( "Cannot verify canonical run-state authority; AetheriaRuntimeStateDocuments.cs is missing." ) ;
if ( canonicalRunState . Contains ( "CurrentZoneEntityIndex" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Canonical typed run state still exposes integer current-entity slot authority." ) ;
}
if ( packageSnapshot . Contains ( "CurrentZoneEntityIndex" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Unity package runtime run snapshot still exposes integer current-entity slot authority." ) ;
}
var snapshotDocumentPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeSnapshotDocuments.cs" ) ;
var snapshotDocument = File . Exists ( snapshotDocumentPath )
? File . ReadAllText ( snapshotDocumentPath )
: throw new InvalidOperationException ( "Cannot verify daemon snapshot authority; AetheriaRuntimeSnapshotDocuments.cs is missing." ) ;
if ( snapshotDocument . Contains ( "CurrentZoneEntityIndex" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Daemon runtime snapshot transport still exposes integer current-entity slot authority." ) ;
}
var forbiddenLegacyCommitSymbols = new [ ]
{
"public enum AetheriaRuntimeCommitKind" ,
"public sealed class AetheriaRuntimeCommitEnvelope" ,
"public sealed class AetheriaRuntimeStateCommitDocument" ,
"internal enum AetheriaRuntimeCommitKind" ,
"internal sealed class AetheriaRuntimeCommitEnvelope" ,
"internal sealed class AetheriaRuntimeStateCommitDocument"
} ;
var legacyCommitHits = forbiddenLegacyCommitSymbols
. Where ( symbol = > snapshotDocument . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( legacyCommitHits . Length > 0 )
{
throw new InvalidOperationException (
"Legacy runtime commit envelope leaked back into the daemon snapshot contract: " +
string . Join ( ", " , legacyCommitHits ) ) ;
}
var forbiddenLegacyFiles = new [ ]
{
Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeStateCommitDocument.cs" ) ,
Path . Combine ( root , "Aetheria.State" , "AetheriaLegacyRuntimeSnapshotImporter.cs" )
} ;
var existingLegacyFiles = forbiddenLegacyFiles
. Where ( File . Exists )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( existingLegacyFiles . Length > 0 )
{
throw new InvalidOperationException (
"Legacy runtime commit compatibility files must stay deleted: " +
string . Join ( ", " , existingLegacyFiles ) ) ;
}
var requiredPackageSymbols = new [ ]
{
"public string RecordKey" ,
"ReadEntitySnapshotPayload(record.Key, record.Payload)"
} ;
var missingPackageSymbols = requiredPackageSymbols
. Where ( symbol = >
! packageSnapshot . Contains ( symbol , StringComparison . Ordinal ) & &
! packageStore . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingPackageSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Unity package entity readback no longer preserves record identity for Continue: " +
string . Join ( ", " , missingPackageSymbols ) ) ;
}
var entitySourcePath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Entity.cs" ) ;
var entitySource = File . Exists ( entitySourcePath )
? File . ReadAllText ( entitySourcePath )
: throw new InvalidOperationException ( "Cannot verify Continue entity restore ownership; Entity.cs is missing." ) ;
var requiredEntityRestoreSymbols = new [ ]
{
"RestoreThermalExposure" ,
"RestoreActiveConsumable" ,
"RestoreStatGrids" ,
"RestoreHullConductivityGrid" ,
"new ConsumableItemEffect(item, this, remainingDuration, duration)"
} ;
var missingEntityRestoreSymbols = requiredEntityRestoreSymbols
. Where ( symbol = > ! entitySource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingEntityRestoreSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Entity no longer exposes narrow runtime-owned restore primitives for typed Continue state: " +
string . Join ( ", " , missingEntityRestoreSymbols ) ) ;
}
}
static void RequireUnityObserverDoesNotTickLocalSimulation ( string root )
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
var galaxyPath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Galaxy.cs" ) ;
var daemonDocumentsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonDocuments.cs" ) ;
var daemonRuntimeOperationsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonOperations.cs" ) ;
var daemonIntentPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonIntentState.cs" ) ;
var daemonObserverPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "AetheriaDaemonObserver.cs" ) ;
var daemonGameplayOperationsPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "AetheriaDaemonOperations.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify Unity observer authority; ActionGameManager.cs is missing." ) ;
var mainMenu = File . Exists ( mainMenuPath )
? File . ReadAllText ( mainMenuPath )
: throw new InvalidOperationException ( "Cannot verify Unity observer authority; MainMenu.cs is missing." ) ;
var galaxy = File . Exists ( galaxyPath )
? File . ReadAllText ( galaxyPath )
: throw new InvalidOperationException ( "Cannot verify Unity observer authority; Galaxy.cs is missing." ) ;
var daemonDocuments = File . Exists ( daemonDocumentsPath )
? File . ReadAllText ( daemonDocumentsPath )
: throw new InvalidOperationException ( "Cannot verify Unity observer authority; daemon command documents are missing." ) ;
var daemonOperationsSource = File . Exists ( daemonRuntimeOperationsPath )
? File . ReadAllText ( daemonRuntimeOperationsPath )
: throw new InvalidOperationException ( "Cannot verify Unity observer authority; daemon operations is missing." ) ;
var daemonIntent = File . Exists ( daemonIntentPath )
? File . ReadAllText ( daemonIntentPath )
: throw new InvalidOperationException ( "Cannot verify Unity observer authority; daemon intent state is missing." ) ;
var daemonObserver = File . Exists ( daemonObserverPath )
? File . ReadAllText ( daemonObserverPath )
: throw new InvalidOperationException ( "Cannot verify Unity observer authority; AetheriaDaemonObserver.cs is missing." ) ;
var daemonOperations = File . Exists ( daemonGameplayOperationsPath )
? File . ReadAllText ( daemonGameplayOperationsPath )
: throw new InvalidOperationException ( "Cannot verify Unity observer authority; AetheriaDaemonOperations.cs is missing." ) ;
2026-06-16 06:09:46 +01:00
2026-06-21 08:56:07 +01:00
var forbiddenGameplaySymbols = new [ ]
2026-06-16 06:09:46 +01:00
{
2026-06-21 08:56:07 +01:00
"CurrentGalaxy" ,
"Zone.Update(Time.deltaTime)" ,
"Zone.Update(" ,
"CurrentEntity.Update(" ,
".Update(Time.deltaTime)" ,
"Death.Subscribe(Die)" ,
"private void Die(CauseOfDeath" ,
"ZoneGenerator.GenerateZone" ,
"TryDock(" ,
"TryUndock(" ,
"IntroCutscene(" ,
"IntroDuration" ,
"ship.CultPositionXZ =" ,
"ship.CultDirection =" ,
"ship.CultVelocity =" ,
"CurrentEntity.Zone.Entities.Remove(CurrentEntity)" ,
"CurrentEntity.Zone = Zone" ,
"Zone.Entities.Add(CurrentEntity)" ,
"CurrentEntity.CultPositionXZ = TowingStation.CultPositionXZ" ,
"PopulateLevel(TowingStation.Zone.GalaxyZone)" ,
"AetheriaRuntimeStateCommitLog.QueueRunCheckpoint" ,
"AetheriaRuntimeStateCommitLog.QueuePlayerSettings" ,
"AetheriaRuntimeStateCommitLog.QueueLoadoutTemplate"
2026-06-16 06:09:46 +01:00
} ;
2026-06-21 08:56:07 +01:00
var forbiddenGameplayHits = forbiddenGameplaySymbols
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-16 06:09:46 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( forbiddenGameplayHits . Length > 0 )
2026-06-16 06:09:46 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity gameplay still owns local simulation or legacy write fallback instead of daemon observation: " +
string . Join ( ", " , forbiddenGameplayHits ) ) ;
2026-06-16 06:09:46 +01:00
}
2026-06-21 08:56:07 +01:00
if ( daemonObserver . Contains ( "AetheriaRuntimeCommandSubmitter.TrySubmitDaemonCommand(" , StringComparison . Ordinal ) )
2026-06-17 09:19:11 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity daemon observer still calls the generic command submitter instead of the typed daemon operation client." ) ;
2026-06-16 06:09:46 +01:00
}
var requiredGameplaySymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"ApplyLatestAuthoritativeDaemonFrame()" ,
"ResolveDaemonObserver()" ,
"observed.IsAuthoritative" ,
"TryRestoreEntityGraphFromDaemonRun(observed.Run)" ,
"CreateDaemonZoneConstructionBlueprint(daemonZone)" ,
"CreateDaemonEntitySnapshots(runId, daemonZone)" ,
2026-06-16 07:52:01 +01:00
"ReplaceZoneEntitiesFromTypedSnapshots" ,
2026-06-21 08:56:07 +01:00
"EntityConstructionBlueprintProjector.ProjectObservedFromBlueprint" ,
"public static Galaxy ObservedGalaxy" ,
"TryRequestDaemonMoveVector" ,
"TryRequestDaemonLookDirection" ,
"TryRequestDaemonTractorPower" ,
"observer.Operations.SetMoveVector" ,
"observer.Operations.SetLookDirection" ,
"observer.Operations.SetTractorPower"
2026-06-16 06:09:46 +01:00
} ;
var missingGameplaySymbols = requiredGameplaySymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingGameplaySymbols . Length > 0 )
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity gameplay no longer behaves as a daemon-frame observer and command lowerer: " +
2026-06-16 06:09:46 +01:00
string . Join ( ", " , missingGameplaySymbols ) ) ;
}
2026-06-16 06:41:35 +01:00
2026-06-21 08:56:07 +01:00
var requiredDaemonControlValidationSymbols = new [ ]
2026-06-16 17:19:58 +01:00
{
2026-06-21 08:56:07 +01:00
"case AetheriaRuntimeDaemonCommandKinds.SetTractorPower:" ,
"!IsNormalizedScalar(command.ScalarValue)" ,
"private static bool IsNormalizedScalar(double value)" ,
"!IsFinite(command.DirectionX)" ,
"!IsFinite(command.DirectionY)"
2026-06-16 17:19:58 +01:00
} ;
2026-06-21 08:56:07 +01:00
var missingDaemonControlValidationSymbols = requiredDaemonControlValidationSymbols
. Where ( symbol = > ! daemonOperationsSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDaemonControlValidationSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon continuous controls no longer validate normalized movement and tractor inputs: " +
string . Join ( ", " , missingDaemonControlValidationSymbols ) ) ;
}
2026-06-16 17:19:58 +01:00
2026-06-21 08:56:07 +01:00
if ( actionGameManager . Contains ( "EntityConstructionBlueprintProjector.InstantiateAuthoritativeFromBlueprint" , StringComparison . Ordinal ) | |
actionGameManager . Contains ( "EntityConstructionBlueprintProjector.InstantiateFromBlueprint" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Unity frame application must project observed daemon state, not instantiate authoritative gameplay entities." ) ;
}
var permittedEntityProjectionMethods = new [ ]
{
"ReplaceZoneEntitiesFromTypedSnapshots" ,
"RestoreChildAndDockingRelationships"
} ;
var unauthorizedEntityProjectionHits = FindMethodScopedLineHits (
actionGameManager ,
new [ ] { "Zone.Entities.Add(" , "Zone.Entities.Remove(" } )
. Where ( hit = > ! permittedEntityProjectionMethods . Contains ( hit . MethodName , StringComparer . Ordinal ) )
. Select ( hit = > $"{hit.MethodName}:{hit.LineNumber}: {hit.Line.Trim()}" )
. ToArray ( ) ;
if ( unauthorizedEntityProjectionHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity gameplay may only add/remove zone entities while lowering observed daemon snapshots: " +
string . Join ( "; " , unauthorizedEntityProjectionHits ) ) ;
}
var zonePath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "Zone.cs" ) ;
var zoneSource = File . Exists ( zonePath )
? File . ReadAllText ( zonePath )
: throw new InvalidOperationException ( "Cannot verify Unity observer authority; Zone.cs is missing." ) ;
var projectorPath = Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" , "EntityConstructionBlueprintProjector.cs" ) ;
var projectorSource = File . Exists ( projectorPath )
? File . ReadAllText ( projectorPath )
: throw new InvalidOperationException ( "Cannot verify Unity observer authority; EntityConstructionBlueprintProjector.cs is missing." ) ;
var requiredProjectionBoundarySymbols = new [ ]
{
"public static Galaxy ProjectObservedDaemonRun" ,
"private Galaxy(" ,
"AetheriaRuntimeRunCheckpointCommit run," ,
"public static Entity InstantiateAuthoritativeFromBlueprint" ,
"public static Entity ProjectObservedFromBlueprint" ,
"private static Entity BuildFromBlueprint" ,
"EntityConstructionBlueprintProjector.InstantiateAuthoritativeFromBlueprint(_itemManager, this, entityBlueprint)"
} ;
var projectionBoundaryCorpus = projectorSource + "\n" + zoneSource + "\n" + galaxy ;
var missingProjectionBoundarySymbols = requiredProjectionBoundarySymbols
. Where ( symbol = > ! projectionBoundaryCorpus . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingProjectionBoundarySymbols . Length > 0 )
{
throw new InvalidOperationException (
"Entity projection no longer separates authoritative construction from observed daemon lowering: " +
string . Join ( ", " , missingProjectionBoundarySymbols ) ) ;
}
if ( galaxy . Contains ( "public Galaxy(\r\n AetheriaRuntimeRunCheckpointCommit run," , StringComparison . Ordinal ) | |
galaxy . Contains ( "public Galaxy(\n AetheriaRuntimeRunCheckpointCommit run," , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Daemon-observed galaxy projection must be reached through ProjectObservedDaemonRun, not a public constructor." ) ;
}
var forbiddenDaemonQueueLanguage = new [ ]
{
"observer.Queue" ,
"_lastQueuedDaemon" ,
"_hasQueuedDaemon" ,
"Failed to queue Aetheria daemon" ,
"Queued Aetheria daemon"
} ;
var forbiddenDaemonQueueHits = forbiddenDaemonQueueLanguage
2026-06-16 17:19:58 +01:00
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( forbiddenDaemonQueueHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity gameplay still uses queue-shaped daemon language instead of typed sent operations: " +
string . Join ( ", " , forbiddenDaemonQueueHits ) ) ;
}
2026-06-16 17:19:58 +01:00
2026-06-21 08:56:07 +01:00
if ( daemonObserver . Contains ( "public AetheriaRuntimeDaemonCommandEnvelope Queue" , StringComparison . Ordinal ) | |
daemonObserver . Contains ( "Queued Aetheria daemon command" , StringComparison . Ordinal ) )
2026-06-16 17:19:58 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"AetheriaDaemonObserver still exposes queue-shaped public daemon APIs instead of typed Operations." ) ;
2026-06-16 17:19:58 +01:00
}
2026-06-21 08:56:07 +01:00
var requiredDaemonNavigationAuthoritySymbols = new [ ]
{
"ApplyEnterWormholeIntent(run, command, context.Intents)" ,
"ApplyTowToStationIntent(run, command, context.Intents)" ,
"MoveEntityToZone(run, actor, command.TargetZoneIndex" ,
"run.CurrentZoneIndex = targetZoneIndex" ,
"run.CurrentEntityKey = movedEntityKey" ,
"run.DiscoveredZoneIndices = discovered.ToArray()"
} ;
var missingDaemonNavigationAuthoritySymbols = requiredDaemonNavigationAuthoritySymbols
. Where ( symbol = > ! daemonOperationsSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDaemonNavigationAuthoritySymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon navigation commands no longer mutate canonical run state inside the daemon operation layer: " +
string . Join ( ", " , missingDaemonNavigationAuthoritySymbols ) ) ;
}
2026-06-16 17:27:28 +01:00
2026-06-21 08:56:07 +01:00
var requiredSentOperationSymbols = new [ ]
{
"_lastSentDaemonMoveVector" ,
"_lastSentDaemonLookDirection" ,
"_lastSentDaemonTractorPower" ,
"_hasSentDaemonMoveVector" ,
"_hasSentDaemonLookDirection" ,
"_hasSentDaemonTractorPower" ,
"Failed to send Aetheria daemon movement operation" ,
"Failed to send Aetheria daemon look operation" ,
"Failed to send Aetheria daemon tractor-power operation"
} ;
var missingSentOperationSymbols = requiredSentOperationSymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSentOperationSymbols . Length > 0 )
2026-06-16 17:27:28 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity gameplay no longer describes daemon input as sent typed operations: " +
string . Join ( ", " , missingSentOperationSymbols ) ) ;
2026-06-16 17:27:28 +01:00
}
2026-06-21 08:56:07 +01:00
var requiredDockingLoweringSymbols = new [ ]
{
"public void RequestDock()" ,
"public void RequestUndock()" ,
"public void RequestTowToStation()" ,
"TryRequestDaemonDock()" ,
"TryRequestDaemonUndock()" ,
"TryRequestDaemonTowToStation()" ,
"observer.Operations.Dock(targetKey)" ,
"observer.Operations.Undock()" ,
"observer.Operations.TowToStation(" ,
"TowingStation.CultPositionXZ.x" ,
"TowingStation.CultPositionXZ.y"
} ;
var missingDockingLoweringSymbols = requiredDockingLoweringSymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDockingLoweringSymbols . Length > 0 )
2026-06-16 17:27:28 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity docking/towing UI no longer lowers intent through daemon commands: " +
string . Join ( ", " , missingDockingLoweringSymbols ) ) ;
2026-06-16 17:27:28 +01:00
}
2026-06-21 08:56:07 +01:00
var forbiddenUnityDockingAuthoritySymbols = new [ ]
{
"public void Dock()" ,
"public void Undock()" ,
"public void TowShip()" ,
"Missing cockpit component" ,
"Missing thruster component" ,
"Missing reactor component" ,
"Must empty docking bay"
} ;
var unityDockingAuthorityHits = forbiddenUnityDockingAuthoritySymbols
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( unityDockingAuthorityHits . Length > 0 )
2026-06-16 17:44:48 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity docking/towing still exposes imperative local authority instead of daemon-bound requests: " +
string . Join ( ", " , unityDockingAuthorityHits ) ) ;
2026-06-16 17:44:48 +01:00
}
2026-06-21 08:56:07 +01:00
var undockLocalAuthorityHits = FindMethodScopedLineHits (
actionGameManager ,
new [ ] { "CurrentEntity?.Parent == null" } )
. Where ( hit = > hit . MethodName = = "TryRequestDaemonUndock" )
. Select ( hit = > $"ActionGameManager.cs:{hit.LineNumber}: {hit.Line.Trim()}" )
. ToArray ( ) ;
if ( undockLocalAuthorityHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity undock request still rejects through renderer-local parent state instead of daemon acceptance: " +
string . Join ( "; " , undockLocalAuthorityHits ) ) ;
}
var dockLocalAuthorityHits = FindMethodScopedLineHits (
actionGameManager ,
new [ ] { "CurrentEntity?.Parent != null" } )
. Where ( hit = > hit . MethodName = = "TryRequestDaemonDock" )
. Select ( hit = > $"ActionGameManager.cs:{hit.LineNumber}: {hit.Line.Trim()}" )
. ToArray ( ) ;
if ( dockLocalAuthorityHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity dock request still rejects through renderer-local parent state instead of daemon acceptance: " +
string . Join ( "; " , dockLocalAuthorityHits ) ) ;
}
var movementLocalAuthorityHits = FindMethodScopedLineHits (
actionGameManager ,
new [ ] { "CurrentEntity?.Parent != null" } )
. Where ( hit = > hit . MethodName = = "TryRequestDaemonEnterWormhole" | | hit . MethodName = = "TryRequestDaemonTowToStation" )
. Select ( hit = > $"ActionGameManager.cs:{hit.LineNumber}: {hit.Line.Trim()}" )
. ToArray ( ) ;
if ( movementLocalAuthorityHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity movement requests still reject through renderer-local parent state instead of daemon acceptance: " +
string . Join ( "; " , movementLocalAuthorityHits ) ) ;
}
var requiredShieldToggleSymbols = new [ ]
{
"TryRequestDaemonShieldToggle" ,
"observer.Operations.ToggleShieldEnabled()" ,
"AetheriaRuntimeDaemonCommandKinds.ToggleShieldEnabled" ,
"ApplyToggleEquipmentBehaviorItem(run, command, \"Shield\""
} ;
var missingShieldToggleSymbols = requiredShieldToggleSymbols
. Where ( symbol = >
! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) & &
! daemonOperationsSource . Contains ( symbol , StringComparison . Ordinal ) & &
! daemonDocuments . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingShieldToggleSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shield input no longer lowers through a semantic daemon shield command: " +
string . Join ( ", " , missingShieldToggleSymbols ) ) ;
}
var forbiddenUnityShieldAuthoritySymbols = new [ ]
{
"RequestShieldEnabled(" ,
"TryRequestDaemonShieldEnabled(" ,
"CurrentEntity.Shield.Item.Enabled" ,
"var shieldItem = CurrentEntity?.Shield?.Item" ,
"CurrentEntity.Equipment.IndexOf(shieldItem)"
} ;
var shieldAuthorityHits = forbiddenUnityShieldAuthoritySymbols
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( shieldAuthorityHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity shield input still derives equipment authority from renderer-local shield components: " +
string . Join ( ", " , shieldAuthorityHits ) ) ;
}
var requiredTargetAuthoritySymbols = new [ ]
{
"ApplySetTarget(run, command)" ,
"actor.Contacts ?? Array.Empty<AetheriaRuntimeEntityContactCommit>()" ,
"contact.TargetEntityIndex == targetIndex && contact.Visible"
} ;
var missingTargetAuthoritySymbols = requiredTargetAuthoritySymbols
. Where ( symbol = > ! daemonOperationsSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingTargetAuthoritySymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon target selection no longer validates visibility through authoritative contact state: " +
string . Join ( ", " , missingTargetAuthoritySymbols ) ) ;
}
var requiredIntentAuthoritySymbols = new [ ]
{
"!HasWeaponGroup(entity, command.WeaponGroup)" ,
"!HasEquipmentBehavior(entity, command.EquipmentIndex, command.BehaviorIndex)" ,
"private static bool HasWeaponGroup(AetheriaRuntimeEntitySnapshotCommit entity, int weaponGroup)" ,
"private static bool HasEquipmentBehavior("
} ;
var missingIntentAuthoritySymbols = requiredIntentAuthoritySymbols
. Where ( symbol = > ! daemonOperationsSource . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingIntentAuthoritySymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon simulation intents no longer validate weapon-group and behavior indices against authoritative entity state: " +
string . Join ( ", " , missingIntentAuthoritySymbols ) ) ;
}
var forbiddenUnityInputApplySymbols = new [ ]
{
"ApplyEnterWormhole(" ,
"ApplyLookDirection(" ,
"ApplyTractorPower(" ,
"ApplyTargetSelection(" ,
"ApplyOverrideShutdown(" ,
"ApplySensorPing(" ,
"ApplyHeatsinksEnabled(" ,
"ApplyShieldEnabled(" ,
"ApplyDock(" ,
"ApplyUndock("
} ;
var unityInputApplyHits = forbiddenUnityInputApplySymbols
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( unityInputApplyHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity input still exposes Apply vocabulary for daemon-bound requests: " +
string . Join ( ", " , unityInputApplyHits ) ) ;
}
var requiredTowDaemonSymbols = new [ ]
{
"TowToStation" ,
"AetheriaRuntimeDaemonCommandKinds.TowToStation" ,
"ApplyTowToStationIntent" ,
"MoveEntityToZone(run, actor, command.TargetZoneIndex, command.PositionX, command.PositionY, out var movedEntityKey)" ,
"intents.Towing.Add" ,
"AetheriaRuntimeDaemonTowIntent" ,
"public AetheriaRuntimeDaemonCommandEnvelope TowToStation("
} ;
var missingTowDaemonSymbols = requiredTowDaemonSymbols
. Where ( symbol = >
! daemonDocuments . Contains ( symbol , StringComparison . Ordinal ) & &
! daemonOperationsSource . Contains ( symbol , StringComparison . Ordinal ) & &
! daemonIntent . Contains ( symbol , StringComparison . Ordinal ) & &
! daemonObserver . Contains ( symbol , StringComparison . Ordinal ) & &
! daemonOperations . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingTowDaemonSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Towing is not fully owned by daemon command state: " +
string . Join ( ", " , missingTowDaemonSymbols ) ) ;
}
var forbiddenMenuSymbols = new [ ]
{
"new Galaxy(Settings" ,
"new Galaxy(settings" ,
"new Galaxy(" ,
"SectorGenerationSettings" ,
"RunGenerator" ,
"ZoneGenerator.GenerateZone"
} ;
var forbiddenMenuHits = forbiddenMenuSymbols
. Where ( symbol = > mainMenu . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( forbiddenMenuHits . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu still has local run generation fallback on the gameplay boot path: " +
string . Join ( ", " , forbiddenMenuHits ) ) ;
}
var requiredMenuSymbols = new [ ]
{
"TryStartDaemonObservedGame" ,
"TryReadDaemonFrame(stateBoot.StateFilePath, out var frame)" ,
"frame.IsAuthoritative" ,
"ActionGameManager.ObservedGalaxy = Galaxy.ProjectObservedDaemonRun(" ,
"frame.Run" ,
"SceneManager.LoadScene(\"ARPG\")"
} ;
var missingMenuSymbols = requiredMenuSymbols
. Where ( symbol = > ! mainMenu . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingMenuSymbols . Length > 0 )
{
throw new InvalidOperationException (
"MainMenu no longer boots gameplay strictly from an authoritative daemon frame: " +
string . Join ( ", " , missingMenuSymbols ) ) ;
}
}
static void RequireUnityDoesNotCallSharedSimulationTicks ( string root )
{
var checkedRoots = new [ ]
{
Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" )
} ;
var forbiddenCallerSymbols = new [ ]
{
"Zone.Update(" ,
".Zone.Update(" ,
"CurrentEntity.Update(" ,
".CurrentEntity.Update(" ,
"entity.Update(delta" ,
"entity.Update(Time.deltaTime" ,
"ship.Update(delta" ,
"ship.Update(Time.deltaTime" ,
"agent.Update(delta" ,
"agent.Update(Time.deltaTime" ,
"equippedItem.Update(delta" ,
"equippedItem.Update(Time.deltaTime" ,
"alwaysUpdatedBehavior.Update(delta" ,
"alwaysUpdatedBehavior.Update(Time.deltaTime"
} ;
var hits = checkedRoots
. Where ( Directory . Exists )
. SelectMany ( path = > Directory . EnumerateFiles ( path , "*.cs" , SearchOption . AllDirectories ) )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenCallerSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Unity gameplay/UI still calls shared simulation tick methods instead of observing daemon frames: " +
string . Join ( "; " , hits ) ) ;
}
var daemonTickRunnerPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeDaemonTickRunner.cs" ) ;
var daemonTickRunner = File . Exists ( daemonTickRunnerPath )
? File . ReadAllText ( daemonTickRunnerPath )
: throw new InvalidOperationException ( "Cannot verify shared simulation authority; daemon tick runner is missing." ) ;
var requiredDaemonTickSymbols = new [ ]
{
"AetheriaRuntimeDaemonTickRunner" ,
"Tick(" ,
"AetheriaRuntimeDaemonFrameDocument.Create" ,
"AetheriaRuntimeDaemonFrameStore.PublishFrame" ,
"AetheriaRuntimeDaemonOperations.Execute("
} ;
var missingDaemonTickSymbols = requiredDaemonTickSymbols
. Where ( symbol = > ! daemonTickRunner . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDaemonTickSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared simulation ticks no longer have a daemon-owned frame publication path: " +
string . Join ( ", " , missingDaemonTickSymbols ) ) ;
}
}
static void RequireUnityPhysicsIsNotGameplayAuthority ( string root )
{
var checkedRoots = new [ ]
2026-06-16 06:41:35 +01:00
{
2026-06-21 08:56:07 +01:00
Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "ServerShared" )
2026-06-16 06:41:35 +01:00
} ;
2026-06-21 08:56:07 +01:00
var forbiddenSymbols = new [ ]
{
"OnTriggerEnter" ,
"OnTriggerStay" ,
"OnTriggerExit" ,
"OnCollisionEnter" ,
"OnCollisionStay" ,
"OnCollisionExit" ,
"OnControllerColliderHit" ,
"Physics.Raycast" ,
"Physics.SphereCast" ,
"Physics.BoxCast" ,
"Physics.CapsuleCast" ,
"Physics.OverlapSphere" ,
"Physics.OverlapBox" ,
"Physics.OverlapCapsule" ,
"Physics.CheckSphere" ,
"Physics.CheckBox" ,
"Physics.CheckCapsule" ,
"Rigidbody" ,
"Rigidbody2D" ,
"Collider2D"
} ;
var hits = checkedRoots
. Where ( Directory . Exists )
. SelectMany ( path = > Directory . EnumerateFiles ( path , "*.cs" , SearchOption . AllDirectories ) )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
2026-06-16 06:41:35 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( hits . Length > 0 )
2026-06-16 06:41:35 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity physics/collision callbacks are forbidden as gameplay authority; route queries through Ymir: " +
string . Join ( "; " , hits ) ) ;
2026-06-16 06:41:35 +01:00
}
2026-06-16 07:01:50 +01:00
2026-06-21 08:56:07 +01:00
var ymirBridgePath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "Physics" , "AetheriaYmirPhysicsBridge.cs" ) ;
var projectilePath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "Weapons" , "Projectile.cs" ) ;
var guidedProjectilePath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "Weapons" , "GuidedProjectile.cs" ) ;
var hitscanPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "Weapons" , "HitscanEffect.cs" ) ;
var clickRaycasterPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "ClickRaycaster.cs" ) ;
var requiredSymbols = new Dictionary < string , string [ ] >
2026-06-16 07:01:50 +01:00
{
2026-06-21 08:56:07 +01:00
[ymirBridgePath] = new [ ]
{
"public string ServiceUrl" ,
"public string OverlapSphereUrl" ,
"public string OverlapCircleUrl" ,
"public string CastCircleUrl" ,
"public string CastSphereUrl" ,
"TryStepProjectile(" ,
"TryCastZoneHulls(" ,
"TryBuildDaemonWorld("
} ,
[projectilePath] = new [ ]
{
"AetheriaYmirPhysicsBridge.Instance.TryStepProjectile" ,
"projectile killed instead of falling back to Unity physics."
} ,
[guidedProjectilePath] = new [ ]
{
"AetheriaYmirPhysicsBridge.Instance.TryCastZoneHulls"
} ,
[hitscanPath] = new [ ]
{
"AetheriaYmirPhysicsBridge.Instance.TryCastZoneHulls"
} ,
[clickRaycasterPath] = new [ ]
{
"AetheriaYmirPhysicsBridge.Instance.TryCastClickables"
}
2026-06-16 07:01:50 +01:00
} ;
2026-06-21 08:56:07 +01:00
var missingSymbols = requiredSymbols
. Where ( pair = > ! File . Exists ( pair . Key ) | | pair . Value . Any ( symbol = > ! File . ReadAllText ( pair . Key ) . Contains ( symbol , StringComparison . Ordinal ) ) )
. SelectMany ( pair = >
{
var text = File . Exists ( pair . Key ) ? File . ReadAllText ( pair . Key ) : "" ;
return pair . Value
. Where ( symbol = > ! text . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, pair.Key)}: missing {symbol}" ) ;
} )
2026-06-16 07:01:50 +01:00
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( missingSymbols . Length > 0 )
2026-06-16 07:01:50 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Ymir gameplay query bridge is incomplete: " +
string . Join ( "; " , missingSymbols ) ) ;
2026-06-16 07:01:50 +01:00
}
2026-06-16 06:09:46 +01:00
}
2026-06-17 07:27:59 +01:00
static void RequireDeadPropertiesPanelShellDeleted ( string root )
2026-06-15 23:19:41 +01:00
{
2026-06-17 07:27:59 +01:00
var deletedShellPaths = new [ ]
2026-06-15 23:19:41 +01:00
{
2026-06-17 07:27:59 +01:00
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "DropdownMenu.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "DropdownMenu.cs.meta" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Properties Panel" , "PropertiesPanel.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Properties Panel" , "PropertiesPanel.cs.meta" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Properties Panel" , "PropertiesList.cs" ) ,
Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Properties Panel" , "PropertiesList.cs.meta" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Dropdown Menu.prefab" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Dropdown Menu.prefab.meta" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Properties Panel" , "Properties.prefab" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Properties Panel" , "Properties.prefab.meta" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Properties Panel" , "Property List.prefab" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Properties Panel" , "Property List.prefab.meta" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Properties Panel" , "Property List 1.prefab" ) ,
Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Properties Panel" , "Property List 1.prefab.meta" )
} ;
var survivingShells = deletedShellPaths
. Where ( File . Exists )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( survivingShells . Length > 0 )
{
throw new InvalidOperationException (
"The dead generic popup inspector shell still survives in source or prefab assets: " +
string . Join ( ", " , survivingShells ) ) ;
2026-06-15 23:19:41 +01:00
}
2026-06-17 07:27:59 +01:00
var guidChecks = new Dictionary < string , string [ ] >
2026-06-15 23:19:41 +01:00
{
2026-06-17 07:27:59 +01:00
[Path.Combine(root, "Assets", "Scenes", "ARPG.unity")] = new [ ]
{
"8c2bf4a7080061d42a87046c37bf0c60" ,
"2544c9c8b6358c54ea58c5fb33bb48b7" ,
"848ae49aff071f5458dcc0322b8b84eb"
} ,
[Path.Combine(root, "Assets", "Scenes", "FieldShieldTest.unity")] = new [ ]
{
"8c2bf4a7080061d42a87046c37bf0c60" ,
"2544c9c8b6358c54ea58c5fb33bb48b7" ,
"848ae49aff071f5458dcc0322b8b84eb"
} ,
[Path.Combine(root, "Assets", "Prefabs", "UI", "Main Menu Canvas.prefab")] = new [ ]
{
"8c2bf4a7080061d42a87046c37bf0c60" ,
"2544c9c8b6358c54ea58c5fb33bb48b7" ,
"848ae49aff071f5458dcc0322b8b84eb"
}
2026-06-15 23:19:41 +01:00
} ;
2026-06-17 07:27:59 +01:00
var survivingGuidLinks = guidChecks
. SelectMany ( entry = >
{
var text = File . Exists ( entry . Key ) ? File . ReadAllText ( entry . Key ) : "" ;
return entry . Value
. Where ( guid = > text . Contains ( guid , StringComparison . Ordinal ) )
. Select ( guid = > $"{Path.GetRelativePath(root, entry.Key)}: {guid}" ) ;
} )
2026-06-15 23:19:41 +01:00
. ToArray ( ) ;
2026-06-17 07:27:59 +01:00
if ( survivingGuidLinks . Length > 0 )
2026-06-15 23:19:41 +01:00
{
throw new InvalidOperationException (
2026-06-17 07:27:59 +01:00
"Scene or prefab YAML still serializes the deleted generic popup inspector shell: " +
string . Join ( "; " , survivingGuidLinks ) ) ;
2026-06-15 23:19:41 +01:00
}
}
2026-06-16 19:55:50 +01:00
static void RequireTypedBehaviorMetadataCoverage ( string root )
{
var metadataPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeBehaviorMetadata.cs" ) ;
2026-06-17 07:27:59 +01:00
var inventoryMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "InventoryMenu.cs" ) ;
2026-06-16 19:55:50 +01:00
var tradeMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "TradeMenu.cs" ) ;
var metadata = File . Exists ( metadataPath )
? File . ReadAllText ( metadataPath )
: throw new InvalidOperationException ( "Cannot verify typed behavior metadata coverage; AetheriaRuntimeBehaviorMetadata.cs is missing." ) ;
2026-06-17 07:27:59 +01:00
var inventoryMenu = File . Exists ( inventoryMenuPath )
? File . ReadAllText ( inventoryMenuPath )
: throw new InvalidOperationException ( "Cannot verify typed behavior metadata coverage; InventoryMenu.cs is missing." ) ;
2026-06-16 19:55:50 +01:00
var tradeMenu = File . Exists ( tradeMenuPath )
? File . ReadAllText ( tradeMenuPath )
: throw new InvalidOperationException ( "Cannot verify typed behavior metadata coverage; TradeMenu.cs is missing." ) ;
var requiredMetadataSymbols = new [ ]
{
"AetheriaRuntimeBehaviorFieldValueKind.Temperature" ,
"Behavior(\"Heat\", \"\", Stat(\"Heat\", 1))" ,
"Behavior(\"MiningTool\", \"\", Stat(\"DamagePerSecond\", 1), Stat(\"Efficiency\", 2), Stat(\"Penetration\", 3), Stat(\"Range\", 4))" ,
"Behavior(\"Switch\", \"\")" ,
"Behavior(\"Thermotoggle\", \"\", Temperature(\"TargetTemperature\", 1))" ,
"Behavior(\"Trigger\", \"\")" ,
"Temperature(\"TemperatureFloor\", 3)"
} ;
var missingMetadataSymbols = requiredMetadataSymbols
. Where ( symbol = > ! metadata . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingMetadataSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Typed behavior metadata no longer covers live heat/mining/thermotoggle payloads and switch/trigger shells: " +
string . Join ( ", " , missingMetadataSymbols ) ) ;
}
var requiredUiSymbols = new [ ]
{
"AetheriaRuntimeBehaviorFieldValueKind.Temperature" ,
"FormatTemperature"
} ;
2026-06-17 07:27:59 +01:00
var missingInventorySymbols = requiredUiSymbols
. Where ( symbol = > ! inventoryMenu . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-16 19:55:50 +01:00
. ToArray ( ) ;
2026-06-17 07:27:59 +01:00
if ( missingInventorySymbols . Length > 0 )
2026-06-16 19:55:50 +01:00
{
throw new InvalidOperationException (
2026-06-17 07:27:59 +01:00
"InventoryMenu no longer renders typed temperature-bearing behavior metadata: " +
string . Join ( ", " , missingInventorySymbols ) ) ;
2026-06-16 19:55:50 +01:00
}
var missingTradeSymbols = requiredUiSymbols
. Where ( symbol = > ! tradeMenu . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingTradeSymbols . Length > 0 )
{
throw new InvalidOperationException (
"TradeMenu no longer renders typed temperature-bearing behavior metadata: " +
string . Join ( ", " , missingTradeSymbols ) ) ;
}
}
2026-06-16 20:26:00 +01:00
static void RequireNameToolsUsesUiToolkit ( string root )
{
var nameToolsPath = Path . Combine ( root , "Assets" , "Scripts" , "Editor" , "NameTools.cs" ) ;
var source = File . Exists ( nameToolsPath )
? File . ReadAllText ( nameToolsPath )
: throw new InvalidOperationException ( "Cannot verify NameTools editor shell; NameTools.cs is missing." ) ;
var requiredSymbols = new [ ]
{
"using UnityEditor.UIElements;" ,
"using UnityEngine.UIElements;" ,
"private void CreateGUI()" ,
"new ObjectField(\"Name File\")" ,
"new IntegerField(" ,
"new Toggle(\"Strip Number Tokens\")" ,
"new Button(CleanNameFile)" ,
"new Button(ProcessNameFile)" ,
"new Button(GenerateName)"
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
"NameTools no longer owns a UI Toolkit editor shell for the remaining name helper workflow: " +
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
"void OnGUI()" ,
"EditorGUILayout." ,
"GUILayout."
} ;
var hits = forbiddenSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"NameTools should not regress to IMGUI editor widgets: " +
string . Join ( ", " , hits ) ) ;
}
}
2026-06-17 09:19:11 +01:00
static void RequireRuntimeStateReaderOwnsUnityStateAcquisition ( string root )
{
var runtimeStateReaderPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeStateReader.cs" ) ;
2026-06-21 08:56:07 +01:00
var runtimeEveSurfaceAdapterPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeEveSurfaceAdapter.cs" ) ;
var unityPackageProjectPath = Path . Combine ( root , "GameCult.Aetheria.State.Unity.csproj" ) ;
2026-06-17 09:19:11 +01:00
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var mainMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "MainMenu.cs" ) ;
var eveSurfacePresenterPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.eve-runtime" , "Runtime" , "AetheriaEveSurfacePresenter.cs" ) ;
var requiredPaths = new [ ]
{
runtimeStateReaderPath ,
2026-06-21 08:56:07 +01:00
runtimeEveSurfaceAdapterPath ,
unityPackageProjectPath ,
2026-06-17 09:19:11 +01:00
actionGameManagerPath ,
mainMenuPath ,
eveSurfacePresenterPath
} ;
var missingPaths = requiredPaths
. Where ( path = > ! File . Exists ( path ) )
. Select ( path = > Path . GetRelativePath ( root , path ) )
. ToArray ( ) ;
if ( missingPaths . Length > 0 )
{
throw new InvalidOperationException (
"Runtime state reader authority cannot be verified because required files are missing: " +
string . Join ( ", " , missingPaths ) ) ;
}
var runtimeStateReader = File . ReadAllText ( runtimeStateReaderPath ) ;
2026-06-21 08:56:07 +01:00
var runtimeEveSurfaceAdapter = File . ReadAllText ( runtimeEveSurfaceAdapterPath ) ;
var unityPackageProject = File . ReadAllText ( unityPackageProjectPath ) ;
2026-06-17 09:19:11 +01:00
var actionGameManager = File . ReadAllText ( actionGameManagerPath ) ;
var mainMenu = File . ReadAllText ( mainMenuPath ) ;
var eveSurfacePresenter = File . ReadAllText ( eveSurfacePresenterPath ) ;
var requiredReaderSymbols = new [ ]
{
"public static class AetheriaRuntimeStateReader" ,
"OpenRuntimeCatalog" ,
"ReadPlayerSettings" ,
2026-06-17 09:51:49 +01:00
"ReadVerseHostSettings" ,
2026-06-17 09:19:11 +01:00
"ReadLoadoutTemplates" ,
"ReadRunStates" ,
"ReadZoneStates" ,
"ReadEntitySnapshots" ,
2026-06-21 08:56:07 +01:00
"ReadEveSurface" ,
"TryReadDaemonGameSurface" ,
"TryReadDaemonGameTuiSurface" ,
"TryReadDaemonEditorSurface" ,
"TryReadDaemonEditorTuiSurface" ,
"AetheriaRuntimeDaemonPublicationStore.TryReadGameSurface" ,
"AetheriaRuntimeDaemonPublicationStore.TryReadGameTuiSurface" ,
"AetheriaRuntimeDaemonPublicationStore.TryReadEditorSurface" ,
"AetheriaRuntimeDaemonPublicationStore.TryReadEditorTuiSurface" ,
"AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId" ,
"AetheriaRuntimeDaemonGameSurfaceBuilder.TuiSurfaceId" ,
"AetheriaRuntimeDaemonEditorSurfaceBuilder.SurfaceId" ,
"AetheriaRuntimeDaemonEditorSurfaceBuilder.TuiSurfaceId" ,
"AetheriaRuntimeEveSurfaceAdapter.ToEveSurfaceDocument(surface)" ,
"AetheriaRuntimeEveSurfaceAdapter.EmptySurface(surfaceId)"
2026-06-17 09:19:11 +01:00
} ;
var missingReaderSymbols = requiredReaderSymbols
. Where ( symbol = > ! runtimeStateReader . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingReaderSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared runtime state reader is incomplete: " +
string . Join ( ", " , missingReaderSymbols ) ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! unityPackageProject . Contains ( "AetheriaRuntimeStateReader.cs" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"GameCult.Aetheria.State.Unity.csproj does not include the shared runtime state reader." ) ;
}
var requiredAdapterSymbols = new [ ]
{
"public static class AetheriaRuntimeEveSurfaceAdapter" ,
"public static EveSurfaceDocument ToEveSurfaceDocument(AetheriaRuntimeSurfaceDocument document)" ,
"public static EveSurfaceDocument EmptySurface(string surfaceId)" ,
"new EveSurfaceDocument(" ,
"new EveSurfaceComponent("
} ;
var missingAdapterSymbols = requiredAdapterSymbols
. Where ( symbol = > ! runtimeEveSurfaceAdapter . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingAdapterSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Shared runtime Eve surface adapter is incomplete: " +
string . Join ( ", " , missingAdapterSymbols ) ) ;
}
if ( ! unityPackageProject . Contains ( "AetheriaRuntimeEveSurfaceAdapter.cs" , StringComparison . Ordinal ) )
2026-06-17 09:19:11 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"GameCult.Aetheria.State.Unity.csproj does not include the shared runtime Eve surface adapter." ) ;
2026-06-17 09:19:11 +01:00
}
2026-06-21 08:56:07 +01:00
var daemonObserverPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "AetheriaDaemonObserver.cs" ) ;
var daemonObserver = File . Exists ( daemonObserverPath )
? File . ReadAllText ( daemonObserverPath )
: throw new InvalidOperationException ( "Cannot verify daemon state acquisition; AetheriaDaemonObserver.cs is missing." ) ;
2026-06-17 09:19:11 +01:00
var requiredActionGameManagerSymbols = new [ ]
{
"AetheriaRuntimeStateReader.ReadPlayerSettings" ,
"AetheriaRuntimeStateReader.ReadLoadoutTemplates" ,
"AetheriaRuntimeStateReader.OpenRuntimeCatalog" ,
2026-06-21 08:56:07 +01:00
"ResolveDaemonObserver()" ,
"LastObservedState"
2026-06-17 09:19:11 +01:00
} ;
var missingActionGameManagerSymbols = requiredActionGameManagerSymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingActionGameManagerSymbols . Length > 0 )
{
throw new InvalidOperationException (
"ActionGameManager does not route typed state reads through the shared runtime reader: " +
string . Join ( ", " , missingActionGameManagerSymbols ) ) ;
}
2026-06-21 08:56:07 +01:00
if ( ! daemonObserver . Contains ( "AetheriaRuntimeStateReader.TryReadObservedDaemonState" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"AetheriaDaemonObserver no longer routes observed daemon state through the shared runtime state reader." ) ;
}
2026-06-17 09:19:11 +01:00
if ( ! mainMenu . Contains ( "AetheriaRuntimeStateReader" , StringComparison . Ordinal ) | |
2026-06-21 08:56:07 +01:00
! mainMenu . Contains ( "TryReadDaemonFrame(stateBoot.StateFilePath, out var frame)" , StringComparison . Ordinal ) )
2026-06-17 09:19:11 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"MainMenu no longer routes daemon-frame lookup through the shared runtime state reader." ) ;
2026-06-17 09:19:11 +01:00
}
if ( ! eveSurfacePresenter . Contains ( "AetheriaRuntimeStateReader.ReadEveSurface" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Aetheria Eve surface presenter no longer routes provider surface lookup through the shared runtime state reader." ) ;
}
var forbiddenDirectStoreSymbols = new Dictionary < string , string [ ] >
{
[actionGameManagerPath] = new [ ]
{
"AetheriaRuntimeCatalogStore.ReadPlayerSettings" ,
"AetheriaRuntimeCatalogStore.ReadLoadoutTemplates" ,
"AetheriaRuntimeCatalogStore.OpenReadOnly" ,
"AetheriaRuntimeCatalogStore.ReadZoneStates" ,
"AetheriaRuntimeCatalogStore.ReadEntitySnapshots"
} ,
[mainMenuPath] = new [ ]
{
2026-06-17 09:51:49 +01:00
"AetheriaRuntimeCatalogStore.ReadRunStates" ,
"AetheriaRuntimeCatalogStore.ReadVerseHostSettings"
2026-06-17 09:19:11 +01:00
} ,
[eveSurfacePresenterPath] = new [ ]
{
"AetheriaRuntimeCatalogStore.ReadEveSurfaces"
}
} ;
var directStoreHits = forbiddenDirectStoreSymbols
. SelectMany ( entry = >
{
var source = File . ReadAllText ( entry . Key ) ;
return entry . Value
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, entry.Key)} -> {symbol}" ) ;
} )
. ToArray ( ) ;
if ( directStoreHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity gameplay/UI still reads typed state directly from the raw store instead of the shared runtime state reader: " +
string . Join ( "; " , directStoreHits ) ) ;
}
2026-06-21 08:56:07 +01:00
var unitySurfaceLowererFiles = Directory
. EnumerateFiles ( Path . Combine ( root , "Assets" , "Scripts" , "UI" ) , "*.cs" , SearchOption . AllDirectories )
. ToArray ( ) ;
var forbiddenLocalSurfaceAdapterSymbols = new [ ]
{
"private static EveSurfaceDocument ToEveSurfaceDocument(AetheriaRuntimeSurfaceDocument document)" ,
"private static EveSurfaceComponent ToEveSurfaceComponent(AetheriaRuntimeSurfaceComponent component)" ,
"new EveSurfaceDocument(\r\n \"surface-state\"" ,
"new EveCommandTemplate(command.Command, command.Label, command.Transport)"
} ;
var localSurfaceAdapterHits = unitySurfaceLowererFiles
. SelectMany ( path = >
{
var source = File . ReadAllText ( path ) ;
return forbiddenLocalSurfaceAdapterSymbols
. Where ( symbol = > source . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, path)} -> {symbol}" ) ;
} )
. ToArray ( ) ;
if ( localSurfaceAdapterHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity UI lowerers still duplicate runtime-to-Eve surface conversion instead of using the shared adapter: " +
string . Join ( "; " , localSurfaceAdapterHits ) ) ;
}
2026-06-17 09:19:11 +01:00
}
2026-06-21 08:56:07 +01:00
static void RequireRuntimeSimulationTuningRequests ( string root )
2026-06-16 00:01:19 +01:00
{
var requiredActionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( requiredActionGameManagerPath )
? File . ReadAllText ( requiredActionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify runtime simulation tuning authority; ActionGameManager.cs is missing." ) ;
2026-06-21 08:56:07 +01:00
var requiredRequestMethods = new [ ]
2026-06-16 00:01:19 +01:00
{
2026-06-21 08:56:07 +01:00
"RequestEntityOverrideShutdown" ,
"RequestEquippedItemOverrideShutdown" ,
"RequestThermotoggleTargetTemperature" ,
"RequestEntityShutdownPerformance"
2026-06-16 00:01:19 +01:00
} ;
2026-06-21 08:56:07 +01:00
var missingMethods = requiredRequestMethods
2026-06-16 00:01:19 +01:00
. Where ( method = > ! actionGameManager . Contains ( method , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingMethods . Length > 0 )
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Runtime simulation tuning request methods are missing from ActionGameManager: " +
2026-06-16 00:01:19 +01:00
string . Join ( ", " , missingMethods ) ) ;
}
var forbiddenUiWrites = new [ ]
{
"EquippableItem.OverrideShutdown =" ,
"thermotoggle.TargetTemperature =" ,
"Settings.ShutdownPerformance =" ,
"CurrentEntity.OverrideShutdown ="
} ;
var uiRoot = Path . Combine ( root , "Assets" , "Scripts" , "UI" ) ;
var hits = Directory . EnumerateFiles ( uiRoot , "*.cs" , SearchOption . AllDirectories )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = > forbiddenUiWrites . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. Take ( 10 )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Runtime simulation tuning still has renderer-local UI write authority: " +
string . Join ( "; " , hits ) ) ;
}
2026-06-21 08:56:07 +01:00
var daemonOperationsPath = Path . Combine ( root , "Packages" , "org.gamecult.aetheria.state" , "Runtime" , "AetheriaRuntimeDaemonOperations.cs" ) ;
var daemonOperations = File . Exists ( daemonOperationsPath )
? File . ReadAllText ( daemonOperationsPath )
: throw new InvalidOperationException ( "Cannot verify runtime simulation tuning authority; AetheriaRuntimeDaemonOperations.cs is missing." ) ;
var requiredDaemonValidationSymbols = new [ ]
{
"case AetheriaRuntimeDaemonCommandKinds.SetShutdownPerformance:" ,
"command.ScalarValue < 0.0 || command.ScalarValue > 1.0"
} ;
var missingDaemonValidationSymbols = requiredDaemonValidationSymbols
. Where ( symbol = > ! daemonOperations . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingDaemonValidationSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Daemon runtime tuning no longer validates shutdown-performance range: " +
string . Join ( ", " , missingDaemonValidationSymbols ) ) ;
}
2026-06-16 00:01:19 +01:00
}
2026-06-21 08:56:07 +01:00
static void RequireHullConductivityRequestAuthority ( string root )
2026-06-16 00:19:58 +01:00
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify hull conductivity authority; ActionGameManager.cs is missing." ) ;
2026-06-21 08:56:07 +01:00
if ( ! actionGameManager . Contains ( "RequestHullConductivityToggle" , StringComparison . Ordinal ) | |
! actionGameManager . Contains ( "TryRequestDaemonHullConductivityToggle" , StringComparison . Ordinal ) | |
! actionGameManager . Contains ( "Operations.ToggleHullConductivity" , StringComparison . Ordinal ) )
2026-06-16 00:19:58 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Hull conductivity no longer has a typed daemon request primitive." ) ;
2026-06-16 00:19:58 +01:00
}
var uiRoot = Path . Combine ( root , "Assets" , "Scripts" , "UI" ) ;
var hits = Directory . EnumerateFiles ( uiRoot , "*.cs" , SearchOption . AllDirectories )
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
. Where ( line = >
line . Line . Contains ( "HullConductivity[" , StringComparison . Ordinal ) & &
line . Line . Contains ( "=" , StringComparison . Ordinal ) & &
! line . Line . Contains ( "=>" , StringComparison . Ordinal ) )
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
. Take ( 10 )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"Hull conductivity still has renderer-local UI write authority: " +
string . Join ( "; " , hits ) ) ;
}
}
2026-06-21 08:56:07 +01:00
static void RequireInventoryEntityRenameRequestAuthority ( string root )
2026-06-16 00:40:04 +01:00
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify inventory entity rename authority; ActionGameManager.cs is missing." ) ;
2026-06-21 08:56:07 +01:00
if ( ! actionGameManager . Contains ( "RequestEntityName" , StringComparison . Ordinal ) | |
! actionGameManager . Contains ( "TryRequestDaemonEntityName" , StringComparison . Ordinal ) | |
! actionGameManager . Contains ( "Operations.SetEntityName" , StringComparison . Ordinal ) )
2026-06-16 00:40:04 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Entity rename no longer has a typed daemon request primitive." ) ;
2026-06-16 00:40:04 +01:00
}
var inventoryPanelPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "InventoryPanel.cs" ) ;
var inventoryPanel = File . Exists ( inventoryPanelPath )
? File . ReadAllText ( inventoryPanelPath )
: throw new InvalidOperationException ( "Cannot verify inventory entity rename authority; InventoryPanel.cs is missing." ) ;
if ( inventoryPanel . Contains ( "_displayedEntity.Name =" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"InventoryPanel still renames entities directly instead of using the typed daemon request primitive." ) ;
2026-06-16 00:40:04 +01:00
}
2026-06-21 08:56:07 +01:00
if ( ! inventoryPanel . Contains ( "GameManager.RequestEntityName" , StringComparison . Ordinal ) )
2026-06-16 00:40:04 +01:00
{
throw new InvalidOperationException ( "InventoryPanel no longer routes entity rename through ActionGameManager." ) ;
}
}
2026-06-21 08:56:07 +01:00
static void RequireWeaponGroupRequestAuthority ( string root )
2026-06-16 01:00:40 +01:00
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify weapon group authority; ActionGameManager.cs is missing." ) ;
2026-06-21 08:56:07 +01:00
if ( ! actionGameManager . Contains ( "RequestWeaponGroupMembership" , StringComparison . Ordinal ) | |
! actionGameManager . Contains ( "TryRequestDaemonWeaponGroupMembership" , StringComparison . Ordinal ) | |
! actionGameManager . Contains ( "Operations.SetWeaponGroupMembership" , StringComparison . Ordinal ) )
2026-06-16 01:00:40 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Weapon group membership no longer has a typed daemon request primitive." ) ;
2026-06-16 01:00:40 +01:00
}
2026-06-17 02:39:13 +01:00
if ( actionGameManager . Contains ( "WeaponGroupDragObject" , StringComparison . Ordinal ) )
2026-06-16 01:00:40 +01:00
{
2026-06-17 02:39:13 +01:00
throw new InvalidOperationException (
"Weapon-group action-bar binding still keeps the dead drag-object path alive instead of routing through live gameplay APIs." ) ;
}
2026-06-16 01:00:40 +01:00
2026-06-21 08:56:07 +01:00
var forbiddenLocalAcceptanceSymbols = new [ ]
{
"item?.Entity?.WeaponGroups == null" ,
"groupIndex >= item.Entity.WeaponGroups.Length" ,
"item.GetBehavior<Weapon>()"
} ;
var localAcceptanceHits = forbiddenLocalAcceptanceSymbols
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( localAcceptanceHits . Length > 0 )
{
throw new InvalidOperationException (
"Unity weapon-group membership still rejects through renderer-local weapon/group checks instead of daemon acceptance: " +
string . Join ( ", " , localAcceptanceHits ) ) ;
}
2026-06-17 02:39:13 +01:00
var assignmentPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "WeaponGroupAssignment.cs" ) ;
var elementPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "WeaponGroupElement.cs" ) ;
var weaponGroupsPrefabPath = Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Properties Panel" , "Fancy" , "Weapon Groups.prefab" ) ;
var weaponGroupPrefabPath = Path . Combine ( root , "Assets" , "Prefabs" , "UI" , "Properties Panel" , "Fancy" , "Weapon Group.prefab" ) ;
2026-06-16 01:00:40 +01:00
2026-06-17 02:39:13 +01:00
var survivingLegacyPaths = new [ ]
2026-06-16 01:00:40 +01:00
{
2026-06-17 02:39:13 +01:00
assignmentPath ,
elementPath ,
weaponGroupsPrefabPath ,
weaponGroupPrefabPath
2026-06-16 01:00:40 +01:00
}
2026-06-17 02:39:13 +01:00
. Where ( File . Exists )
. ToArray ( ) ;
2026-06-16 01:00:40 +01:00
2026-06-17 02:39:13 +01:00
if ( survivingLegacyPaths . Length > 0 )
2026-06-16 01:00:40 +01:00
{
2026-06-17 02:39:13 +01:00
throw new InvalidOperationException (
"Legacy inventory weapon-group uGUI shells still survive after the Eve surface cut: " +
string . Join ( ", " , survivingLegacyPaths . Select ( path = > Path . GetRelativePath ( root , path ) ) ) ) ;
2026-06-16 01:00:40 +01:00
}
}
2026-06-21 08:56:07 +01:00
static void RequireActionBarBindingRequestAuthority ( string root )
2026-06-16 20:42:42 +01:00
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify action-bar binding authority; ActionGameManager.cs is missing." ) ;
var requiredSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"RequestActionBarBinding(" ,
"TryRequestDaemonActionBarBinding(" ,
"TryRequestDaemonActionBarBindingClear(" ,
2026-06-16 20:42:42 +01:00
"RestoreActionBarBindingsFromTypedRun(" ,
"ApplyActionBarBindings(" ,
2026-06-21 08:56:07 +01:00
"RequestActionBarBinding(slot, dragAction)"
2026-06-16 20:42:42 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Action-bar binding no longer has a typed daemon request and observed restore path: " +
2026-06-16 20:42:42 +01:00
string . Join ( ", " , missingSymbols ) ) ;
}
var forbiddenSymbols = new [ ]
{
"var newbinds = Enumerable.Range(0, 64)" ,
2026-06-21 08:56:07 +01:00
".Zip(\r\n bindings," ,
"groupIndex >= CurrentEntity.WeaponGroups.Length"
2026-06-16 20:42:42 +01:00
} ;
var legacyHits = forbiddenSymbols
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( legacyHits . Length > 0 )
{
throw new InvalidOperationException (
"Action-bar binding still contains the old unconditional weapon-group overwrite path: " +
string . Join ( ", " , legacyHits ) ) ;
}
}
2026-06-21 08:56:07 +01:00
static void RequireInventoryDoubleClickTransferRequestAuthority ( string root )
2026-06-16 01:59:18 +01:00
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify inventory transfer authority; ActionGameManager.cs is missing." ) ;
2026-06-21 08:56:07 +01:00
var requiredRequests = new [ ]
2026-06-16 01:59:18 +01:00
{
2026-06-21 08:56:07 +01:00
"RequestCargoItemTransfer" ,
"RequestCargoItemEquip" ,
"RequestEquippedItemStore" ,
"RequestEquippedItemEquip" ,
"TryRequestDaemonCargoItemTransfer" ,
"TryRequestDaemonCargoItemEquip" ,
"TryRequestDaemonEquippedItemStore" ,
"TryRequestDaemonEquippedItemEquip"
2026-06-16 01:59:18 +01:00
} ;
2026-06-21 08:56:07 +01:00
var missingRequests = requiredRequests
2026-06-16 01:59:18 +01:00
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( missingRequests . Length > 0 )
{
throw new InvalidOperationException (
"Inventory transfer no longer has complete typed daemon request primitives: " +
string . Join ( ", " , missingRequests ) ) ;
}
var forbiddenUnityInventoryAcceptanceSymbols = new [ ]
{
"TryFindSpace(" ,
"ItemFits(" ,
"Cargo.ContainsKey(" ,
"Equipment.Contains(" ,
"origin == destination" ,
"ReferenceEquals(equippedItem.Entity, origin)"
} ;
var unityInventoryAcceptanceHits = forbiddenUnityInventoryAcceptanceSymbols
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( unityInventoryAcceptanceHits . Length > 0 )
2026-06-16 01:59:18 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity inventory request code still rejects requests through renderer-local capacity or membership checks: " +
string . Join ( ", " , unityInventoryAcceptanceHits ) ) ;
2026-06-16 01:59:18 +01:00
}
2026-06-16 02:35:13 +01:00
var inventoryPanelPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "InventoryPanel.cs" ) ;
var inventoryPanel = File . Exists ( inventoryPanelPath )
? File . ReadAllText ( inventoryPanelPath )
: throw new InvalidOperationException ( "Cannot verify inventory transfer authority; InventoryPanel.cs is missing." ) ;
2026-06-16 01:59:18 +01:00
var inventoryMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "InventoryMenu.cs" ) ;
var inventoryMenu = File . Exists ( inventoryMenuPath )
? File . ReadAllText ( inventoryMenuPath )
: throw new InvalidOperationException ( "Cannot verify inventory transfer authority; InventoryMenu.cs is missing." ) ;
var forbiddenSymbols = new [ ]
{
".DropItem(" ,
".CargoBay.Remove(" ,
2026-06-16 02:35:13 +01:00
".TryUnequip(" ,
".TryEquip(" ,
".TryStore(" ,
".OriginInventory.Remove("
2026-06-16 01:59:18 +01:00
} ;
2026-06-16 02:35:13 +01:00
var hits = new [ ] { inventoryMenuPath , inventoryPanelPath }
. SelectMany ( path = > File . ReadLines ( path )
. Select ( ( line , index ) = > new { Path = path , LineNumber = index + 1 , Line = line } ) )
2026-06-16 01:59:18 +01:00
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
2026-06-16 02:35:13 +01:00
. Select ( line = > $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}" )
2026-06-16 01:59:18 +01:00
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
2026-06-16 02:35:13 +01:00
"Inventory UI still owns direct inventory mutation: " +
2026-06-16 01:59:18 +01:00
string . Join ( "; " , hits ) ) ;
}
2026-06-21 08:56:07 +01:00
var forbiddenInventoryUiAcceptanceSymbols = new [ ]
{
".ItemFits(" ,
".TryFindSpace("
} ;
var inventoryUiAcceptanceHits = File . ReadLines ( inventoryPanelPath )
. Select ( ( line , index ) = > new { LineNumber = index + 1 , Line = line } )
. Where ( line = > forbiddenInventoryUiAcceptanceSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, inventoryPanelPath)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( inventoryUiAcceptanceHits . Length > 0 )
{
throw new InvalidOperationException (
"InventoryPanel still rejects transfer placement through renderer-local fit checks instead of daemon authority: " +
string . Join ( "; " , inventoryUiAcceptanceHits ) ) ;
}
2026-06-16 01:59:18 +01:00
var requiredUiCalls = new [ ]
{
2026-06-21 08:56:07 +01:00
"GameManager.RequestCargoItemTransfer" ,
"GameManager.RequestCargoItemEquip" ,
"GameManager.RequestEquippedItemStore" ,
"GameManager.RequestEquippedItemEquip"
2026-06-16 01:59:18 +01:00
} ;
var missingUiCalls = requiredUiCalls
2026-06-16 02:35:13 +01:00
. Where ( symbol = >
! inventoryMenu . Contains ( symbol , StringComparison . Ordinal ) & &
! inventoryPanel . Contains ( symbol , StringComparison . Ordinal ) )
2026-06-16 01:59:18 +01:00
. ToArray ( ) ;
if ( missingUiCalls . Length > 0 )
{
throw new InvalidOperationException (
2026-06-16 02:35:13 +01:00
"Inventory UI no longer routes transfer requests through ActionGameManager: " +
2026-06-16 01:59:18 +01:00
string . Join ( ", " , missingUiCalls ) ) ;
}
}
2026-06-21 08:56:07 +01:00
static void RequireTradePurchaseRequestAuthority ( string root )
2026-06-16 02:57:45 +01:00
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify trade purchase authority; ActionGameManager.cs is missing." ) ;
2026-06-21 08:56:07 +01:00
var requiredRequests = new [ ]
2026-06-16 02:57:45 +01:00
{
2026-06-21 08:56:07 +01:00
"RequestTradePurchase" ,
"TryRequestDaemonTradePurchase" ,
"Operations.TradePurchase"
2026-06-16 02:57:45 +01:00
} ;
2026-06-21 08:56:07 +01:00
var missingRequests = requiredRequests
2026-06-16 02:57:45 +01:00
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
2026-06-21 08:56:07 +01:00
if ( missingRequests . Length > 0 )
{
throw new InvalidOperationException (
"Trade purchase no longer has a typed daemon request primitive: " +
string . Join ( ", " , missingRequests ) ) ;
}
var forbiddenUnityTradeAcceptanceSymbols = new [ ]
{
"price > Credits" ,
"totalPrice > Credits" ,
"targetCargo.TryFindSpace("
} ;
var unityTradeAcceptanceHits = forbiddenUnityTradeAcceptanceSymbols
. Where ( symbol = > actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( unityTradeAcceptanceHits . Length > 0 )
2026-06-16 02:57:45 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Unity trade/loadout request code still rejects requests through renderer-local credits or cargo-capacity checks: " +
string . Join ( ", " , unityTradeAcceptanceHits ) ) ;
2026-06-16 02:57:45 +01:00
}
var tradeMenuPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "TradeMenu.cs" ) ;
var tradeMenu = File . Exists ( tradeMenuPath )
? File . ReadAllText ( tradeMenuPath )
: throw new InvalidOperationException ( "Cannot verify trade purchase authority; TradeMenu.cs is missing." ) ;
var forbiddenSymbols = new [ ]
{
"GameManager.Credits -=" ,
"Inventory.TryTransferItem(" ,
"new Ship(" ,
".SetParent(GameManager.DockedEntity)"
} ;
var hits = File . ReadLines ( tradeMenuPath )
. Select ( ( line , index ) = > new { LineNumber = index + 1 , Line = line } )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, tradeMenuPath)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"TradeMenu still owns direct purchase mutation: " +
string . Join ( "; " , hits ) ) ;
}
2026-06-21 08:56:07 +01:00
var forbiddenLocalAcceptanceSymbols = new [ ]
{
"price > GameManager.Credits" ,
"totalPrice > GameManager.Credits" ,
"GameManager.Credits / price" ,
"\"Insufficient Credits!\"" ,
"\"Insufficient Cargo Space!\"" ,
"\"Unable to create ship!\""
} ;
var localAcceptanceHits = File . ReadLines ( tradeMenuPath )
. Select ( ( line , index ) = > new { LineNumber = index + 1 , Line = line } )
. Where ( line = > forbiddenLocalAcceptanceSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, tradeMenuPath)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( localAcceptanceHits . Length > 0 )
{
throw new InvalidOperationException (
"TradeMenu still rejects purchases through renderer-local acceptance checks instead of daemon authority: " +
string . Join ( "; " , localAcceptanceHits ) ) ;
}
if ( ! tradeMenu . Contains ( "GameManager.RequestTradePurchase" , StringComparison . Ordinal ) )
2026-06-16 02:57:45 +01:00
{
throw new InvalidOperationException ( "TradeMenu no longer routes purchases through ActionGameManager." ) ;
}
}
2026-06-21 08:56:07 +01:00
static void RequireLootPickupRequestAuthority ( string root )
2026-06-16 04:27:07 +01:00
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify loot pickup authority; ActionGameManager.cs is missing." ) ;
2026-06-21 08:56:07 +01:00
if ( actionGameManager . Contains ( "CommitLootPickup" , StringComparison . Ordinal ) )
2026-06-16 04:27:07 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Loot pickup should not have a Unity-owned gameplay mutation primitive; daemon snapshots own pickup state." ) ;
2026-06-16 04:27:07 +01:00
}
var shieldManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ShieldManager.cs" ) ;
var shieldManager = File . Exists ( shieldManagerPath )
? File . ReadAllText ( shieldManagerPath )
: throw new InvalidOperationException ( "Cannot verify loot pickup authority; ShieldManager.cs is missing." ) ;
var forbiddenSymbols = new [ ]
{
".TryStore(" ,
".CargoBays.Any("
} ;
var hits = File . ReadLines ( shieldManagerPath )
. Select ( ( line , index ) = > new { LineNumber = index + 1 , Line = line } )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, shieldManagerPath)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"ShieldManager still owns direct loot cargo mutation: " +
string . Join ( "; " , hits ) ) ;
}
2026-06-21 08:56:07 +01:00
if ( shieldManager . Contains ( "ActionGameManager.Instance.CommitLootPickup" , StringComparison . Ordinal ) )
throw new InvalidOperationException ( "ShieldManager still routes loot pickup through Unity gameplay authority." ) ;
2026-06-16 04:27:07 +01:00
}
2026-06-21 08:56:07 +01:00
static void RequireEntityDestroyedRequestAuthority ( string root )
2026-06-16 04:47:42 +01:00
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify entity destruction authority; ActionGameManager.cs is missing." ) ;
2026-06-21 08:56:07 +01:00
if ( actionGameManager . Contains ( "CommitEntityDestroyed" , StringComparison . Ordinal ) )
2026-06-16 04:47:42 +01:00
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Entity destruction should not have a Unity-owned gameplay mutation primitive; daemon frames own destruction and drops." ) ;
2026-06-16 04:47:42 +01:00
}
var entityInstancePath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "EntityInstance.cs" ) ;
var entityInstance = File . Exists ( entityInstancePath )
? File . ReadAllText ( entityInstancePath )
: throw new InvalidOperationException ( "Cannot verify entity destruction authority; EntityInstance.cs is missing." ) ;
var forbiddenSymbols = new [ ]
{
"ZoneRenderer.DropItem(" ,
"entity.Zone.Entities.Remove(entity)" ,
"Random.onUnitSphere" ,
"LootDropProbability"
} ;
var hits = File . ReadLines ( entityInstancePath )
. Select ( ( line , index ) = > new { LineNumber = index + 1 , Line = line } )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, entityInstancePath)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"EntityInstance still owns direct destruction/drop mutation: " +
string . Join ( "; " , hits ) ) ;
}
2026-06-21 08:56:07 +01:00
if ( entityInstance . Contains ( "ActionGameManager.Instance?.CommitEntityDestroyed" , StringComparison . Ordinal ) )
throw new InvalidOperationException ( "EntityInstance still routes destruction through Unity gameplay authority." ) ;
2026-06-16 04:47:42 +01:00
}
2026-06-16 05:24:11 +01:00
static void RequireDroppedPickupCheckpointState ( string root )
{
var requiredFiles = new Dictionary < string , string [ ] >
{
2026-06-21 08:56:07 +01:00
[Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeSnapshotDocuments.cs")] = new [ ]
2026-06-16 05:24:11 +01:00
{
"AetheriaRuntimeDroppedPickupCommit" ,
"DroppedPickups"
} ,
[Path.Combine(root, "Aetheria.State", "Documents", "AetheriaRuntimeStateDocuments.cs")] = new [ ]
{
"AetheriaDroppedPickupSnapshot" ,
"DroppedPickups"
} ,
[Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeCatalogStore.cs")] = new [ ]
{
2026-06-16 05:51:07 +01:00
"ReadZoneStatePayload(record.Key, record.Payload)" ,
2026-06-16 05:24:11 +01:00
"ReadFieldDroppedPickups" ,
"AetheriaRuntimeDroppedPickupSnapshot"
} ,
[Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeCatalogSnapshot.cs")] = new [ ]
{
2026-06-16 05:51:07 +01:00
"public string RecordKey" ,
2026-06-16 05:24:11 +01:00
"AetheriaRuntimeDroppedPickupSnapshot" ,
"public IReadOnlyList<AetheriaRuntimeDroppedPickupSnapshot> DroppedPickups"
} ,
[Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionGameManager.cs")] = new [ ]
{
2026-06-21 08:56:07 +01:00
"RestoreDroppedPickupsFromDaemonZoneState" ,
2026-06-16 05:51:07 +01:00
"ZoneRenderer.DropItem("
2026-06-16 05:24:11 +01:00
} ,
[Path.Combine(root, "Assets", "Scripts", "Zone Display", "ZoneRenderer.cs")] = new [ ]
{
"ActiveLoot"
}
} ;
var missing = requiredFiles
. SelectMany ( pair = >
{
if ( ! File . Exists ( pair . Key ) )
return new [ ] { $"{Path.GetRelativePath(root, pair.Key)}: missing file" } ;
var text = File . ReadAllText ( pair . Key ) ;
return pair . Value
. Where ( symbol = > ! text . Contains ( symbol , StringComparison . Ordinal ) )
. Select ( symbol = > $"{Path.GetRelativePath(root, pair.Key)}: missing {symbol}" ) ;
} )
. ToArray ( ) ;
if ( missing . Length > 0 )
{
throw new InvalidOperationException (
"Dropped pickup checkpoint state is incomplete: " +
string . Join ( "; " , missing ) ) ;
}
}
2026-06-21 08:56:07 +01:00
static void RequireInventoryLoadoutSaveRequestAuthority ( string root )
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify loadout save authority; ActionGameManager.cs is missing." ) ;
var eveCommandDocumentPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeEveCommandDocument.cs" ) ;
var loadoutCommandsPath = Path . Combine (
root ,
"Packages" ,
"org.gamecult.aetheria.state" ,
"Runtime" ,
"AetheriaRuntimeLoadoutTemplateCommands.cs" ) ;
var eveBridgePath = Path . Combine ( root , "Aetheria.State" , "AetheriaEveCommandBridge.cs" ) ;
var runtimeStateMapperPath = Path . Combine ( root , "Aetheria.State" , "AetheriaRuntimeStateMapper.cs" ) ;
var requiredFiles = new [ ] { eveCommandDocumentPath , loadoutCommandsPath , eveBridgePath , runtimeStateMapperPath } ;
var missingFiles = requiredFiles . Where ( path = > ! File . Exists ( path ) ) . ToArray ( ) ;
if ( missingFiles . Length > 0 )
{
throw new InvalidOperationException (
"Cannot verify loadout save authority; missing files: " +
string . Join ( ", " , missingFiles . Select ( path = > Path . GetRelativePath ( root , path ) ) ) ) ;
}
var eveCommandDocument = File . ReadAllText ( eveCommandDocumentPath ) ;
var loadoutCommands = File . ReadAllText ( loadoutCommandsPath ) ;
var eveBridge = File . ReadAllText ( eveBridgePath ) ;
var runtimeStateMapper = File . ReadAllText ( runtimeStateMapperPath ) ;
var requiredActionSymbols = new [ ]
{
"RequestLoadoutTemplateSave" ,
"TrySendRuntimeLoadoutTemplateCommand(loadout" ,
"private static bool TrySendRuntimeLoadoutTemplateCommand(" ,
"AetheriaRuntimeEveCommands.TrySendLoadoutTemplateCommand" ,
} ;
var missingActionSymbols = requiredActionSymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingActionSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Loadout save no longer sends a typed Eve loadout-template command: " +
string . Join ( ", " , missingActionSymbols ) ) ;
}
if ( actionGameManager . Contains ( "AetheriaRuntimeStateCommitLog.QueueLoadoutTemplate" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"ActionGameManager still saves loadout templates through the runtime commit log." ) ;
}
var requiredBridgeSymbols = new [ ]
{
"AetheriaRuntimeLoadoutTemplateCommands.Save" ,
"ExecuteLoadoutTemplateCommandAsync" ,
"command.LoadoutTemplate" ,
"AetheriaRuntimeStateMapper.ToLoadoutTemplate" ,
"AetheriaRuntimeStateMapper.LoadoutKey"
} ;
var missingBridgeSymbols = requiredBridgeSymbols
. Where ( symbol = > ! eveBridge . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingBridgeSymbols . Length > 0 )
{
throw new InvalidOperationException (
"Eve command bridge no longer owns typed loadout-template persistence: " +
string . Join ( ", " , missingBridgeSymbols ) ) ;
}
if ( ! eveCommandDocument . Contains ( "AetheriaRuntimeLoadoutTemplateCommit? LoadoutTemplate" , StringComparison . Ordinal ) | |
! loadoutCommands . Contains ( "SurfaceId = \"aetheria.loadout_templates\"" , StringComparison . Ordinal ) | |
! loadoutCommands . Contains ( "Save = \"aetheria.loadout_templates.save\"" , StringComparison . Ordinal ) | |
! runtimeStateMapper . Contains ( "public static AetheriaLoadoutTemplate ToLoadoutTemplate" , StringComparison . Ordinal ) | |
! runtimeStateMapper . Contains ( "public static CultRecordKey LoadoutKey" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Shared loadout-template Eve command contract is incomplete." ) ;
}
}
static void RequireInventoryLoadoutRestoreRequestAuthority ( string root )
2026-06-16 03:18:55 +01:00
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify loadout restore authority; ActionGameManager.cs is missing." ) ;
var requiredSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"RequestRuntimeLoadoutRestore" ,
"TryRequestDaemonLoadoutRestore" ,
"Operations.RestoreLoadout"
2026-06-16 03:18:55 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Loadout restore no longer has a typed daemon request primitive: " +
2026-06-16 03:18:55 +01:00
string . Join ( ", " , missingSymbols ) ) ;
}
2026-06-21 08:56:07 +01:00
if ( actionGameManager . Contains ( "price > Credits" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Unity loadout restore still rejects requests through renderer-local credits instead of daemon acceptance." ) ;
}
2026-06-16 03:18:55 +01:00
var inventoryPanelPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "InventoryPanel.cs" ) ;
var inventoryPanel = File . Exists ( inventoryPanelPath )
? File . ReadAllText ( inventoryPanelPath )
: throw new InvalidOperationException ( "Cannot verify loadout restore authority; InventoryPanel.cs is missing." ) ;
var forbiddenSymbols = new [ ]
{
"EntityConstructionBlueprintProjector.InstantiateFromBlueprint" ,
2026-06-21 08:56:07 +01:00
"EntityConstructionBlueprintProjector.ProjectObservedFromBlueprint" ,
"EntityConstructionBlueprintProjector.InstantiateAuthoritativeFromBlueprint" ,
2026-06-16 03:18:55 +01:00
"GameManager.Credits -=" ,
".SetParent(GameManager.DockedEntity)"
} ;
var hits = File . ReadLines ( inventoryPanelPath )
. Select ( ( line , index ) = > new { LineNumber = index + 1 , Line = line } )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, inventoryPanelPath)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"InventoryPanel still owns direct loadout restore mutation: " +
string . Join ( "; " , hits ) ) ;
}
2026-06-21 08:56:07 +01:00
var forbiddenLocalAcceptanceSymbols = new [ ]
{
"price < GameManager.Credits" ,
"price <= GameManager.Credits" ,
"price > GameManager.Credits" ,
"price >= GameManager.Credits"
} ;
var localAcceptanceHits = File . ReadLines ( inventoryPanelPath )
. Select ( ( line , index ) = > new { LineNumber = index + 1 , Line = line } )
. Where ( line = > forbiddenLocalAcceptanceSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, inventoryPanelPath)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( localAcceptanceHits . Length > 0 )
{
throw new InvalidOperationException (
"InventoryPanel still filters loadout restore through renderer-local credit checks instead of daemon acceptance: " +
string . Join ( "; " , localAcceptanceHits ) ) ;
}
if ( ! inventoryPanel . Contains ( "GameManager.RequestRuntimeLoadoutRestore" , StringComparison . Ordinal ) )
2026-06-16 03:18:55 +01:00
{
throw new InvalidOperationException ( "InventoryPanel no longer routes loadout restore through ActionGameManager." ) ;
}
}
2026-06-21 08:56:07 +01:00
static void RequireDockedCurrentShipRequestAuthority ( string root )
2026-06-16 03:43:21 +01:00
{
var actionGameManagerPath = Path . Combine ( root , "Assets" , "Scripts" , "Gameplay" , "ActionGameManager.cs" ) ;
var actionGameManager = File . Exists ( actionGameManagerPath )
? File . ReadAllText ( actionGameManagerPath )
: throw new InvalidOperationException ( "Cannot verify docked current-ship authority; ActionGameManager.cs is missing." ) ;
var requiredSymbols = new [ ]
{
2026-06-21 08:56:07 +01:00
"RequestDockedCurrentShip" ,
"TryRequestDaemonDockedCurrentShip" ,
"Operations.SetDockedCurrentShip"
2026-06-16 03:43:21 +01:00
} ;
var missingSymbols = requiredSymbols
. Where ( symbol = > ! actionGameManager . Contains ( symbol , StringComparison . Ordinal ) )
. ToArray ( ) ;
if ( missingSymbols . Length > 0 )
{
throw new InvalidOperationException (
2026-06-21 08:56:07 +01:00
"Docked current-ship selection no longer has a typed daemon request primitive: " +
2026-06-16 03:43:21 +01:00
string . Join ( ", " , missingSymbols ) ) ;
}
2026-06-21 08:56:07 +01:00
if ( actionGameManager . Contains ( "DockedEntity.Children.Contains(ship)" , StringComparison . Ordinal ) )
{
throw new InvalidOperationException (
"Unity docked current-ship selection still rejects through renderer-local child membership instead of daemon acceptance." ) ;
}
2026-06-16 03:43:21 +01:00
var inventoryPanelPath = Path . Combine ( root , "Assets" , "Scripts" , "UI" , "Menu" , "InventoryPanel.cs" ) ;
var forbiddenSymbols = new [ ]
{
"GameManager.CurrentEntity =" ,
"GameManager.DockingBay.DockedShip ="
} ;
var hits = File . ReadLines ( inventoryPanelPath )
. Select ( ( line , index ) = > new { LineNumber = index + 1 , Line = line } )
. Where ( line = > forbiddenSymbols . Any ( symbol = > line . Line . Contains ( symbol , StringComparison . Ordinal ) ) )
. Select ( line = > $"{Path.GetRelativePath(root, inventoryPanelPath)}:{line.LineNumber}: {line.Line.Trim()}" )
. ToArray ( ) ;
if ( hits . Length > 0 )
{
throw new InvalidOperationException (
"InventoryPanel still owns direct docked current-ship mutation: " +
string . Join ( "; " , hits ) ) ;
}
var inventoryPanel = File . ReadAllText ( inventoryPanelPath ) ;
2026-06-21 08:56:07 +01:00
if ( ! inventoryPanel . Contains ( "GameManager.RequestDockedCurrentShip" , StringComparison . Ordinal ) )
2026-06-16 03:43:21 +01:00
{
throw new InvalidOperationException ( "InventoryPanel no longer routes current-ship selection through ActionGameManager." ) ;
}
}
2026-06-21 08:56:07 +01:00
static IEnumerable < ( string MethodName , int LineNumber , string Line ) > FindMethodScopedLineHits (
string source ,
IReadOnlyList < string > symbols )
{
var currentMethod = "<top-level>" ;
var methodDepth = 0 ;
var pendingMethod = "" ;
var lines = source . Replace ( "\r\n" , "\n" , StringComparison . Ordinal ) . Split ( '\n' ) ;
for ( var index = 0 ; index < lines . Length ; index + + )
{
var line = lines [ index ] ;
var trimmed = line . Trim ( ) ;
var declaredMethod = TryReadMethodName ( trimmed ) ;
if ( ! string . IsNullOrWhiteSpace ( declaredMethod ) )
pendingMethod = declaredMethod ;
foreach ( var symbol in symbols )
{
if ( line . Contains ( symbol , StringComparison . Ordinal ) )
yield return ( currentMethod , index + 1 , line ) ;
}
var opens = line . Count ( character = > character = = '{' ) ;
var closes = line . Count ( character = > character = = '}' ) ;
if ( ! string . IsNullOrWhiteSpace ( pendingMethod ) & & opens > 0 )
{
currentMethod = pendingMethod ;
pendingMethod = "" ;
methodDepth = opens - closes ;
if ( methodDepth < = 0 )
{
currentMethod = "<top-level>" ;
methodDepth = 0 ;
}
continue ;
}
if ( methodDepth < = 0 )
continue ;
methodDepth + = opens - closes ;
if ( methodDepth < = 0 )
{
currentMethod = "<top-level>" ;
methodDepth = 0 ;
}
}
}
static string TryReadMethodName ( string trimmedLine )
{
if ( ! StartsWithMethodAccessModifier ( trimmedLine ) | |
! trimmedLine . Contains ( '(' ) | |
trimmedLine . Contains ( "=>" , StringComparison . Ordinal ) | |
trimmedLine . Contains ( "=" , StringComparison . Ordinal ) )
{
return "" ;
}
var beforeParameters = trimmedLine . Substring ( 0 , trimmedLine . IndexOf ( '(' ) ) . Trim ( ) ;
var tokens = beforeParameters . Split ( new [ ] { ' ' , '\t' } , StringSplitOptions . RemoveEmptyEntries ) ;
return tokens . Length = = 0 ? "" : tokens [ ^ 1 ] ;
}
static bool StartsWithMethodAccessModifier ( string trimmedLine )
{
return trimmedLine . StartsWith ( "private " , StringComparison . Ordinal ) | |
trimmedLine . StartsWith ( "public " , StringComparison . Ordinal ) | |
trimmedLine . StartsWith ( "protected " , StringComparison . Ordinal ) | |
trimmedLine . StartsWith ( "internal " , StringComparison . Ordinal ) | |
trimmedLine . StartsWith ( "static " , StringComparison . Ordinal ) ;
}
2026-06-13 10:01:20 +01:00
static bool ContainsBehaviorValueKind ( AetheriaBehaviorValue value , string kind )
{
return string . Equals ( value . Kind , kind , StringComparison . OrdinalIgnoreCase ) | |
value . Children . Any ( child = > ContainsBehaviorValueKind ( child , kind ) ) | |
value . MapEntries . Any ( entry = > ContainsBehaviorValueKind ( entry . Value , kind ) ) ;
}
2026-06-15 16:10:13 +01:00
static int CountRequiredBehaviorItemRefsMissingItemKeys ( AetheriaBehaviorPayload payload )
{
return payload . Fields . Count ( field = >
IsBehaviorItemRefField ( payload . Kind , field . Key ) & &
IsNonEmptyLegacyRefMissingItemKey ( field . Value ) ) ;
}
static bool IsBehaviorItemRefField ( string behaviorKind , int fieldKey )
{
return ( string . Equals ( behaviorKind , "ItemUsage" , StringComparison . OrdinalIgnoreCase ) & & fieldKey = = 1 ) | |
( IsWeaponBehavior ( behaviorKind ) & & fieldKey = = 12 ) ;
}
static bool IsWeaponBehavior ( string behaviorKind )
{
return behaviorKind is "GuidedWeapon" or "InstantWeapon" or "ConstantWeapon" or "ChargedWeapon" or "AutoWeapon" or "LockWeapon" ;
}
static bool IsNonEmptyLegacyRefMissingItemKey ( AetheriaBehaviorValue value )
{
return string . Equals ( value . Kind , "legacy-id" , StringComparison . OrdinalIgnoreCase ) & &
! IsEmptyLegacyId ( value . LegacyIdValue ) & &
string . IsNullOrWhiteSpace ( value . ItemKeyValue ) ;
}
static bool IsEmptyLegacyId ( string legacyId )
{
return string . IsNullOrWhiteSpace ( legacyId ) | |
( Guid . TryParse ( legacyId , out var parsed ) & & parsed = = Guid . Empty ) ;
}
2026-06-13 07:54:30 +01:00
static async Task RequireLegacyLookupAsync < T > (
string legacyId ,
Func < Task < T ? > > lookup ,
string label ) where T : class
{
if ( string . IsNullOrWhiteSpace ( legacyId ) )
{
throw new InvalidOperationException ( $"Cannot verify {label}: legacy id is empty." ) ;
}
var value = await lookup ( ) . ConfigureAwait ( false ) ;
if ( value = = null )
{
throw new InvalidOperationException ( $"Typed legacy-id lookup failed for {label} {legacyId}." ) ;
}
}