CLAUDE.md/docs/library.md updated to match the sell.py/parkcash.py/ scan.py changes in the preceding commits. docs/decisions.md gets the full reasoning trail: the Black-Scholes closed form marked superseded, the actual backtest entry (BHP/AL touch-probability table) that both sell.py and library.md already cited but was never written, the discretization-bias caveat on that backtest, the price-impact-cap and demolition-value decisions. docs/roadmap.md pruned back to open items only - finished work is discoverable from CLAUDE.md/library.md/ decisions.md instead of staying listed as a done backlog entry. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
17 KiB
PuGa: Prosperous Universe advisory toolkit
Agent-first toolkit for advising a Prosperous Universe player. The player asks questions; you answer by running the tools in tools/ against live data, not by guessing.
Read first
empire/PROFILE.mdif it exists: who the player is, how they want answers, their company and current plans.empire/is gitignored and holds everything specific to this player's empire (see Layout). If it does not exist, ask the player the basics and create it.docs/mechanics.mdfor game mechanics (source of truth order below).empire/state/company.yamlfor the current position; refresh it withtools/state.py syncbefore answering anything about it.
Working rules
- Numbers, tables, ROI per day. Short answers, no fluff. Match the style in the player's profile.
- The player plays on browser APEX and may send screenshots; read them carefully, they override assumptions.
- Never guess APEX commands or numbers. Pull data (tools, FIO, PRUNplanner) or search.
- Label every number: live (tool output), state (company.yaml), or estimate; say which in-game command would confirm an estimate.
- Prices move; re-run tools before quoting. Anything older than a day is stale.
- Do not commit or push unless asked. Writes to the player's PRUNplanner account follow the guardrails in
docs/decisions.md.
Source-of-truth rules
- PRUNplanner code wins over our own docs for game mechanics (
ref/frontend/src/features/planning/calculations/,ref/backend/). If any older handoff or note conflicts with it, the note is wrong; fixdocs/mechanics.md. - In-game numbers the player reports beat both, especially for planet resource factors.
ref/is a gitignored copy of the PRUNplanner repos; refresh withtools/refresh_refs.sh.
Setup
- Python venv at
.venv, package installed editable (pip install -e ".[dev]"), which provides thepugacommand:puga <tool> [args](.venv/bin/pugaif the venv is not activated).tools/*.pyalso run directly with.venv/bin/python. Dependencies are inpyproject.toml. - Secrets in
.env(gitignored; template.env.example): FIO REST key, FIO API key, PRUNplanner key, FIO username, company code. Never print or commit them; do not ask the player to paste keys into chat. - Scope: whole universe supported; default exchange from
DEFAULT_CX(AI1 = Antares),--cxto change.
Layout
CLAUDE.mdthis file;README.mdfor humans (short pitch),docs/usage.mdthe human tool/setup reference.docs/GAME and toolkit knowledge, safe to publish:mechanics.md(verified rules),saturation-design.md(market model),decisions.md(toolkit decisions and guardrails),roadmap.md.empire/(gitignored) OUR game state, nothing here goes public:PROFILE.md,state/company.yaml(synced from FIO),plans/(own plan specs),docs/(handoffs, build plans, personal notes).puga/library;tools/CLIs;plans/examples/generic plan specs (also test fixtures);state/company.example.yamlexample state;tests/;data/(gitignored fetched data cache);ref/(gitignored PRUNplanner source).- Rule: game facts and generic methods go in
docs/; anything about the player's own bases, cash, decisions or preferences goes inempire/.
Library use (not just CLI)
For anything the CLI tools don't expose (custom what-ifs, cost breakdowns with wages/capex, ad-hoc market checks) import puga.* directly in a short inline script instead of stretching a tool's flags. See docs/library.md for the module map and patterns; most non-trivial answers in this project came from a python - <<EOF block, not a CLI flag.
Self-improve the harness
If you catch yourself writing similar ad-hoc python - <<EOF code for the same kind of workload more than once in a session (a reverse recipe lookup, a planet/resource search, a marginal-ROI diff, a jump-distance check), stop and abstract it into a puga/ function plus a tools/*.py CLI instead of repeating it by hand again. This is exactly how tools/route.py, tools/whatif.py, tools/chain.py --consumers and tools/planet.py got built - noticing the repeated-by-hand pattern, then promoting it. Register the new tool in this file's Tools list and in docs/roadmap.md; add a matching entry to docs/library.md if it's a new puga/ module. This is the harness improving itself through use, not a one-off - keep doing it.
Tools
Run as puga <tool> ... (e.g. puga scan --min-n 3, puga plan pull <uuid>); the tools/x.py paths below are the same programs.
tools/state.py sync|showrefreshesempire/state/company.yamlfrom live FIO (cash, permits, buildings, real production efficiency, storage, ships).tools/plan_push.pybuilds/validates PRUNplanner plans from YAML specs (plans/examples/,empire/plans/). Dry run by default;--applyonly after the player says yes; only[PuGa]-named plans are created/updated/deleted (delete <uuid>, only when asked);listshows the account's plans,pull <uuid> -o file.yamlreads a plan back into a YAML spec including edits made in the PRUNplanner UI. Workflow: push a plan, the player refines it in the UI and saves, thenpull/simulate --uuidread it back and you continue from there.tools/simulate.py <spec.yaml | --uuid U> [--off EXT,SME] [--basis real|uni30|vwap30|ask|...]replica of PRUNplanner's simulator (efficiency, workforce, material I/O, profit). Flows and efficiency verified exact vs screenshots; profit within ~2%. The NEW CAPEX line = planned minus already built (from state), the honest payback; ignore the plan-level ROI (PRUNplanner always adds a core module).--uuidreads the SAVED plan from the account (UI edits must be saved first).tools/scan.pyDEPTH-AWARE recipe scan (use this): buildings the market could absorb, ROI for our own size, patient prices, ask-walked inputs. Fully staffed AND understaffed variants by default (--staffing), freight netted out (--trip-cost,--cargo), HQ/COGC/experts/faction,--planet IDadds that planet's extraction, fertility and COGC (a planet not in state is a new base: no HQ, permits+1),--deprec 60 [--reclaim-frac 0.65]prices demolish-later: amortizes the UNRECLAIMED capex (not full capex - real FIO reclaim data shows 50-86% comes back even at near-100% condition, seedocs/decisions.md) over the holding period.--min-nis ONLY a noise filter on market capacity (use 3, ideally 10); ROI is always for our own size (--own, default 1 building), never at the filtered scale. Below 1 lets sub-building junk in.--json rows.jsonfeedspersistence.py.--sort arearanks by profit/day per tile of BUILDING area (FIOAreaCost, not hab area) - once an existing base's area cap binds, this is the real allocation criterion, not %-yield/payback (a higher-%% recipe needing much more area can lose to a lower-%% one that fits more densely).--max-impact 10(stage 2 only) flags[exit-thin]when the LIVE bid book can't absorb even 1 day of 1 building's output within that %% (puga.saturation.max_units_at_impact) - an INSTANT-DUMP/panic-exit liquidity check, not production price impact (that's whatmarket/limalready measure via real 30-day flow); a single snapshot (orders change constantly), so it's a WARNING, not folded intomarket/lim. Library:puga/saturation.py.tools/history.py TICKERmonthly margin history of the recipe producing TICKER;tools/persistence.py rows.jsonre-prices scan rows over history (mean ROI 7/14/30/90/180d, payback, net gain over 7 and 14 days, % days profitable). ALWAYS check persistence before recommending: current margins are often a spike.tools/buy.py <spec.yaml|--uuid U> --days Ncash-aware shopping list: construction gap (plan vs empire state's built buildings) plus N days of the plan's NET operating stock (viasimulate(), so self-produced inputs like AL net against consumption), priced at real fill cost, checked against cash/reserve. Pass a plan covering the WHOLE base for restocking, not just a new addition (seedocs/library.md).tools/sell.py TICKER QTY [--tight]where to post an ask, expected hours to clear, and a self-contained tranche split: aggressive (undercut) tranche capped at the median 30d-volume quantile (or 80th pct with--tight, for when a payment is imminent and stockout risk outweighs a few points of margin), patient tranche priced just under the next competitor tier.--show-bidfor the instant-bid comparison (off by default; usually worse).--tiers [--rate R]answers "is it ever worth pricing above the current market": combines queue-depth days (real resting quantity, within the traded range) with an EMPIRICAL first-passage estimate from the ticker's own multi-year price history (no random-walk assumption - a driftless-walk closed form was tried first and backtested wrong, seedocs/decisions.md's "Optimal ask price" entry), via max (the binding constraint).rateshould be the player's REALIZED capital growth rate (read off their equity-growth chart, default 0.05), not a theoretical build ROI. Library:puga/history.py::empirical_touch().tools/network.py <chain.yaml>models a MULTI-BASE chain (e.g. mine LST at Nike, ship it, make BSE at Deimos): simulates each base, nets a transferred material's producer-surplus against consumer-need, charges real freight only on what's moved (puga/network.py). Spec and worked example inplans/chains/.cm_free: trueper base for a new founding covered by a Core Module Kit. profit/day per base is the WHOLE plan, not the marginal addition - diff against the base's plan without the addition for a true marginal ROI.tools/whatif.py <before.yaml> [--add BUILDING:AMOUNT[:RECIPE]] [--infra HB1:+1] [--after after.yaml]marginal payback of adding to a base you already run (whole-plan diff, same rule as buy/runway - pass the WHOLE base, not just the addition).--addmutates a copy ofbeforein-memory (reuses an existing building's recipe if omitted, e.g.--add POL:1);--aftertakes a second full spec instead. Flags any workforce tier the addition newly overloads (vs. a pre-existing one, which it won't flag) - this is what caught the PP2/pioneer-housing and 2nd-CHP/pioneer-housing surprises by hand before this tool existed. Charges marginal freight too (real fuel model, CX-to-planet, viapuga.shipping): a CONTINUOUS amortized rate (extra tonnage/cargo-hold x round-trip cost), scaling with the addition's own tonnage, not a step function - a small addition adds a small continuous freight cost, not exactly zero, and a recipe whose ROI looks great only because its freight cost was left out still gets caught.tools/runway.py <spec.yaml|--uuid U> [--no-inbound]days of runway per material before a base stalls: real NET consumption viasimulate()(self-produced inputs excluded), base storage, PLUS (by default) any ship currently flying to that planet, auto-detected from live flight data (tools/state.pytags each ship with its flight origin/destination). Pass the WHOLE base plan (seedocs/library.md).tools/undervalued.py [--cx AI1] [--min-vol 5] [--top 20]manufactured goods currently priced below implied production cost - buy-and-hold-for-reversion candidates, not build candidates. Expected ROI is much lower than production builds (~0.10-0.24%/day backtested, vs 14-29%/day for the builds this toolkit recommends) - only worth it for excess idle capital once better-yielding builds are exhausted. Library:puga/undervalued.py.tools/chain.py TICKERmake-vs-buy cost tree plus sourcing depth of inputs.--consumersflips it: what recipes EAT this ticker as an input, with each output's market depth - for "is there a deeper downstream product worth chaining into" questions (e.g. checking BOR/BSE/BBH for further processing).tools/planet.py RESOURCE [--min-factor X] [--from PLANET] [--top N]ranks planets for a raw resource by extraction factor, with founding cost straight from FIO's ownBuildRequirements(real environment-extras materials, not guessed temperature/pressure thresholds - PRUNplanner's own calculations don't model those either) and optional jump distance viatools/route.py. Pure-factor ranking alone is misleading - the best-factor planets are often thousands of ly away; always pass--froman existing base. Library:puga/planet.py.tools/route.py <from planet/system> <to planet/system>jump-distance route (not a PRUNplanner feature - built from FIO's/systemstarsgraph, Dijkstra by straight-line ly since that's what FTL fuel/time scale with, jump count reported alongside). Accepts planet natural IDs, system natural IDs, or names. Library:puga/route.py.tools/shiptrip.py SHIP FROM TO [--v0 X --v1 Y]the simple ship-trip tool: give a ship registration + start + stop, it works out jumps/charges/landing fuel itself viatools/route.py's jump graph and reports fuel cost (defaults to max sliders). Direct-jump routes only - an unconnected pair means gateway-only, and it tells you exactly what to read off the live APEX Ship Flight Control screen (the LOCK fee - genuinely not available via FIO/PRUNplanner, confirmed by inspecting both) and what to run instead (tools/shipcost.py). Warns if the given ship's live specs (FIO/ship/ships/{user}) don't match the calibrated hull/reactor.tools/shipcost.py [--jumps N --charges N] [--v0 X --v1 Y] [--gateways N --lock-fee AMOUNT:CURRENCY] [--to PLANET]the explicit/manual version: fuel/AIC cost of a ship trip from physics parameters you already know (jump count, slider values, gateway LOCK fees read off the live game) - usetools/shiptrip.pyinstead if you just want "ship X from A to B". Landing fuel is a measured-planet lookup via--to, independent of whether the route used jumps or gateways. Both tools scoped to this account's one hull/reactor (blueprint BP-STRT-0000, shared by both ships AVI-07JFB/AVI-07JFC); open caveats (v0 relative to current fuel load, landing fuel confounded with gravity/distance, no second hull to cross-check) are inpuga/shipping.py's module docstring - don't trust magnitudes beyond what's there. Raw calibration data (APEX DOM reads, 2026-10-09):empire/data/shipping_calibration/. Also wired intotools/network.py's freight cost (direct-jump transfers use the real model; gateway transfers need an explicittrip_costin the chain YAML, else a 9250 AIC fallback). Library:puga/shipping.py(trip_fuel(),round_trip_cost()).tools/parkcash.py [--min-spread X] [--min-trade-days N] [--rate R] [--top N]where to park idle cash as a buy-low/sell-high position (not a build) while ships are busy or cash has no better use right now - scans every ticker for a big bid/ask spread AND real trading liquidity (trade-days/gap over a calendar window - a huge spread with few trading days is a stale-quote trap, confirmed by hand: AST showed a 130% spread but its resting ask had gone stale after a 60% real price crash). Round-trip wait/value is EMPIRICAL (puga.history.empirical_touch, same model astools/sell.py --tiers), not an assumed random walk - a driftless-walk version was tried and backtested wrong (overstated reachability 2-4x vs 3 years of real data, seedocs/decisions.md). This dropped the whole universe's top pick from ~94%/day to ~1%/day with real waits of 19-194+ days - treat any number from the old model as stale.--ratedefaults to 0.0 (matches the idle-cash premise; pass your realized growth rate if this cash IS earmarked) and shows up as a separatediscounted_pctcolumn (margin% x real odds both legs ever happen) alongside the simple non-compoundingdaily_pctused for ranking. Library:puga/parkcash.py.tools/lmscan.py PLANET [--direction source|destination] [--min-margin X]live Local Market hauling-ad scan (FIO, public/no-auth - real player-posted freight jobs, not the CONTD player-contract system), ranked by margin after real fuel cost (puga.shipping.leg_cost). One-way cost only - margin assumes the next job is picked up near the destination, not an empty flight home. A small measured gateway-fee table covers ANT<->HRT/BEN; other gateway routes can't be priced (LOCK fee isn't available via any API). Library:puga/contracts.py.tools/price.pyprices across exchanges with VWAP, volume, fill price for a quantity;tools/book.pyorder-book ladder.tools/prun_scan.py,tools/prun_cxarb.pylegacy top-of-book scans; overstate thin markets. Baseline only.tools/arb.pyreplacement is on the roadmap.puga/econ.pypure formulas ported from PRUNplanner (efficiency stack, workforce, extraction, production I/O, hab optimizer);tests/test_econ.pyholds the reference values.
Model policy
Sonnet builds; spawn a bigger model (Agent model: opus|fable) for the review points in docs/decisions.md. Roadmap: docs/roadmap.md.
Key analytic rule: depth matters
PRUNplanner's ROI Overview ranks recipes as if the market absorbs unlimited output (e.g. 0.25 day ROI on a recipe whose whole market fits in 2 buildings). Every opportunity we report must include saturation (buildings the market can absorb) and, for anything we would act on, persistence (is the margin a spike?). Report ROI at realistic fill prices, not top-of-book.