# FREEEXILE: CHARACTER STAT AGGREGATOR & MODIFIER ENGINE ARCHITECTURAL SURVEY REPORT

> **Document Type**: In-Depth Technical Domain & Architecture Investigation  
> **Author**: `explorer_survey_1` (teamwork_preview_explorer / Codebase Researcher)  
> **Target Project**: FreeExile (Grimdark Savage ARPG Server Engine)  
> **Target Request**: Character Stat Aggregator Core, Modifier System, Formula Persistence & Server Engine Integration (Request `2026-10-04T09:01:16Z`)  
> **Integrity Mode**: Read-Only Survey (Zero Source Code Mutation)  
> **Date**: October 4, 2026 (UTC)  

---

## 1. EXECUTIVE SUMMARY & INVESTIGATION OVERVIEW

This report provides the exhaustive technical foundation and architectural blueprint for the implementation of the **Character Stat Aggregator** system for FreeExile. The aggregator must authoritatively calculate and aggregate stats from equipped items, the Meridian passive constellation (Huyết Cốt Ma Đồ), and character base attributes, supporting **flat modifiers**, **percentage modifiers** (additive *increased* vs multiplicative *more*), **tag-based modifiers**, and **conditional modifiers**, while persisting formula calculation ASTs into SQLite for historical auditing and initializing `CombatActor` in `server_engine_loop.py`.

### Key Findings at a Glance:
1. **Item & Inventory Domain**:
   - Equipment is managed via `server/inventory/inventory_service.py` and `server/inventory/inventory_types.py`. Character equipment is stored in `CharacterInventory.equipment: Dict[EquipmentSlot, InventoryItem]`, supporting 12 equipment slots (`MAIN_HAND`, `OFF_HAND`, `HELMET`, `BODY_ARMOR`, `GLOVES`, `BOOTS`, `AMULET`, `RING_1`, `RING_2`, `BELT`, etc.).
   - Affixes are modeled across dual representations: `world.primal_stones_crafting.Affix` (legacy 7-stone crafting: `stat_key`, `current_val`) and `server/world/item_crafting_types.py` + `item_affix_catalog.py` (canonical 15-tier PoE2 affixes: `AffixMod` with `mod_id`, `tier`, `value`, `is_downside`, `downside_type`, `downside_val`).
   - Weapon mechanics in `server/world/weapon_engine.py` define Two-Handed bonuses (+50% More damage), Dual-Wielding (+10% APS, +15% Block), weapon class scaling, and implicit modifiers (`WeaponImplicitStat`).
2. **Meridian Passive Tree Domain**:
   - Implemented in `server/world/meridian_service.py`, `meridian_types.py`, and `meridian_catalog.py`.
   - Constellation consists of **29 Acupoints** across 5 clusters (`CENTER`, `NORTH`, `EAST`, `SOUTH`, `WEST`) and 5 node types: `ORIGIN`, `VESSEL` (Minor), `MAJOR` (Notable), `KEYSTONE` (Nghịch Mệnh), and `JEWEL_SOCKET` (Khảm Tọa).
   - Node bonuses are structured in `MeridianStatBonus` (hp, mp, dps, dps_mult, crit_rate, crit_dmg, armor, evasion, life_leech, armor_pen, attack_speed, resist, dmg_reduction).
   - Socketable jewels (`JewelItemDef`) grant additional stat packages. Aggregation currently occurs in `MeridianService.compute_total_stats()`, which combines node and jewel stats.
3. **Existing Stat & Attribute Landscape**:
   - Base attributes (Tông Môn Căn Cốt) in `server/world/martial_types.py`: `cuong_the` (Physique / STR), `than_phap` (Agility / DEX), `than_niem` (Spirit / INT) with baseline 50 each in `CharacterLoadout`.
   - Attribute scaling rules:
     - `cuong_the` (STR): scales Max HP (+HP flat) and Melee/Slam/Physical damage (+0.2% per point).
     - `than_phap` (DEX): scales Attack/Cast Speed (+0.15% per point) and Critical Strike Chance (+0.1% per point).
     - `than_niem` (INT): scales Max Qi/Mana, Ward/Barrier, and Spell/Elemental damage (+0.2% per point).
   - Core combat vitals on `CombatActor` (`server/world/combat_engine.py`): `max_hp`, `current_hp`, `base_attack` (default `50.0`), `crit_chance` (default `0.05`), `crit_multiplier` (default `1.50`), and `resistances: Dict[FiveElements, float]`.
