Refine anchor-first universe migration docs

This commit is contained in:
2026-04-07 00:28:38 -04:00
parent fdcf83ccec
commit 75568324f5
5 changed files with 504 additions and 56 deletions

View File

@@ -40,7 +40,6 @@ Recommended global ID families:
- `commanderId`
- `systemId`
- `anchorId`
- `localspaceId`
- `stationId`
- `shipId`
- `moduleId`
@@ -59,15 +58,14 @@ The intended core entities are:
2. `Commander`
3. `System`
4. `Anchor`
5. `Localspace`
6. `Station`
7. `Ship`
8. `ModuleInstance`
9. `Claim`
10. `ConstructionSite`
11. `MarketOrder`
12. `Recipe`
13. `PolicySet`
5. `Station`
6. `Ship`
7. `ModuleInstance`
8. `Claim`
9. `ConstructionSite`
10. `MarketOrder`
11. `Recipe`
12. `PolicySet`
## Faction
@@ -120,12 +118,12 @@ Suggested fields:
- `label`
- `galaxyPosition`
- `star definition`
- `nodeIds`
- `anchorIds`
- `faction influence later`
## Anchor
An anchor is a meaningful location in a system that owns a localspace.
An anchor is a meaningful location in a system.
Suggested fields:
@@ -133,7 +131,7 @@ Suggested fields:
- `systemId`
- `kind`
- `systemPosition`
- `localspaceId`
- `localspaceRadius`
- `parentAnchorId?`
- `orbital metadata?`
- `constructionIds?`
@@ -146,25 +144,9 @@ Recommended anchor kinds:
- `lagrange-point`
- `resource-node`
## Localspace
A localspace is the tactical simulation context attached to one anchor.
Suggested fields:
- `localspaceId`
- `anchorId`
- `systemId`
- `radius`
- `occupantShipIds`
- `occupantStationIds`
- `occupantClaimIds`
- `occupantConstructionSiteIds`
- `serverAuthorityId later`
## Station
A station is a constructed structure that lives inside one localspace.
A station is a constructed structure that lives at one anchor.
Suggested fields:
@@ -173,7 +155,6 @@ Suggested fields:
- `commanderId?`
- `anchorId`
- `systemId`
- `localspaceId`
- `moduleIds`
- `inventory`
- `population`
@@ -236,7 +217,6 @@ Suggested fields:
- `commanderId?`
- `systemId`
- `anchorId`
- `localspaceId`
- `placedAt`
- `activatesAt`
- `state`
@@ -258,7 +238,6 @@ Suggested fields:
- `constructionSiteId`
- `ownerFactionId`
- `anchorId`
- `localspaceId`
- `targetKind`
- `targetDefinitionId`
- `requiredItems`
@@ -351,7 +330,6 @@ Recommended ship spatial state fields:
- `spaceLayer`
- `currentSystemId`
- `currentAnchorId?`
- `currentLocalspaceId?`
- `localPosition?`
- `systemPosition?`
- `movementRegime`

View File

@@ -34,7 +34,6 @@ Every event should conceptually have:
- `kind`
- `spaceLayer`
- `systemId?`
- `localspaceId?`
- `anchorId?`
- `primaryEntityKind`
- `primaryEntityId`

View File

@@ -73,7 +73,7 @@ Current state:
Primary gaps:
- [`RuntimeModels.cs`](/home/jbourdon/repos/space-game/apps/backend/Simulation/RuntimeModels.cs) has no `AnchorRuntime`, `LocalspaceRuntime`, `ClaimRuntime`, or `ConstructionSiteRuntime`.
- [`RuntimeModels.cs`](/home/jbourdon/repos/space-game/apps/backend/Simulation/RuntimeModels.cs) has no first-class `AnchorRuntime` aligned with the target design, plus no fully anchor-native `ClaimRuntime` or `ConstructionSiteRuntime`.
- [`ScenarioLoader.cs`](/home/jbourdon/repos/space-game/apps/backend/Simulation/ScenarioLoader.cs) computes station positions directly instead of creating anchor-backed placement.
- [`SimulationEngine.cs`](/home/jbourdon/repos/space-game/apps/backend/Simulation/SimulationEngine.cs) still treats travel as raw coordinate movement rather than anchor-to-anchor transit between spaces.
@@ -247,7 +247,6 @@ Work:
- extend [`RuntimeModels.cs`](/home/jbourdon/repos/space-game/apps/backend/Simulation/RuntimeModels.cs) with:
- `AnchorRuntime`
- `LocalspaceRuntime`
- `CommanderRuntime`
- `ClaimRuntime`
- `ConstructionSiteRuntime`
@@ -262,7 +261,6 @@ Work:
- add structured ship spatial state:
- current space layer
- current anchor
- current localspace
- current transit
Why first:

View File

@@ -0,0 +1,398 @@
# 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

View File

