chore(engine): server hygiene batch — workers tests, surname dedup, save pin (T-1063, T-1064)

- workers/pool.rs: 5 new tests; catch_unwind keeps worker threads alive on
  handler panic (in-flight request loss unchanged, pinned by test + #843
  docs); stubs.rs no longer falsely claims the pool is tested
- save/load: execute_save_load pinned .after(Storyteller) so the scheduler
  cannot legally save pre-Input state; exclusive-system exception recorded
  in tick_phases.rs rules
- surname corpus extracted to bin/shared/surname_corpus.rs (both economy
  generators import it; byte-identical output verified on 23.6MB+1.45MB
  TOMLs); all three stamp/watch registries updated
- generator_spike gated behind non-default 'generator-spike' feature
- economy.rs: 11 new D-181 signal-derivation tests on the new
  econ_sim Simulation::from_economy in-memory constructor
- perception exemption comments now state the consumer sort contract;
  unused bytemuck removed; rayon comment corrected; the 22 allow(dead_code)
  documented as serde schema enforcement

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 16:22:28 +02:00
co-authored by Claude Fable 5
parent c64231e8ee
commit 0bd895fcac
19 changed files with 903 additions and 673 deletions
+2
View File
@@ -50,6 +50,8 @@ GENERATOR_SOURCES: dict[str, list[Path]] = {
REPO_ROOT / "tooling" / "economy-db" / "import_economics.py",
REPO_ROOT / "server" / "src" / "bin" / "generate_brands" / "main.rs",
REPO_ROOT / "server" / "src" / "bin" / "generate_brands" / "names.rs",
# Shared surname corpus extracted from the two names.rs copies (T-1064).
REPO_ROOT / "server" / "src" / "bin" / "shared" / "surname_corpus.rs",
REPO_ROOT / "tooling" / "generate-brands",
REPO_ROOT / "tooling" / "schema_version.py",
# D-237 authored specialization layer data TOMLs (#1013). Keep in sync
+12 -2
View File
@@ -54,13 +54,23 @@ impl Simulation {
let conn = db::open_db(&db_pathbuf);
let economy = db::load_economy(&conn);
Ok(Self::from_economy(economy, run_seed))
}
/// Build a simulation directly from an in-memory [`db::Economy`] — no DB file.
///
/// Identical initialization to [`Simulation::load`] after the DB read:
/// deterministic productivity/shadow seeding from `run_seed`, warm-start
/// node states. Intended for unit tests and embedded callers that
/// construct small, fully-controlled economies (T-1064).
pub fn from_economy(economy: db::Economy, run_seed: u64) -> Self {
let productivity = seed::seed_all_productivity(&economy, run_seed);
let shadow = currency::seed_shadow_economy(&economy, run_seed);
let adjacency = trade::build_adjacency(&economy);
let archetypes = agents::build_archetype_map(economy.corp_archetype_data.clone());
let nodes = model::init_nodes(&economy);
Ok(Simulation {
Simulation {
economy,
productivity,
shadow,
@@ -72,7 +82,7 @@ impl Simulation {
tick: 0,
alpha: model::ALPHA,
beta: trade::BETA,
})
}
}
/// Try to load from the auto-detected DB path (same search as the CLI binary).
+7 -4
View File
@@ -60,6 +60,8 @@ GENERATED_BRANDS_TOML = REPO_ROOT / "wiki" / "economics" / "corporations" / "gen
# either must invalidate the meta stamp even though Python hasn't changed.
GENERATE_BRANDS_RS = REPO_ROOT / "server" / "src" / "bin" / "generate_brands" / "main.rs"
GENERATE_BRANDS_NAMES_RS = REPO_ROOT / "server" / "src" / "bin" / "generate_brands" / "names.rs"
# Shared surname corpus extracted from the two names.rs copies (T-1064).
GENERATE_BRANDS_SURNAMES_RS = REPO_ROOT / "server" / "src" / "bin" / "shared" / "surname_corpus.rs"
GENERATE_BRANDS_WRAPPER = REPO_ROOT / "tooling" / "generate-brands"
@@ -87,6 +89,7 @@ IMPORT_ECONOMICS_SOURCES: tuple[Path, ...] = (
Path(__file__),
GENERATE_BRANDS_RS,
GENERATE_BRANDS_NAMES_RS,
GENERATE_BRANDS_SURNAMES_RS,
GENERATE_BRANDS_WRAPPER,
REPO_ROOT / "tooling" / "schema_version.py",
# D-237 authored specialization layer: these data TOMLs feed the DB, so a
@@ -2155,8 +2158,8 @@ def _specialization_checks(conn, vocab, systems, strict):
print(f" economic: {n_econ} authored ({n_inhabited} inhabited; rest on fallback once #1014 lands)")
print(f" cultural: {n_cult} authored ({n_named} named; rest on corridor default)")
print(f" faction: {n_fac} authored ({n_named} named; rest on derivation)")
print(f" BulkClass: " + " | ".join(f"{k}={v}" for k, v in sorted(bulk_dist.items())))
print(f" ProductionUbiquity: " + " | ".join(f"{k}={v}" for k, v in sorted(pu_dist.items())))
print(" BulkClass: " + " | ".join(f"{k}={v}" for k, v in sorted(bulk_dist.items())))
print(" ProductionUbiquity: " + " | ".join(f"{k}={v}" for k, v in sorted(pu_dist.items())))
if warnings:
print(f" Specialization warnings ({len(warnings)}):")
for w in warnings:
@@ -2363,10 +2366,10 @@ def main():
print(f"error: {db_path} not found", file=sys.stderr)
sys.exit(1)
print(f"\n Economics Import Pipeline")
print("\n Economics Import Pipeline")
print(f" DB: {db_path}")
if args.dry_run:
print(f" Mode: DRY RUN")
print(" Mode: DRY RUN")
print()
# Load wiki corps before opening DB — allows early exit on parse failures