4. **Modifier Calculation Requirements**:
   - **Flat Modifiers**: $\sum Flat$ directly incrementing base value.
   - **Percentage Modifiers**: Strict Path of Exile separation between additive increased ($1.0 + \frac{\sum Inc - \sum Red}{100.0}$) and multiplicative more ($\prod (1.0 + More) \times \prod (1.0 - Less)$). Already implemented in `server/combat/damage_calculator.py` for damage packets, now needed globally for all character stats.
   - **Tag-Based Modifiers**: Modifiers carry required tag subsets (using `SkillTag` from `server/world/martial_types.py`). Applied if modifier tags $\subseteq$ action/context tags.
   - **Conditional Modifiers**: Modifiers guarded by evaluation flags (`ON_LOW_HEALTH`, `WHILE_WIELDING_SWORD`, `AGAINST_BLEEDING_ENEMY`, `TWO_HANDED`, etc.).
5. **Server Engine Integration Point**:
   - `server/world/server_engine_loop.py` line 105: `register_player()` currently instantiates `CombatActor` with hardcoded `base_attack = 50.0`.
   - Wiring `CharacterStatAggregator` into `ServerEngineLoop.register_player` enables initializing `CombatActor` with fully aggregated attack, HP, crit, and resistances.
6. **Blast Radius Gate Notice**:
   - `server/world/server_engine_loop.py` is categorized as `CRITICAL` risk with 7 dependents. Any code modifications require acknowledging via `python tools/analysis/blast_radius.py --target server/world/server_engine_loop.py --ack`.

---

## 2. INVENTORY & ITEM AFFIX ARCHITECTURE

### 2.1 File Map & Responsibilities
- `server/inventory/inventory_service.py` (342 lines): Authoritative service governing bag capacity, slot manipulation, equipping/unequipping, personal stashes, and merchant listings.
- `server/inventory/inventory_types.py` (116 lines): Dataclass specifications for `InventoryItem`, `CharacterInventory`, `EquipmentSlot`, `ItemType`, and stash models.
- `server/inventory/inventory_helpers.py` (115 lines): Helper routines for slot allocation and equipment compatibility validation.
- `server/world/primal_stones_crafting.py` (267 lines): Legacy/primal crafting engine defining `Affix` and `ItemRarity`.
- `server/world/item_crafting_types.py` (148 lines): Modern itemization contracts: `AffixMod`, `AffixDefinition`, `DownsidePenaltyType`, and `CraftingItem`.
- `server/world/item_affix_catalog.py` (379 lines): 15-tier canonical affix definitions, weights, min/max ranges, and curse trade-off affixes.
- `server/world/weapon_engine.py` (265 lines): Authoritative weapon damage scaling, attribute requirement checks, and two-handed/dual-wield rules.
- `server/world/weapon_types.py` (160 lines): Weapon classification, implicit modifiers (`WeaponImplicitStat`), and templates.

### 2.2 Item Modeling & Storage in Inventory
In `server/inventory/inventory_types.py`:
```python
class EquipmentSlot(str, Enum):
    MAIN_HAND = "MAIN_HAND"
    OFF_HAND = "OFF_HAND"
    SWAP_MAIN_HAND = "SWAP_MAIN_HAND"
    SWAP_OFF_HAND = "SWAP_OFF_HAND"
    HELMET = "HELMET"
    BODY_ARMOR = "BODY_ARMOR"
    GLOVES = "GLOVES"
    BOOTS = "BOOTS"
    AMULET = "AMULET"
    RING_1 = "RING_1"
    RING_2 = "RING_2"
    BELT = "BELT"

@dataclass(slots=True)
class InventoryItem:
    item_uuid: str
    item_id: str
    name: str
    item_type: ItemType
    rarity: ItemRarity = ItemRarity.PHAM_PHAM
    item_level: int = 1
    stack_count: int = 1
    max_stack: int = 1
    affixes: List[Affix] = field(default_factory=list)
    is_identified: bool = True
    is_corrupted: bool = False
    # ... grid coordinates and metadata
    metadata: Dict[str, Any] = field(default_factory=dict)
```

In `CharacterInventory`, equipped gear is held in:
```python
equipment: Dict[EquipmentSlot, InventoryItem] = field(default_factory=dict)
```

### 2.3 Dual Affix Representations in Codebase
The aggregator must handle two affix structures that exist in the codebase:

1. **Primal Affix (`world.primal_stones_crafting.Affix`)**:
   ```python
   @dataclass
   class Affix:
       name: str
       affix_type: AffixType  # PREFIX | SUFFIX
       stat_key: str          # e.g. "phys_dmg", "fire_dmg", "max_hp", "atk_speed", "all_res"
       min_val: int
       max_val: int
       current_val: int
   ```
   *Stat keys mapped in crafting engine*:
   - `phys_dmg`: Flat Physical Damage
   - `fire_dmg`: Flat Fire Damage
   - `cold_dmg`: Flat Cold Damage
   - `energy_shield`: Flat Ward / Energy Shield
   - `max_hp`: Flat Maximum Life
   - `atk_speed`: Percentage Attack Speed (+5% to 15%)
   - `crit_rate`: Percentage Critical Strike Chance (+5% to 12%)
   - `all_res`: Percentage All Elemental Resistances (+8% to 20%)
   - `energy_regen`: Flat Energy/Qi Regeneration
   - `hp_regen`: Flat Life Regeneration

