# Universe Model Migration Worksheet This worksheet is the implementation-facing companion to [UNIVERSE-MODEL.md](/home/jbourdon/repos/space-game/docs/UNIVERSE-MODEL.md). It exists to drive the migration from the current runtime model to the target spatial model without turning the work into a blind rewrite. This is not a new design authority. If this worksheet conflicts with [UNIVERSE-MODEL.md](/home/jbourdon/repos/space-game/docs/UNIVERSE-MODEL.md), the universe model wins. ## Goal Move the simulation and viewer from the current flatter `system + coordinates + node/celestial overlays` model to: - `system` - `anchor` - `localspace` as the tactical space around an anchor - ships and constructions living at one anchor - intra-system travel represented as anchor-to-anchor warp transit ## Scope ### Required - Introduce first-class `Anchor` runtime concepts. - Treat `localspace` as the tactical space around an anchor, not a separately identified entity. - Rebind ships, stations, claims, construction sites, and resource nodes to anchors. - Make intra-system travel anchor-to-anchor instead of raw free coordinate travel. - Expose anchor-based spatial truth in backend contracts. - Update the viewer to consume anchors as the primary spatial model. ### Recommended - Preserve current authored content and scenario data where possible. - Migrate incrementally behind compatibility adapters instead of a big-bang rewrite. - Keep galaxy/system/local presentation working throughout the migration. - Prefer additive contract changes first, then remove old fields once the stack is stable. - Make every compatibility bridge loud, temporary, and easy to delete. ### Optional - Rename every old `node` term immediately. - Rework the full event streaming model during the first spatial pass. - Rebuild AI/commander architecture in the same pass. ## Current Reality The codebase already has partial pieces of the target model: - celestials and Lagrange points exist - resource nodes already behave like anchored locations - ships already carry transit and space-layer state - claims, construction sites, market orders, and policies already exist - the viewer already has galaxy/system/local presentation logic The real problem is that these are still layered on top of a flatter simulation core. ## Main Gaps ### 1. No First-Class Anchor Entity Current code spreads the concept across: - `CelestialRuntime` - `ResourceNodeRuntime` - `SystemId` - raw positions - `CurrentCelestialId` - `DestinationNodeId` This is the biggest structural gap. ### 2. Stations And Construction Are Still Celestial-Centric Current contracts and runtime bind station-like structures to `CelestialId`. That does not match the target model where: - anchors are generic - not all anchors are celestials - construction happens at valid construction anchors ### 3. Movement Truth Is Still Coordinate-First Ships still fundamentally operate through: - local positions - target positions - system IDs Transit exists, but the simulation still thinks in free coordinates first. The target model wants: - localspace movement inside an anchor-owned tactical bubble - explicit anchor-to-anchor warp state inside a system ### 4. Viewer Contracts Still Mirror The Old World The viewer still consumes: - systems - celestials - resource nodes - stations - ships It does not consume: - anchors - localspaces - anchor topology as the primary graph ### 5. Localspace Is Not Yet The Simulation Boundary The docs assume localspace is the natural tactical partition. The code still treats the system as the practical runtime container, with local context layered on top. ### 6. Units And Coordinate Separation Are Not Explicit Enough The target model assumes separate spatial units by layer: - galaxy in `ly` - system in `AU` - localspace in `m` and `m/s` The codebase currently mixes spatial concerns more freely than the target model allows. That creates risk of accidental cross-layer coordinate coupling. ## Migration Strategy Do this in seams. Do not try to land the whole universe model in one rewrite. ## Units And Coordinate Rules The migration must preserve separate native units by layer. ### Required - Galaxy space uses `ly`. - System space uses `AU`. - Localspace uses `m` and `m/s`. - Do not introduce a universal shared coordinate model spanning all layers. - Cross-layer movement should use references and travel state, not coordinate projection. ### Recommended - Keep unit-specific types or field names obvious in code and contracts. - Keep conversion logic local to presentation helpers when a client animation needs it. - Treat viewer zoom transitions as presentation-only effects. ### Disallowed - Treating localspace offsets as meaningful system-space coordinates. - Treating system-space offsets as meaningful galaxy coordinates. - Building migration helpers that silently convert all positions into one synthetic global frame. ## Compatibility Rules Compatibility bridges are allowed only as explicit migration tools. They must not become silent permanent architecture. ### Required - Every compatibility field, mapper, and adapter must be clearly marked. - In C#, use `[Obsolete]` with a concrete migration reason when possible. - In TypeScript/Vue, use an explicit `OBSOLETE` or `COMPATIBILITY BRIDGE` comment. - Compatibility names should be obvious: - `Legacy...` - `Compat...` - `...CompatibilityAdapter` - Compatibility code should stay at seams: - contracts - mapping - serialization - viewer ingestion ### Recommended - Avoid compatibility logic in the core simulation path unless there is no safer seam. - Every compatibility bridge should have a removal condition. - When a compatibility bridge stops being necessary, remove it in the same migration branch if practical. ### Disallowed - Silent fallback fields with no marker. - Compatibility helpers with neutral names that look permanent. - New feature work that depends on deprecated spatial fields without an explicit exception. ## Compatibility Inventory This section should remain in the worksheet during the migration and be kept current. Each entry should be removed from this list when the compatibility bridge is removed from the codebase. | Status | Area | Compatibility Item | Reason | Removal Condition | | --- | --- | --- | --- | --- | | Planned | Backend contracts | Legacy `CelestialId` placement fields alongside `anchorId` | Preserve contract compatibility while moving structures and ships to anchor-based placement truth | Remove once backend and viewer both consume anchor placement directly | | Planned | Backend ship state | Legacy `DestinationNodeId` / node-based transit naming during anchor migration | Preserve existing travel wiring while renaming and rebinding spatial state | Remove once runtime and contracts use anchor terminology end-to-end | | Planned | Viewer ingestion | Compatibility mapping from legacy world snapshot fields into anchor-based view models | Allow incremental frontend migration without blocking backend progress | Remove once viewer contracts expose anchors as primary data | ## Phase 1. Stabilize Vocabulary ### Objective Introduce the target terms and IDs without breaking current gameplay. ### Required - Add runtime `Anchor` models. - Add contract snapshots/deltas for anchors. - Add `anchorId` to: - ships - stations - claims - construction sites - resource nodes - Keep existing `CelestialId` and `NodeId` compatibility fields temporarily where needed. ### Success Criteria - world snapshot can describe anchors directly - old code still runs through compatibility mapping ## Phase 2. Make Scenario Loading Anchor-First ### Objective Make scenario/spatial generation create anchors as first-class data. ### Required - Treat stars, planets, moons, Lagrange points, and resource nodes as anchor-generation inputs. - Define the localspace around each anchor. - Stop treating station placement as a direct system-coordinate choice. - Resolve starter stations and spawned stations through valid anchors. ### Recommended - Preserve `planetIndex` and `lagrangeSide` authored hints by translating them into anchor selection. ### Success Criteria - every meaningful location is represented as an anchor - every anchor has a localspace around it - station spawn/founding resolves to an anchor, not a raw point ## Phase 3. Rebind Structures And Sites ### Objective Make infrastructure belong to anchors instead of generic system/celestial placement. ### Required - Stations reference `anchorId`. - Claims reference `anchorId`. - Construction sites reference `anchorId`. - Resource nodes reference `anchorId`. ### Recommended - Keep `CelestialId` only as a derived convenience when the anchor is a celestial. ### Success Criteria - no structure requires `CelestialId` as its primary placement key ## Phase 4. Replace Free Intra-System Travel ### Objective Make intra-system travel obey the universe model. ### Required - Ships in tactical state belong to one anchor's localspace. - Ships leaving a localspace enter explicit warp transit. - Transit stores: - origin anchor - destination anchor - started at - arrival due at - progress - Arrival resolves at the destination anchor. ### Recommended - Keep current vector movement only for local tactical motion. ### Success Criteria - ships no longer “just move across the whole system” as the primary truth - warp is anchor-to-anchor by model, not just by UI ## Phase 5. Update Viewer Contracts ### Objective Expose the new world truth cleanly to the frontend. ### Required - Add anchor snapshots/deltas. - Update ship snapshots to reference anchor-based state explicitly. - Update station/claim/construction snapshots similarly. ### Recommended - Keep a short compatibility window where old viewer fields still exist. ### Success Criteria - the viewer no longer has to infer the world model from mixed legacy fields ## Phase 6. Update Viewer Presentation ### Objective Make the viewer's tactical style align with the new simulation model. ### Required - System view uses anchors as the primary navigable graph. - Local view resolves the localspace around one anchor. - Ships in warp display as transit-state entities between anchors. - Construction and claims render at valid anchors. ### Recommended - Keep a tactical-style presentation; do not regress into cinematic free-flight assumptions. ### Success Criteria - viewer hierarchy matches `galaxy -> system -> localspace` ## What Can Stay These areas are worth preserving: - data-driven item/module/recipe definitions - current snapshot/delta transport pattern - current tactical-style visual direction - current claims/construction/policy/market-order runtime concepts - current player onboarding and per-player faction work ## What Must Change First If implementation starts immediately, start here: 1. add `Anchor` runtime models 2. add backend contracts for them 3. make `SpatialBuilder` produce them 4. add `anchorId` to ships and structures 5. only then refactor movement and viewer logic This is the smallest seam that changes the model without exploding the whole codebase. ## Risks ### High Risk - changing movement and placement truth at the same time - trying to rename everything in one pass - mixing commander/AI refactors into the first spatial migration ### Medium Risk - stale viewer assumptions around `systemId + celestialId` - hidden GM/player tooling that assumes node/celestial placement - compatibility bugs during contract overlap ### Lower Risk - doc drift, now that `UNIVERSE-MODEL.md` is canonical ## Non-Goals For The First Migration Pass - full commander architecture rewrite - full event streaming redesign - full economy redesign - sovereignty redesign - fleet hierarchy implementation Those systems should adapt to the new spatial model, not block it. ## Exit Condition This worksheet is complete when the codebase can truthfully say: - every meaningful place in a system is an anchor - every anchor has a localspace around it - ships and constructions live at anchors - intra-system travel is anchor-to-anchor warp - the viewer consumes anchors directly