# DISPATCH — Worker Core 1 (Character Stat Aggregator Implementation)

## 2026-10-04T09:20:00Z
- **Your Role**: teamwork_preview_worker (Lead Backend & Systems Engineer)
- **Working Directory**: c:\Projects\FreeExile\.agents\teamwork\worker_core_1
- **Project Root**: c:\Projects\FreeExile
- **Authoritative Request**: c:\Projects\FreeExile\.agents\teamwork\ORIGINAL_REQUEST.md (under ## 2026-10-04T09:01:16Z)
- **Project Architecture**: c:\Projects\FreeExile\.agents\teamwork\orchestrator_21\PROJECT.md
- **Explorer Survey Reports**:
  - `c:\Projects\FreeExile\.agents\teamwork\explorer_survey_1\survey_report.md`
  - `c:\Projects\FreeExile\.agents\teamwork\explorer_survey_2\survey_report.md`
  - `c:\Projects\FreeExile\.agents\teamwork\explorer_survey_3\survey_report.md`

### MANDATORY INTEGRITY WARNING
DO NOT CHEAT. All implementations must be genuine. DO NOT hardcode test results, create dummy/facade implementations, or circumvent the intended task. A teamwork_preview_auditor will independently verify your work. Integrity violations WILL be detected and your work WILL be rejected.

### File Ownership (Exclusive Write Access):
1. `server/stats/__init__.py`
2. `server/stats/stat_types.py` (< 200 lines)
3. `server/stats/formula_persistence.py` (< 250 lines)
4. `server/stats/stat_aggregator.py` (< 350 lines)
5. `server/world/server_engine_loop.py` (Under Blast Radius ACK)
6. `tests/unit/test_character_stat_aggregator.py` (< 350 lines)

### Implementation Requirements:
1. **Stat Types & Core Data Structures (`server/stats/stat_types.py`)**:
   - `ModifierType` enum: `FLAT`, `INCREASED`, `REDUCED`, `MORE`, `LESS`.
   - `StatModifier` dataclass: `stat_key: str`, `mod_type: ModifierType`, `value: float`, `tags: FrozenSet[str]`, `condition: Optional[str]`, `source: str`.
   - `ASTNode` hierarchy (`ConstantNode`, `SumNode`, `ScaleFactorNode`, `ProductNode`, `ModifierContributionNode`, etc.) with `to_dict()` for JSON serialization.
   - `EvaluationContext` dataclass: `active_tags: FrozenSet[str]`, `conditions: FrozenSet[str]`, `current_hp_ratio: float = 1.0`.
   - `AggregatedCharacterStats` dataclass: `attack_damage: float`, `max_hp: float`, `crit_chance: float`, `crit_multiplier: float`, `move_speed: float`, `resistances: Dict[str, float]`, `all_stats: Dict[str, float]`.
2. **Formula Persistence Engine (`server/stats/formula_persistence.py`)**:
   - `FormulaPersistenceService(db_path: Optional[str] = None)`
   - SQLite table `character_stat_calculations` in `data/character_stat_formulas.db` (supports `:memory:` when `db_path=":memory:"`):
     - `id INTEGER PRIMARY KEY AUTOINCREMENT`
     - `player_id TEXT NOT NULL`
     - `calculation_id TEXT NOT NULL`
     - `timestamp REAL NOT NULL`
     - `formula_ast_json TEXT NOT NULL`
     - `final_stats_json TEXT NOT NULL`
     - `context_tags_json TEXT NOT NULL`
     - `created_at TEXT NOT NULL`
   - Pragmas: WAL mode, synchronous=NORMAL, foreign_keys=ON, row_factory=sqlite3.Row.
   - Methods: `save_calculation(...)`, `get_calculation(calculation_id)`, `get_player_calculations(player_id, limit=10)`.
3. **Character Stat Aggregator (`server/stats/stat_aggregator.py`)**:
   - `CharacterStatAggregator(persistence_service: Optional[FormulaPersistenceService] = None)`
   - Combines:
     - Base stats (from loadout or defaults: 50 cuong_the/STR, 50 than_phap/DEX, 50 than_niem/INT, 1000.0 HP, 500.0 Qi, 50.0 attack)
     - Item affixes from `CharacterInventory.equipment` (supports both `Affix` and `AffixMod`, weapon two-handed +50% More damage, dual wield +10% APS, +15% block)
     - Meridian passives from `MeridianService.compute_total_stats(player_id)`
   - PoE math:
     $$\text{FinalStat} = (\text{Base} + \sum \text{Flat}) \times (1.0 + \frac{\sum \text{Inc} - \sum \text{Red}}{100.0}) \times \prod (1.0 + \text{More}) \times \prod (1.0 - \text{Less})$$
   - Evaluates tag filters (`SkillTag` / matching tags in context) and conditions (`on_low_health` when HP <= 35%, etc.).
   - Generates AST tree and persists to SQLite.
4. **Server Engine Loop Wiring (`server/world/server_engine_loop.py`)**:
   - **CRITICAL**: Run `python tools/analysis/blast_radius.py --target server/world/server_engine_loop.py --ack` BEFORE modifying `server_engine_loop.py`!
   - In `ServerEngineLoop.__init__`, accept optional `stat_aggregator: Optional[CharacterStatAggregator] = None`.
   - In `register_player`, accept optional `(player_id=None, account_id=None, character_id=None, aggregated_stats=None, context_tags=None, conditions=None)`.
   - When aggregated stats provided (or computed via `stat_aggregator`):
     - `CombatActor` initialized with `base_attack=stats.attack_damage`, `max_hp=stats.max_hp`, `current_hp=stats.max_hp`, `crit_chance=stats.crit_chance`, `crit_multiplier=stats.crit_multiplier`, `resistances=stats.resistances`, `is_player=True`.
     - `PlayerCharacter.move_speed` synchronized to `stats.move_speed`.
   - When omitted, fallback to default values (`base_attack=50.0`, `max_hp=1000.0`, `move_speed=6.0`), guaranteeing zero regressions for existing tests.
5. **Comprehensive Unit Tests (`tests/unit/test_character_stat_aggregator.py`)**:
   - Test suite with >= 10 tests covering all acceptance criteria:
     - Math verification (flat, percent inc/red, more/less)
     - Tag-based filtering
     - Conditional modifiers
     - Mock player with multiple items and passive nodes
     - Formula AST valid JSON serialization and SQLite persistence
     - `server_engine_loop.py` registration showing `CombatActor` attack damage > 50.0
     - Backward compatibility when omitted (defaults to 50.0)
6. **Verification & Quality Gates**:
   - Run `pytest tests/unit/test_character_stat_aggregator.py`
   - Run existing tests: `pytest tests/unit/test_isometric_engine_loop.py tests/unit/test_combat_engine.py tests/unit/test_inventory_service.py tests/unit/test_meridian_server_service.py`
   - Run `python tools/lint/check_code_and_doc_hygiene.py --strict`
   - Document all test and build results in `handoff.md`.