2. **Canonical 15-Tier Affix (`server/world/item_crafting_types.AffixMod`)**:
   ```python
   @dataclass(slots=True)
   class AffixMod:
       mod_id: str             # e.g. "pref_phys_pct_t1", "pref_flat_phys_t1"
       mod_name: str
       mod_type: AffixType     # PREFIX | SUFFIX | IMPLICIT | CORRUPTED
       tier: int               # 1 (Apex) to 15 (Sơ Cấp)
       value: float
       is_fractured: bool = False
       is_downside: bool = False
       downside_type: Optional[DownsidePenaltyType] = None
       downside_val: float = 0.0
       downside_desc: str = ""
   ```
   *12 Canonical Families in `item_affix_catalog.py`*:
   - `pref_phys_pct_t[1-15]`: +5.0% to +180.0% Physical Damage (Increased %)
   - `pref_flat_phys_t[1-15]`: +1.0 to +80.0 Flat Physical Damage
   - `pref_fire_flat_t[1-15]`: +1.0 to +110.0 Flat Fire Damage
   - `pref_life_t[1-15]`: +5.0 to +150.0 Flat Maximum Life
   - `pref_armor_flat_t[1-15]`: +8.0 to +650.0 Flat Armour
   - `suff_move_spd_t[1-15]`: +2.0% to +35.0% Movement Speed (Increased %)
   - `suff_atk_spd_t[1-15]`: +1.0% to +30.0% Attack Speed (Increased %)
   - `suff_crit_chance_t[1-15]`: +1.0% to +45.0% Critical Strike Chance (Increased %)
   - `suff_crit_multi_t[1-15]`: +2.0% to +65.0% Critical Multiplier (Flat % bonus)
   - `suff_fire_res_t[1-15]`: +1.0% to +50.0% Fire Resistance (Flat % added to res)
   - `suff_cold_res_t[1-15]`: +1.0% to +50.0% Cold Resistance (Flat % added to res)
   - `suff_chaos_res_t[1-15]`: +1.0% to +35.0% Chaos Resistance (Flat % added to res)

3. **Overpowered Trade-Off Currencies (`CANONICAL_TRADEOFF_AFFIXES`)**:
   - `curse_blood_frenzy`: +140% Increased Damage, downside `LIFE_DEGEN_PCT = 5.0%`
   - `curse_void_reaper`: +75% Crit Multiplier, 8% Life Leech, downside `REDUCE_MAX_RES_CAP = 10.0%`
   - `curse_titan_carapace`: +1200 Flat Armour, downside `CANNOT_EVADE_OR_DASH`

### 2.4 Weapon Scaling & Implicits (`weapon_engine.py` & `weapon_types.py`)
- Base Weapons have `min_damage`, `max_damage`, `attacks_per_second`, `base_crit_chance`.
- **Grip Style Mechanics**:
  - `TWO_HANDED`: Grants `1.50` (+50% More Base Damage) and `stun_potency = 1.35`.
  - `ONE_HANDED` Dual Wielding: Grants `+10% APS` and `+15% Block Chance`.
- **Implicit Modifiers (`WeaponImplicitStat`)**:
  - `GLOBAL_CRIT_CHANCE`, `GLOBAL_CRIT_MULTIPLIER`, `MELEE_BLEED_CHANCE`, `FIRE_PENETRATION`, `LIGHTNING_PENETRATION`, `COLD_PENETRATION`, `POISON_DOT_MULTIPLIER`, `BLOCK_CHANCE`, `MAX_WARD_BONUS`, `LIFE_ON_HIT`.

---

## 3. MERIDIAN PASSIVE TREE (HUYẾT CỐT MA ĐỒ) ARCHITECTURE

### 3.1 File Map & Responsibilities
- `server/world/meridian_service.py` (355 lines): Authoritative service managing acupoint unsealing, topological validation, point economy, Jewel socketing, and persistence.
- `server/world/meridian_types.py` (145 lines): Data models for `MeridianStatBonus`, `MeridianNodeDef`, `JewelItemDef`, `PlayerMeridianState`, and result DTOs.
- `server/world/meridian_catalog.py` (578 lines): 29 Acupoints across 5 Constellation Clusters, topological connections graph, and 4 Canonical Jewels.

