# Issue #1097 — Bannerlord M5 Sovereign Victory: Implementation **Date:** 2026-03-23 **Status:** Python stack implemented — game infrastructure pending ## Summary Issue #1097 is the final milestone of Project Bannerlord (#1091): Timmy holds the title of King with majority territory control through pure local strategy. This PR implements the Python-side sovereign victory stack (`src/bannerlord/`). The game-side infrastructure (Windows VM, GABS C# mod) remains external to this repository, consistent with the scope decision on M4 (#1096). ## What was implemented ### `src/bannerlord/` package | Module | Purpose | |--------|---------| | `models.py` | Pydantic data contracts — KingSubgoal, SubgoalMessage, TaskMessage, ResultMessage, StateUpdateMessage, reward functions, VictoryCondition | | `gabs_client.py` | Async TCP JSON-RPC client for Bannerlord.GABS (port 4825), graceful degradation when game server is offline | | `ledger.py` | SQLite-backed asset ledger — treasury, fiefs, vassal budgets, campaign tick log | | `agents/king.py` | King agent — Qwen3:32b, 1× per campaign day, sovereign campaign loop, victory detection, subgoal broadcast | | `agents/vassals.py` | War / Economy / Diplomacy vassals — Qwen3:14b, domain reward functions, primitive dispatch | | `agents/companions.py` | Logistics / Caravan / Scout companions — event-driven, primitive execution against GABS | ### `tests/unit/test_bannerlord/` — 56 unit tests - `test_models.py` — Pydantic validation, reward math, victory condition logic - `test_gabs_client.py` — Connection lifecycle, RPC dispatch, error handling, graceful degradation - `test_agents.py` — King campaign loop, vassal subgoal routing, companion primitive execution All 56 tests pass. ## Architecture ``` KingAgent (Qwen3:32b, 1×/day) └── KingSubgoal → SubgoalQueue ├── WarVassal (Qwen3:14b, 4×/day) │ └── TaskMessage → LogisticsCompanion │ └── GABS: move_party, recruit_troops, upgrade_troops ├── EconomyVassal (Qwen3:14b, 4×/day) │ └── TaskMessage → CaravanCompanion │ └── GABS: assess_prices, buy_goods, establish_caravan └── DiplomacyVassal (Qwen3:14b, 4×/day) └── TaskMessage → ScoutCompanion └── GABS: track_lord, assess_garrison, report_intel ``` ## Subgoal vocabulary | Token | Vassal | Meaning | |-------|--------|---------| | `EXPAND_TERRITORY` | War | Take or secure a fief | | `RAID_ECONOMY` | War | Raid enemy villages for denars | | `TRAIN` | War | Level troops via auto-resolve | | `FORTIFY` | Economy | Upgrade or repair a settlement | | `CONSOLIDATE` | Economy | Hold territory, no expansion | | `TRADE` | Economy | Execute profitable trade route | | `ALLY` | Diplomacy | Pursue non-aggression / alliance | | `RECRUIT` | Logistics | Fill party to capacity | | `HEAL` | Logistics | Rest party until wounds recovered | | `SPY` | Scout | Gain information on target faction | ## Victory condition ```python VictoryCondition( holds_king_title=True, # player_title == "King" from GABS territory_control_pct=55.0, # > 51% of Calradia fiefs ) ``` ## Graceful degradation When GABS is offline (game not running), `GABSClient` logs a warning and raises `GABSUnavailable`. The King agent catches this and runs with an empty game state (falls back to RECRUIT subgoal). No part of the dashboard crashes. ## Remaining prerequisites Before M5 can run live: 1. **M1-M3** — Passive observer, basic campaign actions, full campaign strategy (currently open; their Python stubs can build on this `src/bannerlord/` package) 2. **M4** — Formation Commander (#1096) — declined as out-of-scope; M5 works around M4 by using Bannerlord's Tactics auto-resolve path 3. **Windows VM** — Mount & Blade II: Bannerlord + GABS mod (BUTR/Bannerlord.GABS) 4. **OBS streaming** — Cinematic Camera pipeline (Step 3 of M5) — external to repo 5. **BattleLink** — Alex co-op integration (Step 4 of M5) — requires dedicated server ## Design references - Ahilan & Dayan (2019): Feudal Multi-Agent Hierarchies — manager/worker hierarchy - Wang et al. (2023): Voyager — LLM lifelong learning pattern - Feudal hierarchy design doc: `docs/research/bannerlord-feudal-hierarchy-design.md` Fixes #1097