@@ -8,7 +8,7 @@ It is the canonical reference for:
- solar systems
- celestials
- anchors
- localspaces
- localspaces as the tactical space around anchors
- ship and station placement
- intra-system travel
- inter-system travel
@@ -27,14 +27,13 @@ The structure can be understood as a tree:
- the galaxy contains solar systems
- each solar system contains celestials and other derived locations
- each meaningful location is an anchor
- each anchor owns one localspace
- each anchor has a localspace around it
The intended structure is:
1. `galaxy`
2. `solar system`
3. `anchor`
4. `localspace`
Ships and stations do not live in arbitrary free-floating "system local space".
@@ -99,7 +98,7 @@ Systems remain important for:
## Anchors
An anchor is a meaningful object in a system that owns a localspace.
An anchor is a meaningful object in a system.
Anchors are first-class world entities.
@@ -120,7 +119,7 @@ Each anchor should have:
- an anchor type
- a position in system space
- optional orbital metadata
- an associated localspace definition
- a localspace around it
- optional parent/child relationships
Examples:
@@ -131,7 +130,7 @@ Examples:
- a resource node is an anchor but not a celestial
- a Lagrange point can be the child of a moon, which is the child of a planet, which is the child of a star
Every anchor has exactly one localspace.
Every anchor has exactly one localspace around it.
## Celestials
@@ -147,9 +146,9 @@ Celestials exist for three reasons:
1. they structure the solar system visually and strategically
2. they define orbital relationships
3. they provide valid anchors for localspaces and derived locations such as Lagrange points
3. they provide valid anchors and derived locations such as Lagrange points
Every star, planet, and moon gets a localspace.
Every star, planet, and moon has a localspace around it.
Not all anchors are celestials, but all celestials are anchors.
@@ -165,7 +164,7 @@ Initial assumptions:
- major orbitals can expose `L1` through `L5`
- each exposed Lagrange point is its own anchor
- each exposed Lagrange point has its own localspace
- each exposed Lagrange point has its own localspace around it
- Lagrange points are valid construction sites
For now, all five may exist for supported orbitals, but the intended direction is that only major planets should necessarily expose all five.
@@ -186,7 +185,7 @@ That means a resource node can have:
- a stable identity
- a place in a solar system
- its own localspace
- its own localspace around it
This is desirable because it allows resources to exist anywhere meaningful in a system while still fitting the anchored localspace model.
@@ -212,7 +211,7 @@ Use:
- `localspace`
Each localspace belongs to exactly one anchor.
Each localspace is the tactical space around exactly one anchor.
Examples:
@@ -237,12 +236,12 @@ Ships and constructions do not exist directly in system space. They exist in one
## Ship Placement
A ship always belongs to exactly one localspace unless it is actively transitioning between anchors.
A ship always belongs to exactly one anchor unless it is actively transitioning between anchors.
Normal ship state should be one of:
- in a localspace
- traveling between localspaces in the same system
- at an anchor
- traveling between anchors in the same system
- traveling between systems
Inside a localspace, ships use tactical movement with thrusters.
@@ -444,6 +443,82 @@ Localspace view should show:
The viewer should not imply that the full solar system is one continuous local battlefield.
Viewer zoom transitions are a client-side presentation effect.
That means:
- zooming from galaxy to system does not imply one shared coordinate space
- zooming from system to localspace does not imply one shared coordinate space
- the client may animate or blend between views for readability
- those effects do not define simulation truth
## Scales And Units
Each spatial layer owns its own units.
Those units should be native to that layer, not projections of one universal master coordinate system.
### Galaxy
The galaxy layer uses:
- `light-years`
This is the scale for:
- system positions
- inter-system distances
- strategic map layout
### System
The system layer uses:
- `AU`
This is the scale for:
- anchor positions inside a system
- orbital relationships
- intra-system routing and warp travel
Variation at localspace scale is intentionally insignificant at this layer.
### Localspace
The localspace layer uses:
- `meters`
- `m/s`
This is the scale for:
- tactical movement
- combat
- docking
- mining
- construction
Variation at system scale is intentionally insignificant at this layer.
## Cross-Layer Rule
The simulation should not try to preserve one continuous coordinate frame across galaxy, system, and localspace.
Cross-layer relationships should be modeled through:
- containment
- references
- travel state
Not through:
- universal coordinate conversion
- raw projection of localspace offsets into system space
- raw projection of system offsets into galaxy space
The viewer may animate transitions between layers, but that is presentation only. It does not mean the simulation uses one continuous spatial frame.
## Ownership And Sovereignty
Ownership and sovereignty should primarily be tracked at system level.
@@ -451,7 +526,7 @@ Ownership and sovereignty should primarily be tracked at system level.
This is not fully defined yet, but the current design direction is:
- systems are the main sovereignty unit
- localspaces and constructions exist inside systems
- anchors and constructions exist inside systems
- local conflicts and control still matter tactically
## Simulation Implications
@@ -466,7 +541,7 @@ This model supports:
It also gives a cleaner authority boundary for later scaling:
- one localspace can become one simulation partition
- one anchor can become one tactical simulation partition
- one system can remain a higher-level strategic container
This supports:
@@ -493,8 +568,8 @@ Until the implementation is updated, the following terms should be used consiste
- `galaxy`: the top-level strategic star map
- `system`: the solar system container
- `celestial`: star, planet, or moon
- `anchor`: anything that owns a localspace
- `localspace`: the tactical simulation bubble attached to one anchor
- `anchor`: a meaningful location in a system and the primary tactical simulation container
- `localspace`: the tactical simulation space around an anchor, not a separately identified entity
- `intra-system warp`: movement between anchors in the same system
- `inter-system FTL`: movement between systems