### 3.2 The 29-Node Constellation
The passive tree is divided into 5 clusters with distinct thematic archetypes:
1. `CENTER` (Tâm Mạch Khởi Nguyên): Origin hub (`m_c1` Đản Trung) + basic vessels (`m_c2` Khí Hải, `m_c3` Thần Khuyết, `m_c4` Mệnh Môn, `m_c5` Trung Phủ).
2. `NORTH` (Cương Thể Ma Cốt): Physique, HP & Armor. Notable: `m_n3` Cự Khuyết (+18% Melee Dmg, +30 Armor), `m_n4` Cương Cốt Đại Cực (+300 HP, +50 Armor, -4% Dmg Taken). Keystone: `m_n_keystone` Đới Mạch Kim Cang (+500 HP, +80 Armor, -10% Dmg Taken, -5% Attack Speed). Socket: `m_n_jewel`.
3. `EAST` (Du Long Thân Pháp): Agility, Attack Speed, Evasion & Crit. Notable: `m_e3` Quang Minh (+18% Crit Dmg, +15 DPS), `m_e4` Tuyệt Ảnh Thần Hành (+6% Move Speed, +12% Evasion). Keystone: `m_e_keystone` Du Long Vô Ảnh (+25% Crit Dmg, +8% Move Speed, -15% Max HP). Socket: `m_e_jewel`.
4. `SOUTH` (U Minh Thần Niệm): Spirit, Mana/Qi, Elemental Penetration. Notable: `m_s3` Bách Hội (+18% Elemental Dmg, +100 Qi), `m_s4` Thần Trí Thông Thiên (+12% Armor/Resist Pen). Keystone: `m_s_keystone` Càn Khôn Đảo Nghịch (All damage converts to Chaos, bypasses Ward, cannot deal elemental ailments). Socket: `m_s_jewel`.
5. `WEST` (Hỗn Nguyên Ngũ Hành): Elemental Resistances & Life Leech. Notable: `m_w3` Hành Gian (+20 All Res, +150 HP), `m_w4` Huyết Tinh Thao Thiết (+8% Life Leech). Keystone: `m_w_keystone` Huyết Ma Thôn Phệ (+15% Life Leech, Leech does not stop on full life, cannot regenerate HP naturally). Socket: `m_w_jewel`.

### 3.3 Stat Data Model: `MeridianStatBonus`
Defined in `server/world/meridian_types.py`:
```python
@dataclass(slots=True, frozen=True)
class MeridianStatBonus:
    hp: int = 0                  # Flat Max Life
    mp: int = 0                  # Flat Max Qi / Mana
    dps: float = 0.0             # Flat Base Damage addition
    dps_mult: float = 0.0        # Percentage Increased Damage (e.g. 0.08 = +8%)
    crit_rate: float = 0.0       # Flat Added Crit Chance (e.g. 0.04 = +4%)
    crit_dmg: float = 0.0        # Flat Added Crit Multiplier (e.g. 0.15 = +15%)
    armor: int = 0               # Flat Armour
    evasion: float = 0.0         # Percentage / Flat Evasion Bonus
    life_leech: float = 0.0      # Percentage Life Leech
    armor_pen: float = 0.0       # Percentage Penetration
    attack_speed: float = 0.0    # Percentage Attack Speed (e.g. 0.04 = +4%)
    resist: float = 0.0          # Flat All Elemental Resistances
    dmg_reduction: float = 0.0   # Percentage Damage Reduction
```

### 3.4 Jewels (Linh Thạch) & Socketing
Jewels (`JewelItemDef`) are socketable items inserted into `JEWEL_SOCKET` nodes:
- `jewel_red_bloodstone`: +120 HP, +12% Melee Damage (`dps_mult=0.12`), +15 Armor.
- `jewel_blue_frost`: +100 Qi, +15 All Resistances, +6% Attack/Cast Speed.
- `jewel_gold_thunder`: +12% Crit Chance, +35% Crit Multiplier, +10% Penetration.
- `jewel_green_emerald`: +6% Life Leech, +80 HP, +8% Evasion.

### 3.5 Persistence Model in SQLite
Stored in table `character_meridians` in `data/game_state.db` (or custom db):
```sql
CREATE TABLE IF NOT EXISTS character_meridians (
    player_id TEXT PRIMARY KEY,
    available_points INTEGER NOT NULL DEFAULT 5,
    spent_points INTEGER NOT NULL DEFAULT 0,
    unlocked_nodes_json TEXT NOT NULL DEFAULT '[]',
    socketed_jewels_json TEXT NOT NULL DEFAULT '{}',
    updated_at REAL NOT NULL
);
```

### 3.6 Limitations of the Legacy `apply_to_combat_actor` Method
In `server/world/meridian_service.py` lines 340-355:
```python
def apply_to_combat_actor(self, player_id: str, actor: CombatActor) -> None:
    stats = self.compute_total_stats(player_id)
    actor.max_hp = actor.max_hp + stats.hp
    actor.base_attack = round((actor.base_attack + stats.dps) * (1.0 + stats.dps_mult), 2)
    actor.crit_chance = min(1.0, round(actor.crit_chance + stats.crit_rate, 4))
    actor.crit_multiplier = round(actor.crit_multiplier + stats.crit_dmg, 4)
    # ...
```
**Architecture Gaps in Legacy Approach**:
1. Does NOT account for equipped items or affixes.
2. Applies percentage multiplier (`dps_mult`) only to meridian `dps` and base attack, completely ignoring weapon base damage and item flat damage.
3. Does not distinguish between *increased* and *more* multipliers.
4. Lacks tag-based filtering (e.g. +18% melee damage applies blindly to all attacks/spells).
5. Does not support conditions (low health, wielding weapon).
6. Does not persist formulas for auditing.

