Overview
Status: COMPLETE — Full design documented
Last updated: October 7, 2026
Lineage — Game Design Overview
Status: COMPLETE — Full design documented
Doc coverage:: COMPLETE — Full code extraction done. All mechanics verified against Java source.
Lineage is a turn-based kingdom management strategy game where players build dynasties across generations. Players manage resources, construct buildings, train armies, conduct diplomacy, research technologies, cast spells, and navigate dynastic succession — all across three distinct game modes (Local, Multiplayer, Global) with different action economies.
Game Identity
| Property | Value |
|---|---|
| play_game ID | lineage |
| skill_identifier | BLOOD_LINEAGE |
| Genre | Turn-based strategy kingdom management |
| Engine | Multi-mode (Local/Multiplayer/Global) with action economy |
| DB Schema | lineage |
North-Star Vision
vision.md is the locked-in three-mode north star (VISION LOCKED 2026-07-17): the bounded, decision-resolved Campaign (~100 turns, 3 acts), the Clash-of-Clans "Eternal Realm" (build-grow-compare, Realm Power ranking), and the Total-War "War of the Realms" scenarios (faction pick, clear objective, difficulty) — plus living named factions and a steward/advisor layer for the two on-ramps (drive the story yourself, or take recommended actions). Read it for WHERE the game is going and WHY; this index stays authoritative for WHAT exists today. The gap between them is the build backlog.
Design Pillars
- Dynastic Legacy — The ruler dies, the kingdom persists. Traits, prestige, and choices carry across generations
- Strategic Depth Over Complexity — 12 unit types, 6 terrain types, 7 resources, but each decision matters
- Three-Mode Flexibility — Solo vs friends vs competitive global, same mechanics but different pacing
- Subscriber Value — Doubled daily actions (10 vs 5) for subscribers, Royal Seals as the premium currency
Core Loop
Manage kingdom each turn (or action):
→ Build/upgrade buildings (5 categories)
→ Assign workers to resource production
→ Train and move military units (12 types)
→ Conduct diplomacy (treaties, war)
→ Research technologies (3 eras)
→ Cast spells (mana-based)
→ Handle events (random with choices)
→ Manage dynasty (heirs, marriage, education)
→ End turn → economy resolves, research progresses
→ Ruler ages, heirs grow, succession may trigger
Game Modes
| Mode | Turn Mechanic | Action Limit | World |
|---|---|---|---|
| Local | Explicit "end turn" | Unlimited | Solo vs AI kingdoms |
| Multiplayer | Simultaneous resolution | Unlimited | Shared world via ChatFriend |
| Global | Immediate actions, daily allowance | 5/day (free), 10/day (sub) spoken on entry | 1000x1000+ persistent world |
Action Economy (Global Mode)
- Daily limit: 5 actions (free) / 10 actions (subscriber), tracked by and spoken on Global entry. Royal Seals refill resets the day's counter.
- Processed: immediately by the same handlers as Local. There is no hourly ROUND queue.
- Subscriber check: (per-skill resolver). only applies the 5/10 daily cap; it does not look up a product of its own.
Resource Routing by Mode
| Mode | Resource Storage |
|---|---|
| Global | |
| Local/Multiplayer | LineageKingdom.resources map |
| Gems (all modes) | CurrencyLedger (premium currency) |
Kingdom Model
Resources (7 types)
| Resource | Purpose |
|---|---|
| Gold | Primary currency — building, recruiting, research |
| Food | Population support |
| Wood | Building material |
| Stone | Fortifications |
| Iron | Military equipment |
| Mana | Magic resource (75 base, 100 max, +5/turn regen) |
| Gems | Secondary premium currency — LINEAGE_GEMS on the cross-game CurrencyLedger (ledger key lineage_gems), used ONLY for the ghost-save apology grant. The PLAYER-VISIBLE premium currency — the balance the panel UI shows, the only currency the Royal Seals Shop debits, and what every login / daily / monthly / holiday reward grants — is Royal Seals (ROYAL_SEAL, ledger key royal_seals). The collect generator credits Royal Seals through (audited); a PREMIUM_CURRENCY reward never lands in mana. Both currencies are registry-handled and operator-grantable/clawable via — see § Admin Grant (EC04-02) and § Game-Specific Currencies. |
Kingdom State
Key fields on LineageKingdom:
resources: Map<String, Integer>— 7 resource typespopulation,morale(0-100),stability(0-100)workerAssignments: Map<String, Integer>— workers per resourcecurrentResearchId,researchPoints,discoveredTechnologiesdynasty,ruler,heirs— dynasty system (JSON)religionId,religiousPolicy(TOLERANCE/ENFORCEMENT/NEUTRAL)isEliminated,hasAchievedVictory,victoryType
Buildings
Categories
Economy, Military, Magic, Defense, Research
Building Costs & Times
| Building | Gold | Wood | Stone | Iron | Turns |
|---|---|---|---|---|---|
| Farm | 100 | 50 | — | — | 2 |
| Mine | 150 | 50 | 50 | — | 3 |
| Lumber Mill | 100 | 25 | — | — | 2 |
| Barracks | 200 | 100 | 50 | — | 3 |
| Market | 250 | 100 | — | — | 3 |
Buildings are placed on territories. Each territory has maxBuildingSlots limiting total buildings.
Military
12 Unit Types
| Unit | Cost | Upkeep/Turn | Strength |
|---|---|---|---|
| Militia | 50 | 1 | 5 |
| Warrior | 75 | 2 | 8 |
| Infantry | 100 | 3 | 10 |
| Archer | 150 | 4 | 12 |
| Scout | 75 | 2 | 6 |
| Cavalry | 200 | 6 | 18 |
| Pikeman | 125 | 4 | 12 |
| Crossbowman | 175 | 5 | 15 |
| Knight | 450 | 9 | 25 |
| Battle Mage | 400 | 10 | 30 |
| Siege | 500 | 12 | 20 |
| Elite | 800 | 16 | 40 |
Army Model
units: Map<String, Integer>— unit type → quantitystance: DEFENSIVE, OFFENSIVE, RAIDING, FORTIFYINGmorale,movementPointsRemaining,maxMovementPointsmovementPath: List<String>— ordered territory IDs for pathing
Combat Resolution
Total Strength = sum(unit_strength × quantity) for each unit type
+ Fortification Bonus: +20 per fortification level
+ Terrain Defense Bonus (defender only)
× Morale Modifier: 50% (morale 0) → 120% (morale 100)
± Combat Variance: ±10%
Base Territory Defense: 50
Terrain Defense Bonuses (defender):
| Terrain | Bonus |
|---|---|
| Mountains | +25 |
| Hills | +15 |
| Forest | +10 |
| Plains | 0 |
| Swamp | -5 |
| Desert | -3 |
Casualty Rates:
| Outcome | Casualties |
|---|---|
| Winner | 10% |
| Loser | 25% |
| Pyrrhic Victory | 40% |
Territory System
Territory Model
Each territory is a tile on the world grid:
terrainType: MOUNTAINS, HILLS, FOREST, PLAINS, SWAMP, DESERTnaturalResources: Map<String, Integer>— per-terrain yieldsbuildings: List<TerritoryBuilding>— up tomaxBuildingSlotsfortificationLevel— defense bonus (+20 per level)enhancementLevel(0-5) — terrain improvementunrestLevel(0-100) — rebellion risk (85+ triggers rebellion)securityLevel(0-3) — police/security presenceterritoryClassification: BASE (protected) or EXPANSION (conquerable)distanceFromCapital— affects maintenance costspowerLevel(1-10) — strategic value
Territory Enhancement
| Enhancement Level | Production Bonus |
|---|---|
| 0 (base) | 0% |
| 1 | +20% |
| 2 | +40% |
| 3 | +60% |
| 4 | +80% |
| 5 (max) | +100% |
Enhancement Cost: base_cost × (level + 1) × (1 + distance × 0.2)
Maintenance Cost: enhancement_level × 10 × (1 + distance × 0.2)
Root Kingdom Protection
In Global mode, the player's BASE territory cannot be attacked or conquered. Only EXPANSION territories are conquerable.
Diplomacy
Treaty Types
| Type | Effect |
|---|---|
| NON_AGGRESSION | Cannot attack each other |
| ALLIANCE | Military support obligation |
| TRADE_AGREEMENT | Resource exchange benefits |
| VASSAL | Subordinate relationship |
Treaty lifecycle: PENDING → ACTIVE → BROKEN/EXPIRED
AI Personalities (7 types)
AGGRESSIVE, PEACEFUL, DIPLOMATIC, SCHOLAR, MERCHANT, MYSTIC, BALANCED
Each AI has trait weights (aggression, diplomacy, greed, ambition, caution, loyalty, pragmatism, mysticism) and priority weights (military, economy, technology, magic, expansion, defense) that determine behavior.
Research & Technology
Three Eras (Cost-Gated, Not Prerequisite-Gated)
| Era | Research Cost | Notes |
|---|---|---|
| Ancient | 100 gold | Available immediately |
| Classical | 200 gold | No prerequisite |
| Medieval | 400 gold | No prerequisite |
- Progress: 10 research points per turn
- Concurrent blocking: Only one research at a time
- No era prerequisites: Can research Medieval tech immediately (intentional design)
Advancement System (3 Categories)
| Category | Purpose |
|---|---|
| TECHNOLOGY | Tech research tree — unlocks abilities |
| MAGIC | Magic advancement — unlocks spells |
| CIVILIZATION | Population/culture/infrastructure |
Nodes have tiers (1-5), resource costs, and required progress points.
Magic System
Source:
Mana
- Base pool: 75 current / 100 max
- Regeneration: 5 mana per turn
- Ruler magic stat affects regeneration and spell power
16 Spells (5 Categories)
Combat Spells (Target: ARMY)
| Spell | Mana | Duration | Effect |
|---|---|---|---|
| Fireball | 20 | Instant | 50 damage (1 damage = kills 1/10 of a unit) |
| Healing Light | 15 | Instant | 30 heal (restores morale by heal/3, units by heal/3%) |
| Arcane Shield | 25 | 3 turns | +25 defense bonus |
| Haste | 20 | 2 turns | +2 movement bonus |
Territory Spells (Target: TERRITORY)
| Spell | Mana | Duration | Effect |
|---|---|---|---|
| Bless the Land | 30 | 5 turns | +25% production bonus |
| Protective Ward | 35 | 10 turns | +30 defense bonus |
| Reveal Secrets | 15 | Instant | Reveals hidden resources |
Kingdom Spells (Target: SELF/KINGDOM)
| Spell | Mana | Duration | Effect |
|---|---|---|---|
| Mana Surge | 10 | Instant | +20 mana regen |
| Prosperity | 40 | 5 turns | +50 gold per turn |
| Inspiration | 25 | 3 turns | +15 morale bonus |
Advanced Spells
| Spell | Mana | Target | Duration | Effect |
|---|---|---|---|---|
| Meteor Strike | 60 | TERRITORY | Instant | 100 damage + building damage |
| Resurrection | 50 | ARMY | Instant | Resurrect 25% of fallen units |
| Earthquake | 45 | TERRITORY | Instant | +50 siege bonus |
Legendary Spells (Require LEGENDARY_MAGIC tech)
| Spell | Mana | Target | Effect | Extra Req |
|---|---|---|---|---|
| Time Stop | 200 | GLOBAL | Victory spell — casting wins the game | LEGENDARY_MAGIC |
| Apocalypse | 150 | GLOBAL | 200 global damage | LEGENDARY_MAGIC + LEGENDARY_WARFARE |
| Divine Intervention | 100 | KINGDOM | +10% all bonuses, 10 turns | LEGENDARY_MAGIC |
Spell Requirements
- Spells costing ≥ 40 mana require
BASIC_MAGICtechnology - Stacking prevention: cannot cast same spell on same target if already active
Enchantments
Spells with duration > 0 create LineageEnchantment effects:
turnsRemainingcountdown, auto-removed at 0effectData: Map<String, Object>— bonuses/penalties- Target types: TERRITORY, ARMY, KINGDOM, GLOBAL
Dynasty System
Dynasty Model
dynastyName,dynastyMotto,dynastySigilprestigePoints— accumulated across generationsgenerationNumber— starts at 1, increments on successiondynastyTraits: List<DynastyTrait>— inheritable traitsrulerHistory: List<LineageRulerHistory>— past rulers
Ruler
- Stats:
leadership,warfare,diplomacy,magic healthStatus: HEALTHY → ILL → DYING → DECEASEDpersonalTraits: List<DynastyTrait>— traits affecting rulespouse: LineageSpouse— provides bonuses and heirs
Ruler Aging & Death
Source: LineageRuler.ageOneYear, LineageRuler.shouldDieNaturally
| Condition | Mechanic |
|---|---|
| Age 60+ (HEALTHY) | 0.05 × (age − 60) chance/year to become ILL (e.g., age 62 = 10%) |
| Age 70+ (ILL) | 0.10 × (age − 70) chance/year to become DYING (e.g., age 73 = 30%) |
| Age 80+ | 0.05 × (age − 80) chance/year to die naturally (escalating) |
| DYING | 25% chance per turn to die |
Heirs
successionOrder— position in line (1 = first)claimStrength(0-100) — affects succession disputeseducationFocus: LEADERSHIP, WARFARE, DIPLOMACY, MAGICeducationProgress(0-100) — toward education completionrelationToRuler: SON, DAUGHTER, BROTHER, SISTER, NEPHEW, NIECE, GRANDSON, GRANDDAUGHTER, GREAT_GRANDSON, GREAT_GRANDDAUGHTER, COUSIN- On succession the remaining heirs are relabeled relative to the NEW monarch (
LineageKingdom.processSuccession->reclassifyHeirsRelativeToNewRuler): the old ruler's other children become BROTHER/SISTER, nieces/nephews become COUSIN, grandchildren via a sibling become NEPHEW/NIECE. A new monarch's own children (matched by parentRulerId) are promoted to SON/DAUGHTER. Cadet accession (nephew, niece, cousin, grandchild) maps the old ruler's children and other nephews to COUSIN; blood siblings sharing parentRulerId become BROTHER/SISTER. A former monarch's child relabelled COUSIN still weds and bears children (; progression.md § Cadet Branches). LineageHeir.toRuler carries the heir's living consort onto the new ruler. Designate heir-apparent lists the current ruler's own children only; the full house stays on view heirs and view family. viewFamily names the ruler's own parents:LineageKingdom.processSuccessionrecords them on the new ruler (LineageRuler.fatherNameandmotherName) from the heir'sparentRulerIdandmotherName, the father being the other partner of the parent the heir was born to, the reigning ruler or a sibling heir of a cadet branch. A regent has no parents in the house and gets no Parents line. A ruler with no recorded parents reads the previous rulerHistory entry as its parents. Each of the ruler's own children is read with both parents: the ruler and the other parent, the mother under a king and the father under a queen. A child's recorded mother is the female partner at birth, the queen herself under a queen. viewRuler's child count is living SON/DAUGHTER, not the consort birth counter. Educate pickers speak existing calculatePotential the same way view heirs does.
- On succession the remaining heirs are relabeled relative to the NEW monarch (
- A child of a SON/DAUGHTER is a GRANDSON/GRANDDAUGHTER; a child of a grandchild is a GREAT_GRANDSON/GREAT_GRANDDAUGHTER. Sibling lines still produce NEPHEW/NIECE.
- Every living heir is on the old-age curve (
LineageHeir.shouldDieOfOldAge, 75+ escalating, certain at age 100), including SON/DAUGHTER — otherwise the direct-heir cap never opens for a new generation. - Must be 16+ years to rule
Marriage
4 marriage types: LOVE, ARRANGED, DIPLOMATIC, CONQUEST
- Female fertility: Until age 45
- Male fertility: Until age 60
- Spouse traits may pass to children via
inheritanceChance
Traits (10 Predefined)
BRILLIANT, WARLIKE, DIPLOMATIC (Silver Tongue), MAGICAL_AFFINITY, SICKLY, CRUEL, AMBITIOUS, PATIENT, DRAGONBLOOD, LEGITIMIZED
Categories: PERSONALITY, PHYSICAL, MENTAL, BLOODLINE
- Bloodline traits always inherit
- Others have
inheritanceChance(0.0-1.0) - Stat bonuses and kingdom effects per trait
Religion System
religionId— current religion (e.g., "REL_001")religiousPolicy: TOLERANCE, ENFORCEMENT, NEUTRAL- Conversion has a penalty period (
conversionPenaltyEnds) - Events can involve religious expansion
Victory Conditions
Source:
5 victory types. Checked each turn. If only 1 kingdom remains, automatic DOMINATION victory.
| Victory | Condition | Threshold |
|---|---|---|
| DOMINATION | Control territories | ≥ 75% of all territories |
| ECONOMIC | Accumulate gold | ≥ 100,000 gold |
| DIPLOMATIC | Form alliances | Allied with ≥ 50% of non-eliminated kingdoms |
| TECHNOLOGICAL | Research all 5 legendary techs | LEGENDARY_WARFARE, MAGIC, ECONOMY, DIPLOMACY, CONSTRUCTION |
| MAGICAL | Cast TIME_STOP spell | Spell status = COMPLETED |
Tie resolution (multiple simultaneous victories): Most territories → Most gold → Highest morale → Alphabetical kingdom ID.
Game completion: Sets world.victorKingdomId, world.victoryType, world.victoryTurn, world.isCompleted = true, world.isActive = false.
Events
Source:
Event Mechanics
- Chance per turn: 10% (0.10 probability)
- Disabled by:
worldSettings["randomEvents"] = false - Eliminated kingdoms do not receive events
- Lifecycle: PENDING → RESOLVED (player chooses) or EXPIRED (auto-penalized)
- History: Last 50 events kept in log
13 Events (10 Generic + 3 Seasonal)
The 10 generic events are rows in
lineage.rule_lineage_common(rule_typeEVENT); the 3 seasonal events (BLIZZARD, DROUGHT, HARVEST_FESTIVAL) are code-driven constants in , not DB rows. A liveEVENTrule count of 10 is correct. Seeprogression.md§ Event Definitions.
Source:
Generic Events (any season)
| Event | Weight | Expiry | Key Choice | Best Outcome |
|---|---|---|---|---|
| Bountiful Harvest | 15 | 3 turns | Store/distribute/trade | Distribute: +morale 10, +50% food |
| Gold Discovery | 10 | 5 turns | Extract/mine carefully/share | Mine carefully: +150% gold |
| Trade Caravan | 15 | 3 turns | Trade/hospitality/tax | Trade: +resource, -50% gold |
| Hero Emerges | 8 | 5 turns | Commander/advisor/quest | Commander: +20% army strength |
| Divine Blessing | 12 | 5 turns | Festival/shrine/spread word | Festival: +25 morale, -50 gold |
| Magical Storm | 10 | 1 turn | Harvest mana/shield/research | Harvest: +30-69 mana (risky) |
| Plague | 8 | 2 turns | Quarantine/let run/magic cure | Magic cure: -25% pop, -30 mana |
| Rebellion | 8 | 2 turns | Address/parade/ignore | Address: +10 stability, -100 gold |
| Natural Disaster | 6 | 1 turn | Repair/evacuate/magic protect | Magic: -40 mana, full protection |
| Bandit Raiders | 8 | 2 turns | Troops/pay off/fortify | Troops: +5 army morale, no gold loss |
Seasonal Events (season-locked, high weight when in season)
| Event | Season | Weight | Effect |
|---|---|---|---|
| Blizzard | WINTER only | 25 | Food stockpile loss, movement penalty, -5 stability |
| Drought | SUMMER only | 20 | Severe food/morale penalty (crop loss %) |
| Harvest Festival | AUTUMN only | 25 | Food bonus, morale boost, choice of celebration type |
Expiration Penalties (auto-applied if ignored)
| Event | Penalty |
|---|---|
| Plague | Full population loss |
| Rebellion | -25 stability, -15 morale |
| Natural Disaster | -50 population, -30 wood |
| Bandit Raiders | Full gold at risk lost |
| Others | -5 morale |
Global World
Map Structure
- Size: 1000x1000+ tiles
- Chunk loading: 20x20 nearby chunk per request
- Spiral coordinates:
ringNumber+ringPositionfor territory placement
Territory Contestation
isAttackable— whether territory can be attackedcontestedByUserId,contestedProgress(0-100)- Contest progresses over time; at 100%, territory changes ownership
- Player BASE territories are protected (not attackable)
Save-State Persistence (2026-04-23)
Column constraint: lineage.lineage_game_map.map_ref must hold sp_{userId} for every player. Alexa account identifiers can reach 254 chars, so the column is sized at VARCHAR(400) to fit a sp_ prefix + a maxed-out Alexa id.
Historical bug: Prior to 2026-04-23 the column was VARCHAR(255). Inserts for users with userId length >= 253 chars produced silently-truncated/rejected writes (DB strict mode dependent). 24 / 76 single-player accounts (31.6%) accumulated accountCache state claiming hasSinglePlayerGame=true with a singlePlayerKingdomId but zero corresponding lineage_game_map rows. On relaunch, LineageGameLoader.loadSinglePlayerGame detected the ghost save, logged CRITICAL, and silently created a fresh game — the players experienced losing their kingdom.
Ghost-save recovery path (LineageGameLoader.loadSinglePlayerGame): when expectedExisting is true but neither Redis nor DB returns a map:
- Log a CRITICAL error + force an ErrorEmailSender alert (#8262).
- Grant 15
lineage_gemsas a one-time apology through (or for gems, which calls it). - Stamp so repeated relaunches do not re-award gems.
- Proceed with the usual
getOrCreateSinglePlayerGameflow.
Save path contract: Single-player writes go through LineageGameSaver.saveGameMap → which writes Redis AND upserts to lineage.lineage_game_map in the same call. Redis TTL is 7 days; the DB row is the source of truth.
Multiplayer and global-world paths are unaffected by the column-size bug (they key on partyRef and (xCoord, yCoord) respectively, neither of which contains the Alexa userId).
Action queues by mode
RETIRED. The Global hourly ROUND queue is retired. Wiring it would have delayed every Global build, recruit, research, spell, move, attack, claim, treaty, war, and trade by up to an hour into a drain that skipped whole batches via OFFSET-while-mutate, charged BUILD/RECRUIT/RESEARCH costs that diverged from the live handlers, and logged treaty/war/trade as success without changing state. The live Global world (Eternal Realm) stays: launch still offers "global world", handlers still resolve immediately, and the daily 5/10 allowance remains the spoken budget plus Royal Seals refill. , the lineage_action_queue insert, , , , and are gone. cancel action / view-actions reads the live JSON queue, not a hardcoded empty sentence.
Local and Multiplayer games queue pending BUILD, RESEARCH, and RECRUIT work on LineageGameMapData.actionQueue, persisted as JSON inside lineage.lineage_game_map.map_data. is the owner: it appends on queue, completes on end-turn, and removes finished rows. That list is live game state. It is kept, and it is bounded. Global has no second table beside it.
The JSON queue must not be collapsed into a database ROUND table. Deleting it would drop Local and Multiplayer pending work.
Combat resolutions
Lineage has three combat resolutions after this change, one per mode. A fourth unused stub (processRegionalCombat) is deleted.
| Mode | Owner | Casualties | Variance |
|---|---|---|---|
| Local | Attacker 10% if they win, 25% if they lose. Defenders 40% if the attacker wins, 15% if they hold. Matches WINNER 10 / LOSER 25 / PYRRHIC 40. | +/-5% (STANDARD_COMBAT_VARIANCE_*) |
|
| Global | Loser 20–40% of the weaker side's strength, winner 10–20% of the weaker side. Territory enters CONTESTED. | +/-10% (COMBAT_VARIANCE_*) |
|
| Multiplayer | Loser 50%, winner 25%, applied through . | Simultaneous resolution; no extra roll here |
Three is correct: Local is a turn-map fight against stationed defenders, Global is a persistent-world conquest against territory defense plus garrison, Multiplayer is a simultaneous pair of armies. They do not share a loop. Global attacks go through when the player attacks, not through a weekly regional sweep.
Serializer invariant
LineageGameMapData must not expose a second Jackson property for the same live collection. An alias getter that returns actionQueue is a writable actionsQueue property under Jackson's default MapperFeature.USE_GETTERS_AS_SETTERS (the shared JsonWriterUtility mapper leaves that default on). Deserialize then replaces the list from actionQueue and appends actionsQueue into the same list, doubling the queue on every load. Derived getters that return a live mutable collection (LineageGlobalKingdom.getTerritories, getArmies) or a second copy of a persisted collection (getPlayerKingdoms, getAIOnlyKingdoms, LineageReligion.getBonusTypes) are @JsonIgnore. Persistence uses the Lombok field accessors only.
map_data size ceiling
The write path refuses any serialized map_data larger than 8 MiB (8,388,608 characters). A healthy map is under 1 MB. 8 MiB is eight times a large healthy game and 1/64 of prod max_allowed_packet (512 MB), so a serializer bug cannot silently grow a row past the dump ceiling in one doubling. and both refuse; the last-good database row is left in place. Constant: LineageMapDataSizeGuard.MAX_MAP_DATA_CHARS.
Meta-Command Bypass During Kingdom-Name Prompt (2026-04-28)
Bug: A fresh player whose kingdom needed naming had every utterance consumed as a candidate kingdom name. "help", "options", "what can I say", "stats", and any other meta-command silently re-rendered the same kingdom-name prompt forever, with no accessibility escape valve. Discovered via scripts/alexa/console_playtest.py --skill blood-lineage — 6/7 turns returned the same prompt regardless of utterance.
Root cause: returns false for KINGDOM_NAME so REPEAT, LINEAGE_RECRUIT, etc. can't strand the player with zero options. But that gate is too coarse — it also blocked legitimate accessibility commands (HELP, LIST_COMMANDS, MAIN_MENU, EXIT) that the player needs to escape the prompt cleanly.
Fix: Added — a universal allowlist of meta-commands (HELP, HELP_DETAILS, LIST_COMMANDS, LIST_COMMAND_DETAILS, REPEAT, MAIN_MENU, EXIT, BASELINE_REPORT_BUG, BASELINE_SUBMIT_FEEDBACK, BASELINE_WHATS_NEW, SUBSCRIBE) that ALWAYS escape forced-interaction prompts. NOTE 2026-06-17: REPEAT is in this allowlist so it escapes the gate, but Lineage's REPEAT handler currently returns an empty response, so the bypass yields silence at the gate, see Known Issues . The pending interaction is preserved across the bypass, so the next turn re-prompts via the still-stored pending. Applied at all three entry points: handleValueEntryInteraction, handleYesNoInteraction, and handleNewInteraction (first-turn path). Universal fix — applies to Lineage kingdom naming, Darkness Falls character creation, Dark Tower, etc.
Affected callers: every game with a forced-interaction provider that returns false from allowsCommandPassThroughDuringValueEntry or allowsCommandPassThroughDuringYesNo. Lineage KINGDOM_NAME is the canonical case; DF/DT character setup providers are similar.
Coverage: scenario_tests rows 351 (launch; help) and 352 (launch; stormhold; yes; status) regression-protect the bypass and the post-fix kingdom-name flow.
Stale Pending Forced Interaction (2026-04-27)
Bug: A KINGDOM_NAME / VALUE_ENTRY pending stored on user_profile.profile_data.pendingForcedInteraction was preserved across sessions even after the kingdom was successfully renamed. always re-prompted from the stored pending without revalidating against current state, so every relaunch repeated "What would you like to name your kingdom" forever despite the kingdom already having a name. Reported by player zoos (24 feedback rows: id 1504-1523) who counted at least 55 launches showing the prompt and asked for the kingdom to be named "Apex Legends Empire".
Fix: Added .isPendingStillRequired(props, pending) (default true). overrides it to return false when the pending is KINGDOM_NAME and the kingdom already has a non-default name. calls this method before processing any stored pending and clears the pending if the underlying state has resolved the prompt out from under it. Same gate (isKingdomUnnamed) is now used by checkForForcedInteraction and the new method so the new-prompt and resume-prompt paths agree.
Affected players: any account whose kingdom was named while a stale pendingForcedInteraction was on user_profile would have hit this. Detection query: SELECT amazon_skill_id FROM game_user.user_profile WHERE JSON_EXTRACT(profile_data, '$.pendingForcedInteraction.interactionType') = 'KINGDOM_NAME' AND JSON_EXTRACT(profile_data, '$.pendingForcedInteraction.category') = 'VALUE_ENTRY' joined against lineage.lineage_game_map rows where kingdoms[*].kingdomName != 'New Kingdom'. Report covers 1 confirmed player; the fix is general and clears any future stale pendings on next launch.
Admin REST API
Source: — base path /api/lineage/admin
Auth: Admin JWT Bearer token required for all endpoints.
| Method | Path | Description |
|---|---|---|
| GET | /worlds |
List all game world summaries (metadata only, no full state) |
| GET | /world/{mapRef} |
Get full LineageGameMapData for a world (sp_{userId} or mp_{partyRef}) |
| PUT | /territory |
Update a territory in a world (query param mapRef, body LineageTerritory) |
| PUT | /kingdom |
Update a kingdom in a world (query param mapRef, body LineageKingdom) |
All endpoints call parseAndValidateAdminFromToken(token) for auth, then delegate to .
Subsystem Design Docs
| Subsystem | Design Doc | Covers |
|---|---|---|
| Military | military.md | 12 unit types, combat formula, movement, conquest |
| Kingdom | kingdom.md | 15 buildings, workers, territory, production rates |
| Diplomacy | diplomacy.md | Relations, treaties, AI personalities, war/alliance formulas |
| Living Factions | factions.md | Named-faction roster (Phase 3, IMPLEMENTED) — identity, agenda, archetype mapping, and the shared-infra wiring across Campaign, Eternal Realm, and (future) War of the Realms |
| Magic | magic.md | 16 spells, mana system, tech gating, enchantments |
| Progression | progression.md | Turn pipeline, 5 victory conditions, dynasty, events, research |
| Endgame | endgame.md + empire-4x-layer.md | Building upgrades, legendary units, endgame threat events (endgame.md Pillars A/B/C), Empire tier + Edicts (SHIPPED via empire-4x-layer.md SE-1, -- endgame.md Pillar D is the origin design, not the current spec) |
| 4X Systems | four-x-systems.md | Resource management screen (all 7 resources, net per turn), meaningful New Game Plus (Legacy screen + scaling founding headstart), emergent-story Dynasty Chronicle |
| Return Briefing | return-briefing.md | Spoken state-of-the-realm briefing delivered on session entry (snapshot + what-changed-since-last-session); the gd-04 voice-memory-load fix. IMPLEMENTED 2026-06-17 via on the welcomeReturningPlayer launch path; snapshot reconciled with the design 2026-06-21 — realm line names the lowest critically-low material, threats line surfaces a running food deficit projected to starve within ten turns. |
| Empire / 4X Build-Out | empire-4x-layer.md | MASTER design for the gd-04 "Invest in the 4X layer" build-out (decision 285): the post-victory Empire Tier (empire screen + Edicts), NG+ depth (chosen ascension), and emergent-story depth (revisitable Dynasty Chronicle) — all governed by an explicit seven-rule voice-memory-load budget. SE-1 (Empire Tier core, Empire Screen, Edicts) IMPLEMENTED; SE-2 (NG+ depth — chosen ascension trait + founding legacy) IMPLEMENTED; SE-3 (emergent-story depth — revisitable Dynasty Chronicle + four new beats) IMPLEMENTED. |
| War of the Realms Scenarios | scenarios.md | Phase 5, IMPLEMENTED 2026-07-18 — the scenario schema (chosen faction, fixed starting board, stated + re-readable objective, difficulty knobs) and the initial four scenarios (Unite the Broken Coast, Break the Iron Compact, Hold the Ashen Gate, The Merchant's Gambit), wired single-player first then multiplayer via the shared-map service. |
Round-2 Playtest Fixes (2026-05-28)
Seven Playtest-BL findings (25716-25722) root-caused and fixed. scenario_tests 1632-1638.
- Recruit into army 2+ (25716, feedback 7571) — normalized spaces to underscores before stripping the "to army N" suffix, so the suffix leaked into the unit token ("Warrior To Army 2"). Strip is now
[\s_]- and INTO-aware; recruit/bare-unit patterns accept the "into army N" connector. Army targeting itself was already re-parsed from raw input. - Propose treaty/alliance (25717, feedback 7573) — handler blindly targeted
aiKingdoms.get(0)— the same kingdom "declare war" hits — so once at war the proposal returned a contentless "Cannot propose treaty at this time." It now walks the AI roster for the first kingdom not at war and without an existing treaty of that type, and gives a specific reason when none qualify. - Event response NLU (25718, feedback 7755) — only the verbose "respond to event N option M" worked. Displayed event options now carry an
EVENT_<n>_OPTION_<m>matchedKey plus "option N"/"choice N"/"choose N" synonyms;handleEventResponseaccepts the structured key, the verbose form, an option-only form, and a bare number; addsEVENT_RESPONSE_PATTERNso "respond option N" / "option N" route instead of FALLBACK. - Territory claim frontier (25719, feedback 7756) — the seed world used a sparse (step-3) unclaimed grid, so claimed cells never exposed adjacent neighbors and expansion dead-ended at ~12 territories. now lazily materializes an unclaimed tile at every in-bounds frontier coordinate; the existing per-held-territory recompute then keeps offering new land outward. NOTE: domination victory (>=50% of all territories) may warrant world-size/threshold tuning now that the denominator grows with exploration — flagged as a balance follow-up, not a blocker.
- "recruit knight" default-5 / "recruit a knight" (25720, feedback 7757) — a bare "recruit knight" defaults to 5 (2250 gold) and looked un-recruitable; "recruit a knight" failed to parse the article. Leading "a"/"an" now means quantity 1, and a gold-shortfall on a defaulted count adds a hint pointing at the single-unit form. The default of 5 is unchanged (balance decision left to design).
- Building upgrade L5 message (25721, feedback 7574) — level 5 is the intended cap. The success line now states the building is maxed and no longer offers a dead "upgrade more" option at the cap.
- Attack/move-army sub-prompt phrasings (25722) — the army picker and move/attack territory pickers suggested phrasings ("army 1", "march north") that misrouted. Each picker option gained short synonyms (index, "army N", army name, "march
", " ", terrain) so the suggested phrasing advances the flow.
Round-3 Playtest Fixes (2026-07-02)
RVF fixes for report group 165 (Blood Lineage building, upgrade, recruit, worker NLU plus spells). scenario_tests 2551-2560.
- Research era names (finding 815) — the research menu prints era HEADERS ("Ancient era", "Classical era") for orientation, but they are not techs, so "research ancient era" hit "Unknown technology: Ancient Era". now treats an era name (Ancient/Classical/Medieval/Advanced/Legendary) as "research the next available tech in that era" (Ancient to Agriculture), or a graceful "no
era technologies available" when none qualify. - Build/upgrade "... in territory N" (finding 845) — the NUMBER_KEY one-shot () does not strip the word "territory" from the extracted key, so "build wall in territory 1" arrived as building "WALL_TERRITORY" ("no building called Wall Territory").
handleBuildnow strips a trailing TERRITORY/CAPITAL/LAND location word (the territory index is already carried in possibleNumbers). - Upgrade a 2nd same-type building (finding 902) — the capital starts with 2 farms; bare "upgrade farm" only ever leveled the first, and "upgrade second farm" failed ("Unknown building type: Second Farm").
handleUpgradeBuildingnow parses an ordinal ("first/second/2nd") or "level N" selector to pick the instance, and with no selector upgrades the LOWEST-level copy so repeated "upgrade farm" reaches every instance; a hint names the "upgrade second farm" form. - Worker assignment robustness (finding 904) — the game's most-used first move silently failed for a dropped verb ("30 workers to food"), synonym/misheard verbs ("sign/hire/gather workers to X"), the "two"-to-"to" homophone, and a missing count. A shared now tolerates all of these; routes verbless and synonym-verb forms; recovers a garbled or missing resource+count from the raw utterance and assigns ALL idle workers when no count is spoken;
handleRecruitdelegates a "hire/sign workers to" utterance (the FREE_FORM "hire" one-shot otherwise stole it) to the worker handler. Every path echoes the concrete result. - Spell cast + list (finding 910) — "cast
" ("cast healing light") and a bare "spells" dead-ended in FALLBACK. adds cast and spell-menu patterns; fuzzy-resolves displayed names and partials to the canonical spell key; a "more spells" option pages the spell menu to the full catalog (a working "more" where "repeat" only replays). - Recruit mishearings (findings 911, 846) — "pipemen"/"pipe men"/"bike men" (pikeman), "night"/"nights" (knight), "malaria"/"malaysia" (militia), and "crossbow men/man" plus bare "crossbow" (crossbowman) were rejected as unknown units. Added to ;
LineageSynonymResolver.normalizeUnitnow falls back to that map so BOTH the recruit-handler one-shot path and the Tier-4 bare-unit path resolve them.
Known residual (pre-existing shared NLU, out of scope): "assign two workers to X" (explicit word "two") can be intercepted by the Tier-2 OPTIONS fuzzy matcher depending on which options are active, routing to a view instead of the worker handler. The homophone "assign to workers to X" and all four documented finding-904 phrasings work.
Sub-Docs In This Directory
Design docs that live alongside this index and were not previously linked from it. Wired in for index-to-sub-doc discoverability by design-audit follow-up (2026-06-15). Read the one matching your task.
| Doc | Covers |
|---|---|
| pacing.md | Win-condition pacing & earnability audit (2026-07-17) — the ~100-turn target bands, the per-victory earnability analysis (MAGICAL currently UNEARNABLE: TIME_STOP 200 mana vs maxMana 100; TECH too slow; DOMINATION moving denominator; DIPLOMATIC risk-too-fast), and the empirical audit method. |
| campaign.md | The Campaign shape (Phase 2, IMPLEMENTED 2026-07-17) — the bounded ~100-turn 3-act campaign (Founding 1-25 / Ascendance 26-70 / Reckoning 71+), the soft Century target, the turn-~100 Twilight-of-the-Age reckoning + decision-authored epilogue (Conqueror/Diplomat/Steward/Archmage/Dynast), and the Phase-2 win-condition earnability fixes (MAGICAL mana cap, TECH rate, DIPLOMATIC hold gate). |
| vision.md | North-star vision (LOCKED 2026-07-17) — the three modes (Campaign / Eternal Realm / War of the Realms), bounded ~100-turn decision-resolved campaign, living factions, advisor layer, and the loop-fix backlog (war/army/diplomacy/onboarding/dynasty). Grounded in live player data + firsthand play. |
| advisor.md | The Steward/advisor (Phase 1, IMPLEMENTED 2026-07-17) — the pull-on-demand recommended-actions layer: state signals read, the ranked candidate list, Act-aware framing, the safe-but-less-optimal on-ramp framing, and the onboarding-hint wiring that fixed the turn-1-5 churn cliff. |
| factions.md | Living factions (Phase 3, IMPLEMENTED 2026-07-17) — the named-faction roster (six factions, one per AIPersonality archetype: identity, sigil, motto, homeland, lore, agenda, signature bias, stance-to-player) and its wiring across Campaign rivals, Eternal Realm AI realms, and the future War of the Realms scenario sides. |
| four-x-systems.md | Lineage — Full 4X Systems: Resource Screen, New Game Plus, Emergent Story |
| prestige.md | Lineage — Prestige & Ascension System Design |
| player-request-backlog.md | Owner-dispositioned player requests + Design Fit Gate (standing idea 25326); L9 one-type-per-territory confirmed |
| eternal-realm.md | The Eternal Realm (Phase 4, IMPLEMENTED 2026-07-18) — the Clash-of-Clans build-grow-compare loop on Global mode: the Realm Power formula (territory+buildings+army+prestige), the speakable Realm Ledger (rank, throne-holder, weekly trend), growth narration on the return briefing, and named-faction board population via . |
Kingdom guard contract (handler precondition)
Every Lineage handler method opens with the kingdom guard:
Boolean guard = lineageKingdomGuard.requireKingdom(gbp);
if (guard != null) return guard;
Contract: requireKingdom returns null when a kingdom is present (proceed), and returns (which is false = CONTINUE_GAME) when absent — so a no-kingdom call launches the kingdom-creation flow inline. handleView with no kingdom therefore returns HandlerResult.HANDLED_FAILURE (the guard already ran launch), not a fallback.
Testing note: Mockito's default answer for an unstubbed Boolean-returning mock is false, NOT null. A with-kingdom unit test that does not stub the guard gets false → guard != null is true → the handler returns early with the wrong result. Stub it explicitly: when(guard.requireKingdom(any())).thenReturn(null) for the with-kingdom path; thenReturn(false) (the propagated launch result) for the no-kingdom path (assertFalse, not assertNull).
Onboarding scenario-test setup (kingdom naming)
Kingdom naming is a forced-interaction VALUE_ENTRY: a fresh character's first input establishes the pending interaction and shows the prompt; the value is processed on the SECOND input. A single-input naming test re-prompts and fails — author it as a chain that establishes the prompt first, e.g. name my kingdom X; yes (the runner's bypass leaves the kingdom unnamed with the pending interaction live). Same mechanism as DT character naming — see dark-tower/index.md § Onboarding scenario-test setup traps.