{"kind":"godot_contract","contract_version":"v0.5.1","api_version":"0.16.23","client_role":"visual_input_client_only","server_authoritative":true,"client_must_not_decide":["movement legality","combat outcome","supply state","resource spending","victory status","objective completion or scoring","turn resolution"],"godot_source_version":"0.5.1","godot_scene_entrypoint":"res://scenes/GameRoot.tscn","godot_source_modules":["api_client.gd","asset_manifest.gd","demo_state_loader.gd","game_state.gd","map_renderer.gd","scene_registry.gd","input_controller.gd","tooltip_panel.gd","hex_tile.gd","edge_marker.gd","unit_marker.gd","city_marker.gd","overlay_marker.gd","camera_controller.gd","order_builder.gd","debug_logger.gd","order_preview_controller.gd","replay_player.gd"],"render_foundation":{"scope":"map_state_render_preview","loads_from":"GET /api/matches/{match_id}/state","renders":["tiles","edges","units","cities","selection_overlay","supply_pips","status_panel"],"does_not_do":["rule decisions","combat resolution","resource booking","victory checks"]},"web_export_shell":{"scope":"browser_export_slot_ready","export_preset":"Web","export_path":"web/public/game/index.html","manifest_path":"web/public/game/manifest.json","fallback_mode":"placeholder_until_real_godot_export","smoke_flags":["GODOT_EXPORT_PRESET_OK","GODOT_WEB_SLOT_OK","GAME_SHELL_OK"]},"api_load_demo":{"scope":"godot_source_loads_live_api_and_renders_demo_state","version":"0.5.1","load_sequence":["GET /api/contracts/godot","GET /api/maps/tutorial_valley","GET /api/maps/bridgefall_1v1","POST /api/matches","GET /api/matches/{match_id}/state"],"interactive_demo_actions":["reload_contract_map_match","submit_demo_move","submit_preview_or_demo_move","load_replay"],"source_files":["api_client.gd","asset_manifest.gd","game_state.gd","game_root.gd","map_renderer.gd","order_builder.gd","replay_player.gd"],"smoke_flags":["GODOT_API_LOAD_SOURCE_OK","GODOT_DEMO_ACTIONS_SOURCE_OK"],"available_demo_maps":["tutorial_valley","bridgefall_1v1"],"does_not_do":["client-side rule validation as authority","combat resolution","resource booking","victory decisions"]},"visual_seed_assets":{"scope":"godot_imported_seed_assets_for_map_rendering","version":"0.5.1","asset_manifest":"client-godot/assets/ASSET_MANIFEST.json","minimum_png_assets":80,"preview_sheet":"client-godot/assets/previews/hexfall_visual_seed_preview_sheet_01.png","renderer_source_files":["asset_manifest.gd","map_renderer.gd"],"renders_with_assets":["terrain textures","edge markers","unit tokens","building markers","selection overlays","supply pips"],"smoke_flags":["VISUAL_SEED_ASSET_MANIFEST_OK","VISUAL_SEED_PNG_COUNT_OK","GODOT_ASSET_RENDERER_SOURCE_OK"],"does_not_do":["client-side rule validation as authority","final art lock","combat resolution","supply computation"]},"premium_vertical_slice_assets":{"scope":"premium visual candidate assets for first hexmap demo","version":"0.5.1","status":"production_candidate_not_final_art_lock","asset_manifest":"client-godot/assets/ASSET_MANIFEST.json","separate_manifest":"client-godot/assets/ASSET_MANIFEST.premium_v0_4_6.json","classified_manifest":"client-godot/assets/ASSET_MANIFEST.classified_v0_4_5.json","minimum_premium_png_assets":35,"quality_mode":"premium_first_with_seed_fallback","renders_with_assets":["premium terrain","premium Kronenbund unit tokens","premium overlays","premium edge markers","premium buildings"],"smoke_flags":["PREMIUM_ASSETS_OK","PREMIUM_ASSET_MANIFEST_OK","GODOT_PREMIUM_RENDERER_OK"],"does_not_do":["final art lock","client-side rule validation as authority","combat resolution","resource booking"]},"first_hexmap_demo":{"scope":"browser first-view premium hexmap fallback and live-api demo","version":"0.5.1","demo_file":"client-godot/demo/premium_hexmap_demo.json","scene":"client-godot/scenes/GameRoot.tscn","loader":"client-godot/scripts/demo_state_loader.gd","fallback_reason":"shown when /api/contracts/godot or live match boot fails","supports":["offline premium first view","live api reload","unit selection","move preview intent","server-authority messaging"],"smoke_flags":["GODOT_FIRST_HEXMAP_DEMO_OK","GODOT_API_BOOT_RESILIENCE_OK"],"does_not_do":["movement legality","turn resolution","combat resolution","supply computation","victory decisions"]},"first_view_polish":{"scope":"premium demo is default first view with live api as explicit user action","version":"0.5.1","default_mode":"premium_hexmap_demo","live_api_mode":"manual_button","canonical_api_paths":["/api/contracts/godot","/api/maps/tutorial_valley","/api/matches"],"fallback_aliases":["/contracts/godot","/contracts"],"ui_nodes":["ModePill","MetricsPanel","HelpPanel","LiveApiButton"],"camera":"fit_to_bounds_after_render","supports":["immediate first map view","background api contract probe","explicit live api boot","map framing","metrics panel","less intrusive api errors"],"smoke_flags":["GODOT_FIRST_VIEW_POLISH_OK","GODOT_API_PREFIX_PRIMARY_OK","GODOT_CAMERA_FIT_SOURCE_OK"],"does_not_do":["movement legality","turn resolution","combat resolution","supply computation","victory decisions"]},"server_move_options":{"scope":"backend-calculated move target hints for selected Godot units","version":"0.6.1","endpoint":"GET /api/matches/{match_id}/units/{unit_id}/move-options?player_id={player_id}","response_kind":"unit_move_options","returns":["source_tile_id","movement_remaining","legal_targets","blocked_targets","server_authoritative","final_validation"],"godot_usage":["fetch after live unit selection","render allowed target hints","show blocked reason from server hint","submit still goes through orders + resolve"],"smoke_flags":["SERVER_MOVE_OPTIONS_API_OK","GODOT_SERVER_MOVE_HINTS_OK","MOVE_OPTIONS_AUTHORITY_OK"],"does_not_do":["client-side movement calculation","state mutation","turn resolution","combat resolution","replacement for resolve validation"]},"api_endpoint_diagnostics":{"scope":"stable browser/API boot diagnostics and status endpoint compatibility","version":"0.6.2","canonical_status_endpoint":"GET /api/status","legacy_health_endpoint":"GET /api/health","runtime_debug_endpoint":"GET /api/debug/runtime","godot_contract_endpoints":["GET /api/contracts/godot","GET /contracts/godot"],"response_requirements":["application/json content","kind field","contract_version where applicable","no HTML fallback accepted as JSON"],"godot_usage":["ringbuffer last API calls","show URL/status/content-type/body-prefix on JSON parse failure","keep premium demo fallback non-fatal"],"smoke_flags":["API_STATUS_ENDPOINT_OK","API_RUNTIME_DEBUG_OK","GODOT_API_TRACE_SOURCE_OK","API_HTML_FALLBACK_GUARD_OK"],"does_not_do":["secrets disclosure","database dump","client-side gameplay authority","replacement for /api/health"]},"godot_web_contract_bootstrap":{"scope":"use compact uncompressed boot contract for browser-exported Godot client","version":"0.6.3","endpoint":"GET /api/contracts/godot/bootstrap","alias":"GET /contracts/godot/bootstrap","response_kind":"godot_contract_bootstrap","godot_usage":["Live API boot loads bootstrap first","HTTPRequest.accept_gzip disabled to avoid Web decompress failures","full contract remains available for docs and smoke only"],"smoke_flags":["GODOT_BOOTSTRAP_CONTRACT_OK","GODOT_HTTPREQUEST_NO_GZIP_OK","GODOT_BOOT_USES_BOOTSTRAP_OK"],"does_not_do":["remove full contract endpoint","client-side gameplay authority","hide backend errors"]},"godot_live_default_move":{"scope":"auto-start Live API match when backend is reachable so target clicks send orders by default","version":"0.6.4","scene":"client-godot/scenes/GameRoot.tscn","client_scripts":["game_root.gd","api_client.gd","map_renderer.gd","order_builder.gd"],"startup_flow":["render premium demo immediately","deferred Live API boot","GET /api/status","GET /api/contracts/godot/bootstrap","GET /api/maps/tutorial_valley","POST /api/matches","selected unit requests server move-options"],"guards":["live_boot_in_flight prevents double boot","live_mode_ready prevents duplicate match creation","offline tile click starts live boot instead of silently staying offline","move submit is blocked while live boot is in flight"],"smoke_flags":["GODOT_LIVE_DEFAULT_BOOT_OK","GODOT_LIVE_MOVE_SEND_GUARD_OK","GODOT_OFFLINE_CLICK_AUTOSTART_OK"],"does_not_do":["client-side movement legality","turn resolution","combat resolution","resource booking","victory decisions"]},"move_ux_reason_hints":{"scope":"make the working v0.6 move loop playable with clear unit ownership, movement points and server rejection reasons","version":"0.6.5","client_scripts":["game_root.gd","map_renderer.gd","order_preview_controller.gd","game_state.gd"],"ui_feedback":["own/enemy unit summary","movement points in selection and preview","blocked target reason labels","rejected order reason banner","last replay event summary"],"server_reason_labels":["not enough movement points","unit is not owned by player","edge blocks movement","target tile is occupied","unit is not on source tile"],"authority_boundary":["Godot formats server-provided reason strings only","Godot always submits active-player move intents","server move-options remain hints","server action execution remains final validation"],"smoke_flags":["GODOT_MOVE_UX_REASON_HINTS_OK","GODOT_MOVE_UX_ACTIVE_PLAYER_INTENT_OK","GODOT_MOVE_UX_AUTHORITY_OK"],"does_not_do":["client-side movement legality","pathfinding","ownership authorization","movement point calculation","state mutation","turn resolution"]},"first_combat_loop":{"scope":"first playable server-authoritative attack preview and resolve loop","version":"0.7.0","endpoint":"GET /api/matches/{match_id}/units/{unit_id}/attack-options?player_id={player_id}","response_kind":"unit_attack_options","order_type":"attack_unit","replay_events":["combat_resolved","attack_rejected","unit_destroyed","unit_moved","city_captured"],"godot_usage":["own unit selection requests server attack-options","enemy unit click sends attack intent","server resolve produces combat_resolved","Godot animates attack pulse and applies canonical state"],"authority_boundary":["Godot shows server-provided attack hints only","combat outcome is calculated in backend engine/combat.py","POST /actions/execute executes exactly one player action","end-turn/bot-turn remain phase-advance validation"],"smoke_flags":["SERVER_ATTACK_OPTIONS_API_OK","GODOT_FIRST_COMBAT_LOOP_OK","COMBAT_AUTHORITY_OK"],"does_not_do":["client-side combat outcome","client-side attack legality","client-side strength mutation","client-side victory decisions"]},"combat_ux_result_hints":{"scope":"make combat readable with server-projected attack hints, strength badges and resolved battle result labels","version":"0.7.1","endpoint":"GET /api/matches/{match_id}/units/{unit_id}/attack-options?player_id={player_id}","client_scripts":["game_root.gd","map_renderer.gd","unit_marker.gd"],"server_hint_fields":["combat_preview","server_projection","effective_defense","attacker_remaining_strength","defender_remaining_strength"],"ui_feedback":["STR/MP badges on unit markers","attack hover projection from server hint","combat resolve result banner","localized combat outcome labels"],"authority_boundary":["backend computes projection hints","backend server action execution remains final validation","Godot formats values only and mutates no combat state"],"smoke_flags":["COMBAT_UX_RESULT_HINTS_OK","COMBAT_UX_SERVER_PROJECTION_OK","COMBAT_UX_AUTHORITY_OK"],"does_not_do":["client-side combat outcome","client-side attack legality","client-side strength mutation","client-side victory decisions"]},"combat_ux_authority_hotfix":{"scope":"rename client-side combat result label helper so authority guards do not see resolver markers in Godot UI code","version":"0.7.1.1","client_scripts":["game_root.gd","map_renderer.gd"],"authority_boundary":["Godot formats backend payloads only","no combat resolver or outcome calculation in Godot","renderer does not inspect combat_resolved event names"],"smoke_flags":["GODOT_LIVE_DEFAULT_AUTHORITY_OK","COMBAT_UX_AUTHORITY_OK"],"does_not_do":["client-side combat outcome","client-side attack legality","client-side strength mutation","client-side victory decisions"]},"godot_debug_fast_export":{"scope":"add structured Godot client diagnostics, F2 debug overlay and a faster cached Docker web export path","version":"0.7.2","endpoint":"POST /api/client-logs","client_scripts":["debug_logger.gd","game_root.gd","api_client.gd"],"client_events":["tile.clicked","unit.selected","order.preview.created","api.request.started","api.request.finished","api.request.failed","resolve.started","replay.event.received","animation.finished"],"export_scripts":["scripts/export_godot_web_fast.sh","scripts/export_godot_web_docker.sh"],"export_cache":"./.cache/godot-exporter/home","authority_boundary":["client logging is diagnostics only","debug overlay shows state but makes no gameplay decisions","fast exporter changes build pipeline only"],"smoke_flags":["GODOT_DEBUG_LOGGER_OK","CLIENT_LOG_ENDPOINT_OK","GODOT_FAST_EXPORT_SCRIPT_OK"],"does_not_do":["client-side movement legality","client-side combat outcome","resource spending","turn resolution","state mutation outside canonical server payloads"]},"turn_action_flow_ux":{"scope":"show server turn state, action state, replay timeline and explicit state refresh controls in Godot Web","version":"0.7.3","client_scripts":["game_root.gd","GameRoot.tscn"],"ui_nodes":["TurnFlowPanel","TurnText","ActionText","EventText","RefreshStateButton","ClearSelectionButton"],"state_source":"canonical server state from create_match, resolve_result and GET /api/matches/{match_id}/state","timeline_source":"server replay events and order submit lifecycle logs","supports":["visible turn/phase/player state","order in-flight status","resolve feedback","server-state refresh","selection clear","compact event timeline"],"authority_boundary":["Godot displays canonical state values only","Godot does not advance turns locally","Godot does not compute available actions","server server action execution remains final validation"],"smoke_flags":["TURN_ACTION_FLOW_UI_OK","TURN_ACTION_FLOW_STATE_REFRESH_OK","TURN_ACTION_FLOW_AUTHORITY_OK"],"does_not_do":["client-side movement legality","client-side combat outcome","resource spending","local turn advance","state mutation outside canonical server payloads"]},"debug_copy_export_ux":{"scope":"make debug data easy to copy/paste without a large blocking in-game overlay","version":"0.7.3.1","endpoints":["GET /api/client-logs/recent?format=text&limit=80","POST /api/client-logs"],"scripts":["scripts/godot_client_logs_recent.sh","scripts/collect_debug_for_chatgpt.sh"],"client_scripts":["game_root.gd","debug_logger.gd"],"ux":["F2 overlay remains hidden by default and compact","H toggles visible HUD panels for board access","copy/paste debug output is available via API and shell scripts"],"authority_boundary":["debug export is diagnostics only","HUD visibility does not alter canonical state","Godot still sends intents only and receives server decisions"],"smoke_flags":["DEBUG_COPY_EXPORT_ENDPOINT_OK","DEBUG_HUD_TOGGLE_OK","DEBUG_COPY_EXPORT_AUTHORITY_OK"],"does_not_do":["client-side movement legality","client-side combat outcome","local turn advance","resource spending"]},"first_production_build_loop":{"scope":"first playable production/build UI loop using server build-options and build_unit order resolve","version":"0.8.0","endpoint":"GET /api/matches/{match_id}/build-options?player_id={player_id}","order_type":"build_unit","replay_events":["unit_created","build_rejected","production_resolved"],"client_scripts":["game_root.gd","api_client.gd","order_builder.gd"],"ui_nodes":["ProductionPanel","ProductionText","RefreshBuildButton","BuildStandardButton","BuildFastButton","BuildDefenseButton"],"authority_boundary":["backend computes build options and costs","Godot displays server build hints and sends build_unit intent only","end-turn/bot-turn remain phase-advance validation"],"smoke_flags":["FIRST_PRODUCTION_BUILD_UI_OK","FIRST_PRODUCTION_BUILD_API_OK","FIRST_PRODUCTION_BUILD_AUTHORITY_OK"],"does_not_do":["client-side cost calculation","client-side spawn authorization","local state mutation","local turn advance"]},"supply_edge_visual_loop":{"scope":"make supply networks and edge-control readable and actionable in the Godot Web client","version":"0.8.1","endpoints":["GET /api/matches/{match_id}/supply","GET /api/matches/{match_id}/edges"],"order_type":"claim_edge","replay_events":["edge_claimed","edge_claim_rejected","supply_updated","supply_cut"],"client_scripts":["game_root.gd","api_client.gd","map_renderer.gd","order_builder.gd"],"ui_nodes":["SupplyEdgePanel","SupplyText","EdgeText","RefreshSupplyButton","ClaimEdgeButton"],"authority_boundary":["backend computes supply read model","backend validates claim_edge and mutates edge control","Godot displays server read models and sends edge claim intent only","end-turn/bot-turn remain phase-advance validation"],"smoke_flags":["SUPPLY_EDGE_VISUAL_UI_OK","SUPPLY_EDGE_VISUAL_API_OK","SUPPLY_EDGE_VISUAL_AUTHORITY_OK"],"does_not_do":["client-side supply computation","client-side edge legality","local state mutation","local turn advance"]},"scenario_victory_panel":{"kind":"scenario_victory_panel_manifest","version":"0.8.2","scope":"scenario_goal_score_victory_read_model_display","client_scripts":["game_root.gd","api_client.gd"],"scene_nodes":["VictoryPanel","VictoryText","ScoreText","RefreshVictoryButton"],"server_reads":["GET /api/matches/{match_id}/victory"],"displayed_fields":["goal","summary","score.ranked","victory.ended","victory.winner","victory.reason"],"smoke_flags":["SCENARIO_VICTORY_PANEL_UI_OK","SCENARIO_VICTORY_PANEL_API_OK","SCENARIO_VICTORY_PANEL_AUTHORITY_OK"],"does_not_do":["client-side victory decisions","score computation","capital capture evaluation","turn limit evaluation"]},"end_turn_phase_flow":{"kind":"end_turn_phase_flow_manifest","version":"0.8.3","scope":"explicit server-authoritative end-turn action and phase feedback in Godot UI","endpoint":"POST /api/matches/{match_id}/end-turn","client_scripts":["game_root.gd","api_client.gd"],"scene_nodes":["TurnFlowPanel","EndTurnButton","TurnText","ActionText","EventText"],"server_response":["resolve_result","turn_flow.previous_turn","turn_flow.next_turn","turn_flow.phase","events","state"],"replay_events":["turn_started","production_resolved","supply_updated","score_updated","turn_resolved"],"authority_boundary":["backend resolver advances the turn","Godot sends end-turn intent only","Godot renders returned turn_flow and canonical state"],"smoke_flags":["END_TURN_PHASE_FLOW_UI_OK","END_TURN_PHASE_FLOW_API_OK","END_TURN_PHASE_FLOW_AUTHORITY_OK"],"does_not_do":["client-side turn advance","client-side phase transition","client-side production","client-side supply or victory calculation"]},"tutorial_scenario_guidance":{"kind":"tutorial_scenario_guidance_manifest","version":"0.8.4","scope":"guided first playable tutorial checklist and server-side restart-friendly UX","endpoint":"GET /api/matches/{match_id}/tutorial?player_id=player_1","client_scripts":["game_root.gd","api_client.gd"],"scene_nodes":["TutorialPanel","TutorialText","RefreshTutorialButton","RestartTutorialButton"],"server_read_model":["tutorial_read_model","steps","current_step","completed_steps","hints"],"authority_boundary":["backend derives checklist from canonical state and replay","Godot displays step guidance only","Restart Tutorial creates a new server match through POST /api/matches"],"smoke_flags":["TUTORIAL_GUIDANCE_UI_OK","TUTORIAL_GUIDANCE_API_OK","TUTORIAL_GUIDANCE_AUTHORITY_OK"],"does_not_do":["client-side movement legality","client-side combat outcome","client-side resource spending","client-side turn resolution"]},"snapshot_hygiene_p0_assets":{"kind":"snapshot_hygiene_p0_assets_manifest","version":"0.8.5","scope":"clean v0.8.4 snapshot before next gameplay pass and wire premium_p0 assets with seed/legacy fallback","client_scripts":["asset_manifest.gd"],"asset_manifests":["client-godot/assets/ASSET_MANIFEST.json","client-godot/assets/_manifests/premium_p0/manifest.json"],"asset_policy":"premium_p0 first, premium v0.4.6 second, visual seed fallback last","smoke_script":"scripts/check_premium_p0_ingame_assets.sh","test_scope":["backend pytest expectation cleanup for next planning state","docker exporter contract hygiene","P0 asset manifest presence"],"smoke_flags":["PREMIUM_P0_ASSETS_OK","PREMIUM_P0_MANIFEST_OK","GODOT_P0_RENDERER_MAPPING_OK"],"does_not_do":["new faction rules","asset flood","client-side gameplay authority","auth or multiplayer work"]},"gen1_faction_data_pass":{"kind":"gen1_faction_data_pass_manifest","version":"0.9.0","scope":"server-authored Gen1 faction identities, rosters, faction names and read models","endpoints":["GET /api/factions/gen1","GET /api/matches/{match_id}/factions"],"state_section":"state.factions","content_files":["data/factions/*.json","data/units/gen1_units.json","data/rulesets/gen1.json"],"faction_ids":["kronenbund","aschhorn","myzelith","aurelisch"],"shared_unit_types":["standard","fast","defense","engineer","siege"],"authority_boundary":["backend owns faction catalog contracts","Godot may display faction identity and roster data","Godot must not apply faction bonuses or mutate faction state"],"smoke_flags":["GEN1_FACTION_DATA_PASS_OK","GEN1_FACTION_CATALOG_OK","MATCH_FACTION_READ_MODEL_OK"],"does_not_do":["faction-specific combat bonuses in resolver","new visual asset flood","multiplayer selection UI","client-side faction rules"]},"faction_ux_surface":{"kind":"faction_ux_surface_manifest","version":"0.9.1","scope":"Godot-visible Gen1 faction panel backed by server-authored faction read models","endpoints":["GET /api/factions/gen1","GET /api/matches/{match_id}/factions"],"client_scripts":["api_client.gd","game_state.gd","game_root.gd"],"scene_nodes":["FactionPanel","FactionText","RefreshFactionButton"],"display_fields":["faction_name","short_tagline","difficulty","tempo","faction_count"],"authority_boundary":["Godot fetches and displays faction read models only","backend keeps roster/catalog ownership","client does not apply bonuses or derive resolver effects"],"smoke_flags":["FACTION_UX_SURFACE_OK","FACTION_UX_CLIENT_SOURCE_OK","FACTION_UX_CONTRACT_OK"],"does_not_do":["client-side faction bonus resolution","faction-specific combat math","multiplayer faction picker","new asset flood"]},"gen1_faction_selection_seed":{"kind":"gen1_faction_selection_seed_manifest","version":"0.9.2","scope":"server-validated Gen1 faction selection seed for single-session tutorial matches","endpoints":["GET /api/factions/gen1/selection-seed","POST /api/matches with player_factions"],"client_scripts":["api_client.gd","game_root.gd"],"scene_nodes":["FactionPanel","FactionText","PrevFactionButton","NextFactionButton","PrevOpponentFactionButton","NextOpponentFactionButton","StartSelectedFactionButton"],"payload_fields":["player_factions","faction_id","opponent_faction_id"],"state_section":"state.faction_selection","faction_bonuses_not_applied":true,"authority_boundary":["backend validates selectable faction ids and duplicate active factions","backend mutates only starting faction ids before state creation","Godot sends selection intent and displays returned selection only"],"smoke_flags":["GEN1_FACTION_SELECTION_SEED_OK","FACTION_SELECTION_API_OK","FACTION_SELECTION_CLIENT_OK"],"does_not_do":["faction-specific resolver bonuses","multiplayer lobby","auth/account persistence","client-side faction rule application","new asset flood"]},"scenario_setup_seed":{"kind":"scenario_setup_seed_manifest","version":"0.9.3","scope":"server-authored Gen1 scenario setup/bootstrap read model for robust demo start","endpoints":["GET /api/scenarios/gen1/setup-seed","POST /api/matches using recommended_create_match_payload"],"client_scripts":["api_client.gd","game_root.gd","game_state.gd"],"state_section":"state.scenario_setup","payload_fields":["scenario_id","map_id","ruleset_id","player_slots","faction_selection_seed","victory_setup","recommended_create_match_payload"],"authority_boundary":["backend composes scenario setup from map/ruleset/faction data","setup seed is read-only and does not create a match","Godot may display setup and send create-match intent only"],"smoke_flags":["SCENARIO_SETUP_SEED_OK","SCENARIO_SETUP_CONTRACT_OK","SCENARIO_SETUP_CLIENT_OK"],"does_not_do":["client-side scenario rules","client-side faction bonuses","visual redesign","multiplayer lobby","auth/account persistence"]},"demo_reliability_pass":{"kind":"demo_reliability_pass_manifest","version":"0.9.4","scope":"deterministic demo reset and stale match recovery for browser demo lifecycle","endpoints":["GET /api/demo/reliability-seed","POST /api/demo/reset","GET /api/matches/{match_id}/state"],"state_section":"state.match_lifecycle","client_scripts":["api_client.gd","game_root.gd","game_state.gd"],"runtime_behaviour":["manual restart uses demo reset endpoint","stale state 404 is recovered by requesting a new canonical match","debug payload includes lifecycle generation and previous match id"],"authority_boundary":["backend creates reset match and canonical state","Godot may discard stale match id and render returned state","Godot must not reconstruct state or apply rules locally"],"smoke_flags":["DEMO_RELIABILITY_PASS_OK","DEMO_RESET_ENDPOINT_OK","DEMO_STALE_MATCH_RECOVERY_OK"],"does_not_do":["visual redesign","client-side state repair","persistent account sessions","multiplayer lobby"]},"demo_flow_contract_hardening":{"kind":"demo_flow_contract_hardening_manifest","version":"0.9.5","scope":"server-authoritative deterministic demo flow fixture: Move -> Attack -> Build -> End Turn","endpoints":["GET /api/demo/flow-contract","POST /api/demo/flow-smoke","POST /api/matches/{match_id}/actions/execute","POST /api/matches/{match_id}/end-turn"],"runtime_flow":["move_opening_unit","attack_forward_enemy","build_fast_unit","end_turn_clean"],"required_replay_events":["unit_moved","combat_resolved","unit_destroyed","unit_created","production_resolved","supply_updated","score_updated","turn_resolved"],"client_scripts":["api_client.gd"],"authority_boundary":["backend creates and resolves the flow fixture","Godot may call/read the fixture but must only submit intents and render returned state","combat/build/production/supply/score/turn advancement stay backend-owned"],"smoke_flags":["DEMO_FLOW_CONTRACT_OK","DEMO_FLOW_RUNTIME_OK","DEMO_FLOW_AUTHORITY_OK"],"does_not_do":["visual redesign","client-side rule execution","multiplayer lobby","auth/account persistence","asset changes"]},"demo_flow_ux_binding":{"kind":"demo_flow_ux_binding_manifest","version":"0.9.6","scope":"Godot guided demo binding for the server-authored Move -> Attack -> Build -> End Turn flow","consumes":["GET /api/demo/flow-contract","POST /api/demo/reset","POST /api/matches/{match_id}/actions/execute","POST /api/matches/{match_id}/end-turn"],"scene_nodes":["DemoFlowButton"],"client_scripts":["api_client.gd","game_root.gd"],"runtime_flow":["load_demo_flow_contract","reset_demo_flow_match","submit_server_step_orders","render_resolve_or_end_turn_response"],"authority_boundary":["Godot reads flow_steps and create_match_payload from the backend contract","Godot submits server-provided order intents only","Godot renders canonical resolve/end-turn payloads and replay events","Godot must not simulate movement legality, combat, build, production, supply, score or turns locally"],"smoke_flags":["DEMO_FLOW_UX_BINDING_OK","DEMO_FLOW_UX_SOURCE_OK","DEMO_FLOW_UX_CONTRACT_OK"],"does_not_do":["visual redesign","client-side rule execution","multiplayer lobby","auth/account persistence","asset changes"]},"demo_flow_guided_assertions":{"kind":"demo_flow_guided_assertions_manifest","version":"0.9.7","scope":"backend-authored guided assertions and debug review for the deterministic demo flow","endpoints":["GET /api/demo/flow-assertions","POST /api/demo/flow-review","POST /api/demo/flow-smoke"],"consumes":["demo_flow_contract_hardening","demo_flow_ux_binding"],"runtime_flow":["build_assertion_cards","execute_backend_flow_smoke","summarize_step_reviews","return_debug_summary"],"assertion_focus":["accepted_orders","required_events","unit_locations","destroyed_units","created_units","next_turn","replay_sorted"],"authority_boundary":["backend defines assertion cards from the server flow contract","backend executes runtime review and produces pass/fail results","Godot may display/log the review but must not decide assertion outcomes locally"],"smoke_flags":["DEMO_FLOW_GUIDED_ASSERTIONS_OK","DEMO_FLOW_GUIDED_REVIEW_OK","DEMO_FLOW_GUIDED_AUTHORITY_OK"],"does_not_do":["visual redesign","client-side assertion authority","client-side rule execution","multiplayer lobby","auth/account persistence","asset changes"]},"demo_telemetry_operator_debug_pack":{"kind":"demo_telemetry_operator_debug_pack_manifest","version":"0.9.8","scope":"operator debug pack and collection script for reproducible browser-demo diagnostics","endpoints":["GET /api/demo/telemetry-seed","POST /api/demo/debug-pack","GET /api/client-logs/recent?format=text&limit=80"],"script":"scripts/collect_demo_debug_bundle.sh","consumes":["demo_flow_contract_hardening","demo_flow_guided_assertions","client_log_endpoint","release_manifest_doctor"],"bundle_sections":["release_manifest","runtime_debug","demo_flow_contract","demo_flow_assertions","demo_flow_review","recent_client_events","operator_summary"],"authority_boundary":["debug pack records backend-reviewed demo flow and compact client diagnostics only","client logs are evidence only and never canonical game state","operators use the bundle for troubleshooting and session handoff"],"smoke_flags":["DEMO_TELEMETRY_OPERATOR_DEBUG_OK","DEMO_TELEMETRY_CONTRACT_OK","DEMO_TELEMETRY_AUTHORITY_OK"],"does_not_do":["visual redesign","new gameplay rules","client-side rule execution","multiplayer lobby","auth/account persistence","asset changes","secret export"]},"vertical_slice_snapshot_candidate":{"kind":"vertical_slice_snapshot_candidate_manifest","version":"0.10.5","scope":"vertical-slice snapshot readiness contract with bridgefall_1v1 player/bot hardening smoke","endpoints":["GET /api/demo/snapshot-candidate","POST /api/demo/debug-pack","POST /api/demo/flow-review","POST /api/demo/hardening-smoke"],"script":"scripts/check_vertical_slice_snapshot_candidate.sh","consumes":["release_manifest_doctor","demo_telemetry_operator_debug_pack","demo_flow_guided_assertions","demo_hardening_snapshot","snapshot_safe"],"readiness_gates":["release_version_current","runtime_version_current","server_authoritative","demo_flow_review_ok","debug_pack_ok","debug_pack_release_consistent","demo_hardening_ok"],"snapshot_command":"./scripts/snapshot_safe.sh","next_after_snapshot":"v0.10.6 Demo Polish/UX Follow-up","authority_boundary":["backend contracts decide snapshot readiness","Godot remains visual/input/debug client only","debug and client-log evidence never becomes gameplay authority","snapshot creation remains an operator action on the VPS"],"smoke_flags":["VERTICAL_SLICE_SNAPSHOT_CANDIDATE_OK","VERTICAL_SLICE_SOURCE_OK","VERTICAL_SLICE_AUTHORITY_OK"],"does_not_do":["visual redesign","client-side rule execution","multiplayer lobby","auth/account persistence","asset changes"]},"gen1_rules_balance_foundation":{"kind":"gen1_rules_balance_foundation_manifest","version":"0.10.0","scope":"centralize Gen1 rules and demo-balance parameters before larger map, bot scenario, round flow and UI polish","endpoints":["GET /api/rulesets/gen1","GET /api/rulesets/gen1/balance","GET /api/matches/{match_id}/units/{unit_id}/attack-options"],"script":"scripts/check_gen1_rules_balance_foundation.sh","consumes":["gen1_faction_data_pass","first_combat_loop","first_production_build_loop","supply_edge_visual_loop","scenario_victory_panel"],"rules_locked_for_demo":["unit strength/movement/cost","combat role modifiers","city combat modifiers","stalemate attrition","supply ranges","edge claim cost","victory score weights"],"authority_boundary":["backend data/rulesets/gen1.json is the canonical ruleset source","backend/app/engine/gen1_rules.py builds read models and combat modifiers","Godot may display balance values but must not resolve combat, supply, production or victory locally"],"next_after":["v0.10.2 deterministic demo bot","v0.10.3 logical round/phase flow","v0.10.4 demo control bar"],"smoke_flags":["GEN1_RULES_BALANCE_FOUNDATION_OK","GEN1_RULES_BALANCE_SOURCE_OK","GEN1_RULES_BALANCE_AUTHORITY_OK"],"does_not_do":["bot decisions","new map generation","visual redesign","client-side rule execution","faction-specific combat bonuses","multiplayer lobby","auth/account persistence"]},"bridgefall_1v1_demo_map":{"kind":"bridgefall_1v1_demo_map_manifest","version":"0.10.1","scope":"larger deterministic Gen1 1v1 map seed for the coming singleplayer-vs-bot demo","map_id":"bridgefall_1v1","map_file":"maps/bridgefall_1v1.json","endpoint":"GET /api/maps/bridgefall_1v1","create_match_payload":{"map_id":"bridgefall_1v1","ruleset_id":"gen1"},"tile_count":35,"edge_count":82,"players":2,"capital_tiles":{"player_1":"h_0_2","player_2":"h_6_2"},"neutral_poi":["Westbrück","Brückenfall","Ostwehr"],"script":"scripts/check_bridgefall_1v1_demo_map.sh","authority_boundary":["map content is loaded and validated by backend map_loader/map_schema","match state is created server-side from map content and Gen1 unit/rules data","Godot may render this map but must not derive movement, combat, supply or victory authority locally"],"next_after":"v0.10.2 deterministic demo bot","smoke_flags":["BRIDGEFALL_1V1_DEMO_MAP_OK","BRIDGEFALL_MAP_MODEL_OK","BRIDGEFALL_MATCH_RUNTIME_OK"],"does_not_do":["bot decisions","generated maps","visual redesign","client-side rule execution","UI control bar"]},"deterministic_demo_bot":{"kind":"deterministic_demo_bot_manifest","version":"0.10.2","scope":"server-side deterministic demo bot for bridgefall_1v1; one validated bot intent per bot-turn call","policy_id":"deterministic_bridgefall_v1","default_map_id":"bridgefall_1v1","default_bot_player_id":"player_2","endpoints":["GET /api/demo/bot-seed","POST /api/matches/{match_id}/bot-turn"],"response_kinds":["demo_bot_seed","demo_bot_policy_decision","demo_bot_turn_result"],"policy_priority_order":["attack","move","claim_edge","build"],"script":"scripts/check_demo_bot_policy.sh","authority_boundary":["bot policy emits server-side intent orders only","normal backend resolver validates and mutates final state","Godot may render bot replay events but must not choose bot actions"],"next_after":"v0.10.3 logical round/phase flow","smoke_flags":["DEMO_BOT_POLICY_OK","DEMO_BOT_API_OK","DEMO_BOT_AUTHORITY_OK"],"does_not_do":["client-side bot decisions","LLM planning","randomness","full round/phase loop","multiplayer AI"]},"logical_round_phase_flow":{"kind":"logical_round_phase_flow_manifest","version":"0.10.3","scope":"server-authoritative player turn -> bot turn -> next round read model for the first singleplayer demo","endpoints":["GET /api/demo/round-flow-seed","GET /api/matches/{match_id}/turn-flow","POST /api/matches/{match_id}/end-turn","POST /api/matches/{match_id}/bot-turn"],"response_kinds":["logical_round_phase_flow_seed","turn_flow_read_model","resolve_result","demo_bot_turn_result"],"phases":["player_planning","bot_planning","game_over"],"state_fields":["state.round","state.phase","state.active_player_id","state.active_actor","state.turn_flow"],"script":"scripts/check_logical_round_phase_flow.sh","authority_boundary":["backend owns phase transitions and writes them to match state","end-turn moves player_1 into bot_planning without client-side phase math","bot-turn resolves through the normal resolver and returns player_1/player_planning for the next round","Godot may display turn_flow but must not advance rounds locally"],"next_after":"v0.10.4 demo control bar","smoke_flags":["LOGICAL_ROUND_PHASE_FLOW_OK","ROUND_FLOW_API_OK","ROUND_FLOW_AUTHORITY_OK"],"does_not_do":["UI control bar","client-side phase transition","multiplayer scheduler","complex bot planning"]},"demo_control_bar":{"kind":"demo_control_bar_manifest","version":"0.10.4","scope":"Godot demo control surface for bridgefall_1v1: topbar, unit card, objective, bot status, event log and explicit Start/End/Bot/Sync/Restart controls","godot_scene":"client-godot/scenes/GameRoot.tscn","godot_script":"client-godot/scripts/game_root.gd","client_api_methods":["get_match_turn_flow","run_bot_turn"],"uses_backend_paths":["GET /api/matches/{match_id}/turn-flow","POST /api/matches/{match_id}/bot-turn","POST /api/demo/reset"],"default_demo_map":"bridgefall_1v1","default_bot_policy":"deterministic_bridgefall_v1","authority_boundary":["Godot displays turn_flow and bot results returned by the backend","Start/End/Bot/Sync/Restart buttons call existing backend endpoints only","Godot does not advance phase, choose bot orders, resolve combat or decide victory locally"],"next_after":"v0.10.5 demo hardening snapshot","smoke_flags":["DEMO_CONTROL_BAR_OK","DEMO_CONTROL_BAR_GODOT_SOURCE_OK","DEMO_CONTROL_BAR_AUTHORITY_OK"],"does_not_do":["new gameplay rules","client-side phase transition","client-side bot decisions","final visual redesign","multiplayer scheduler"]},"scenario_objective_pressure":{"kind":"scenario_objective_pressure_manifest","version":"0.11.1","scope":"server-authoritative bridgefall_1v1 objective pressure readmodel, objective score pressure and resolver replay events","endpoint":"GET /api/matches/{match_id}/objectives?player_id=player_1","replay_events":["objective_progressed","objective_completed"],"objectives":["capture_bridgefall_city","secure_bridge_chain","keep_supply_open","threaten_enemy_capital","capture_enemy_capital"],"client_role":"display_only_no_objective_decision","script":"scripts/check_scenario_objective_pressure.sh","action_hints":"server-authored read-model hints; clients must still submit orders to the backend","authority_boundary":["backend computes objective state from canonical match state","resolver emits objective replay events after authoritative state mutation","Godot displays objective pressure only and must not decide objective completion locally"],"does_not_do":["client-side objective completion","new map generation","new bot policy","victory replacement"]},"objective_victory_pressure":{"kind":"objective_victory_pressure_manifest","version":"0.11.1","scope":"completed scenario objectives become server-side score pressure for turn-limit victory","ruleset_field":"victory.score.objective_weight_points","score_bucket":"score.scores.{player_id}.objectives","read_models":["GET /api/matches/{match_id}/objectives?player_id={player_id}","GET /api/matches/{match_id}/victory"],"client_role":"display_score_only_no_victory_decision","script":"scripts/check_objective_victory_pressure.sh","authority_boundary":["backend computes completed objective weight from canonical state","backend converts completed objective weight into score points using ruleset data","Godot displays objective score bucket returned by the backend only"],"does_not_do":["client-side scoring","client-side victory tiebreaking","new combat rules","new production rules"]},"objective_action_hints":{"kind":"objective_action_hints_manifest","version":"0.11.2","scope":"server-authored next-action hints for bridgefall_1v1 objectives, displayed by Godot as guidance only","read_model_field":"action_hints","endpoint":"GET /api/matches/{match_id}/objectives?player_id={player_id}","hint_contract":{"kind":"objective_action_hint","action_types":["move_unit","claim_edge","attack_unit","restore_supply"],"order_preview_is_not_authorization":true,"client_must_submit_order_to_api":true},"client_role":"display_hint_only_no_order_auto_execute_no_local_validation","script":"scripts/check_objective_action_hints.sh","authority_boundary":["backend derives hints from canonical state and objective read-model data","hint order_preview is explanatory and not a validated order result","normal order endpoints still validate movement, edge claims, combat and victory","Godot displays the top hint but must not auto-execute it or mark objectives complete locally"],"does_not_do":["client-side order validation","client-side auto-move","client-side objective completion","new bot policy"]},"objective_order_candidates":{"kind":"objective_order_candidates_manifest","version":"0.11.4","scope":"server-side dry-run validation of objective action hint order previews before clients display them as submit-ready candidates and guide confirmed submit flow","endpoint":"GET /api/matches/{match_id}/objective-orders?player_id={player_id}","read_model_kind":"objective_order_candidates_read_model","candidate_kind":"objective_order_candidate","validation_path":"app.engine.orders.validate_order","client_role":"display_validated_candidates_only_no_auto_execute_no_local_validation","guided_submit_flow":"POST /api/matches/{match_id}/actions/execute after explicit user action","script":"scripts/check_objective_order_candidates.sh","authority_boundary":["backend derives candidates from canonical objective action hints and match state","backend validates each order preview with the same order validation path used by normal submits","GET /objective-orders is dry-run only and never queues or resolves orders","Godot may display valid candidates and submit one only after explicit user action"],"does_not_do":["auto queue orders","auto resolve orders without user action","client-side order validation","new bot policy"]},"objective_guided_submit_flow":{"kind":"objective_guided_submit_flow_manifest","version":"0.11.4","scope":"Godot can submit the top server-validated objective candidate after a deliberate button click","read_model":"GET /api/matches/{match_id}/objective-orders?player_id={player_id}","submit_path":"POST /api/matches/{match_id}/actions/execute","resolve_path":"","client_role":"explicit_user_submit_only_no_local_validation_no_auto_execute","script":"scripts/check_objective_guided_submit_flow.sh","authority_boundary":["backend builds and validates objective candidates before the UI exposes a submit button","Godot submits exactly one candidate order only after a user click","POST /actions/execute is the single-player action mutation path; end-turn/bot-turn advance phase","backend resolver may still reject the order; client only renders the returned result"],"does_not_do":["new order endpoint","client-side order validation","local objective completion","auto-turn bot logic"]},"objective_result_feedback":{"kind":"objective_result_feedback_manifest","version":"0.11.5","scope":"post-resolve objective, score and replay highlight feedback derived only from backend results","read_model_field":"resolve_result.objective_feedback","response_kind":"objective_result_feedback_read_model","trigger":"POST /api/matches/{match_id}/resolve","client_role":"display_backend_result_only_no_local_delta_or_rule_decision","script":"scripts/check_objective_result_feedback.sh","fields":["objective_delta","score_delta","replay_highlights","changed_players","completed_objective_ids"],"authority_boundary":["backend compares pre-resolve objective read model with post-resolve canonical state","backend attaches objective_feedback to the normal resolve_result payload","Godot displays feedback strings only and then reloads canonical state/objective/victory/replay read models","client never computes objective deltas, objective score deltas or replay relevance as authority"],"does_not_do":["new mutation endpoint","client-side objective delta calculation","client-side score calculation","local victory decision"]},"objective_guided_turn_tutorial":{"kind":"objective_guided_turn_tutorial_manifest","version":"0.11.7","scope":"server-side guided learning rail for the objective turn loop after Objective Result Feedback","endpoint":"GET /api/matches/{match_id}/objective-tutorial?player_id={player_id}","read_model_kind":"objective_guided_turn_tutorial_read_model","uses_read_models":["objectives","objective_order_candidates","replay markers"],"client_role":"display_guided_learning_path_only_no_order_or_rule_decision","script":"scripts/check_objective_guided_turn_tutorial.sh","stages":["read_objective_pressure","inspect_server_hint","confirm_candidate","submit_and_resolve","read_result_feedback","continue_turn_loop"],"authority_boundary":["backend derives the tutorial rail from canonical match state, objective read models, server-validated candidates and replay","GET /objective-tutorial is read-only and never queues or resolves orders","Godot displays headline/current stage/primary action only and still uses existing /actions/execute for single-player action mutation","client never computes objective completion, candidate validity, scoring or replay relevance as authority"],"does_not_do":["new mutation endpoint","auto-submit","auto-resolve","client-side gameplay validation","local objective completion"]},"objective_guided_turn_loop_polish":{"kind":"objective_guided_turn_loop_polish_manifest","version":"0.11.7","scope":"UI-order, button-label and event-log polish for the existing Objective Guided Turn Tutorial endpoint","endpoint":"GET /api/matches/{match_id}/objective-tutorial?player_id={player_id}","read_model_field":"turn_loop_ui","read_model_kind":"objective_guided_turn_loop_ui","client_role":"display_loop_polish_only_no_rule_decision","script":"scripts/check_objective_guided_turn_loop_polish.sh","ui_fields":["panel_order","status_line","primary_button","status_badges","loop_steps","event_log_hint"],"authority_boundary":["backend provides UI ordering and button state as read-only metadata","Godot only changes labels, status text and event-log ordering from backend payload","the existing Submit button calls /actions/execute only after explicit user action","client never validates candidates, computes objective completion or resolves turns locally"],"does_not_do":["new mutation endpoint","client-side gameplay validation","auto-submit","auto-resolve","local objective scoring"]},"first_playable_scenario_loop":{"kind":"first_playable_scenario_loop_manifest","version":"0.13.7","scope":"server-side turn-summary and playable-actions read models for a short human-vs-bot scenario loop","endpoints":{"turn_summary":"GET /api/matches/{match_id}/turn-summary?player_id={player_id}","playable_actions":"GET /api/matches/{match_id}/playable-actions?player_id={player_id}","scenario_result":"GET /api/matches/{match_id}/scenario-result?player_id={player_id}","execute_action":"POST /api/matches/{match_id}/actions/execute","end_turn":"POST /api/matches/{match_id}/end-turn","bot_turn":"POST /api/matches/{match_id}/bot-turn"},"read_models":["turn_summary_read_model","playable_actions_read_model","playable_action_submit_binding","playable_action_selection_ui","scenario_result_read_model"],"client_role":"display_server_playable_actions_and_submit_user_selected_orders_only","authority_boundary":["playable actions are backend dry-run read models","POST /actions/execute mutates one player action; end-turn/bot-turn advance the phase","the primary playable button is backend metadata and still requires explicit user click","the direct action choices are backend-provided cards and require explicit user click","Godot displays and submits user-selected orders but does not validate or score locally"],"does_not_do":["client-side rule validation","client-side mutation API","new mutation API","auto-play","client-side victory calculation","client-side bot policy"],"scenario_result_screen":{"kind":"scenario_result_screen","version":"0.13.7","endpoint":"GET /api/matches/{match_id}/scenario-result?player_id={player_id}","read_model_kind":"scenario_result_read_model","client_role":"display_server_result_overlay_only","screen_fields":["title","detail","status","ended","screen.primary_button","screen.secondary_button"],"authority_boundary":["server decides victory/defeat/draw","Godot only displays the result and offers explicit restart/replay actions"]}},"demo_first_time_player_flow":{"kind":"demo_first_time_player_flow_manifest","version":"0.11.8","scope":"read-only first-time player rail for the bridgefall_1v1 vertical slice","endpoint":"GET /api/matches/{match_id}/demo-first-time-flow?player_id={player_id}","read_model_kind":"demo_first_time_player_flow_read_model","ui_field":"first_time_ui","ui_kind":"demo_first_time_flow_ui","client_role":"display_first_time_player_flow_only_no_rule_decision","script":"scripts/check_demo_first_time_player_flow.sh","flow_steps":["start_bridgefall","read_objective_loop","play_guided_objective","read_result_feedback","end_player_turn","run_bot_turn","continue_next_round"],"asset_hooks":["objective_active","objective_completed","guided_action_ready","player_turn","bot_turn","result_feedback"],"authority_boundary":["backend derives first-time flow from canonical match state, objective tutorial, turn-flow, victory and replay","GET /demo-first-time-flow is read-only and never queues orders, resolves turns or runs bot logic","Godot displays first-time status/CTA text and still uses existing explicit user actions for mutation","client never computes objective completion, candidate validity, turn phase, bot decisions or scoring"],"does_not_do":["new mutation endpoint","auto-submit","auto-resolve","client-side bot logic","client-side objective scoring"]},"demo_hardening_snapshot":{"kind":"demo_hardening_snapshot_manifest","version":"0.10.5","scope":"snapshot-grade runtime hardening for bridgefall_1v1 player end-turn, deterministic bot turn and control-bar demo readiness","endpoints":["GET /api/demo/hardening-seed","POST /api/demo/hardening-smoke","GET /api/demo/snapshot-candidate"],"script":"scripts/check_demo_hardening_snapshot.sh","demo_map_id":"bridgefall_1v1","bot_policy_id":"deterministic_bridgefall_v1","runtime_sequence":["create bridgefall match","verify player_planning round 1","end-turn to bot_planning","run deterministic bot-turn","verify player_planning round 2"],"readiness_gates":["release_version_current","runtime_version_current","bridgefall_match_created","initial_player_phase","end_turn_to_bot_phase","bot_policy_selected_order","bot_turn_resolved","final_next_player_round","replay_events_present"],"consumes":["gen1_rules_balance_foundation","bridgefall_1v1_demo_map","deterministic_demo_bot","logical_round_phase_flow","demo_control_bar","demo_telemetry_operator_debug_pack"],"authority_boundary":["backend creates and resolves the hardening smoke match","backend selects bot orders and resolves them through the normal resolver","Godot may display the result but does not compute readiness or mutate canonical state"],"next_after":"snapshot if deploy/export/external smoke are green; then v0.10.6 Demo Polish/UX Follow-up","smoke_flags":["DEMO_HARDENING_SNAPSHOT_OK","DEMO_HARDENING_RUNTIME_OK","DEMO_HARDENING_AUTHORITY_OK"],"does_not_do":["new gameplay rules","new map content","visual redesign","client-side bot logic","multiplayer scheduler","snapshot archive creation"]},"api_boot_resilience":{"scope":"make Godot API boot robust in browser export","version":"0.5.1","canonical_base":"/api","same_origin_web_base":"window.location.origin + /api","compat_aliases":["/contracts/godot","/contracts","/maps","/matches"],"nginx_aliases":["web/nginx.conf:/contracts","web/nginx.conf:/maps","web/nginx.conf:/matches"],"fallback":"premium_hexmap_demo","smoke_flags":["GODOT_API_BASE_RESOLUTION_OK","GODOT_CONTRACT_ALIAS_OK"]},"scene_marker_foundation":{"scope":"typed_scene_markers_for_map_renderer_next_slice","version":"0.5.1","scenes":["client-godot/scenes/HexTile.tscn","client-godot/scenes/EdgeMarker.tscn","client-godot/scenes/UnitMarker.tscn","client-godot/scenes/OverlayMarker.tscn","client-godot/scenes/CityMarker.tscn"],"scripts":["hex_tile.gd","edge_marker.gd","unit_marker.gd","overlay_marker.gd","city_marker.gd","scene_registry.gd"],"smoke_flags":["GODOT_SCENE_MARKERS_OK","GODOT_SCENE_AUTHORITY_OK"],"does_not_do":["client-side rule validation as authority","final gameplay interaction","combat resolution"]},"scene_instancing":{"scope":"map_renderer_uses_typed_packed_scene_instances","version":"0.5.1","renderer":"client-godot/scripts/map_renderer.gd","registry":"client-godot/scripts/scene_registry.gd","instanced_scenes":["HexTile","EdgeMarker","UnitMarker","CityMarker","OverlayMarker"],"delegated_to_markers":["terrain texture/fallback","edge marker texture/fallback","unit token/supply pip","city marker","selection overlay"],"smoke_flags":["GODOT_SCENE_INSTANCING_OK","GODOT_RENDERER_DELEGATION_OK","GODOT_RENDERER_NO_MONOLITH_OK"],"does_not_do":["client-side rule validation as authority","combat resolution","supply computation","resource booking","victory decisions"]},"input_foundation":{"scope":"hover_selection_touch_tooltip_foundation_without_client_rule_authority","version":"0.5.1","input_controller":"client-godot/scripts/input_controller.gd","tooltip_panel":"client-godot/scripts/tooltip_panel.gd","scene":"client-godot/scenes/TooltipPanel.tscn","renderer_entrypoints":["handle_pointer_motion","handle_primary_click","clear_selection"],"signals":["pointer_world_moved","primary_world_pressed","cancel_selection_requested","hover_changed","selection_changed"],"supports":["mouse hover","left click select","right click clear","touch select","escape clear","tooltip text"],"smoke_flags":["GODOT_INPUT_FOUNDATION_OK","GODOT_HOVER_SELECTION_SOURCE_OK","GODOT_TOOLTIP_SOURCE_OK"],"does_not_do":["movement legality","combat resolution","supply computation","resource booking","victory decisions","turn resolution"]},"order_preview_foundation":{"scope":"selected_unit_plus_target_tile_creates_visual_move_preview_only","version":"0.5.1","preview_controller":"client-godot/scripts/order_preview_controller.gd","renderer_entrypoints":["lock_move_preview","set_move_preview","request_current_preview_order"],"ui_label":"PreviewText","signals":["move_preview_changed","move_preview_order_ready"],"supports":["hover target preview","click target lock","move preview overlay","preview path line","submit preview as order intent"],"preview_payload_fields":["kind","status","client_legality","server_validation_required","unit_id","from","to","order"],"smoke_flags":["GODOT_ORDER_PREVIEW_SOURCE_OK","GODOT_ORDER_PREVIEW_RENDERER_OK","GODOT_ORDER_PREVIEW_UI_OK","GODOT_ORDER_PREVIEW_AUTHORITY_OK"],"does_not_do":["movement legality","path finding authority","combat resolution","supply computation","resource booking","victory decisions","turn resolution"]},"first_playable_move_loop":{"scope":"selected_unit_target_click_submit_resolve_replay_animation","version":"0.6.0","scene":"client-godot/scenes/GameRoot.tscn","client_scripts":["game_root.gd","map_renderer.gd","order_preview_controller.gd","order_builder.gd","api_client.gd","replay_player.gd"],"live_user_flow":["Live API boot creates tutorial match","user selects a unit","user clicks a target tile","client sends move_unit order intent","server queues order and resolver validates final legality","resolve_result returns unit_moved or rejection data","Godot animates unit_moved before re-rendering canonical state","UI reports accepted/rejected status"],"requires_replay_event":"unit_moved","smoke_flags":["GODOT_FIRST_PLAYABLE_MOVE_LOOP_OK","GODOT_MOVE_LOOP_AUTHORITY_OK","GODOT_MOVE_LOOP_ANIMATION_OK"],"does_not_do":["movement legality","path finding authority","combat resolution","supply computation","resource booking","victory decisions","turn resolution"]},"godot_docker_exporter":{"kind":"godot_docker_exporter_manifest","version":"0.5.1","mode":"docker_compose_profile","service":"hexfall-godot-exporter","compose_file":"docker-compose.export.yml","dockerfile":"tools/godot-exporter/Dockerfile","host_install_required":false,"commands":["./scripts/build_godot_exporter.sh","./scripts/export_godot_web_docker.sh","./scripts/deploy.sh","./scripts/smoke.sh"],"output_dir":"web/public/game","expected_files_after_export":["index.html","*.wasm","*.pck","*.js"],"smoke_flags":["GODOT_DOCKER_EXPORTER_OK","GODOT_DOCKER_COMPOSE_OK","GODOT_EXPORT_RUNTIME_SMOKE_OK","GODOT_FIRST_VIEW_SMOKE_OK"],"authority_boundary":"Godot exports and renders only; backend remains authoritative."},"godot_export_runtime_smoke":{"scope":"verify_browser_game_slot_before_real_or_after_real_godot_web_export","version":"0.5.1","preflight_script":"scripts/godot_export_preflight.sh","export_script":"scripts/export_godot_web.sh","runtime_smoke_script":"scripts/check_godot_runtime_smoke.sh","web_manifest":"web/public/game/manifest.json","runtime_probe":"web/public/game/runtime_probe.json","allowed_status":["placeholder_runtime_ready","exported_by_godot_docker"],"godot_binary_policy":"docker_compose_exporter_required_for_real_export_no_host_install","server_authoritative":true,"smoke_flags":["GODOT_EXPORT_PREFLIGHT_OK","GODOT_RUNTIME_MANIFEST_OK","GODOT_RUNTIME_PROBE_OK","GODOT_EXPORT_RUNTIME_SMOKE_OK"],"does_not_do":["client-side rule validation as authority","combat resolution","supply computation","resource booking","victory decisions","turn resolution"]},"snapshot_foundation":{"scope":"safe_project_snapshot_without_secrets_or_runtime_db","version":"0.5.1","script":"scripts/snapshot_safe.sh","default_output_dir":"/srv/projects/_snapshots","excludes":[".env",".env.*","secrets/","data/postgres/","data/valkey/","data/backups/","client-godot/.godot/","large web export blobs"],"manifest_kind":"hexfall_snapshot_manifest","smoke_flags":["SNAPSHOT_SCRIPT_OK","SNAPSHOT_DRY_RUN_OK","SNAPSHOT_EXCLUDES_OK","SNAPSHOT_LEAK_GUARD_OK"]},"load_sequence":["GET /api/contracts/godot","GET /api/contracts/scenarios","GET /api/contracts/fixtures","GET /api/maps/{map_id}","POST /api/matches","GET /api/matches/{match_id}/state","POST /api/matches/{match_id}/actions/execute","GET /api/matches/{match_id}/replay"],"state_sections":["turn","round","phase","active_player_id","active_actor","turn_flow","players","factions","faction_selection","scenario_setup","scenario_objectives","match_lifecycle","tiles","edges","units","resources","supply","score","victory_status"],"scenario_ids":["foundation_contract_boot","tutorial_move_resolve_replay","combat_supply_edge_build_victory","tutorial_guided_first_loop","contract_error_envelope"],"golden_fixture_ids":["fixture_manifest","foundation_contract_boot","tutorial_move_resolve_replay","combat_supply_edge_build_victory","tutorial_guided_first_loop","contract_error_envelope"],"order_payload_examples":{"move_unit":{"type":"move_unit","player_id":"player_1","payload":{"unit_id":"u_p1_standard_1","from":"h_0_0","to":"h_1_0"}},"attack_unit":{"type":"attack_unit","player_id":"player_1","payload":{"attacker_id":"u_p1_standard_1","defender_id":"u_p2_standard_1","from":"h_1_0","to":"h_2_0"}},"claim_edge":{"type":"claim_edge","player_id":"player_1","payload":{"unit_id":"u_p1_standard_1","from":"h_2_0","to":"h_3_0"}},"build_unit":{"type":"build_unit","player_id":"player_1","payload":{"unit_type":"fast","tile_id":"h_0_0"}}},"replay_event_types":["turn_started","unit_moved","move_rejected","attack_rejected","combat_resolved","unit_destroyed","edge_claimed","edge_claim_rejected","supply_updated","supply_cut","city_captured","production_resolved","unit_created","build_rejected","score_updated","objective_progressed","objective_completed","match_ended","turn_resolved"],"links":{"contracts":"/api/contracts","health":"/api/health","version":"/api/version","release":"/api/release","scenario_setup_seed":"/api/scenarios/gen1/setup-seed","maps":"/api/maps","golden_fixtures":"/api/contracts/fixtures","godot_source":"client-godot/project.godot","godot_export_preset":"client-godot/export_presets.cfg","godot_web_slot":"web/public/game/index.html","snapshot_script":"scripts/snapshot_safe.sh","scene_registry":"client-godot/scripts/scene_registry.gd","scene_instancing_check":"scripts/check_godot_scene_instancing.sh","input_foundation_check":"scripts/check_godot_input_foundation.sh","order_preview_check":"scripts/check_godot_order_preview.sh","first_playable_move_loop_check":"scripts/check_first_playable_move_loop.sh","server_move_options_check":"scripts/check_server_move_options.sh","godot_export_preflight":"scripts/godot_export_preflight.sh","godot_runtime_smoke":"scripts/check_godot_runtime_smoke.sh","premium_asset_check":"scripts/check_premium_vertical_slice_assets.sh","premium_p0_asset_check":"scripts/check_premium_p0_ingame_assets.sh","gen1_faction_data_check":"scripts/check_gen1_faction_data_pass.sh","faction_ux_surface_check":"scripts/check_faction_ux_surface.sh","faction_selection_seed_check":"scripts/check_gen1_faction_selection_seed.sh","scenario_setup_seed_check":"scripts/check_scenario_setup_seed.sh","demo_flow_contract_check":"scripts/check_demo_flow_contract_hardening.sh","demo_flow_guided_assertions_check":"scripts/check_demo_flow_guided_assertions.sh","demo_telemetry_operator_debug_check":"scripts/check_demo_telemetry_operator_debug_pack.sh","vertical_slice_snapshot_candidate_check":"scripts/check_vertical_slice_snapshot_candidate.sh","gen1_rules_balance_check":"scripts/check_gen1_rules_balance_foundation.sh","demo_debug_bundle_collector":"scripts/collect_demo_debug_bundle.sh","vertical_slice_snapshot_candidate":"/api/demo/snapshot-candidate","gen1_rules_balance":"/api/rulesets/gen1/balance","first_hexmap_demo_check":"scripts/check_first_hexmap_demo.sh"}}