---

## 4. STAT DEFINITIONS, BASE ATTRIBUTES & DERIVED STATS

### 4.1 Base Attributes (Tông Môn Căn Cốt)
From `server/world/martial_types.py` lines 149-166 (`CharacterLoadout`):
- `cuong_the` (Physique / Strength / STR):
  - Base: 50
  - Scaling:
    - Max HP: $+1.0$ HP per point ($50 \times 1 = +50$ HP)
    - Melee / Slam / Physical Damage: $+0.2\%$ per point ($1.0 + cuong\_the \times 0.002 = +10\%$ at 50 STR)
- `than_phap` (Agility / Dexterity / DEX):
  - Base: 50
  - Scaling:
    - Attack Speed / Cast Speed: $+0.15\%$ per point ($1.0 + than\_phap \times 0.0015 = +7.5\%$ at 50 DEX)
    - Critical Strike Chance: $+0.1\%$ per point ($than\_phap \times 0.001 = +5\%$ flat crit at 50 DEX)
    - Evasion Rating: $+2.0$ flat Evasion per point ($50 \times 2 = +100$ Evasion)
- `than_niem` (Spirit / Intelligence / INT):
  - Base: 50
  - Scaling:
    - Max Qi / Mana: $+1.0$ Qi per point ($50 \times 1 = +50$ Qi)
    - Ward Barrier / Energy Shield: $+0.5$ Ward per point ($50 \times 0.5 = +25$ Ward)
    - Spell / Elemental Damage: $+0.2\%$ per point ($1.0 + than\_niem \times 0.002 = +10\%$ at 50 INT)

### 4.2 Derived Combat Stats Matrix
| Stat Key | Base Value | Flat Sources | Additive % (*Increased*) | Multiplicative % (*More*) | Target Field on `CombatActor` |
|:---|:---:|:---|:---:|:---|:---|
| `MAX_HP` | 1000.0 | STR (1/pt), `pref_life`, Meridian nodes (`m_c1`, `m_n4`), Jewels | % Max HP affixes, passive nodes | Keystones, Boss/Party scaling | `actor.max_hp`, `actor.current_hp` |
| `MAX_MANA` / `QI` | 500.0 | INT (1/pt), Meridian nodes (`m_c1`, `m_s1`), Jewels | % Max Qi passives | Blood Magic (converts to 0) | `actor.max_energy`, `actor.current_energy` |
| `WARD_BARRIER` | 0.0 | INT (0.5/pt), `energy_shield` affix, Armour bases | % Increased Ward affixes, Staff implicit | Tradeoff curses (disables ward) | Defense Ward pool |
| `BASE_ATTACK` / `DAMAGE` | 50.0 (Unarmed) or Weapon Base | `pref_flat_phys`, `pref_fire_flat`, Meridian `dps` | STR % Melee, INT % Elem, `pref_phys_pct`, Meridian `dps_mult` | Two-Handed (+50%), Support Gem multipliers | `actor.base_attack` |
| `ATTACK_SPEED` | 1.00 (APS) | Weapon base APS | DEX (+0.15%/pt), `suff_atk_spd`, Meridian `attack_speed` | Dual-Wield (+10%), Support Gem speed mult | Combat cast/animation speed |
| `MOVEMENT_SPEED` | 6.00 | Boots base | `suff_move_spd`, Meridian `m_e4` (+6%), Keystone (+8%) | Keystone penalties (-5%), Slow debuffs | Movement authority / PlayerChar |
| `CRIT_CHANCE` | 0.05 (5.0%) | DEX (+0.1%/pt), `suff_crit_chance`, Meridian `crit_rate`, Jewels | % Increased Global Crit Chance | Cannot crit curses (sets to 0.0) | `actor.crit_chance` (capped [0, 1]) |
| `CRIT_MULTIPLIER` | 1.50 (150%) | `suff_crit_multi`, Meridian `crit_dmg`, Jewels | None (crit multi is purely additive pool) | Special keystones | `actor.crit_multiplier` |
| `ARMOUR` | 0.0 | `pref_armor_flat`, Body Armour, Shield, Meridian `armor` | % Increased Armour passives | Keystone trade-offs | DamageCalculator mitigation |
| `EVASION` | 50.0 | DEX (+2/pt), Boots/Gloves bases | % Increased Evasion, Meridian `evasion` | Cannot evade curses (sets to 0.0) | DamageCalculator entropy |
| `RESISTANCE_FIRE` | 0.0% | `suff_fire_res`, `all_res` affix, Meridian `resist` | None (uncapped resistance sum) | Overcap / Soft cap at 75% | `actor.resistances[FiveElements.HOA]` |
| `RESISTANCE_COLD` | 0.0% | `suff_cold_res`, `all_res` affix, Meridian `resist` | None (uncapped resistance sum) | Overcap / Soft cap at 75% | `actor.resistances[FiveElements.THUY]` |
| `RESISTANCE_LIGHTNING`| 0.0% | `suff_light_res`, `all_res` affix, Meridian `resist` | None (uncapped resistance sum) | Overcap / Soft cap at 75% | `actor.resistances[FiveElements.KIM]` |
| `RESISTANCE_CHAOS` | 0.0% | `suff_chaos_res` affix, Meridian `resist` | None (uncapped resistance sum) | Overcap / Soft cap at 75% | `actor.resistances[FiveElements.MOC]` |

