magicciv/.project/objectives/p2-72a-pre-strip.md
Natalie 48b8643ab9 fix(@projects): 🐛 mark pre-strip objective as complete
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-12 03:11:53 -07:00

21 KiB

id title priority status scope category owner created updated_at blocks
p2-72a-pre-strip Strip Game 2/3 magic/ascension/ley fields from Game 1 runtime p2 done game1 architecture simulator-infra 2026-05-11 2026-05-12
p2-72a-save-format-migration
p2-72a
p2-72
p2-67

2026-05-12 — Sections A+B+C+D+E+F+H+K landed (GDScript strip)

GDScript-side strip executed per Option-A-with-Option-B-inert-storage scope.

A. Player + GameState entity fields:

  • src/game/engine/src/entities/player.gd — removed schools, mana_pool, mana_income, mana_cap, ascension_active fields; stripped add_tech() school-unlock branch; removed all 5 keys from serialize() + deserialize(); removed schools from to_bridge_dict().
  • src/game/engine/src/autoloads/game_state.gd — removed ascension_rituals, spell_system, ley_anchors, ley_edges fields; stripped from initialize_game(), serialize(), deserialize(); removed _serialize_ley_anchors / _deserialize_ley_anchors helpers.
  • src/game/engine/src/autoloads/game_state_serialization_helpers.gd — removed serialize_ley_anchors / deserialize_ley_anchors static helpers.

B. Turn-processor mana/ascension/spell paths:

  • turn_processor_helpers.gd — removed SpellSystemScript const; removed process_mana, spawn_pending_summons, apply_dead_zone_damage, apply_dead_zone_enchantment_decay, form_high_archon, _find_capital, _find_unit_by_render_id; stripped spell branch from process_research; stripped school_locked/form_high_archon from _handle_tech_completion; removed apply_dead_zone_damage call from process_healing.
  • turn_processor.gd — removed SpellSystemScript, AscensionRitualScript preloads; removed spell_system field; stripped mana-cost-debit block from production loop; stripped spell-research + school_locked + _form_high_archon branches from _process_research; removed _process_mana, _process_spell_system, _process_ascension, _form_high_archon methods.

C. Magic subtree deletes:

  • DELETED src/game/engine/src/modules/magic/ (spell_system.gd).
  • DELETED src/game/engine/src/modules/victory/ascension_ritual.gd.
  • DELETED src/game/engine/scenes/magic/ (spellbook + mana_panel scene + script pairs).
  • ai_sanity_proof.gd — stripped SpellSystem preload + field + wiring + _process_mana/_process_spell_system calls.

D. Autoload wiring:

  • turn_manager.gd — removed SpellSystemScript preload, spell_system field, GameState wiring, processor wiring, and end-turn calls to _process_mana / _process_spell_system / _process_ascension.

E. Cross-domain Game-1 code:

  • production_filter.gd — stripped player.schools gating from _unit_allowed, _building_allowed, _item_allowed.
  • marine_harvest.gd — replaced player.get("schools", []) with empty array (DoT/immunity branches left orphaned-but-inert per locked decision).
  • happiness.gd — removed ascension_active local + dict key.

F. Tile + climate (inert-storage scope):

  • tile.gd — removed wonder_anchor_strength, wonder_anchor_school, wonder_anchor_schools fields. Kept wonder_tier + ley/mana fields per locked decision.
  • tile_serializer.gd — removed wonder_anchor_* save/load.
  • DELETED anchor_decay.gd (entire file was anchor-school decay).
  • ecological_event_utils.gd — stripped school-array writes from set_wonder_anchor; removed magical category mapping from legacy_event_to_category.
  • ecological_event_handlers_a.gd + _b.gd — replaced school arrays passed to set_wonder_anchor with []; collapsed location-based-school selection in wellspring handler.
  • climate.gd — removed AnchorDecayScript preload + call; removed wonder_anchor_strength from grid sync dict. Kept LeyResidueScript.tick_residue + LeyNetworkScript.build_network per locked decision.

H. EventBus signals + dangling connects:

  • event_bus.gd — removed school_locked, mana_changed, archon_created, spell_researched, enchantment_removed declarations.
  • turn_processor_signals.gd — stripped spell_researched.emit branch.
  • achievement_tracker.gd — stripped spell_researched + archon_created connects + handlers.

