Novolis Docs
novolis-governance / completed-plans/racing_placement_split_aad63395.plan.md

Racing placement: Simulation facet + dogfood composition

dotnetgovernancenovolis

name: Racing placement split overview: Extract the headless racing simulation into Novolis.Simulation.Racing (platform facet, ML-agnostic). Keep neural evolution glue in novolis-dogfooding as app composition code. Do not add MachineLearning.Racing or any domain-specific ML packages. todos:

  • id: sim-racing-project

content: Create Novolis.Simulation.Racing project; move ML-agnostic sim code from dogfood; rename namespaces status: completed

  • id: sim-racing-tests

content: Add Novolis.Simulation.Racing.Tests (11 test files); wire slnx, packages.json, registry status: completed

  • id: dogfood-slim

content: Remove NeuralRacing.Sim; keep Training + Neural controllers in app; PackageReference sim + neural status: completed

  • id: dogfood-tests

content: Reduce NeuralRacing.Tests to 2 trainer test files only status: completed

  • id: governance

content: Update library-boundaries, frank docs, migrate-frank-slice; optional wave-12 brief status: completed isProject: false


Problem

MachineLearning.Racing would be reverse leakage: a domain scenario (racing + evolution) packaged under a repo that should stay orthogonal building blocks like novolis-math or novolis-raylib.

Per library-boundaries.md:

  • Math / Physics / Simulation = closed stack; Simulation depends only on Math + Physics.
  • MachineLearning = orthogonal (not in stack); apps compose it with other repos.
  • Apps own cross-repo glue: "Apps may reference any combination; they own cross-repo glue."

Racing today in novolis-dogfooding/apps/NeuralRacing is a hidden library (~55 sim files + 266 tests) with an invalid dependency: NeuralRacing.Sim ProjectReferences Novolis.MachineLearning.Neural — the sim layer should not depend on ML.

flowchart TB
  subgraph ml_repo [novolis-machinelearning]
    Core[Core]
    NeuralAbs[Neural.Abstractions]
    Neural[Neural]
    AutoMl[AutoMl]
  end

  subgraph sim_repo [novolis-simulation]
    SimRacing[Simulation.Racing]
  end

  subgraph dogfood [novolis-dogfooding]
    App[NeuralRacing app]
    Trainer[Training folder]
    NeuralCtrl[NeuralRaceCarController]
    TrainerTests[Trainer tests only]
  end

  Neural --> NeuralAbs
  SimRacing --> MathBCL[BCL numerics only at first]

  App --> SimRacing
  App --> Neural
  Trainer --> SimRacing
  Trainer --> Neural
  NeuralCtrl --> SimRacing
  NeuralCtrl --> Neural

What belongs where