---

## 5. MODIFIER MECHANICS SPECIFICATION

### 5.1 Modifier Taxonomy & Enums
The modifier engine must implement four primary dimensions:
```
StatModifier = (StatType, ModifierType, Value, Tags, Condition, Source)
```

1. **`StatType`**: Target attribute being modified (`MAX_HP`, `BASE_ATTACK`, `PHYSICAL_DAMAGE`, `FIRE_DAMAGE`, `ATTACK_SPEED`, `CRIT_CHANCE`, `ARMOUR`, etc.).
2. **`ModifierType`**:
   - `FLAT`: Numerical addition to base.
   - `INCREASED`: Additive percentage multiplier ($+X\%$).
   - `REDUCED`: Additive percentage reduction ($-X\%$).
   - `MORE`: Independent multiplicative factor ($\times (1 + \frac{X}{100})$).
   - `LESS`: Independent multiplicative reduction ($\times (1 - \frac{X}{100})$).
   - `OVERRIDE`: Hard replacement (e.g. `cannot_crit` sets crit to 0).
3. **`Tags` (`frozenset[SkillTag]`)**:
   - Reuses `SkillTag` from `server/world/martial_types.py`:
     - Delivery: `ATTACK`, `SPELL`, `WARCRY`, `TOTEM`, `CHANNELING`.
     - Shape: `MELEE`, `STRIKE`, `SLAM`, `PROJECTILE`, `AOE`, `CHAINING`.
     - Element: `PHYSICAL`, `FIRE`, `COLD`, `LIGHTNING`, `POISON`, `CHAOS`, `DOT`.
     - Ailment: `BLEED`, `IGNITE`, `FREEZE`, `CHILL`, `SHOCK`.
4. **`ConditionType`**:
   - `ALWAYS_ACTIVE`: Unconditional modifier.
   - `ON_LOW_HEALTH`: Triggers when $\frac{CurrentHP}{MaxHP} \le 0.35$.
   - `ON_FULL_HEALTH`: Triggers when $CurrentHP \ge MaxHP$.
   - `WHILE_WIELDING_SWORD`, `WHILE_WIELDING_BLADE`, `WHILE_WIELDING_STAFF`, `WHILE_WIELDING_BOW`, `UNARMED`.
   - `TWO_HANDED`, `DUAL_WIELDING`, `USING_SHIELD`.
   - `WHILE_STATIONARY`, `WHILE_MOVING`.
   - `AGAINST_BLEEDING_ENEMY`, `AGAINST_FROZEN_ENEMY`, `AGAINST_IGNITED_ENEMY`.

### 5.2 Mathematical Aggregation Formula
For each stat $S$ under evaluation context with tags $Q$ and active conditions $C$:

$$\text{Active}(M) = (M.condition \in C) \land (M.tags \subseteq Q)$$

1. **Flat Aggregation**:
   $$BasePlusFlat = BaseValue + \sum_{M \in Flat, \text{Active}(M)} M.value$$

2. **Additive Percentage Multiplier**:
   $$IncSum = \sum_{M \in Increased, \text{Active}(M)} M.value - \sum_{M \in Reduced, \text{Active}(M)} M.value$$
   $$IncMultiplier = 1.0 + \max\left(-0.90, \frac{IncSum}{100.0}\right)$$

3. **Multiplicative Percentage Multiplier**:
   $$MoreProduct = \prod_{M \in More, \text{Active}(M)} \left(1.0 + \frac{M.value}{100.0}\right) \times \prod_{M \in Less, \text{Active}(M)} \left(1.0 - \frac{M.value}{100.0}\right)$$

4. **Final Computed Value**:
   $$FinalStat = BasePlusFlat \times IncMultiplier \times MoreProduct$$

---

## 6. FORMULA PERSISTENCE IN DATABASE (REQUIREMENT R2)

### 6.1 AST (Abstract Syntax Tree) Structure
To enable full auditing of player calculations, the aggregator must generate a serialized AST capturing every term in the calculation:

