Docs: align CLAUDE.md + vault to scene-split / Automation / Saves / UITK

CLAUDE.md: rewrite Bootstrap&worlds (scene split + on-demand frontend), add M7 Automation + disk-persistence + UITK HUD bullets, new build-gotchas, note new folders fit existing asmdefs. Vault: Milestones (M6 + polish pass -> Done; HUD + cleanup rows), Backlog (ConnectionUI done + cleanup notes), Home/Systems_Index dates. Add the 3 prior session logs + DR-019/020/021 + the 2026-06-06 cleanup log + screenshots.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 15:06:26 -07:00
parent adf78570f8
commit 8aed336340
24 changed files with 1144 additions and 17 deletions
@@ -0,0 +1,82 @@
---
date: 2026-06-05
type: session
tags: [session, frontend, menu, ui-toolkit, settings, saves, netcode, world-lifecycle, build, infra]
---
# Session 2026-06-05 — Game infrastructure: Main Menu, Settings, Saves foundation, Build wiring
## Goal
Operator: *"Create all the surrounding infra around the game. A main menu with the various options to play (host/join/single etc.), settings that actually adjust settings within the game, persistent saves. Basically everything needed to be able to build the game and test it."*
Intake decisions: **Saves** = foundation + a minimal real slice (resource ledger + goal charge + settings); **UI** = **UI Toolkit**; **Settings** = **Graphics + Audio** only; **Build** = wire-up only (operator runs the actual standalone build). Full rationale + the de-risked architecture: [[DR-019_Frontend_Menu_Settings_Saves_Build]].
The core change: adopt the Netcode **frontend pattern** — boot a menu with NO netcode worlds, create the right worlds **on demand** per menu choice, load the gameplay scene *after* the worlds exist (the subscene-streaming invariant). Replaces the always-create-both-worlds bootstrap.
## Process
- **Phase 13 (research, context7-first):** 3 Explore agents (netcode world-lifecycle · UITK runtime · settings/persistence/save-slice grounding) + direct ctx7 on the Netcode 1.13.2 `ClientServerBootstrap` frontend API. Confirmed `CreateClientWorld/CreateServerWorld(name)` are the public on-demand helpers ("frontends that allow Create Game vs Join Game"), `RequestedPlayType` is the MPPM switch, `CreateLocalWorld` is **internal** in 1.13.2.
- **Phase 4 (design) + adversarial review (Workflow, ultracode):** one Plan agent drafted the file-by-file map; a 3-critic + synthesis Workflow (netcode-lifecycle · determinism/teardown · UITK/build) red-teamed it. The review changed the design materially (see Decisions).
- Plan approved → 5 staged steps, each `create/edit → read_console → EditMode → (Play where relevant)`.
## Done
### Stage 1 — Settings (Graphics + Audio that actually change the game)
- `Client/Settings/GameVolume.cs` (static **Music/Sfx** bus trims — NOT named `AudioSettings`, which collides with `UnityEngine.AudioSettings`; Subsystem-reset), `GameSettings.cs` (`[Serializable]` model + `Clamped()`/`Defaults()`), `SettingsService.cs` (boot-load+apply via `[RuntimeInitializeOnLoadMethod]`; atomic JSON at `persistentDataPath`; `Apply` drives `Screen`/`QualitySettings`/`Application.targetFrameRate` + `AudioListener.volume`=Master + the buses).
- Wired `GameVolume` into the 3 client audio systems (`AmbientAudioSystem`=Music; `CombatFeedbackSystem`+`WorldFeedbackSystem`=Sfx, one edit each at their `PlayClip` helper). **Master rides `AudioListener.volume`; buses multiply per-call — no double-attenuation.**
- Tests: `GameSettingsTests` ×5 (JSON round-trip, clamps, defaults, bus statics).
### Stage 3 — Saves foundation + minimal real slice
- `Simulation/Persistence/`: `SaveData`(versioned `{GoalCharge,GoalTarget,LedgerRow[],SavedAtMs}` — array as a FIELD), `SaveComponents` (`PendingSave`+`PendingSaveLedgerRow` unmanaged bridge, `SaveRequest` byte flag), `SaveApply.WriteLedger` (Burst-safe buffer copy), `SaveService` (single-slot JSON, atomic, null on bad version).
- **Born-correct load** (review fix): `CycleDirectorSpawnSystem` applies a staged `PendingSave` AT SPAWN (GoalProgress + StorageEntry ledger), so the director ghost never replicates a default first. **Autosave:** `CyclePhaseSystem` raises the `SaveRequest` byte on the Siege→Calm checkpoint; server `SaveWriteSystem` (managed, host-only) writes the JSON. Quit-to-menu also saves (`WorldLauncher.TrySaveFromServer`).
- `ServerConnectionControlSystem` now binds `LoopbackIpv4` when Address=="127.0.0.1" (single-player, no firewall prompt) else AnyIpv4.
- Tests: `SavePersistenceTests` ×5 (schema round-trip, version handling, `WriteLedger` overwrite/empty).
### Stage 2 — UITK front end + world lifecycle
- `Client/UI/`: `MenuUi` (palette + styled element factories + **EventSystem(+InputSystemUIInputModule)** ensure + Resources PanelSettings load), `SessionRunner` (persistent `DontDestroyOnLoad` coroutine host), `WorldLauncher` (the **single** lifecycle funnel: `StartSession`/`TeardownToMenu`), `SettingsScreen` (shared Graphics/Audio panel — cycle rows + volume sliders), `MainMenuController` (Single/Host/Join+IP/Continue/Settings/Quit; ensures the menu world on return), `PauseMenuController` (Esc overlay) + `PauseMenuSystem` (client spawns it).
- Asset: `Assets/_Project/Resources/RuntimePanelSettings.asset` + `RuntimeTheme.tss` (default runtime theme). Scene: `Assets/Scenes/MainMenu.unity` (Camera, Light, EventSystem, MenuRoot=UIDocument+MainMenuController).
### Stage 4 — Bootstrap branch + build
- `GameBootstrap.Initialize` branch: **editor default = today's instant-play + MPPM unchanged**; a `ProjectM/Boot Into Menu (Editor)` EditorPrefs toggle (gated to `RequestedPlayType==ClientAndServer` so MPPM virtual players never boot to the menu) flips the main editor to the frontend; **player build** → frontend menu (`return false` → single default menu world), `Server` playtype → auto-host.
- `Editor/BuildTool.cs`: `ProjectM/Build/Windows Player` (`BuildPipeline.BuildPlayer`, MainMenu+Game) + the Boot-Into-Menu toggle. `EditorBuildSettings` reordered: **0:MainMenu 1:Game 2:DevSandbox 3:SampleScene**.
## Validation
- **EditMode 152/152** (142 prior + 10 new), console clean.
- **Runtime (focused editor, Play, via `execute_code`/screenshots):**
- Menu **renders** (UITK + hand-made theme + EventSystem) — `MainMenu_Boot.png`.
- **Single Player**: menu world disposed → ServerWorld+ClientWorld created → Game.unity loaded → **subscene streamed into the on-demand worlds** → loopback connect (`serverConns=1`) → player spawned (`serverPlayers=1`) → replicated (`clientPlayerGhosts=1`) → playable base + HUD — `MainMenu_Single_InGame.png`. *(The review's biggest risk — subscene into on-demand worlds — works.)*
- **Teardown**: `ServerWorld`/`ClientWorld` disposed, `MenuWorld` recreated, scene→MainMenu, **quit-save written** → menu shows **Continue**`MainMenu_WithContinue.png`.
- **Born-correct load** (Continue with a staged save Charge=42, ledger {1:15}): director ghost born `charge=42 target=10 ledger=[(1:15)] pendingRemaining=0` — no default-then-overwrite flicker.
- **Settings apply**: `vSync`, `AudioListener.volume`(master), `GameVolume.Music/Sfx`, `targetFrameRate` all driven by `SettingsService.Apply`.
- Only console output = the **pre-existing** in-editor "Server Tick Batching" perf warning (single-process multi-world cost; not from this work — see [[Backlog]]).
## Decisions
- **Created [[DR-019_Frontend_Menu_Settings_Saves_Build]]** (frontend world-lifecycle, UITK menu, settings, saves foundation, build wiring). Extends [[DR-005_M4_Connection_Model_Direct_IP]] (reuses `ConnectionConfig` + control systems + GoInGame handshake unchanged).
## Build gotchas (→ [[CLAUDE]] addendum candidates)
- **`CreateLocalWorld` is `internal` in Netcode 1.13.2** — use the public `CreateClientWorld`/`CreateServerWorld` (they register the `ServerWorld`/`ClientWorld` statics that `ConnectionUI`/`DebugOverlay` read); for the menu world, `DefaultWorldInitialization.Initialize(name,false)` (or `return false` from `Initialize`).
- **Subscene streams into on-demand netcode worlds IF a netcode world is the `DefaultGameObjectInjectionWorld` when the scene loads** — dispose the menu world first, set the default to the server world, then `LoadScene(Game)`. Validated (player spawns, replicates).
- **Runtime UITK needs a PanelSettings asset WITH a `themeStyleSheet`** (a `.tss` importing `unity-theme://default`) or it renders nothing; and an **EventSystem + `InputSystemUIInputModule`** (Input System project) or buttons are silently dead.
- **Don't dispose worlds inside an ECS system** — `WorldLauncher` runs all create/dispose/scene-load on a frame-boundary coroutine (`SessionRunner`, `DontDestroyOnLoad`).
- **`Cursor` is ambiguous** under `using UnityEngine.UIElements;` — fully-qualify `UnityEngine.Cursor`.
- **Validate UITK/world-lifecycle by rendering the menu in Play then driving `WorldLauncher` via `execute_code`** — no button-click simulation needed; `runInBackground=True` lets an in-Play editor tick.
- **A reverted URP/engine upgrade can stamp `UniversalRenderPipelineGlobalSettings.asset` with `m_AssetVersion` AHEAD of the current package's `k_LastVersion`** (here 11 vs 10, from the reverted 6.6 alpha — [[DR-002_Unity66_Alpha_Netcode_Transport]]). URP migrates **forward only**, so its build preprocessor (`URPPreprocessBuild`) rejects the "from-the-future" asset with *"is not at last version"***blocks builds but NOT editor Play** (so it lurks until the first build). Fix: reflection-set `m_AssetVersion` back to `k_LastVersion`, `SetDirty`+`SaveAssets` (the data already renders under the current package, so only the stamp is wrong). The Mono standalone build then succeeds (~7.5 min; `level0`=MainMenu, `level1`=Game, subscene in `StreamingAssets/EntityScenes`).
## Build (now succeeds)
- The first-ever player build surfaced a **pre-existing URP config blocker** (global-settings asset version 11 > package `k_LastVersion` 10, from the reverted 6.6 alpha) — fixed (see Build gotchas). **`ProjectM/Build/Windows Player` now produces a complete Mono Win64 standalone** (`Builds/Windows/ProjectM.exe` + 270 MB `_Data`, both scenes + the baked subscene). The build compiling proves all new runtime code (menu/settings/saves/world-lifecycle/bootstrap) is player-target clean (no editor-only-API leaks).
## Open / deferred
- **Operator-run**: launch `Builds/Windows/ProjectM.exe` and confirm it boots the menu → Single/Host/Join play; a real 2-build LAN Host↔Join; MPPM co-op regression in default-editor mode.
- Controls settings + key rebinding (out of scope); full base-state saves (structures/threat/storage — schema is versioned, additive later); Unity Relay; dedicated-server build (branch added defensively, untested).
- Legacy IMGUI `ConnectionUI` left in `Game.unity` (shows a harmless "Net: Connected" label; auto-hides). Optional cleanup — left to avoid touching the load-bearing subscene scene.
- Volume **sliders** render via the default theme; if a build shows them unstyled, swap to +/- cycle rows (the enum settings already use cycle rows).
## Next
- Operator: run the build + a real LAN join; then resume **M7 Automation** (the next milestone) or base/automation expansion on the now-buildable, persistent-save foundation.
@@ -0,0 +1,41 @@
---
date: 2026-06-05
type: session
tags: [session, hud, ui-toolkit, build-palette, presentation, uitk, juice]
---
# Session 2026-06-05 — HUD rework to UI Toolkit + build-palette HUD (click-to-place + ghost preview)
## Goal
Operator: *"Lets do a build pallete hud - rework all the hud in the game use the new uitoolkit so it is consistent."*
Intake decisions (AskUserQuestion): build palette = **full click-to-place + ground ghost preview** (green/red validity, conveyor rotation, fire suppressed in build mode); scope = the persistent screen HUD → UITK **plus** re-skin the floating combat juice to the Aether palette. Defaults taken: world-space juice (reticle/VFX) + dev overlays (DebugOverlay/legacy IMGUI ConnectionUI) untouched; HUD root `pickingMode=Ignore` so it never eats game clicks; build mode suppresses fire + is suspended while paused.
## Done
Converted the in-game HUD from code-built **uGUI** (`HudSystem`'s RawImage bars + legacy `Text`) to **UI Toolkit**, reading in the same Aether-cyan language as the menu/pause/settings (`MenuUi`). New + changed:
- **`HudUi`** (new) — UITK HUD factories (percent-width bar track+fill, bold labels, group/round/border), all `pickingMode=Ignore` by default.
- **`HudSystem`** (rewrite) — a `PresentationSystemGroup` observe-only system that now owns a runtime **UIDocument** (shared `RuntimePanelSettings`, `sortingOrder=50` — behind the pause overlay's 100), builds the tree on the first frame its root exists (panel-init timing), and pushes ECS values each frame: health+cooldown bars (bottom-left), HUSKS (top-right), phase/cycle/countdown + resources + location + goal bar (top-center), DOWNED overlay, and the **build-palette bar** (bottom-center). The palette is lazy-built from the **client-side `StructureCatalog`** (the subscene streams into both worlds), shows each buildable's Ore cost, greys the unaffordable, highlights the selected (cyan border), and hides off-base.
- **Build palette → click-to-place + ghost** (`BuildSendSystem` extended, `BuildPaletteState` new, `BuildPreviewMath` new): palette buttons set `BuildPaletteState.Selected`; `BuildSendSystem` then raycasts cursor→ground→cell (`AimMath.TryGroundHit`+`BaseGridMath.WorldToCell`), drives a procedural **translucent ground-ghost cube** (green=valid / red=blocked via `BuildPreviewMath.Evaluate` — in-plot, unoccupied, affordable; the client mirror of the server check), **left-click places** (the existing `BuildPlaceRequest` RPC), **right-click/Esc cancels**, **`[`/`]`/R rotates** a conveyor. Self-contained interaction guards: skip placing on the frame the selection changes (`_lastSelected`), suspend while `PauseMenuController.Open`, and `AimPresentation.ForceCursorVisible` shows the cursor in build mode.
- **Fire suppression** (`PlayerInputGatherSystem`, 1 line) — `Fire` is gated by `!BuildPaletteState.Active`, so the place-click never also fires.
- **Combat juice restyle** (`CombatFeedbackSystem`) — floating damage numbers re-skinned to the Aether palette + bold: **Blight orange** when you're hurt, **Aether cyan** when you hit a Husk.
- **`PauseMenuController`** — exposes `static bool Open` (for the build-mode pause guard).
- Keyboard hotkeys (B/V/N/H/F/C, `[`/`]`) kept as a power-user fallback (suppressed in palette mode); the editor `Place*` statics kept for headless validation.
## Validation
- **EditMode 190→194** (4 new `BuildPreviewMath` tests: valid / out-of-plot-first / occupied / unaffordable-with-exact-funds-ok). No regressions.
- **Runtime (real netcode session, focused editor, `execute_code` + screenshots):** the UITK HUD renders all panels in the Aether palette (`~HUD` UIDocument, 5 root groups); the build palette shows all 6 buildables + costs from the catalog; selecting Harvester **highlights** its button (cyan border) and activates build mode → `ForceCursorVisible` + a live **ground ghost at the cursor cell**; clearing hides the ghost; placement through the build RPC path works (harvester landed at the target cell). Console clean. Screenshots: `Assets/Screenshots/HudRework_Base.png`, `HudRework_BuildMode.png`.
## Decisions
[[DR-021_HUD_UITK_BuildPalette]]. Resolves the M7 follow-up "Build-palette HUD + ghost preview".
## Next-session intent
- **Per-buildable icons** + a conveyor-facing arrow on the ghost (text-only palette + cube ghost today).
- **Throughput visuals** (item-on-belt) still need a small replicated field (server-only machine buffers don't reach the client).
- Remove the legacy IMGUI `ConnectionUI` "Net: Connected" label (backlog) — the only non-UITK on-screen UI left in-game.
- Optional: a one-line "BUILD: [name] • click to place, [/] rotate, Esc cancel" hint while in build mode; a build toggle key to open/close the palette focus.
@@ -0,0 +1,49 @@
---
date: 2026-06-05
type: session
tags: [session, m7, automation, production-chains, conveyor, persistence, netcode, determinism]
---
# Session 2026-06-05 — M7 Automation: self-running production chains (Harvester → Conveyor → Fabricator)
## Goal
Operator: *"Let us start the automation milestone."***M7 — Automation**: self-running, tick-based production chains with deterministic offline catch-up, building on the DR-014 locked structure model.
Intake decisions (AskUserQuestion):
- **Scope:** the FULL chain (Harvester → Conveyor → Fabricator), not a reduced harvester-only slice.
- **What it makes:** AUTO-GATHER EXISTING resources (no new resource ids) — the chain refines existing resources (default 2 Ore → 1 Aether) into the global ledger so setup compounds.
- **Persistence:** FOLDED IN now (SaveData → v2; player-built structures survive quit→Continue).
- (Informed re-confirm after the adversarial review:) **keep the full chain** with a deterministic conveyor; **"offline catch-up" = within-session tick math + preserved stockpile** across quit (NO wall-clock minting — honors the determinism pillar).
## Process (ultracode: workflows + adversarial verification)
- **Phases 14 (research + design):** code-graph scan + context7 API verification (NetworkTick/NetworkTime accessors, ECB singleton, server-only `IBufferElementData` stays off the wire); a 3-lens design swarm — code architect · persistence/authoring architect · **adversarial netcode/determinism/reuse critic**. The critic earned its keep: flagged the conveyor as the dominant complexity/risk, a catch-up `period=0` div-by-zero + per-tick/catch-up **double-count** footgun, the restore **tick-rebase** blocker, and the `StorageEntry` singleton-collision; all folded into the plan.
- **Phase 5:** plan approved; the two informed forks resolved (keep full chain; within-session catch-up + preserved stockpile).
- **Phase 6 (source workflow → controlled MCP apply):** a draft→critique Workflow produced the source bundle. **Incident:** the Workflow's general-purpose draft agents ignored the "return source only" instruction and raw-`Write`-wrote the 3 systems + 5 tests into `Assets/` — which **compile but get no `.meta`/test-discovery until `refresh_unity scope=all mode=force`** (logged as native memory [[raw-written-cs-needs-full-refresh]]). Recovered: kept the (high-quality, internally-consistent) agent systems + tests, authored the foundation to satisfy them, and applied everything via MCP with reconciliations — the key one: **`CyclesDue` lower-bound 0, not 1** (prevents a premature mint on a restored `remaining==0` machine), and `BuildPlaceSystem` stamps `LastProcessedTick=0` so runtime-placed machines hit `NeedsInit`.
## Done — code (compiles clean; EditMode 187→**190**)
- **Components** (`Simulation/Automation/AutomationComponents.cs`): `Harvester{byte ResourceId;int Yield;int PeriodTicks}`, `Fabricator{byte In/OutResourceId;int In/OutAmount;int PeriodTicks}`, `Conveyor{byte Direction;int PeriodTicks}`, server-only buffers `MachineInput`/`MachineOutput`, `ConveyorItem` (enableable, baked DISABLED), `RuntimePlacedTag`. The DR-014 "recipe column on `StructureCatalogEntry`" was **deliberately dropped** — dead weight; the recipe is baked on each machine component (server-only, not needed client-side).
- **Pure math** (unit-tested, byte-only): `ProductionMath` (`NeedsInit`/`CyclesDue`/`RemainingTicks`/`RestoreNextTick` — the single gated catch-up path), `ConveyorMath` (`DirOffset`/`CellKey`/**`ResolveMoves`** — deterministic + order-independent: snapshot → stable-sort by CellKey → at-most-one destination claim → stall losers no-loss), `MachineSlotMath` (byte-id deposit/withdraw/total, the non-replicated twin of `StorageMath`).
- **Systems** (`Server/Automation/`, server-only, ordered `[UpdateAfter(PredictedSimulationSystemGroup)]` chain Harvester→Conveyor→Fabricator): `HarvesterProductionSystem` (fixed-yield → its `MachineOutput`), `ConveyorTransportSystem` (pull from adjacent upstream `MachineOutput``ResolveMoves` → settle `ConveyorItem` enable-bit / deposit to sink `MachineInput`), `FabricatorProductionSystem` (input-limited recipe → GLOBAL ledger). `Tuning.MaxProductionCatchup = 600`.
- **Persistence (SaveData v2):** `StructureSave[]` + flat `StructureIoRow[]` (cooldown as epoch-independent REMAINING ticks; in-flight conveyor item inline); `SaveComponents` `PendingStructure`/`PendingStructureIo` staging buffers; **shared `SaveStructureScan.Collect`** (both autosave + quit-save scan ONLY `RuntimePlacedTag` structures → kills the two-path drift the critic flagged); `SaveWriteSystem` + `WorldLauncher` (StagePendingSave + TrySaveFromServer) extended; new **one-shot `BaseRestoreSystem`** (gates on `StructureCatalog`+`BaseAnchor`+`NetworkTime`, replays each saved structure CHARGE-FREE, refills buffers + conveyor item, re-tags, self-destructs).
- **Build wiring:** `BuildPlaceRequest` +`byte Direction` (additive scalar; RPC wire-hash shifts → single-build); `BuildPlaceSystem` stamps conveyor `Direction` (via a `ComponentLookup<Conveyor>` on the prefab) + `RuntimePlacedTag` + `LastProcessedTick=0`; `BuildSendSystem` H/F/C keys + `[`/`]` conveyor-rotate + editor statics `PlaceHarvester/Fabricator/Conveyor`.
- **Authoring + prefabs:** 3 MonoBehaviours + bakers; `StructureCatalogAuthoring` +3 rows (Harvester 20 / Fabricator 30 / Conveyor 2 Ore); `Harvester/Fabricator/Conveyor.prefab` (duplicated from `Turret.prefab` → swapped authoring; ownerless-interpolated `GhostAuthoringComponent` + mesh free) wired into the catalog in the Gameplay subscene.
## Validation
- **EditMode 190/190** (35 new automation tests): catch-up gating/clamp/no-mint/period-0-guard; conveyor Y-junction tie-break + 4-cell line + shuffle-invariance + blocked-stall; fabricator input-limit (no mint-from-nothing); SaveData v2 round-trip + epoch-independent cooldown.
- **Runtime (real netcode ServerWorld+ClientWorld, focused editor, via `execute_code`):** catalog bakes all 6 entries (T1/T5/T6 + **T2/T3/T4 machine prefabs valid**); placed a live H(18,18)→C(19,18 +X)→F(20,18) chain (RPC → cost Ore 200→148); the chain **ran end-to-end** (Harvester output flowed → Conveyor transported → Fabricator `2 Ore → 1 Aether` → ledger), **SERVER Aether == CLIENT Aether** (22, then 52 — replicated). **Quit autosaved** v2 (3 structures + rebased `RemainingTicks`); **Continue restored** all 3 at the exact cells, **charge-free** (Ore unchanged at 148), born-correct ledger, and the **restored chain resumed producing**. Console clean of errors throughout.
## Decisions
[[DR-020_M7_Automation_Production_Chains]]. Resolves the open [[Backlog]] "production replication (predicted vs server-only)" question: **server-only** — production runs in the plain server `SimulationSystemGroup`; results replicate via the existing global ledger + `PlacedStructure` ghosts, never predicted.
## Next-session intent
- **Throughput visuals** (item-on-belt): server-only machine buffers don't reach the client (clients see only `PlacedStructure.Type`); add ONE small replicated byte if live belt visuals are wanted.
- **Build-palette HUD + ghost preview** + a conveyor-facing indicator (dev `[`/`]` rotate + H/F/C keys only today).
- **Relevancy ceiling:** `RegionRelevancySystem`'s O(structures×connections)/tick scan becomes load-bearing at higher conveyor counts (DR-014 ceiling note) — batch it when counts grow.
- **Recipe depth:** multi-input fabricator recipes; fabricator→conveyor output chaining (additive — give the fabricator a `MachineOutput`); per-machine distinct meshes (machines reuse the Turret mesh as a placeholder).
- **Operator live play-through** + a real 2-build LAN run to exercise the chain under latency (single-client validated).
@@ -0,0 +1,53 @@
---
date: 2026-06-06
type: session
tags: [session, cleanup, alignment, docs, refactor, hygiene, connectionui, uitk]
---
# Session 2026-06-06 — Cleanup & alignment (docs, dead code, refactor) before more development
## Goal
Operator: *"Start a cleanup session — clean up documentation code and align everything in the project so it's ready for more development."* Run via `/dots-dev` (ultracode).
Three large 2026-06-05 sessions (M7 Automation, Game-infrastructure menu/settings/saves, HUD rework to UITK) were done but **uncommitted**, with session logs + DRs already written. `CLAUDE.md` had drifted from the code (still described `SampleScene` as the gameplay scene; nothing on Automation / Saves / UITK / the frontend lifecycle), and the logs had flagged cleanup candidates (legacy IMGUI `ConnectionUI`, the `_Recovery/` artifact).
Intake (AskUserQuestion): **deep refactor + docs** · **delete aggressively** · **group & commit for me** (authorizes git writes; commits land on `main`, matching the solo history; no push).
## Process
- **Phase 01 (read-only scan):** editor-state + git + vault scan; a 3-agent Explore swarm (code/asset audit · doc-delta audit · dead-asset + git-grouping audit). Verified the risky findings myself before acting: `ConnectionUI` has **zero code deps** (all hits were `<c>ConnectionUI</c>` doc-comments); `_Recovery` = 2 tracked + 1 untracked recovery scenes; **no new asmdefs** (the new feature folders live inside the existing four — corrected an agent's wrong "add `ProjectM.Automation`/`Persistence` rows" suggestion).
- **Plan-gate:** scoped "deep refactor" to **behavior-preserving** (DRY / dead-code / readability) — explicitly NOT rewriting working netcode/sim; the determinism-critical paths (`ProductionMath.CyclesDue`, `ConveyorMath.ResolveMoves`, `BaseRestoreSystem`) were left untouched. Approved → executed A→F.
## Done
### A — Dead-asset deletion (via `manage_asset`, AssetDatabase-consistent)
- Deleted `Assets/_Recovery/` (3 crash-recovery scenes + metas; zero references, not in build settings) and the stray empty `Assets/Resources.meta` (top-level `Assets/Resources/` was empty). Kept `Assets/_Project/Resources/` (`RuntimePanelSettings` + theme) and `Assets/UI Toolkit/UnityThemes` (UITK default scaffolding).
### B — Removed the legacy IMGUI `ConnectionUI`
- Removed the standalone `NetConnectionUI` GameObject from `Game.unity`, `DevSandbox.unity`, `SampleScene.unity` (each only held Transform + `ConnectionUI`); deleted `ConnectionUI.cs` (+ meta); confirmed zero remaining GUID references in any scene. Repointed the 4 stale doc-comments (`GameBootstrap`, `DebugOverlay`, `EditorAutoHostSystem`, `ConnectionConfig`) from `<c>ConnectionUI</c>` to the UITK `MainMenuController` / `WorldLauncher` (via `script_apply_edits anchor_replace`). The UITK frontend menu owns Host/Join+IP everywhere now.
### C — Targeted behavior-preserving refactor (all via MCP, verified vs live file)
- **DRY'd the duplicated UITK `Round`/`Border` helpers:** `MenuUi` is now the canonical home (made `public`); `HudUi`'s byte-identical copies deleted and its 2 internal call sites + `HudSystem`'s 3 call sites repointed to `MenuUi.*`. `HudUi` is now a genuine thin extension.
- **`HudSystem`:** cache `_huskQuery.CalculateEntityCount()` once/frame (was called twice).
- **`BuildSendSystem`:** replaced the 6 near-identical hotkey `if` branches (drifting `cell`/`wcell`/… vars) with a `static readonly (Key, byte)[] s_BuildHotkeys` table + one loop — adding a buildable is now one row.
- Deliberately **skipped** low-value/higher-risk tidies (SettingsService clamp order, ConveyorTransport struct-array, WorldFeedback lookup, PlayerInput dedup) → logged as a [[Backlog]] follow-up.
### D — Documentation alignment
- **`CLAUDE.md`** (30.7 KB → 36.4 KB, still < the 40 KB limit): rewrote **Bootstrap & worlds** (`AutoConnectPort=0`, editor-instant-play vs frontend toggle, MainMenu(0)/Game(1) scene split, on-demand `WorldLauncher` lifecycle + subscene-streams-into-on-demand-world rule); added an **M7 Automation** + **disk-persistence** bullet to Build/structures (replacing the now-false "deferred to post-M7"); replaced the "uGUI HUD" claim with a **UITK HUD + build-palette** bullet; added build-gotchas (frontend world lifecycle, `CreateLocalWorld` internal, don't-dispose-worlds-in-a-system, URP `m_AssetVersion`-from-the-future, raw-`Write` new-`.cs`-needs-force-refresh, `script_apply_edits` anchor/delete_method on ISystems); noted the new feature folders fit the **existing four asmdefs** (no new assemblies).
- **Vault:** `Milestones` (M6 + the 2026-06-04 polish pass → ✅ Done; added rows for the HUD rework + this cleanup), `Backlog` (ConnectionUI item → done; added a delivered-note + a cleanup-follow-ups section), `Home` + `Systems_Index` (date + latest-session pointer). New DR not warranted (hygiene, no new architecture).
## Validation
- **EditMode 194/194** (`ProjectM.Tests.EditMode`, 0.29 s) — same count as pre-cleanup → no regressions, no tests lost; proves the whole codebase (refactor + ConnectionUI removal + comment edits) compiles clean.
- **Play smoke (focused editor, real netcode):** `ServerWorld` + `ClientWorld` each `conns=1 players=1` (worlds boot → loopback connect → player spawn + replicate → subscene streamed fine after the scene edits); the **UITK HUD renders correctly** (`Cleanup_PlaySmoke.png`) — rounded bars (`MenuUi.Round`), the 6-buildable palette with borders (`MenuUi.Border` + the hotkey/palette path), "HUSKS 0" (cached count), and **no "Net: Connected" IMGUI label** (ConnectionUI gone). Console clean of project errors.
- **Note:** the only console "errors" are engine-internal `Assert`s — *"Access version should be odd when acquiring lock"* from `Modules/Audio/Public/DualThreadManager.h:197` — that fire on domain reload / in Play. They are NOT from project code (audio-thread engine noise), are Assert-level, and don't affect tests/runtime. Left as-is (not a cleanup-pass concern).
## Decisions
No DR (hygiene pass). Reaffirmed conventions: edit `Assets/*.cs` only via MCP; `script_apply_edits` `anchor_replace`/`delete_method` are safe for comment/method edits even on a `struct : ISystem`; `manage_asset delete` keeps the AssetDatabase consistent for asset removals.
## Next-session intent
- Resume feature work on the now-aligned, committed base. Candidate threads from the M7 follow-ups: throughput/belt visuals (one replicated byte), recipe depth (multi-input fabricator, fabricator→conveyor chaining), per-machine meshes; operator live play-through + a real 2-build LAN run.
- Optional code-tidy follow-ups (logged in [[Backlog]]) if churn budget allows.