# Calc bridge The Galaxy frontend renders predictive numbers (free production potential, forecast output for a chosen production type, ship build progress, tech progress) that depend on the same formulas the engine uses at turn cutoff. To keep one source of truth, those formulas live in Go under `pkg/calc/` and are surfaced to the UI through a planned Go → WASM → TypeScript bridge mounted under `ui/core/calc/` and a matching TS adapter in `ui/frontend/src/`. The bridge does not exist yet. This document is the audit trail for what it must expose, what is already in place, and what is missing. ## Current `pkg/calc/` exports | Function | Purpose | | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------ | | `ShipProductionCost(shipEmptyMass float64) float64` | Production units required per unit of ship empty mass (×10). | | `PlanetProduceShipMass(L, Mat, Res float64) float64` | Ship mass produced per turn given free production `L`, material stockpile `Mat`, resources `Res`.| | `DriveEffective`, `Speed`, `EmptyMass`, `FullMass`, … | Ship-level derivations (`pkg/calc/ship.go`). | | `ValidateShipTypeValues`, `CheckShipTypeValueDWSC` | Ship-design validators (`pkg/calc/validator.go`). | Nothing else lives in `pkg/calc/` today. Production-side formulas (industry / materials / per-tech research / production capacity) sit in `game/internal/model/game/planet.go` and `…/science.go` and have never been exported. ## Required calc functions per UI feature The table below tracks what UI features need from the bridge and whether the underlying Go function exists. | UI feature | Go formula | In `pkg/calc/`? | Surfaced to TS? | | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | :-------------: | :-------------: | | Free production potential (`freeIndustry`) | `Planet.ProductionCapacity` → `industry*0.75 + population*0.25` (`game/internal/model/game/planet.go`) | no | no | | Industry production output per turn | `Planet.ProduceIndustry(freeProduction)` (`planet.go`); `freeProduction/5` modulo material constraint | no | no | | Materials production output per turn | `Planet.ProduceMaterial(freeProduction)` (`planet.go`); `freeProduction * resources` | no | no | | Per-tech research progress (DRIVE/WEAPONS/…) | `ResearchTech` (`game/internal/model/game/science.go`); `freeProduction / 5000` per tech level | no | no | | Custom-science progress | weighted form of `ResearchTech` driven by `Race.Sciences[].(Drive\|Weapons\|Shields\|Cargo)` (`science.go`) | no | no | | Ship build progress | `PlanetProduceShipMass(L, Mat, Res) / ShipProductionCost(class.EmptyMass)` (combination of two existing exports) | partial | no | `partial` means the Go primitives exist in `pkg/calc/` but the composition (and the conversion of TS-side `ReportPlanet`/ `ShipClass` to the formula inputs) is not implemented anywhere. ## Phase 15 waiver Phase 15 ships the inspector's planet production controls (segmented control + sub-pickers + collapse-by-`planetNumber` order command) but **deliberately does not surface the per-type forecast number**. The planning gate explicitly raised the gap as a blocker per the plan's audit clause ("if any are missing in `pkg/calc/`, raise as blocker") and the project owner approved deferring the forecast to a dedicated future bridge phase. The inspector still renders the existing `freeIndustry` row (free production potential) — that number is computed engine-side and ships in the report payload, so no calc-bridge access is required for it today. Acceptance criterion 3 of Phase 15 ("forecast output number reflects the chosen production type and matches `pkg/calc/` outputs") is therefore intentionally not satisfied; the rewritten Phase 15 stage text records this decision and points back at this document. ## Planned bridge shape (follow-up phase) When the bridge phase lands, the contract should be: 1. Promote every formula in the table above into `pkg/calc/` so the engine and the UI share one Go-side implementation. The engine continues to call them through `game/internal/...` wrappers. 2. Mount a `ui/core/calc/` Go module that re-exports the subset the UI needs. Keep it WASM-friendly (no `unsafe`, no goroutines, simple in/out values). 3. Wire the WASM glue in `ui/wasm/main.go` so each calc function is reachable from `globalThis.galaxyCore`. 4. Add a TypeScript adapter under `ui/frontend/src/platform/core/` that wraps the WASM calls in typed helpers (`forecastIndustry(freeProduction, …)` etc.). 5. Update this document with the live function inventory and delete the "missing" rows above.