Code (current NeuralRacing.Sim)TargetRationale
Tracks/, Race/, Sensors/, Rewards/, Progress/, Cars/* except neural types`Novolis.Simulation.Racing`Headless tick-based sim; IRaceCarController is policy-agnostic
Cars/NeuralRaceCarController.cs, INeuralRaceCarController.cs`apps/NeuralRacing/` (app source)Adapts INeuralNetwork → IRaceCarController; ML-specific
Training/* (4 files)`apps/NeuralRacing/`Evolution loop over sim + DenseNetwork; product/demo glue
Program.cs`apps/NeuralRacing/`Demo entry
11 sim test files (~264 tests)`Novolis.Simulation.Racing.Tests`Library tests live with the library
EvolutionaryRacingTrainer*Tests.cs (2 files)`apps/NeuralRacing.Tests`Tests composition, not a platform package

[novolis-machinelearning](d:\novolis\novolis-machinelearning) stays four packable facets only:

  • Novolis.MachineLearning.Core
  • Novolis.MachineLearning.Neural.Abstractions
  • Novolis.MachineLearning.Neural
  • Novolis.MachineLearning.AutoMl

No new ML packages. README already states racing is not in this repo — keep that.

Why Simulation.Racing fits the stack

  • Matches existing facets (World, Kinematics): domain runtime under Simulation, not under ML.
  • RaceSimulation is orchestration over discrete ticks (like Simulation’s clock/world model), with custom kinematics — does not require Novolis.Physics.* on day one.
  • Must not reference Novolis.MachineLearning.* (enables sim tests without ML on the graph).

Not Simulation.Racing + MachineLearning.Racing: the second package would re-introduce domain naming in the wrong repo and tempt Simulation ↔ ML coupling in platform code.

Phase 1 — Add Novolis.Simulation.Racing (novolis-simulation)

  1. Create project at src/Novolis.Simulation.Racing/ using the same pattern as Novolis.Simulation.Kinematics.csproj:
  • Import Novolis.Simulation.Packaging.props
  • PackageId = Novolis.Simulation.Racing
  • No project references to MachineLearning
  • No project reference to Physics required initially (racing is self-contained); optional Novolis.Math.Geometry later if shared ray/grid helpers appear
  1. Move + rename from NeuralRacing.Sim:
  • Namespace: NeuralRacing.* → Novolis.Simulation.Racing.* (folder layout unchanged: Tracks, Race, Cars, etc.)
  • Exclude: Training/, NeuralRaceCarController.cs, INeuralRaceCarController.cs
  1. Add tests tests/Novolis.Simulation.Racing.Tests/:
  • Port 11 files from NeuralRacing.Tests/Sim (all except the two EvolutionaryRacingTrainer* files)
  • Namespace: Novolis.Simulation.Racing.Tests
  • TUnit exe pattern like Novolis.Simulation.World.Tests
  • Small local TestInfrastructure (copy BaseTest / StructuredTestOutput from dogfood or ML test support — do not reference ML test projects)
  1. Wire repo:

Phase 2 — Slim dogfooding to composition-only

  1. Delete apps/NeuralRacing/NeuralRacing.Sim/ after move.
  1. Restructure apps/NeuralRacing:
   apps/NeuralRacing/
     NeuralRacing.csproj          # Exe, IsPackable=false
     Program.cs
     Training/                    # 4 files from old Sim/Training
     Controllers/                 # NeuralRaceCarController + INeuralRaceCarController
  • PackageReference to Novolis.Simulation.Racing and Novolis.MachineLearning.Neural (2026.1.* per Directory.Packages.props)
  • Remove monorepo ProjectReference into novolis-machinelearning (aligns with dogfooding design); use local GPR or artifacts/nuget-local during dev per nuget-setup.md
  1. Slim tests NeuralRacing.Tests:
  • Keep only EvolutionaryRacingTrainerTests.cs + EvolutionaryRacingTrainerValidationTests.cs
  • Reference app + packages (not Simulation test internals)
  1. Update Novolis.Dogfooding.slnx and README apps table: NeuralRacing exercises Simulation.Racing + MachineLearning.Neural.

Phase 3 — Governance and migration script

Update docs to codify the rule “no domain packages in ML repo”:

DocumentChange
library-boundaries.mdAdd row: headless racing sim → Simulation.Racing; NN evolution on racing → apps
simulation-layer-policy.mdList Simulation.Racing facet
wave-8-machinelearning-neural.mdRacing → Simulation.Racing + dogfood glue (not ML package)
frank-naming-and-structure.mdFrank.ML.Domain.Racing → Novolis.Simulation.Racing + dogfood
frank-inventory.mdSame mapping
migrate-frank-slice.ps1Frank.ML.Domain.Racing → Novolis.Simulation.Racing (not NeuralRacing)

Optional: add extraction-briefs/wave-12-simulation-racing.md for the Frank.ML racing sim slice.

Deferred (explicitly out of scope)

ItemReason
`Vector2` → XZ `Vector3`Racing uses System.Numerics.Vector2 throughout; library-boundaries forbids Vector2 in stack — schedule as follow-up, do not block the move
Integrate `RaceSimulation` with `SimulationWorld` / `ISimulationSystem`Future alignment; current code is a standalone loop
Frank.ML Avalonia / Spectre UIStays private Frank.ML; may consume packages later
`MachineLearning.Racing`Rejected — not a building block

Success criteria

  • novolis-simulation builds; Novolis.Simulation.Racing.Tests passes (~264 tests).
  • novolis-machinelearning unchanged package set (4 facets); no racing code.
  • novolis-dogfooding NeuralRacing is a thin exe + ~6 glue files + 2 trainer test files; no embedded sim library.
  • Dependency graph: Simulation.Racing does not reference MachineLearning.*.