Rework network to 6-tier Clos topology with individual switch entities
CI / build-and-push (push) Successful in 31s

Replace aggregate network health stats with a full 6-tier Clos topology
(ToR → T1 → T2 → T3 → T4 → T5) where every switch is an individually
tracked entity with uplinks, repair pipelines, and failure cascades.

Key mechanics:
- Bottleneck bandwidth model (min along path) affects FLOPS and satisfaction
- Rackdown on full disconnect → racks re-enter testing pipeline on recovery
- Binomial failure sampling per tier, dirty-flag cascade optimization
- Flat switch registry for performance at scale
- Three new research nodes: network-redundancy, fast-repair, hot-standby

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-25 01:33:59 -04:00
parent f8d7a25c6e
commit 54220fca70
9 changed files with 725 additions and 284 deletions
+21 -22
View File
@@ -9,7 +9,7 @@ import type {
Cluster, Campus, DataCenter, DCTier, RackSkuId, TrainingJob,
ActiveResearch, OwnedDataset, LocationId,
DeploymentCohort, PipelineStage,
NetworkHealthState, CampusRetrofitQueue,
CampusRetrofitQueue,
} from '@ai-tycoon/shared';
import type { FundingRoundType, OverloadPolicy, TuningPreset, ModelTuning } from '@ai-tycoon/shared';
import {
@@ -25,9 +25,12 @@ import {
FUNDING_ROUNDS,
OPEN_SOURCE_REPUTATION_BOOST,
LOCATION_CONFIGS,
networkSlotsRequired, maxComputeRacks,
estimateNetworkSlots, maxComputeRacks,
uuid,
} from '@ai-tycoon/shared';
import {
emptyDCNetworkSummary, emptyCampusNetworkSummary, emptyClusterNetworkSummary,
} from '@ai-tycoon/game-engine';
import { INITIAL_RIVALS } from '@ai-tycoon/game-engine';
export type ActivePage = 'dashboard' | 'infrastructure' | 'research' | 'models'
@@ -57,8 +60,14 @@ export interface GameNotification {
read: boolean;
}
function emptyNetworkHealth(): NetworkHealthState {
return { tier1Required: 0, tier1Healthy: 0, tier2Required: 0, tier2Healthy: 0, tier3Required: 0, tier3Healthy: 0, racksDisconnected: 0 };
function emptyDC(): Pick<DataCenter, 'networkSummary' | 'effectiveComputeRacks' | 'usedSlots' | 'usedPowerKW' | 'energyCostPerTick' | 'maintenanceCostPerTick' | 'currentUptime'> {
return {
networkSummary: emptyDCNetworkSummary(),
effectiveComputeRacks: 0,
usedSlots: 0, usedPowerKW: 0,
energyCostPerTick: 0, maintenanceCostPerTick: 0,
currentUptime: 1,
};
}
interface Actions {
@@ -189,7 +198,7 @@ export function computeFillForDC(
const sku = RACK_SKU_CONFIGS[skuId];
const tierConfig = DC_TIER_CONFIGS[dc.tier];
const maxCompute = maxComputeRacks(tierConfig.rackSlots);
const maxCompute = maxComputeRacks(tierConfig.rackSlots, dc.tier);
const pipelineCount = dc.deploymentCohorts.filter(c => c.stage !== 'decommission').reduce((sum, c) => sum + c.count, 0);
const existingCompute = dc.computeRacksOnline + pipelineCount;
const available = maxCompute - existingCompute;
@@ -319,6 +328,7 @@ export const useGameStore = create<Store>()(
status: isFirst ? 'operational' : 'constructing',
constructionProgress: isFirst ? 0 : 0,
constructionTotal: isFirst ? 0 : CLUSTER_COST_CONFIG.buildTimeTicks,
networkSummary: emptyClusterNetworkSummary(),
};
return {
@@ -358,6 +368,7 @@ export const useGameStore = create<Store>()(
constructionProgress: 0,
constructionTotal: buildTime,
retrofitQueue: null,
networkSummary: emptyCampusNetworkSummary(),
};
return {
@@ -398,17 +409,11 @@ export const useGameStore = create<Store>()(
rackSkuId: null,
computeRacksOnline: 0,
computeRacksFailed: 0,
networkHealth: emptyNetworkHealth(),
...emptyDC(),
deploymentCohorts: [],
retrofitState: null,
coolingLevel: 0,
redundancyLevel: 0,
effectiveComputeRacks: 0,
usedSlots: 0,
usedPowerKW: 0,
energyCostPerTick: 0,
maintenanceCostPerTick: 0,
currentUptime: 1,
};
return {
@@ -437,14 +442,14 @@ export const useGameStore = create<Store>()(
if (sku.requiredResearch && !s.research.completedResearch.includes(sku.requiredResearch)) return s;
const tierConfig = DC_TIER_CONFIGS[dc.tier];
const maxCompute = maxComputeRacks(tierConfig.rackSlots);
const maxCompute = maxComputeRacks(tierConfig.rackSlots, dc.tier);
const pipelineCount = dc.deploymentCohorts.filter(c => c.stage !== 'decommission').reduce((sum, c) => sum + c.count, 0);
const existingCompute = dc.computeRacksOnline + pipelineCount;
const available = maxCompute - existingCompute;
const actualQty = Math.min(quantity, available);
if (actualQty <= 0) return s;
const totalNetSlots = networkSlotsRequired(existingCompute + actualQty);
const totalNetSlots = estimateNetworkSlots(existingCompute + actualQty, dc.tier);
const totalSlotsNeeded = existingCompute + actualQty + totalNetSlots;
if (totalSlotsNeeded > tierConfig.rackSlots) return s;
@@ -484,7 +489,7 @@ export const useGameStore = create<Store>()(
const dc = found.dc;
const tierConfig = DC_TIER_CONFIGS[dc.tier];
const maxCompute = maxComputeRacks(tierConfig.rackSlots);
const maxCompute = maxComputeRacks(tierConfig.rackSlots, dc.tier);
const pipelineCount = dc.deploymentCohorts.filter(c => c.stage !== 'decommission').reduce((sum, c) => sum + c.count, 0);
const existingCompute = dc.computeRacksOnline + pipelineCount;
const available = maxCompute - existingCompute;
@@ -522,17 +527,11 @@ export const useGameStore = create<Store>()(
rackSkuId: null,
computeRacksOnline: 0,
computeRacksFailed: 0,
networkHealth: emptyNetworkHealth(),
...emptyDC(),
deploymentCohorts: [],
retrofitState: null,
coolingLevel: 0,
redundancyLevel: 0,
effectiveComputeRacks: 0,
usedSlots: 0,
usedPowerKW: 0,
energyCostPerTick: 0,
maintenanceCostPerTick: 0,
currentUptime: 1,
});
}