```json
{
  "stat": "BASE_ATTACK",
  "base_value": 50.0,
  "flat_nodes": [
    {"source": "wpn_azure_sword", "name": "Thanh Phong Cổ Kiếm", "value": 30.0, "tags": ["PHYSICAL", "MELEE", "ATTACK"]},
    {"source": "affix_pref_flat_phys_t3", "name": "Kình Lực Toái Cốt (T3)", "value": 38.0, "tags": ["PHYSICAL"]},
    {"source": "meridian_m_c5", "name": "Trung Phủ", "value": 15.0, "tags": []}
  ],
  "flat_sum": 83.0,
  "base_plus_flat": 133.0,
  "increased_nodes": [
    {"source": "attribute_str", "name": "Cương Thể (STR 60)", "value": 12.0, "tags": ["MELEE", "PHYSICAL"]},
    {"source": "affix_pref_phys_pct_t2", "name": "Cương Thiết Trảm Kích % (T2)", "value": 140.0, "tags": ["PHYSICAL"]},
    {"source": "meridian_m_c3", "name": "Thần Khuyết", "value": 8.0, "tags": []}
  ],
  "increased_sum_pct": 160.0,
  "increased_multiplier": 2.60,
  "more_nodes": [
    {"source": "weapon_grip_two_handed", "name": "Cự Kiếm Trọng Binh (2H)", "multiplier": 1.50, "tags": ["MELEE"]}
  ],
  "more_product": 1.50,
  "final_value": 518.7,
  "formula_expression": "(50.0 + 83.0) * (1.0 + 1.60) * 1.50 = 518.7"
}
```

### 6.2 SQLite Audit Table Schema
The table should be created in the local SQLite database (`data/game_design_matrix.db` or a dedicated store `data/character_stats_audit.db`):
```sql
CREATE TABLE IF NOT EXISTS character_stat_calculations (
    calculation_id TEXT PRIMARY KEY,
    player_id TEXT NOT NULL,
    character_id TEXT NOT NULL,
    calculated_at REAL NOT NULL,
    trigger_reason TEXT NOT NULL,     -- e.g. "MAP_JOIN", "GEAR_EQUIP", "MERIDIAN_ALLOCATE", "LEVEL_UP", "TEST"
    final_stats_json TEXT NOT NULL,    -- Compact dict of all final stat values
    formula_ast_json TEXT NOT NULL,    -- Full AST per stat with every source node
    active_conditions_json TEXT NOT NULL,
    created_at REAL NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_stat_calc_player ON character_stat_calculations(player_id, calculated_at DESC);
```

---

## 7. SERVER ENGINE INTEGRATION (REQUIREMENT R3)

### 7.1 Current `register_player` Implementation
In `server/world/server_engine_loop.py` lines 86-114:
```python
def register_player(
    self,
    entity_id: int,
    initial_x: float,
    initial_y: float,
    initial_z: float = 0.0,
    element: FiveElements = FiveElements.KIM,
    move_speed: float = 6.0,
    max_hp: float = 1000.0
) -> None:
    # ...
    combat_actor = CombatActor(
        actor_id=entity_id,
        name=f"Player_{entity_id}",
        element=element,
        current_hp=max_hp,
        max_hp=max_hp,
        base_attack=50.0  # <--- HARDCODED BASE ATTACK
    )
    self.combat_engine.register_actor(combat_actor)
```

### 7.2 Integration Architecture
The proposed integration preserves backward-compatibility while injecting real calculated stats:

```python
class ServerEngineLoop:
    def __init__(
        self,
        cell_size: float = 64.0,
        stat_aggregator: Optional[CharacterStatAggregator] = None,
    ):
        # ...
        self.stat_aggregator = stat_aggregator

    def register_player(
        self,
        entity_id: int,
        initial_x: float,
        initial_y: float,
        initial_z: float = 0.0,
        element: FiveElements = FiveElements.KIM,
        move_speed: float = 6.0,
        max_hp: float = 1000.0,
        player_id: Optional[str] = None,
        account_id: Optional[str] = None,
        character_id: Optional[str] = None,
        active_conditions: Optional[Set[str]] = None,
    ) -> CombatActor:
        # Default baseline values
        eff_hp = max_hp
        eff_attack = 50.0
        eff_crit_chance = 0.05
        eff_crit_mult = 1.50
        eff_resistances = {}

        # Authoritative Stat Aggregation hook
        if self.stat_aggregator and player_id:
            agg = self.stat_aggregator.aggregate_character_stats(
                player_id=player_id,
                account_id=account_id or player_id,
                character_id=character_id,
                active_conditions=active_conditions,
                trigger_reason="MAP_JOIN",
            )
            eff_hp = agg.max_hp
            eff_attack = agg.base_attack
            eff_crit_chance = agg.crit_chance
            eff_crit_mult = agg.crit_multiplier
            eff_resistances = agg.resistances
            move_speed = agg.movement_speed

        combat_actor = CombatActor(
            actor_id=entity_id,
            name=f"Player_{entity_id}",
            element=element,
            current_hp=eff_hp,
            max_hp=eff_hp,
            base_attack=eff_attack,
            crit_chance=eff_crit_chance,
            crit_multiplier=eff_crit_mult,
            resistances=eff_resistances,
            is_player=True,
            player_id=player_id,
        )
        self.combat_engine.register_actor(combat_actor)
        return combat_actor
```

