Files
Project-M/Docs/Vault/07_Sessions/2026/2026-06-05_M7_Automation.md
T
kronic 8aed336340 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>
2026-06-06 15:06:26 -07:00

7.7 KiB
Raw Blame History

date, type, tags
date type tags
2026-06-05 session
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 MachineOutputResolveMoves → 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).