magicciv/.project/objectives/p3-06-civic-anarchy-and-axis-switching.md
Natalie 35f7b1a0ce feat(@projects/@magic-civilization): update civic objectives and proof capture
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-14 20:00:21 -07:00

4.9 KiB

id title priority status scope owner updated_at evidence blocked_by
p3-06 Civic anarchy — 5-turn anarchy on axis switch p3 done game1 unassigned 2026-05-14
src/simulator/crates/mc-core/src/civic.rs:128 CivicState::switch_axis sets anarchy_turns_remaining = ANARCHY_DURATION (=5) on real swaps and bypasses Anarchy sentinel transitions
src/simulator/crates/mc-core/src/civic.rs:154 CivicState::tick_anarchy saturating decrement
src/simulator/crates/mc-economy/src/anarchy.rs:57 process_anarchy halves production and zeros gold income
src/simulator/crates/mc-economy/src/anarchy.rs test_anarchy_halves_production green
src/simulator/crates/mc-economy/src/anarchy.rs test_anarchy_decrements_per_turn green
src/simulator/crates/mc-core/src/civic.rs test_axis_switch_triggers_5_turn_anarchy green
cargo test -p mc-turn --lib 203/203 ok with new civic_state field
cargo check --workspace green
src/simulator/api-gdext/src/lib.rs GdGameState::request_civic_switch + get_anarchy_turns_remaining added (p3-06-gdext-bridge)
cargo check -p magic-civ-physics-gdext green (only pre-existing mc-trade warning, no new errors)
2026-05-14 reverify: cargo test -p mc-core -p mc-economy --lib green (mc-core 249/249, mc-economy 30/30 incl. all four anarchy tests)

Context

Per public/games/age-of-dwarves/docs/civics/CIVICS.md, switching the active civic on an axis triggers a 5-turn anarchy state on that axis. While in anarchy: no modifiers from the previous OR new civic apply (the axis contributes zero, per p3-05e's anarchy-zero rule), and the civic UI shows a countdown. After 5 turns the new civic activates.

This objective scoped to anarchy timer + production penalty + gold penalty mechanics in mc-core/mc-economy. GDExt bridge and turn-processor wiring (calling process_anarchy from the per-turn loop) are tracked separately.

Acceptance

  • mc_core::civic::CivicState::switch_axis(axis, choice) sets anarchy_turns_remaining = ANARCHY_DURATION (= 5) on a real switch; bypasses the timer when entering or leaving the AxisChoice::Anarchy sentinel — src/simulator/crates/mc-core/src/civic.rs:128. Same-civic re-selection is a no-op.
  • mc_core::civic::CivicState::tick_anarchy decrements (saturating) per turn — src/simulator/crates/mc-core/src/civic.rs:154.
  • mc_economy::anarchy::process_anarchy(state, gold, city_production) halves production yields and zeros gold income while leaving upkeep intact — src/simulator/crates/mc-economy/src/anarchy.rs:57. Re-exported from crate root as mc_economy::process_anarchy.
  • ✓ Save/load round-trips the anarchy countdown via PlayerState.civic_state (#[serde(default)] on the field, custom-id round-trip test in mc-core).
  • cargo test -p mc-core -p mc-economy --lib green: test_axis_switch_triggers_5_turn_anarchy, test_anarchy_halves_production, test_anarchy_zeroes_gold_income_keeps_upkeep, test_anarchy_decrements_per_turn, anarchy_sentinel_bypasses_timer_trigger, tick_decrements_saturating all pass.
  • cargo test -p mc-turn --lib green (203/203) with the new field threaded through PlayerState.
  • cargo check --workspace green (only pre-existing unrelated lint warnings).
  • ✓ GDExt bridge GdGameState::request_civic_switch(pi, axis, choice) + get_anarchy_turns_remaining(pi)src/simulator/api-gdext/src/lib.rs. Axis parsed from string ("authority"/"labor"/"economy"); choice parsed via serde so any catalog id (including future JSON-authored civics) is accepted without enum churn; unknown ids fall through to AxisChoice::Custom. Out of scope here, tracked under p3-06-processor-wiring: TurnProcessor per-turn invocation of process_anarchy and tick_anarchy. Mechanics layer is the SSoT in this objective; phase ordering belongs in the next objective so it can sequence correctly relative to p3-05e modifier propagation.

Source-of-truth rails

  • Rust crate: mc-core::civic owns the typed state and timer math. mc-economy::anarchy owns the gold/production penalty layer. No GDScript shadow.
  • JSON path: anarchy duration is currently the const mc_core::civic::ANARCHY_DURATION; promotes to public/resources/civics/_config.json once the catalog ships in p3-05b/c/d.
  • mc-core wrapper: uses AxisChoice::Anarchy from p3-05a as the sentinel; the countdown lives on the shared CivicState rather than per-axis to keep the Game-1 model simple.

Out of scope

  • Civic catalog content — p3-05b/c/d.
  • Modifier propagation while a civic is active — p3-05e.
  • AI logic for choosing when to switch — separate AI ticket.
  • GDExt bridge surface — p3-06-gdext-bridge (already shipped, see evidence list).
  • Per-turn TurnProcessor wiring of tick_anarchy + process_anarchyp3-06-processor-wiring.
  • Per-turn processor wiring — p3-06-processor-wiring.

References

  • public/games/age-of-dwarves/docs/civics/CIVICS.md
  • Parent: p3-05a (now also closed partial).