#!/usr/bin/env python3 """Standalone tests for the globals registry tooling. Run: py -3.11 -X utf8 tools/test_globals.py""" import os, sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import paths import globals_build as G import sys4load FAILS = [] def check(cond, msg): print((" ok " if cond else " FAIL ") + msg) if not cond: FAILS.append(msg) def test_load_and_lint(): entries, meta = G.load_toml(paths.VM_MAP / "globals.toml") check(0xa57 in entries, "0xa57 present in globals.toml") check(entries[0xa57]["category"] == "story-flag", "0xa57 is category story-flag") check(entries[0x3234]["category"] == "story-flag", "0x3234 is category story-flag") check({0xa57, 0xa58, 0xa59} <= set(entries), "Lily form flags A/B/C all present") check(entries[0x4e085]["columns"] == { "0": "current_hp", "1": "current_sp", "2": "current_fs" }, "entity current-resource columns are curated") check(entries[0x4e11b]["columns"]["10"] == "movement" and entries[0x4e11b]["columns"]["13"] == "max_fs", "entity effective-stat columns are curated") check(entries[0x5231f]["name"] == "entity_tile_x" and entries[0x52351]["name"] == "entity_tile_y", "entity map-coordinate arrays are curated") check(entries[0x4e021]["name"] == "entity_runtime_flags" and entries[0x522ed]["name"] == "entity_faction_ids", "entity activity and faction arrays are curated") check(entries[0x4e693]["name"] == "entity_skill_flags", "per-entity skill flags are curated") check(entries[0x6f70]["name"] == "unit_stat_growth_fractions" and entries[0x6f70]["type"] == "int[100][14]" and entries[0x6f70]["columns"]["13"] == "max_fs", "persistent unit stat-growth fractions are curated") check(entries[0x53e13]["name"] == "entity_carried_item_ids" and entries[0x53e77]["name"] == "entity_carried_item_counts", "per-entity carried-item slots are curated") check(entries[0xeff77]["name"] == "routine_execution_state" and entries[0xeff77]["value_domain"] == "0..3", "movement routine result states are curated") check(entries[0x56b20]["name"] == "entity_patrol_waypoint_indices", "RTN_M011 waypoint state is curated") check(entries[0xaba96]["name"] == "pathfinding_remaining_route_steps" and entries[0xcc9f1]["name"] == "movement_search_mode", "movement-search reachability state is curated") check(entries[0xbf6fe]["name"] == "pathfinding_filtered_route_scores" and entries[0xcc9f4]["name"] == "offensive_action_scope_masks", "movement target filtering and offensive scope are curated") check(entries[0xb8d86]["name"] == "action_range_distance_grid" and entries[0xcc9f2]["name"] == "usable_action_min_range" and entries[0xcc9f3]["name"] == "usable_action_max_range", "action-range search state is curated") check(entries[0x66713]["name"] == "acting_entity_index" and entries[0x66714]["name"] == "target_entity_index" and entries[0x52289]["name"] == "entity_selected_action_ids", "AI actor, target, and selected action state are curated") check(entries[0xcca08]["name"] == "offensive_action_attack_elements" and entries[0xab5ba]["name"] == "attack_element_effectiveness_percent", "AI action-element eligibility state is curated") check(entries[0xcc9fe]["name"] == "healing_action_scope_masks", "AI healing-action eligibility state is curated") check(entries[0x15a2a8]["name"] == "current_glossary_topic_id" and entries[0x15a2a9]["name"] == "glossary_topic_unlock_seen_decision_ids" and entries[0x463b]["name"] == "glossary_topic_titles", "VIINIT/VIMES glossary state is curated") check(entries[0x15a759]["name"] == "current_enemy_encyclopedia_unit_id" and entries[0x56b85]["name"] == "enemy_encyclopedia_revealed_flags", "EBINIT/EIMES enemy-encyclopedia state is curated") check(entries[0x15a117]["name"] == "current_character_profile_id" and entries[0x15a118]["name"] == "character_profile_unit_ids" and entries[0x15a17c]["name"] == "character_profile_portrait_asset_ids" and entries[0x45d7]["name"] == "character_profile_names", "CIINIT/CIMES character-profile state is curated") check(entries[0x1560e7]["name"] == "current_magic_action_id" and entries[0x45b9]["name"] == "magic_action_names" and entries[0x1561f6]["name"] == "magic_action_information_handler_script_ids", "MAINIT/MAMES action state is curated") check(entries[0x453b]["name"] == "training_action_text" and entries[0x155bbc]["name"] == "training_action_required_story_flag_ids" and entries[0x155e9b]["columns"]["13"] == "max_fs" and entries[0x156015]["columns"]["9"] == "execution_10" and entries[0x6722]["name"] == "familiar_alignment" and entries[0x6727]["name"] == "training_action_execution_counts", "TRINIT/TRAIN action state is curated") check(entries[0x152485]["name"] == "current_card_generation_list_id" and entries[0x152486]["columns"]["2"] == "growth_weight" and entries[0x1525b2]["name"] == "card_generation_card_ids" and entries[0x1519f8]["name"] == "current_card_id" and entries[0x151a5d]["columns"]["2"] == "engine_dead_required_flag_3" and entries[0x204f4]["name"] == "current_stage_turn", "CDINIT/FIELD card-generation state is curated") check(entries[0x1519f9]["name"] == "card_definition_type_ids" and entries[0x151de1]["columns"]["2"] == "fs" and entries[0x15222d]["name"] == "card_definition_maximum_resource_damage" and entries[0x152359]["name"] == "card_definition_condition_ids" and entries[0x152421]["name"] == "card_definition_visual_asset_ids" and entries[0x4dfbb]["name"] == "stage_card_spendable_point_bonus", "CDINIT2/FIELD card-effect state is curated") check(entries[0x15288c]["name"] == "selected_battle_animation_id" and entries[0x15288e]["columns"]["4"] == "reserved_effect_slot_4" and entries[0x153ffe]["name"] == "battle_animation_effect_start_delays_ms" and entries[0x15576e]["name"] == "battle_animation_duration_ms" and entries[0x155b5c]["name"] == "battle_effect_visual_mode_ids" and entries[0x155b7a]["name"] == "battle_effect_atlas_row_counts" and entries[0x155baa]["columns"]["2"] == "pulse_3", "BTANINIT/BTANINIT2 battle-animation state is curated") check(entries[0x27bd]["name"] == "stage_display_names" and entries[0x2ba5]["columns"]["5"] == "cleared_line_3" and entries[0xe7e8d]["name"] == "stage_unlock_group_ids" and entries[0xe8275]["name"] == "stage_main_progression_flags" and entries[0xe865d]["columns"]["6"] == "forbidden_flag_7" and entries[0xea1b5]["columns"]["6"] == "required_flag_7", "STINIT2 stage text and availability state is curated") check(entries[0xebd0d]["name"] == "stage_display_number_major" and entries[0xec0f5]["name"] == "stage_display_number_minor" and entries[0xed47d]["name"] == "stage_minimap_atlas_origin_y" and entries[0xed865]["name"] == "stage_clear_base_spendable_point_rewards" and entries[0xedc4d]["name"] == "stage_authoring_difficulty_tiers", "STINIT2 numbering, minimap, points, and authoring tiers are curated") check(entries[0x3239]["columns"] == { "0": "primary_surface_slot", "1": "alternate_surface_slot", "2": "transition_surface_slot", } and entries[0x62424]["name"] == "adv_gfx_resource_id" and entries[0x62450]["name"] == "adv_gfx_layer_index" and entries[0x62452]["name"] == "adv_gfx_surface_slot_work" and entries[0x62455]["name"] == "adv_gfx_object_handles", "ADV graphics layer slots and companion work globals are curated") check(entries[0xee035]["columns"] == {"0": "entry", "1": "clear", "2": "failure"} and entries[0xeebed]["name"] == "stage_extra_dungeon_flags" and entries[0xeefd5]["columns"]["2"] == "gold_coin_item_93" and entries[0xefb8d]["name"] == "stage_loader_script_ids", "STINIT2 flow, EX, coin, and loader columns are curated") check(entries[0x15a095]["name"] == "information_tab_index" and entries[0x15a096]["name"] == "information_message_handled" and entries[0x15a097]["name"] == "information_message_handler_script_ids", "INFOMES handler registry state is curated") check(entries[0x7db]["name"] == "modal_message_lines" and entries[0x665d6]["name"] == "modal_message_line_count" and entries[0x7e5]["name"] == "modal_annotation_texts" and entries[0x665e3]["name"] == "modal_annotation_horizontal_cells", "MES modal-buffer state is curated") check(entries[0x5f0ed]["name"] == "scene_decision_seen_flags", "glossary prerequisite seen-state is curated") check(entries[0x20543]["name"] == "tile_faction_traversal_masks", "faction-specific tile traversal masks are curated") check(entries[0xaba64]["name"] == "stage_object_runtime_flags", "stage-object targeting flags are curated") check(entries[0xb240e]["name"] == "pathfinding_movement_costs", "movement-cost work grid is curated") check(entries[0xc6077]["name"] == "selected_movement_route_steps" and entries[0xc6077]["type"] == "int[1000][27]" and entries[0xccbcf]["name"] == "cardinal_tile_delta_x" and entries[0xccbd4]["name"] == "cardinal_tile_delta_y" and entries[0xccc0a]["name"] == "map_target_tile_x" and entries[0xccc0b]["name"] == "map_target_tile_y", "selected movement route grid and coordinate helpers are curated") check(entries[0x15261e]["name"] == "battle_actor_hp_recovery" and entries[0x15261f]["name"] == "battle_triggered_passive_skill_flags" and entries[0x15261f]["type"] == "int[2][300]", "battle recovery output and triggered-passive matrix are curated") # lint clean against a permissive address universe (curated addrs are self-consistent) errors, warnings = G.lint(entries, set(entries)) check(errors == [], f"globals.toml lints clean (errors={errors})") def test_lint_catches_bad_vocab(): bad = {0x1: {"_addr": 0x1, "name": "x", "category": "bogus", "source": "auto-shape", "confidence": "high", "columns": {"not-an-index": "x", "-1": ""}}} errors, _ = G.lint(bad, {0x1}) check(any("category" in e for e in errors), "lint flags bad category") check(any("confidence" in e for e in errors), "lint flags auto-shape claiming high confidence") check(any("column index" in e for e in errors), "lint flags nonnumeric column indices") check(any("negative column" in e for e in errors), "lint flags negative column indices") def test_adv_layer_surface_slot_evidence(): scene = sys4load.load(paths.scripts()["SC0000.BIN"]) expected = { 0x3239 + row * 3 + column: value + row for row in range(8) for column, value in enumerate((4, 43, 51)) } initialized = { instruction.args[0][1]: instruction.args[1][1] for instruction in scene.instructions if 0x125bd <= instruction.offset <= 0x12630 and len(instruction.args) == 2 and instruction.args[0][0] == 3 and instruction.args[0][1] in expected and instruction.args[1][0] == 0 } check(initialized == expected, "SC0000 initializes all eight ADV layer surface-slot triplets") table_lookups = [ instruction.args for instruction in scene.instructions if (3, 0x3239) in instruction.args and (0, 3) in instruction.args ] static_columns = { args[-1][1] for args in table_lookups if args[-1][0] == 0 } check(static_columns == {0, 1, 2}, "SC0000's common graphics paths read all three surface-slot columns") init2 = sys4load.load(paths.scripts()["INIT2.BIN"]) handle_writes = { instruction.args[0][1]: instruction.args[1][1] for instruction in init2.instructions if len(instruction.args) == 2 and instruction.args[0][0] == 3 and 0x62455 <= instruction.args[0][1] <= 0x6245d and instruction.args[1][0] == 0 } expected_handles = [ 0xcb20, 0xcb2a, 0xcb8e, 0xcb98, 0xcba2, 0xcbac, 0xcbb6, 0xcbc0, 0xcf08, ] check([handle_writes.get(0x62455 + index) for index in range(9)] == expected_handles, "INIT2 seeds the nine ADV retained-object handles") def test_selected_movement_route_grid_evidence(): target = 0xc6077 refs = [] clears = [] loaded = {} for name, path in paths.scripts().items(): if path.read_bytes()[:8] != b"SYS4422 ": continue script = sys4load.load(path) loaded[name] = script for index, instruction in enumerate(script.instructions): if not any(arg[1] == target for arg in instruction.args): continue refs.append((name, instruction)) if instruction.opcode == 0x6c: clears.append((script, index, instruction)) check(len(refs) == 56 and sum(instruction.opcode == 0x12c for _, instruction in refs) == 51 and len(clears) == 5 and len({name for name, _ in refs}) == 21, "selected route grid has the exact 51 lookups and five clears in 21 scripts") clear_geometry_ok = True for script, index, instruction in clears: previous = script.instructions[index - 1] clear_geometry_ok &= ( previous.label == "mul" and previous.args[1:] == [(0, 1000), (0, 27)] and instruction.args[1] == previous.args[0] ) check(clear_geometry_ok, "every selected route-grid clear covers the reserved 1000 by 27 cells") providers = { name for name, instruction in refs if name.startswith("RTN_M") and instruction.opcode == 0x12c } check(providers == {f"RTN_M{provider:03d}.BIN" for provider in range(2, 19)}, "all seventeen RTN_M002..RTN_M018 providers test selected route endpoints") setroute = loaded["SETROUTE.BIN"].instructions copy_pairs = 0 for index in range(len(setroute) - 2): first, second, third = setroute[index:index + 3] if (first.opcode == 0x12c and (3, 0xaba96) in first.args and second.opcode == 0x12c and (3, target) in second.args and third.label == "mov" and third.args == [(12, 1), (12, 0)]): copy_pairs += 1 check(copy_pairs == 2, "SETROUTE copies flood-fill scores into the destination and each traced route tile") init2 = loaded["INIT2.BIN"] footer_refs = { instruction.args[0][1]: instruction.args[1][1] for instruction in init2.instructions if instruction.opcode == 0x64 and instruction.args[0] in {(3, 0xccbcf), (3, 0xccbd4)} } vectors = {} for address, offset in footer_refs.items(): count = init2.dwords[offset] vectors[address] = [ value if value < 0x80000000 else value - 0x100000000 for value in init2.dwords[offset + 1:offset + 1 + count] ] check(vectors == { 0xccbcf: [0, 0, -1, 0, 1], 0xccbd4: [0, 1, 0, -1, 0], }, "INIT2 defines the no-move plus four-cardinal-neighbor vectors") def test_battle_triggered_passive_skill_flags_evidence(): target = 0x15261f recovery = 0x15261e refs = [] recovery_refs = [] loaded = {} for name, path in paths.scripts().items(): if path.read_bytes()[:8] != b"SYS4422 ": continue script = sys4load.load(path) loaded[name] = script for index, instruction in enumerate(script.instructions): if (3, target) in instruction.args: refs.append((name, script, index, instruction)) if (3, recovery) in instruction.args: recovery_refs.append((name, instruction)) check({name for name, _, _, _ in refs} == {"BTL.BIN", "CALCDMG.BIN"} and len(refs) == 40 and sum(instruction.opcode == 0x12c for _, _, _, instruction in refs) == 37 and sum(instruction.opcode == 0x6c for _, _, _, instruction in refs) == 3, "triggered-passive matrix has exactly 37 lookups and three clears in BTL/CALCDMG") clears_cover_both_rows = True for _, script, index, instruction in refs: if instruction.opcode != 0x6c: continue previous = script.instructions[index - 1] clears_cover_both_rows &= ( previous.label == "mul" and previous.args[1:] == [(0, 2), (0, 300)] and instruction.args[1] == previous.args[0] ) check(clears_cover_both_rows, "every triggered-passive clear covers both reserved 300-skill side rows") calcdmg = loaded["CALCDMG.BIN"].instructions eligibility_columns = { instruction.args[-1][1] for index, instruction in enumerate(calcdmg[:-1]) if 0x1f1 <= instruction.offset <= 0x3b9 and instruction.opcode == 0x12c and instruction.args[1] == (9, 0xf) and instruction.args[3] == (0, 300) and calcdmg[index + 1].label == "mov" and calcdmg[index + 1].args[1] == (0, 1) } check(eligibility_columns == set(range(28, 51)) - {44}, "CALCDMG seeds the actor/target eligibility surface for shipped passive skill ids") category_gate = [ instruction for instruction in calcdmg if instruction.offset == 0x4d6 ] dynamic_writes = { instruction.offset for instruction in calcdmg if instruction.opcode == 0x12c and instruction.args[1] == (3, target) and instruction.args[3] == (0, 300) and instruction.args[-1] == (9, 8) } check(len(category_gate) == 1 and category_gate[0].args == [ (12, 0), (3, 0xa6f86), (9, 8) ] and dynamic_writes == { 0x52e, 0x57c, 0x5d3, 0x612, 0x62a, 0x65b, 0x688, 0x6bd, 0x725, 0x799, 0x7b5, 0x7fb, 0x817, 0x85d, }, "CALCDMG gates category-4 skills then filters the side/skill activation cells") btl_dynamic_consumers = { instruction.offset for instruction in loaded["BTL.BIN"].instructions if instruction.opcode == 0x12c and instruction.args[1] == (3, target) and instruction.args[3] == (0, 300) and instruction.args[-1] == (9, 0x12) } check(btl_dynamic_consumers == {0x1b8c, 0x2270, 0x2319}, "BTL indexes triggered flags by equipped skill for icons and both-side animations") check({name for name, _ in recovery_refs} == {"BTL.BIN", "CALCDMG.BIN"} and len(recovery_refs) == 20 and any(instruction.offset == 0xa3f and instruction.label == "div" and instruction.args == [ (3, recovery), (3, 0x15261d), (0, 2) ] for name, instruction in recovery_refs if name == "CALCDMG.BIN") and any(instruction.offset == 0x943 and instruction.label == "add" and instruction.args[-1] == (3, recovery) for name, instruction in recovery_refs if name == "BTL.BIN"), "CALCDMG computes actor recovery and BTL adds it to the acting entity's HP") def test_unit_stat_growth_fractions_evidence(): target = 0x6f70 expected_offsets = { "ADDEXP.BIN": {0x1ea, 0x200, 0x212, 0x241, 0x24c, 0x263, 0x2a0}, "EVOLVE.BIN": {0xa2a, 0xa3a}, "GAMECLEAR.BIN": {0x331, 0x341, 0x417}, "GAMESTART.BIN": {0x942, 0xac5}, "TRAIN.BIN": {0x901, 0x917, 0x929, 0x958, 0x963, 0x975, 0x9a5, 0x9db}, "UNITECH.BIN": {0x403, 0x40e, 0x420, 0x44f, 0x45a}, } refs = [] loaded = {} for name, path in paths.scripts().items(): if path.read_bytes()[:8] != b"SYS4422 ": continue script = sys4load.load(path) loaded[name] = script refs.extend( (name, instruction) for instruction in script.instructions if (3, target) in instruction.args ) actual_offsets = {} for name, instruction in refs: actual_offsets.setdefault(name, set()).add(instruction.offset) check(actual_offsets == expected_offsets and len(refs) == 27 and all(instruction.opcode == 0x12c and instruction.args[3] == (0, 14) for _, instruction in refs), "stat-growth fractions have exactly 27 stride-14 lookups in six scripts") instructions = { name: {instruction.offset: instruction for instruction in loaded[name].instructions} for name in expected_offsets } growth_paths = { "ADDEXP.BIN": { "source": (0x1f5, 0x7e5e6), "add": 0x20b, "divide": 0x21d, "current": 0x224, "modulo": 0x257, }, "TRAIN.BIN": { "source": (0x90c, 0x155e9b), "add": 0x922, "divide": 0x934, "current": 0x93b, "modulo": 0x96e, }, "UNITECH.BIN": { "source": (0x3f1, 0x7e5e6), "add": 0x419, "divide": 0x42b, "current": 0x432, "modulo": 0x465, }, } growth_mechanics_ok = True for name, path in growth_paths.items(): script = instructions[name] source_offset, source_address = path["source"] growth_mechanics_ok &= ( script[source_offset].opcode == 0x12c and script[source_offset].args[1] == (3, source_address) and script[path["add"]].label == "add" and script[path["divide"]].label == "div" and script[path["divide"]].args[-1] == (0, 100) and script[path["current"]].opcode == 0x12c and script[path["current"]].args[1] == (3, 0x69f8) and script[path["modulo"]].label == "mod" and script[path["modulo"]].args[-1] == (0, 100) ) check(growth_mechanics_ok, "level-up, training, and catch-up growth carry hundredths into current stats") persistence_ok = all( instructions["GAMESTART.BIN"][offset + 0xb].label == "string-lookup-set" for offset in expected_offsets["GAMESTART.BIN"] ) for name, first, second, copy in ( ("GAMECLEAR.BIN", 0x331, 0x341, 0x351), ("EVOLVE.BIN", 0xa2a, 0xa3a, 0xa4a), ): script = instructions[name] persistence_ok &= ( script[first + 0xb].opcode == 0x63 and script[second + 0xb].opcode == 0x63 and script[copy].opcode == 0x1b0 and script[copy].args[-1] == (0, 14) ) persistence_ok &= ( instructions["GAMECLEAR.BIN"][0x422].opcode == 0x1a2 ) check(persistence_ok, "save/load, game-clear, and evolution preserve the complete fractional rows") def test_merge_precedence(): curated, _ = G.load_toml(paths.VM_MAP / "globals.toml") auto = G.load_auto(paths.BUILD / "global-var-map.json") merged = G.merge(curated, auto) check(merged[0xa57]["name"] == "lily_form_a", "curated 0xa57 name wins over auto label") check(merged[0xa57]["category"] == "story-flag", "curated 0xa57 category overrides auto string-table") check(merged[0xa57]["provenance"] == "curated", "0xa57 marked curated") check(merged[0x9f541]["columns"]["8"] == "critical_chance", "curated row-table column semantics survive the merge") # an address only in the auto map falls through as provenance=auto auto_only = next((a for a in auto.get("globals", {}) if int(a, 16) not in curated and auto["globals"][a].get("label")), None) check(auto_only is not None and merged[int(auto_only, 16)]["provenance"] == "auto", "auto-only address retained with provenance=auto") def test_sys4load_labels_from_registry(): import importlib, sys4load importlib.reload(sys4load) # re-run _load_global_labels against current build/globals.json lbl = sys4load.GLOBAL_LABELS.get(0x3234, "") check("chapter_mode" in lbl, f"sys4load labels 0x3234 with curated name (got {lbl!r})") lbl2 = sys4load.GLOBAL_LABELS.get(0xa57, "") check("lily_form_a" in lbl2, f"sys4load labels 0xa57 with curated name (got {lbl2!r})") def test_miner_finds_known_flags(): import story_flags cands = story_flags.mine() check(0x3234 in cands, "miner surfaces chapter flag 0x3234") check(set(range(1, 9)) <= set(cands[0x3234]["consts"]), "0x3234 compared against 1..8 enum") check(cands[0x3234]["category"] == "story-flag", "0x3234 classified story-flag") check(0xa57 in cands, "miner surfaces Lily form flag 0xa57") check(cands[0xa57]["reach_scenes"] >= 70, "0xa57 high scene reach") check(cands[0xa57]["category"] == "story-flag", "0xa57 classified story-flag") def test_bootstrap_is_additive_and_idempotent(): import tempfile, pathlib, story_flags # Minimal fixture (one curated entry) so bootstrap always has candidates to add, independent # of how many the real globals.toml already holds. src = ('[meta]\nnote = "fixture"\n\n[[global]]\naddress = "0xa57"\nname = "lily_form_a"\n' 'category = "story-flag"\ntype = "int"\nvalue_domain = "{0,1}"\nusage = "curated"\n' 'source = "investigation"\nconfidence = "high"\ndepends_on = []\n') with tempfile.TemporaryDirectory() as d: tp = pathlib.Path(d) / "globals.toml" tp.write_text(src, encoding="utf-8") before, _ = G.load_toml(tp) cands = story_flags.mine() story_flags.bootstrap(cands, toml_path=tp) after, _ = G.load_toml(tp) check(len(after) > len(before), "bootstrap adds new skeleton entries") check(after[0xa57]["name"] == "lily_form_a", "bootstrap preserves curated 0xa57") # idempotent: second run adds nothing n1 = len(after) story_flags.bootstrap(story_flags.mine(), toml_path=tp) after2, _ = G.load_toml(tp) check(len(after2) == n1, "second bootstrap is a no-op (idempotent)") # every skeleton is auto-shape and not high-confidence added = set(after) - set(before) check(all(after[a]["source"] == "auto-shape" for a in added), "skeletons are source=auto-shape") check(all(after[a]["confidence"] != "high" for a in added), "skeletons never high confidence") if __name__ == "__main__": test_load_and_lint() test_lint_catches_bad_vocab() test_adv_layer_surface_slot_evidence() test_selected_movement_route_grid_evidence() test_battle_triggered_passive_skill_flags_evidence() test_unit_stat_growth_fractions_evidence() test_merge_precedence() test_sys4load_labels_from_registry() test_miner_finds_known_flags() test_bootstrap_is_additive_and_idempotent() print(f"\n{len(FAILS)} failures") sys.exit(1 if FAILS else 0)