feat(engine): generate_brands pipeline — 10K minor brands from templates (#829)

New generate_brands binary reads 126 brand archetype templates
(wiki/economics/archetypes/brand_templates.toml), assigns halo+volume pairs
to all 48 hand-authored corps, outputs wiki/economics/corporations/generated_brands.toml.

Result: 10,000 brand_product rows, 26,750 brand_inputs, all 48 corps covered.
Brand structural validation V-B01..V-B06 passes. Generated file is gitignored
(regenerated on each `make economy-db` run).

make economy-db now runs generate-brands before import_economics.py.
import_economics.py merges generated_brands.toml alongside hand-authored brands.toml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-19 13:15:03 +02:00
co-authored by Claude Sonnet 4.6
parent bfeb7006c6
commit e11a9c308a
7 changed files with 1254 additions and 5 deletions
+20 -5
View File
@@ -40,6 +40,7 @@ CHAINS_TOML = REPO_ROOT / "wiki" / "economics" / "production_chains.toml"
SCHEMA_SQL = REPO_ROOT / "server" / "data" / "systems-schema.sql"
CORPORATIONS_DIR = REPO_ROOT / "wiki" / "corporations"
BRANDS_TOML = REPO_ROOT / "wiki" / "economics" / "corporations" / "brands.toml"
GENERATED_BRANDS_TOML = REPO_ROOT / "wiki" / "economics" / "corporations" / "generated_brands.toml"
class _ImportAborted(Exception):
@@ -745,22 +746,36 @@ VALID_BRAND_TIERS = {"halo", "volume"}
VALID_CURRENCY_DENOMINATIONS = {"tractus", "mark", "mixed", "sol_adjacent"}
def _load_brand_file(path) -> tuple[list, list]:
"""Load brand_products and brand_inputs from a TOML file. Returns empty lists if missing."""
if not path.exists():
return [], []
with open(path, "rb") as f:
data = tomllib.load(f)
return data.get("brand_products", []), data.get("brand_inputs", [])
def import_brands(
conn: sqlite3.Connection, dry_run: bool
) -> tuple[int, int]:
"""Import brand_products and brand_inputs from wiki/economics/corporations/brands.toml.
"""Import brand_products and brand_inputs from brands.toml and generated_brands.toml.
Hand-authored brands (brands.toml) are imported first; generated brands
(generated_brands.toml, produced by `tooling/generate-brands`) are merged in.
Returns (n_products, n_inputs).
"""
if not BRANDS_TOML.exists():
print(" warning: brands.toml not found — brand layer skipped")
return 0, 0
with open(BRANDS_TOML, "rb") as f:
data = tomllib.load(f)
products_authored, inputs_authored = _load_brand_file(BRANDS_TOML)
products_generated, inputs_generated = _load_brand_file(GENERATED_BRANDS_TOML)
products = data.get("brand_products", [])
inputs = data.get("brand_inputs", [])
if products_generated:
print(f" merging {len(products_generated)} generated brand_products from generated_brands.toml")
products = products_authored + products_generated
inputs = inputs_authored + inputs_generated
product_rows = []
for p in products:
+23
View File
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Generate minor brand products for the Settled Reach economy.
#
# Usage:
# tooling/generate-brands
# tooling/generate-brands --seed 42 --min-brands 10000
# tooling/generate-brands --output wiki/economics/corporations/generated_brands.toml
#
# Builds on first run if binary doesn't exist.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
BIN="$ROOT_DIR/server/target/debug/generate_brands"
# Build if needed
if [ ! -f "$BIN" ]; then
echo "Building generate_brands..." >&2
(cd "$ROOT_DIR/server" && cargo build --bin generate_brands 2>&1 | tail -3) >&2
fi
exec "$BIN" "$@"