---

## 8. BLAST RADIUS & HYGIENE COMPLIANCE

### 8.1 Blast Radius Impact Analysis
Running `python tools/analysis/blast_radius.py` yields:
- `server/world/server_engine_loop.py`: **CRITICAL RISK** (7 dependents):
  - `server/agent/agent_decision_core.py`
  - `server/world/agent_orb_service.py`
  - `tests/security_fuzzing/test_agent_security_fuzzing.py`
  - `tests/unit/test_agent_decision_core.py`
  - `tests/unit/test_agent_orb_hmac.py`
  - `tests/unit/test_agent_orb_service.py`
  - `tests/unit/test_isometric_engine_loop.py`
- `server/world/meridian_service.py`: **HIGH RISK** (2 dependents)
- `server/inventory/inventory_service.py`: **HIGH RISK** (4 dependents)

> **Mandatory Directive for Implementer**:
> Before modifying `server_engine_loop.py`, run:
> ```bash
> python tools/analysis/blast_radius.py --target server/world/server_engine_loop.py --ack
> ```

### 8.2 File Hygiene & Modularity Budget (< 350 Lines Cap)
To comply with `GEMINI.md` limits ($\le 350$ lines soft cap, $\le 500$ lines hard cap):
1. **Types & Enums**: Place in `server/world/stat_aggregator_types.py` (~150-200 lines).
2. **Aggregator Engine Logic**: Place in `server/world/character_stat_aggregator.py` (~280-340 lines).
3. **Database Audit Layer**: Ingest directly into `character_stat_aggregator.py` or separate `server/world/stat_audit_repository.py`.
4. **Integration**: Modify `server/world/server_engine_loop.py` with minimal additions (~15 lines).

---

## 9. STEP-BY-STEP IMPLEMENTATION ROADMAP & TEST MATRIX

### Phase 1: Data Contracts (`server/world/stat_aggregator_types.py`)
- Define `StatType`, `ModifierType`, `ConditionType`.
- Define `StatModifier`, `CharacterBaseAttributes`, `FormulaNode`, `StatCalculationAST`, `AggregatedCharacterStats`.
- Strict typing with `@dataclass(slots=True, frozen=True)`.

### Phase 2: Aggregator Engine (`server/world/character_stat_aggregator.py`)
- Extraction adapters:
  - From `InventoryService`: iterate `CharacterInventory.equipment`, extract `Affix` and `AffixMod` values, item implicits, base weapon values.
  - From `MeridianService`: extract `compute_total_stats()` or iterate unlocked nodes + jewels.
  - From Base Attributes: convert STR/DEX/INT into corresponding flat and increased modifiers.
- Core math resolution pipeline:
  - Flat summation -> Additive percentage sum -> Multiplicative products.
  - Tag filtering.
  - Conditional checks.
- SQLite formula AST persistence:
  - Table `character_stat_calculations`.
  - JSON serialization of AST.

### Phase 3: Engine Loop Integration (`server/world/server_engine_loop.py`)
- Run `python tools/analysis/blast_radius.py --target server/world/server_engine_loop.py --ack`.
- Add `stat_aggregator` attribute to `ServerEngineLoop.__init__`.
- Update `register_player` to calculate stats when `player_id` is passed and instantiate `CombatActor` with aggregated attributes.

### Phase 4: Autonomous TDD Verification Stack
- Write unit test `tests/unit/test_character_stat_aggregator.py`:
  - **Test Case 1**: Mock character with baseline attributes (50 STR, 50 DEX, 50 INT) -> verify baseline values.
  - **Test Case 2**: Equip items with flat physical (+30), increased physical (+50%), and 2H weapon (+50% more) -> verify exact mathematical result $(50 + 30) \times (1 + 0.50 + 0.10) \times 1.50 = 192.0$.
  - **Test Case 3**: Allocate Meridian nodes (+200 HP, +8% damage) and socket Jewel -> verify stats combine cleanly.
  - **Test Case 4**: Tag-based filtering: Fire Spell query applies Fire/Spell mods but ignores Melee mods.
  - **Test Case 5**: Conditional modifiers: `ON_LOW_HEALTH` (+30% damage) active only when `current_hp / max_hp <= 0.35`.
  - **Test Case 6**: Formula persistence in SQLite: query `character_stat_calculations` and assert valid JSON AST.
  - **Test Case 7**: Integration test with `ServerEngineLoop.register_player`: assert `CombatActor.base_attack > 50.0`.
- Verify existing tests continue to pass: `pytest tests/unit/test_isometric_engine_loop.py tests/unit/test_meridian_server_service.py tests/unit/test_inventory_service.py`.
