Add tools/threat_report.py (action item 7)

Reads recipes/ships/modules/world.toml and reports per-item threat
values, module contributions, hull-only and fitted ship threats
(default_modules), producer:consumer ratio tables, and belt
feasibility against the single-belt cap.

Implements the agreed design semantics: per-unit threat (recipe threat
divided by output amount), the scrap-fallback rule, and fixpoint
resolution through reprocessing-only items. Running it against the
current configs surfaced two ThreatCostCalculator deviations, recorded
as new action items: multi-output recipes are double-priced (no
per-unit division), and items downstream of reprocessing-only items
never resolve, underestimating capital hull threat.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN
This commit is contained in:
2026-07-03 16:47:40 +02:00
parent 3aefc05118
commit b9e70ba83a
3 changed files with 265 additions and 10 deletions

View File

@@ -380,9 +380,15 @@ in `requirements.md` and the git history). Still open:
rule for the reprocessing path. Otherwise the scrap→ingot smelter
recipe would inflate the basic materials' threat via the
max-across-recipes rule, poisoning every downstream value.
7. **Threat report tool.** Port the numbers-pass calculator to
`tools/threat_report.py`: per-item threat values, module
contributions, fitted ship threats vs. the ladder targets,
producer:consumer ratio tables, and belt-feasibility checks, read
from the real config files. Re-run after any recipe or material change, like
`verify_recipes.py`.
7. **Per-unit item threat.** Amend REQ-THREAT-ITEM and
`ThreatCostCalculator`: a recipe's threat is divided by its output
amount, so item threat is production-seconds *per unit*. Currently a
recipe producing 2 copper_wire per run assigns each wire the full
run's threat, double-pricing multi-output items and everything
downstream of them.
8. **Fixpoint resolution in ThreatCostCalculator.** Items downstream of
reprocessing-only items (e.g. capital parts built from the scrap-only
input) never resolve, because resolution stops after the reprocessing
pass instead of iterating; their consumers silently drop the missing
materials, so capital hull threat is currently underestimated (found
by `tools/threat_report.py`, which implements the correct fixpoint).