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 |
|
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— removedschools,mana_pool,mana_income,mana_cap,ascension_activefields; strippedadd_tech()school-unlock branch; removed all 5 keys fromserialize()+deserialize(); removedschoolsfromto_bridge_dict().src/game/engine/src/autoloads/game_state.gd— removedascension_rituals,spell_system,ley_anchors,ley_edgesfields; stripped frominitialize_game(),serialize(),deserialize(); removed_serialize_ley_anchors/_deserialize_ley_anchorshelpers.src/game/engine/src/autoloads/game_state_serialization_helpers.gd— removedserialize_ley_anchors/deserialize_ley_anchorsstatic helpers.
B. Turn-processor mana/ascension/spell paths:
turn_processor_helpers.gd— removedSpellSystemScriptconst; removedprocess_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 fromprocess_research; stripped school_locked/form_high_archon from_handle_tech_completion; removedapply_dead_zone_damagecall fromprocess_healing.turn_processor.gd— removedSpellSystemScript,AscensionRitualScriptpreloads; removedspell_systemfield; stripped mana-cost-debit block from production loop; stripped spell-research + school_locked +_form_high_archonbranches from_process_research; removed_process_mana,_process_spell_system,_process_ascension,_form_high_archonmethods.
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_systemcalls.
D. Autoload wiring:
turn_manager.gd— removedSpellSystemScriptpreload,spell_systemfield, 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— strippedplayer.schoolsgating from_unit_allowed,_building_allowed,_item_allowed.marine_harvest.gd— replacedplayer.get("schools", [])with empty array (DoT/immunity branches left orphaned-but-inert per locked decision).happiness.gd— removedascension_activelocal + dict key.
F. Tile + climate (inert-storage scope):
tile.gd— removedwonder_anchor_strength,wonder_anchor_school,wonder_anchor_schoolsfields. Keptwonder_tier+ ley/mana fields per locked decision.tile_serializer.gd— removedwonder_anchor_*save/load.- DELETED
anchor_decay.gd(entire file was anchor-school decay). ecological_event_utils.gd— stripped school-array writes fromset_wonder_anchor; removed magical category mapping fromlegacy_event_to_category.ecological_event_handlers_a.gd+_b.gd— replaced school arrays passed toset_wonder_anchorwith[]; collapsed location-based-school selection in wellspring handler.climate.gd— removedAnchorDecayScriptpreload + call; removedwonder_anchor_strengthfrom grid sync dict. KeptLeyResidueScript.tick_residue+LeyNetworkScript.build_networkper locked decision.
H. EventBus signals + dangling connects:
event_bus.gd— removedschool_locked,mana_changed,archon_created,spell_researched,enchantment_removeddeclarations.turn_processor_signals.gd— strippedspell_researched.emitbranch.achievement_tracker.gd— strippedspell_researched+archon_createdconnects + handlers.
K. Tests + remaining files:
- DELETED
src/game/engine/src/autoloads/test_save_manager.gd(stale misplaced duplicate). tests/unit/test_save_manager.gd— renamedtest_save_then_load_restores_research_and_magic_fields→..._restores_research_fields; stripped schools/mana_* asserts; strippedascension_rituals/ley_anchorsreset.tests/integration/test_save_load_round_trip.gd— strippedascension_rituals/ley_anchorsreset.tests/integration/test_happiness_turn.gd— strippedascension_activefrom base_input dict. Leftascension_penaltybreakdown-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"fromscience_techs_requiredarray. The science-victory chain now ends atastral_projection.public/games/age-of-dwarves/data/setup.json:141— removed"victory_arcane_ascension": trueflag fromdefaultsblock.public/games/age-of-dwarves/data/ascension.json— DELETED (Game-2/3 victory config, 20 lines). Defined thearcane_ascensionvictory type,required_tech, and ascension threshold metadata.public/games/age-of-dwarves/data/techs/dwarven_warfare.json:439,461— removed"ascension"fromencyclopedia.tagsarrays on the two apex techs (mountain_kingunlocker andancestral_legacy). The techs themselves stay; only the magic-school tag goes.public/games/age-of-dwarves/data/achievements.json:74-83— removedhigh_archonachievement entry (referencedarchon_createdsignal — Game-3 Archon system).public/games/age-of-dwarves/data/lenses/manifest.json:23-25— removedley_lines,death_sight,life_sightentries fromincludesarray (lens definitions don't exist inresources/lenses/).public/games/age-of-dwarves/data/improvements/manifest.json:13— removedmana_wellentry.public/games/age-of-dwarves/data/deposits/manifest.json:37— removedmana_node_resourceentry.
Not found / out of scope:
anchor_schools— zero matches anywhere underdata/. Noecological_events/directory at the path the brief named (public/games/age-of-dwarves/data/ecological_events/); the existingevents/directory contains noanchor_schoolskeys. Section I no-op for this target.- Schemas (
schemas/terrain.schema.jsonmana_major/mana_minor,schemas/unit.schema.jsonschool/mana_cost,schemas/building.schema.jsonschool/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— removedpub ascension_active: boolfield fromHappinessInput.src/simulator/crates/mc-happiness/src/pool.rs:210— replaced theif input.ascension_active { config.ascension_unhappiness } else { 0 }branch with a hardcodedlet 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:1231unmodified — out of scope for this section).src/simulator/crates/mc-happiness/src/pool.rs:299,481,616— removed field from internal-testHappinessInputconstructors indefault_input(),tier_distinctness_city_heavy_scenario, andbreakdown_surfaces_modifier_triggers.src/simulator/crates/mc-happiness/src/pool.rs:355-364— renamed test fromwar_weariness_and_ascension_penaltytowar_weariness_scales_with_units_in_enemy_territory; now assertsresult.ascension_penalty == 0(Game-1 invariant) instead of the old== 2ascension-active expectation. War-weariness coverage preserved.src/simulator/crates/mc-happiness/tests/golden_age_window.rs:24,37— removed field fromcontent_input()andhappy_input().src/simulator/crates/mc-turn/tests/serde_roundtrip.rs:304— removed field fromhappiness_pool_json_roundtrip_is_stablegolden. The round-trip now reflects the smallerHappinessInputshape.
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, includinghappiness_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: 0502e8bc1 — fix(@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 theunlocks_schoollookup; 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 theschoolskey (the Rust bridgePlayerStatedoes 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_systeminturn_processor.gd:502-508is 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_anchorshelper inturn_processor_helpers.gdper the save-migration audit's reference list, and anyascension_ritualsenvelope ingame_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_poolmutate, theEventBus.mana_changed.emitcall). 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_costdictionary 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 theEventBus.item_produced.emitline (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_archonif 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 theinput_dict(line 81). - Verify and update the Rust
mc-happinessHappinessInputstruct: theascension_activefield 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). IfHappinessInputis shared with Games 2/3 (improbable inmc-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_lockedif magic- only — verify with grep,school_lockedis referenced inturn_processor.gd:152comment; 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 formana,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 fromage-of-dwarves).
G. Tests & proof scenes
src/game/engine/tests/andsrc/game/engine/scenes/tests/— grep for any GUT test or proof scene that exercisesmana_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_unusedmarkers, no commented-out blocks.
H. Rust surface
mc-happiness::HappinessInput— removeascension_active: boolfield (item D).api-gdext::GdGameState::set_players_from_dicts(if it parses theschoolskey from the bridge dict per line 202 above) — remove theschoolsparse step. Already known to be a passthrough that the simulation does not read, per the Wall 1 gap-matrix evidence.cargo check --workspacemust remain green after the strip.cargo test --workspacemust remain green (excluding the known pre-existingmc-sim/bin/solo_dominionerrors; 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, andhappiness.gdper 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-wto 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:schoolsis 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 themc-happiness::HappinessInputfield 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_activefield deleted (Rust source confirms). - ☐
EventBus.mana_changedsignal removed; no remaining listeners. - ☐
cargo check --workspacegreen. - ☐
cargo test --workspacegreen (modulo known pre-existingsolo_dominionerrors, untouched here). - ☐ GUT headless run green (
./run test:gd:headlessor apricot equivalent percanonical-commands.md). - ☐
scope-game1-vs-game2.mdcross-check: every Game 1 runtime reference to magic/ascension/leylines is absent. - ☐
p2-72a-save-format-migrationblocked_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_techto 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_techcallers. Same signature stays; only the internal school-unlock side-effect is removed. SpellSystemscript revival. Already stubbed out perturn_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+ RustHappinessInputfield 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.gdexercises 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.mdRail 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 carryingascension_active.