Every building needs a planet-hazard material bundle on top of its normal
BuildingCosts (SEA for low pressure, HSE for high pressure, MGC for low
gravity, BL for high gravity, INS for cold, TSH for heat - area-scaled
where applicable), confirmed against live in-game build panels on Nike.
FIO's own BuildRequirements field is an unreliable superset across hazard
types, not a direct per-building list - don't use it directly.
- puga/planet.py::environment_extras(): the real threshold table.
- tools/buildcost.py: full construction material list for one building,
optionally on a given planet.
- Wired into whatif.py/buy.py (via simulate(planet=...)) and scan.py's
own bcost(), so their capex numbers stop silently omitting this.
- docs/mechanics.md: the rule, plus a separate forum-sourced note on the
CX order-cancellation penalty.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- marginal_freight()'s docstring claimed small additions "cost ~0" if
they fit in slack - the code is actually a continuous amortized rate
with no such threshold. Fixed the wording to match the real behavior.
- The one known failing test asserted fixed money values from a 2026-09-18
screenshot against live, drifting market prices - removed that
assertion (it was testing market drift, not code correctness) but kept
the exact area check. Replaced with a structural, price-independent
regression check instead of just deleting: degradation == prod_cost/180
and plan_cost == prod_cost+infra_cost+cm_cost, asserted directly from
simulate()'s own fields, holds at any price.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
network.py: tools/network.py now computes freight cost from the real fuel
model (puga.shipping.round_trip_cost) for direct-jump transfers instead of
the flat 9250 AIC placeholder; gateway transfers or an explicit trip_cost
in the chain YAML still use that as a fallback. puga/network.py's combine()
docstring now flags that its "flows cancel, only freight matters" claim
only holds under a symmetric-price basis (uni30, the default) - under
--basis real it understates the benefit of physical transfer by the
bid-ask spread on the moved quantity.
whatif.py: now charges marginal freight too - only the EXTRA round trips
an addition needs beyond the base's existing traffic, so a small addition
riding along on already-necessary hauls costs ~0, while one that adds
meaningfully to shipped tonnage doesn't get a free pass.
Terminology: "profit/day ÷ capex" was being reported as "ROI/day X%",
which invites reading it as a compoundable rate. It's just 1/payback_days -
there's no reinvestment mechanism and every opportunity here is
capacity/market-limited, so it can't be annualized like a real yield.
Payback in days is now the primary number everywhere (scan.py, simulate.py,
network.py, whatif.py); see docs/decisions.md for the reasoning.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Was wrong earlier this session claiming demolition gives nothing back.
FIO's /rain/userplanetbuildingreclaimables confirms partial material
return (observed 50-90% depending on material, not a fixed fraction -
looked up live, not approximated). --remove now prices the real
reclaim for anything actually built and nets it against new capex,
so a demolish-and-reinvest question gets the true net cash picture
instead of double-penalizing the removed building.
Used this to check "demolish BHP's HWP, build a 2nd CHP or POL
instead" - both are clearly net losers even counting the ~57,750 AIC
reclaim credit (net capex still positive, and marginal profit is
negative either way) - dominated by just adding the new building
without removing anything, since nothing scarce actually forces the
swap.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Mirrors --add - removes AMOUNT (default: all) of a building from the
copied spec. Used this to check whether BHP's HWP is still worth
running after its price drop (still +19.8k-28.4k/day marginal,
nowhere near a teardown call) without hand-writing two spec files.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Formalizes a pattern repeated by hand all session: copy the current
whole-base plan, add a building/recipe, diff simulate() against the
baseline for the true marginal profit and capex (not PRUNplanner's
plan-level ROI, which divides whole-plan profit by only the new capex
and badly overstates once a base exists).
Also flags any workforce tier the addition newly overloads, distinct
from a pre-existing overload it didn't cause - this is what caught the
PP2/pioneer-housing and 2nd-CHP/pioneer-housing surprises by hand
before this tool existed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>