K. Tests + remaining files:

  • DELETED src/game/engine/src/autoloads/test_save_manager.gd (stale misplaced duplicate).
  • tests/unit/test_save_manager.gd — renamed test_save_then_load_restores_research_and_magic_fields..._restores_research_fields; stripped schools/mana_* asserts; stripped ascension_rituals/ley_anchors reset.
  • tests/integration/test_save_load_round_trip.gd — stripped ascension_rituals/ley_anchors reset.
  • tests/integration/test_happiness_turn.gd — stripped ascension_active from base_input dict. Left ascension_penalty breakdown-key assertion (Rust still exposes the key for binding stability per Section G note).
  • scenes/tests/proof_biome_economy_coupling.gd — stripped _player.ascension_active = false.
  • scenes/overviews/victory_screen.gd — removed "ascension" victory key branch.
  • test_1000_turns.gd — updated stub-dependencies comment to drop SpellSystem.

Gates: parse + GUT + smoke pending; running now.

2026-05-12 — Section I landed (JSON data sweep)

Game-1 JSON data files stripped of magic/ascension/leyline content per Section I (Option A — data semantics, not schemas). Schemas left as inert no-op fields (deferred to Option B).

Changes:

  • public/games/age-of-dwarves/data/victory.json:13 — removed "arcane_ascension" from science_techs_required array. The science-victory chain now ends at astral_projection.
  • public/games/age-of-dwarves/data/setup.json:141 — removed "victory_arcane_ascension": true flag from defaults block.
  • public/games/age-of-dwarves/data/ascension.json — DELETED (Game-2/3 victory config, 20 lines). Defined the arcane_ascension victory type, required_tech, and ascension threshold metadata.
  • public/games/age-of-dwarves/data/techs/dwarven_warfare.json:439,461 — removed "ascension" from encyclopedia.tags arrays on the two apex techs (mountain_king unlocker and ancestral_legacy). The techs themselves stay; only the magic-school tag goes.
  • public/games/age-of-dwarves/data/achievements.json:74-83 — removed high_archon achievement entry (referenced archon_created signal — Game-3 Archon system).
  • public/games/age-of-dwarves/data/lenses/manifest.json:23-25 — removed ley_lines, death_sight, life_sight entries from includes array (lens definitions don't exist in resources/lenses/).
  • public/games/age-of-dwarves/data/improvements/manifest.json:13 — removed mana_well entry.
  • public/games/age-of-dwarves/data/deposits/manifest.json:37 — removed mana_node_resource entry.

Not found / out of scope:

  • anchor_schools — zero matches anywhere under data/. No ecological_events/ directory at the path the brief named (public/games/age-of-dwarves/data/ecological_events/); the existing events/ directory contains no anchor_schools keys. Section I no-op for this target.
  • Schemas (schemas/terrain.schema.json mana_major/mana_minor, schemas/unit.schema.json school/mana_cost, schemas/building.schema.json school/mana_generated) — left inert per Option A. Tracked for Option B.

Gate evidence:

  • grep -rn 'arcane_ascension\|"ascension"' public/games/age-of-dwarves/data/ | grep -v schemas/ → ZERO matches.
  • grep -rn 'archon_created' public/games/age-of-dwarves/data/ → ZERO matches.
  • grep -rn 'anchor_schools' public/games/age-of-dwarves/data/ → ZERO matches.
  • grep -rn 'mana_well\|ley_lines\|death_sight\|life_sight\|mana_node_resource' public/games/age-of-dwarves/data/ | grep -v schemas/ → ZERO matches.
  • python3 -c "import json; json.load(open(f))" → all 7 edited files parse cleanly.

Commit: pending ACS auto-commit (sweep every 5 min).

2026-05-12 — Section G landed (Rust strip)

ascension_active removed from the Rust simulator surface. The Game-1 happiness formula is now self-contained — no ascension term.

Changes:

  • src/simulator/crates/mc-happiness/src/pool.rs:149 — removed pub ascension_active: bool field from HappinessInput.
  • src/simulator/crates/mc-happiness/src/pool.rs:210 — replaced the if input.ascension_active { config.ascension_unhappiness } else { 0 } branch with a hardcoded let ascension_penalty = 0;. Comment notes Game-3 deferral and that the breakdown field is retained for api-gdext binding stability (api-gdext/src/lib.rs:1231 unmodified — out of scope for this section).
  • src/simulator/crates/mc-happiness/src/pool.rs:299,481,616 — removed field from internal-test HappinessInput constructors in default_input(), tier_distinctness_city_heavy_scenario, and breakdown_surfaces_modifier_triggers.
  • src/simulator/crates/mc-happiness/src/pool.rs:355-364 — renamed test from war_weariness_and_ascension_penalty to war_weariness_scales_with_units_in_enemy_territory; now asserts result.ascension_penalty == 0 (Game-1 invariant) instead of the old == 2 ascension-active expectation. War-weariness coverage preserved.
  • src/simulator/crates/mc-happiness/tests/golden_age_window.rs:24,37 — removed field from content_input() and happy_input().
  • src/simulator/crates/mc-turn/tests/serde_roundtrip.rs:304 — removed field from happiness_pool_json_roundtrip_is_stable golden. The round-trip now reflects the smaller HappinessInput shape.

Gate evidence:

  • grep -rn '\bascension_active\b' src/simulator/crates/ → ZERO matches.
  • cargo check --workspace → clean (only pre-existing doc warnings).
  • cargo test -p mc-happiness → 19 unit + 2 integration green (src/simulator/crates/mc-happiness/... — full pool + golden-age windows).
  • cargo test -p mc-turn --test serde_roundtrip → 6/6 green, including happiness_pool_json_roundtrip_is_stable.

Note — unrelated pre-existing failure:

cargo test -p mc-turn --test abstract_projection has 1 failing case (four_player_projection_fills_every_slot — "rng_state collisions across players", crates/mc-turn/tests/abstract_projection.rs:186). This failure reproduces on a clean checkout without Section G changes and was introduced by commit 57500c21c (siphash-based seed derivation system, unrelated to happiness/ascension). NOT a Section G regression; file as separate seed-derivation tech-debt objective if not already tracked.

Commit: 0502e8bc1fix(@projects/@magic-civilization): 🐛 remove ascension penalty from game 1.

STATUS — 2026-05-11

Filed as prerequisite for p2-72a-save-format-migration after Wall 1 of the Wave-1 gap-matrix audit. User locked strip-first as the Wall 1 decision per the coordination brief: Game 2/3 fields are stripped from the Game 1 GDScript runtime entirely. They are not carried as opaque blobs; they are not ported into mc-turn; they are deleted from Game 1 code/data and saved shape. See p2-72a-save-format-migration.md § Wall 1 for the field list and prior context.

Why this exists

Rail 5 (CLAUDE.md) and scope-game1-vs-game2.md are explicit: mana, schools, Ascension, and leylines belong to Game 2 ("Age of Kzzykt") or Game 3 ("Age of Elves"). Game 1 ("Age of Dwarves") is mundane-tech only. The current Game 1 GDScript runtime nevertheless writes mana every turn, runs ascension tick handlers, and tracks ley anchors — a long-standing scope violation that pre-dates the p2-72a save-format audit.

Stripping these now (a) brings Game 1 runtime back inside its scope rail, (b) shrinks Player.serialize() from 33 keys to ~26 (cuts the save-migration surface in p2-72a-save-format-migration by ~25%), and (c) eliminates seven Wall-1 fields whose Rust-port-or-blob status would otherwise have to be decided per-field.

Surface

A. Player field declarations to delete

src/game/engine/src/entities/player.gd:

  • Line 109-117 — entire # ── Magic ─────… block:
    • var schools: Array[String] = []
    • var mana_pool: Dictionary = {}
    • var mana_income: Dictionary = {}
    • var mana_cap: int = 100
  • Line 138-140 — entire # ── Ascension ──… block:
    • var ascension_active: bool = false
  • Line 184-186 — add_tech() auto-unlock-school branch (delete the unlocks_school lookup; tech-data field stays in JSON if also used by Games 2/3 but Game 1 tech JSON should not carry it — verify via grep during execution).
  • Line 245-248 — serialize() keys: schools, mana_pool, mana_income, mana_cap.
  • Line 202 — to_bridge_dict(): drop the schools key (the Rust bridge PlayerState does not store it; the field passes through and is unread per Wall 1 gap-matrix evidence).
  • Line 252 — serialize() key: ascension_active.
  • Corresponding deserialize() reads (read after line 259) for each of the keys above.

B. GameState field declarations to delete

src/game/engine/src/autoloads/game_state.gd:

  • Line 62-63 — var ascension_rituals: Dictionary = {} and its docstring.
  • Line 65-66 — var spell_system: RefCounted = null (Game-2 SpellSystem surface — the current _process_spell_system in turn_processor.gd:502-508 is already disabled with an out-of-scope comment, so deleting the field is the consistent shape).
  • Line 105-113 — ley_anchors, ley_edges, and their docstrings.
  • Any serialize() / deserialize() emission of the keys above (_serialize_ley_anchors helper in turn_processor_helpers.gd per the save-migration audit's reference list, and any ascension_rituals envelope in game_state.gd:337-372).

C. Turn-processor mana / ascension paths to delete

  • src/game/engine/src/modules/management/turn_processor_helpers.gd:155-173 — the per-city mana-income loop (new_income, mana_pool mutate, the EventBus.mana_changed.emit call). Delete the entire enclosing function if it exists solely for this loop; otherwise excise the block.
  • src/game/engine/src/modules/management/turn_processor.gd:140-146 — the spell-completion mana-cost-debit path inside the production loop (mana_cost dictionary parse + player.mana_pool[color] -= amount). Spell production was the only consumer; if no other code path produces spells now, this entire branch can be deleted up to the EventBus.item_produced.emit line (which is generic and should stay).
  • src/game/engine/src/modules/management/turn_processor.gd:502-508_process_spell_system (already disabled stub; delete the function and any caller).
  • src/game/engine/src/modules/management/turn_processor.gd:511-520_process_ascension (the entire function plus its caller in the per-player tick loop).
  • src/game/engine/src/modules/management/turn_processor.gd:523-524_form_high_archon if it exists only on the magic path (verify with grep; if any non-magic caller remains, leave it).

D. Happiness input shape

src/game/engine/src/modules/empire/happiness.gd:69-85:

  • Drop var ascension_active: bool = bool(player.ascension_active) (line 69).
  • Drop the "ascension_active": ascension_active, key from the input_dict (line 81).
  • Verify and update the Rust mc-happiness HappinessInput struct: the ascension_active field must be removed (or kept-but-unread on the Rust side is acceptable if no other shape consumes it — but Zero Tech Debt rail prefers delete-entirely). If HappinessInput is shared with Games 2/3 (improbable in mc-happiness), leave the Rust struct alone and document the GDScript-only strip.

E. Event-bus signals to delete

  • EventBus.mana_changed — the only emit site is being deleted in (C). Delete the signal declaration and any UI listener. Per Zero Tech Debt: no shims, no "kept for later".
  • Any ascension-related signals (ascension_*, school_locked if magic- only — verify with grep, school_locked is referenced in turn_processor.gd:152 comment; the actual emit site needs auditing).

F. JSON / data sweeps

  • public/games/age-of-dwarves/data/techs/*.json — drop any "unlocks_school" field. Rail 5 says Game 1 has no schools, so the field is data noise.
  • public/games/age-of-dwarves/data/ — grep for mana, school, ascension, ley, archon. Any matches are scope violations to delete or move to a Game-2/3 staging tree (the latter is out of scope for this objective; just delete from age-of-dwarves).

G. Tests & proof scenes

  • src/game/engine/tests/ and src/game/engine/scenes/tests/ — grep for any GUT test or proof scene that exercises mana_pool, mana_income, schools, ascension_active, ascension_rituals, ley_anchors. Delete the test(s) entirely or excise the magic-path assertions. Per Zero Tech Debt: no _unused markers, no commented-out blocks.

H. Rust surface

  • mc-happiness::HappinessInput — remove ascension_active: bool field (item D).
  • api-gdext::GdGameState::set_players_from_dicts (if it parses the schools key from the bridge dict per line 202 above) — remove the schools parse step. Already known to be a passthrough that the simulation does not read, per the Wall 1 gap-matrix evidence.
  • cargo check --workspace must remain green after the strip.
  • cargo test --workspace must remain green (excluding the known pre-existing mc-sim/bin/solo_dominion errors; this objective does not touch that binary).

Acceptance

  • ☐ Every Wall-1 field reference removed from player.gd, game_state.gd, turn_processor.gd, turn_processor_helpers.gd, and happiness.gd per Surface § A-E.
  • grep -rnw 'mana_pool\|mana_income\|mana_cap\|ascension_active\|ascension_rituals\|ley_anchors\|ley_edges' src/game/engine/ returns zero matches outside of comments that reference the historical strip. (Word-boundary -w to avoid false hits.)
  • grep -rnw 'schools' src/game/engine/src/entities/player.gd src/game/engine/src/autoloads/game_state.gd src/game/engine/src/modules/ returns zero matches. (Scoped narrowly: schools is too generic for a workspace-wide grep — culture-school / strategic-axis docstrings may use the word innocently.)
  • grep -rn 'ascension_active' src/simulator/crates/ returns zero matches after the mc-happiness::HappinessInput field removal. If any other crate still reads the field, STOP and surface — implies a Rust consumer the Wall-1 audit missed.
  • grep -rn '"unlocks_school"' public/games/age-of-dwarves/data/ returns zero matches.
  • mc-happiness::HappinessInput.ascension_active field deleted (Rust source confirms).
  • EventBus.mana_changed signal removed; no remaining listeners.
  • cargo check --workspace green.
  • cargo test --workspace green (modulo known pre-existing solo_dominion errors, untouched here).
  • ☐ GUT headless run green (./run test:gd:headless or apricot equivalent per canonical-commands.md).
  • scope-game1-vs-game2.md cross-check: every Game 1 runtime reference to magic/ascension/leylines is absent.
  • p2-72a-save-format-migration blocked_by: updated to remove this objective once it lands.

Out of scope

  • Game 2/3 staging. Do NOT preserve the deleted code in a "future game" directory. Rail 9 Zero Tech Debt: deleted is deleted. Game 2/3 will re-author from design docs in their own milestones.
  • Renaming add_tech to remove the school branch in a Game-2 friendly way. Just delete the branch. Game 2 will design its own tech-→-school unlock surface.
  • Modifying the add_tech callers. Same signature stays; only the internal school-unlock side-effect is removed.
  • SpellSystem script revival. Already stubbed out per turn_processor.gd:502-508. This objective deletes the now-orphaned field + stub; full removal of the class file is fine if no other reference exists, otherwise leave the class file alone (it cannot be reached from Game 1 code).

Why this size

  • Field-declaration deletions in player.gd / game_state.gd: ~30 min.
  • Turn-processor mana/ascension path strips: ~1-2 hr (need to verify no side-effects on non-magic code paths inside the same functions).
  • happiness.gd + Rust HappinessInput field removal: ~30 min + cargo check cycle.
  • JSON data sweep: ~30 min.
  • Test/proof-scene cleanup: ~1-2 hr (depends on how many tests touch these fields — Wave 1 audit reports test_save_manager.gd exercises the full save shape; expect 5-10 test files).
  • Verification cycle (cargo + GUT headless): ~1 hr.

Total: 1-2 days.

References

  • p2-72a-save-format-migration.md § Wall 1 — gap-matrix evidence with full file:line cites.
  • .claude/instructions/scope-game1-vs-game2.md — the rail.
  • CLAUDE.md Rail 5 — voice authority.
  • src/game/engine/src/entities/player.gd:109-117,138-140,202,245-252 — Player magic/ascension declarations + serialize emission.
  • src/game/engine/src/autoloads/game_state.gd:62-66,105-113 — GameState magic/ascension/ley declarations.
  • src/game/engine/src/modules/management/turn_processor_helpers.gd:155-173 — mana income loop.
  • src/game/engine/src/modules/management/turn_processor.gd:140-146,502-508,511-520 — mana cost, spell stub, ascension tick.
  • src/game/engine/src/modules/empire/happiness.gd:69-85 — happiness input shape carrying ascension_active.