4.8 KiB
| id | title | priority | status | scope | category | owner | created | updated_at | blocked_by | follow_ups | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| p1-29c-followup-empty-params-json-regression | GdEconomy::process_turn fails — `_build_params_json` produces empty string for autoplay seeds | p1 | done | game1 | bug | shipwright | 2026-05-14 | 2026-05-15 |
|
Context
p1-29c autoplay batch ran on apricot 2026-05-14 (autoplay_batch_p1_29c-1778813509, DONE 20:08). All 12 seeds FAILED the E2E gate with hundreds of repeated errors:
ERROR: GdEconomy::process_turn params_json parse: EOF while parsing a value at line 1 column 0
The cities_json + units_json args parsed OK (no preceding parse errors logged) — only params_json is empty.
Caller: src/game/engine/src/modules/empire/economy.gd:43
var params_json: String = _build_params_json(player)
var result: Dictionary = gd_economy.process_turn(cities_json, units_json, params_json)
_build_params_json ends with JSON.stringify({"yield_mult": GameState.get_effective_yield_mult(player, "gold"), ...}). Empty string output from JSON.stringify happens on degenerate inputs (NaN, +/-Inf, cyclic refs). Most likely: get_effective_yield_mult returns NaN for some clan-personality combination.
Acceptance
- ✓ Reproduce: pick one failing seed, run with verbose logging, capture the
_build_params_jsoninputs that trigger the empty stringify. — Inspection:GameState.get_effective_yield_multwas called from 5 sites (economy.gd:112,turn_processor.gd:51/153/353/398) but never defined anywhere in the codebase (grep -rn "func get_effective_yield_mult" src/→ 0 hits). Calling a missing method on theGameStateautoload pushes a runtime error and returnsnull; the surrounding GDScript evaluation poisons the Dictionary literal, andJSON.stringifyemits"". Not NaN/Inf as the prior hypothesis suggested — missing function entirely. - ✓ Identify the input that breaks stringify —
GameState.get_effective_yield_mult(player, "gold")ateconomy.gd:112(and the fourturn_processor.gdsites). Function symbol absent fromsrc/game/engine/src/autoloads/game_state.gd. - ✓ Fix at source — added
GameState.get_effective_yield_mult(player, yield_kind)returning the documented per-yield multiplier:"production"→ai_difficulty_modifier(+ per-player override viaai_per_player_production_mult[player.index]),"research"→ai_research_modifier(+ override), all other kinds →1.0(Rust-side default atapi-gdext/src/lib.rs:6178). NaN/Inf/negative results clamp to 1.0 withpush_warning. Belt-and-suspenders defensive sanitizer added ineconomy.gd::_build_params_json(same NaN/Inf/negative clamp) so any future regression cannot reproduce the empty-stringify failure mode. - ✓ Re-run
autoplay-batch.sh 10 300on apricot; E2E gate ≥10/12 PASS — verified on batch20260515_215705(10/10 PASS, after subsequentupdate_tile+set_mapfixes in commitse200634df+8820ce04a). - ✓ Then unblock
p1-29cfinal acceptance bullet — bullet is now MEASURABLE (apricot infrastructure clean), result is 0/10 PASS (P1 stuck at tier_peak=1 in all 10 seeds, eliminated before tier 2 in 80%). Documented inp1-29citself; not p1-29c-followup's bullet to flip.
Verification (2026-05-15)
Apricot smoke batch 20260515_072145 launched via scripts/apricot-run.sh launch smoke 10 300.
Behavioral side-effect note (recorded for follow-up triage): the four other call sites of get_effective_yield_mult in turn_processor.gd (production, research, two culture sites) were also hitting the missing function and silently degrading. Restoring the function returns ai_difficulty_modifier / ai_research_modifier / 1.0 to those paths, so per-yield AI scaling now applies as originally intended (warcouncil p1-29 H4 / p1-31). If this batch reveals a different-flavored regression (e.g. AI runaway / production stalls), it is downstream of restoring the intended multiplier path and belongs in a new follow-up rather than re-fixing here.
PASS verdict (filled in on batch completion):
<pending — batch in flight; see scripts/apricot-run.sh status 20260515_072145>
References
- Batch log:
apricot:~/.local/var/p1_29c/run.log - Batch dir:
apricot:/var/home/lilith/Code/project-buildspace/magic-civilization/.local/batches/autoplay_batch_p1_29c-1778813509/ - Caller:
src/game/engine/src/modules/empire/economy.gd:_build_params_json - Sink:
src/simulator/api-gdext/src/lib.rs:6221(the godot_error site).
Why this blocks p1-29c closure
The batch infrastructure must produce clean games before the tier-peak gate (p1-29c bullet 1) can be meaningfully measured. With 0/12 PASS this run, no tier_peak data was captured — p1-29c bullet 1 stays ✗ until the regression is fixed and a follow-up batch succeeds.