diff --git a/_session_handoff/session-summary.md b/_session_handoff/session-summary.md new file mode 100644 index 0000000..ca62fae --- /dev/null +++ b/_session_handoff/session-summary.md @@ -0,0 +1,151 @@ +# Résumé de session — PF1e Simulator (finalize-engine) + +> Document de transfert de session entre ordinateurs. Généré le 29 août 2026. + +## Contexte du projet + +**Projet** : `pf1e-simulator` — simulateur de combat Monte Carlo déterministe pour Pathfinder 1re édition (Python ≥3.12, `uv`, Pydantic v2, pytest, ruff, basedpyright strict). +**Chemin** : `/home/armorial/Documents/pf1e-simulator` (git, branche `main`). +**Objectif global** : finaliser le moteur de combat pour couvrir ~90% des patterns mécaniques PF1e, prêt pour reprise de campagne **janvier 2027**. Pas de couche LLM, pas de base complète 9500 entrées. + +## Décisions clés (validées par l'utilisateur) + +- Compétences = **système complet** (37 skills) +- Manœuvres = **système complet** (incl. lutte/grapple) +- Summoning = **complet** +- Ordre des phases : Fondations (A) → Magie (B) → Systèmes (C) → Contenu 8 PJ (D) → Validation (E) +- **1 commit par étape**, gate verte à chaque commit +- Subagents réactivés (fallback_models réparés) +- Ne jamais push (l'utilisateur pousse manuellement) + +## Contraintes / garde-fous + +- Pas toucher à `fiches_personnages/`, `.codegraph/`, `.omo/` (sauf plans + evidence) +- Pas de suppression de type (`type: ignore`, `Any`) pour masquer les erreurs +- Pas de base complète PF1e, pas de tailles Large+, pas de 3D/élévation +- Réponses en français; commits en anglais, style conventionnel +- **Gate obligatoire** avant chaque commit : + ```bash + export PATH="$HOME/.local/bin:$PATH" && uv run ruff check src tests && uv run basedpyright src && uv run pytest -q + ``` + +## Bugs critiques identifiés du moteur (à corriger dans le plan) + +1. Flags comportementaux (`cant_act`, `must_flee`, `denies_dex_to_ac`, `speed_mult`...) jamais appliqués +2. `state.effects.clear()` au début de chaque tour efface les buffs quel que soit `duration_rounds` ; les conditions n'expirent jamais +3. DC de sort incorrect (`base_dc + level` au lieu de `10 + niveau sort + mod stat casting`) +4. `default_policy` ne lance JAMAIS de sorts (101 sorts PJ inertes en MC) +5. Pas de ressources (slots, grit, channel, hexes, rounds bardique) +6. Pas d'AoO sur incantation, pas de concentration +7. Pas d'AoE / multi-cibles +8. Pas de manœuvres (CMB/CMD) +9. Pas de summoning +10. Pas de métamagie +11. Pas de compétences (StatTarget : 14 cibles combat uniquement) +12. Pas de capacités de classe (deed, hex, channel, performance) +13. Pas de capacités de monstres (ex. liche) + +## Contenu des 8 PJ (source `fiches_personnages/*_sheet.json`) + +- **Esha** (lv4) : Luring Cavalier / Phantom Thief — Mount, Order, Tactician. 11 feats, 1 sort. +- **Harvie** (lv2) : Animal Companion — Link, Natural Armor. 3 feats, 0 sort. +- **Ierlieth** (lv4) : Court Drake Companion. 3 feats, 0 sort. +- **Jeanne** (lv4) : Animal Companion (Bodyguard). 6 feats, 0 sort. +- **Misty** (lv4) : Magus (Eldritch Scion/Fiend Flayer) + Bard (Dervish Dancer) — Spell Combat, Arcane Pool, Bloodline, Battle Dance. 10 feats, 24 sorts. +- **Nairda** (lv4) : Sage WyrmWitch (Witch Sage/Sorcerer Arcane) — Hexes. 7 feats, 32 sorts. +- **Oni** (lv4) : Cleric (Blossoming Light)/Shifter (Feyform) — Channel Energy 2d6 6/jour, Domaines. 7 feats, 20 sorts. +- **Tammara** (lv4) : Gunslinger (Pistolero)/Warpriest — Grit, Deeds. 13 feats, 56 sorts. + +Total : **55 feats uniques**, **101 sorts uniques** (dont 10 déjà implémentés dans `data/spells/`). + +## Architecture (fichiers clés) + +- `src/pf1e_simulator/combat.py` (~1150 ln) — moteur : CombatantState:169-193, Action:221-239, _take_turn:284-299, _stat_modifiers:380-399, resolve_save:481-496, _enemies_threatening:535-544, _check_provoked_aoo:560, _cast_spell:781-827, _apply_spell_effects:829-866, default_policy:1126-1152 +- `models.py` — Combatant:93-115 (ajouter `casting_ability`, `resources`, `cmb`, `cmd`, `skills`) +- `spells.py` — types d'effets:32-57, _parse_effect:84-117, load_spell:120-132, load_spell_registry:135-140 +- `effects.py` — StatTarget:40-44, StatModifier:49-65 (ajouter filtre `skill`) +- `conditions.py` — Condition:21-39, 18 conditions prédef, registry:212-220 +- `abilities.py` — AbilitySpec:24-36, factories (weapon_focus:41, toughness:52) +- `loaders/combatant.py` — _KNOWN_FEATS:47, _resolve_feat:59-75, load_combatant:107-147 +- `loaders/foundry.py` — _FEAT_MAP:52-61, load_sheet_detailed:280-355 +- `data/pcs/*.json` (8), `data/monsters/{goblin,orc}.json`, `data/spells/` (10) +- Tests : `tests/test_combat.py`, `tests/test_spells.py` (:113-124 all-10-spells), `tests/test_combatant_loader.py` (24), `tests/test_pc_combat_golden.py` (à créer) + +## État git (au moment du transfert) + +- Branche `main`, dernière ligne : `37f07d9 chore(data): drop Plot Armor from misty features (non-combat feat)` +- Commit précédent : `82148ce test(loaders): add unit tests for combatant feat resolution...` +- **Gate verte** : 401 tests pass, ruff clean, basedpyright strict clean (0 erreur) +- Arbre de travail propre + +## Fichiers plan/draft (.omo/) + +- **Plan complet** : `.omo/plans/finalize-engine.md` (écrit, validé par l'utilisateur) — NOTE : `.omo/` est dans `.gitignore`, ce fichier n'est PAS versionné. +- **Draft** : `.omo/drafts/finalize-engine.md` (status: `plan-written`) +- Evidence par todo : `.omo/evidence/task--finalize-engine.md` (à créer par chaque agent) + +## Exécution en cours — état + +**Problème rencontré** : les délégations des subagents étaient interrompues (« le mauvais modèle était chargé »). L'utilisateur a réparé `fallback_models` et demandé de relancer la délégation. Tentatives sur Todo 1 (catégorie `deep` puis `unspecified-high`, skill `programming`) — certaines ont été abortées/modèle incorrect. Dernière tentative : `Task aborted`, session continuable `ses_fea75176fffeAxRSe6huDrsiWL`. L'utilisateur a ensuite demandé un **arrêt** et la sauvegarde de ce résumé. + +**Note d'ajustement importante** : l'exécution se fait **séquentiellement** (pas en vagues parallèles) car tous les todos modifient les mêmes fichiers cœur (`combat.py`, `models.py`, `effects.py`) — des agents parallèles créeraient des conflits. La structure en vagues du plan est maintenue pour l'ordre/dépendances mais les agents s'exécutent un par un. + +## À reprendre (prochaines étapes) + +**Tout d'abord** : relancer la délégation du **Todo 1** (prompt standard référençant le plan ; exige TDD + evidence + commit `feat(combat): effect and condition durations`). + +### Liste des tâches (Todo) — plan approuvé + +#### Wave 1 (fondations) +- [ ] **T1** — Durées effets/conditions : `ActiveBuff`/`ActiveCondition` (rounds_left), retirer `effects.clear()`, charge = ActiveBuff 1r. Commit `feat(combat): effect and condition durations` +- [ ] **T3** — Formule DC = 10 + niveau sort + mod casting_ability ; enrichir 8 JSON. Commit `fix(combat): correct spell DC formula` +- [ ] **T4** — Ressources : `resources` dict + `resources_left` + `spend_resource` ; tammara grit:3, oni channel:6. Commit `feat(models): combat resources` +- [ ] **T9** — Métamagie Empower/Maximize/Extend à la volée + `metamagic_feats`. Commit `feat(combat): metamagic` + +#### Wave 2 : T2, T6, T10 +- [ ] **T2** — Enforcement flags (cant_act, cant_move, must_flee→withdraw, denies_dex→flat_footed, speed_mult, cant_aoo, cant_charge). Commit `feat(combat): enforce behavioral condition flags` +- [ ] **T6** — AoO sur incantation + concentration DC 15+2×niveau (Combat Casting +4). Commit `feat(combat): AoO and concentration on casting` +- [ ] **T10** — Compétences complètes (37) : `SkillSpec`, `resolve_skill`, Skill Focus (+3), loader Foundry (rank/total/classSkill/armorPenalty). Commit `feat(combat): skills system` + +#### Wave 3 : T5, T7, T11, T8 +- [ ] **T5** — Politique de sort dans `default_policy` (soin si <50%, offensif si en portée, buff sinon ; jamais si menacé sans concentration). Commit `feat(combat): spellcasting default policy` +- [ ] **T7** — AoE : `AreaEffect` burst + fireball 20-ft, demi-dégâts sur save. Commit `feat(combat): area-of-effect spells` +- [ ] **T11** — Manœuvres CMB/CMD (`Action kind="maneuver"` : trip, disarm, bull_rush, sunder). Commit `feat(combat): combat maneuvers CMB/CMD` +- [ ] **T8** — Summoning : `SummonEffect` + `data/monsters/celestial_wolf.json`, insertion après lanceur dans l'initiative. Commit `feat(combat): summoning` + +#### Wave 4 : T12, T13, T14 +- [ ] **T12** — Lutte complète : GRAPPLED, grappled_with, maintain/pin/escape/release, Escape Artist. Commit `feat(combat): grapple` +- [ ] **T13** — 55 feats des 8 PJ → AbilitySpec (Combat Reflexes + AoO, Skill Focus, autres passifs/no-op notés) ; sync _KNOWN_FEATS/_FEAT_MAP. Commit `feat(loaders): map 8-PC feats` +- [ ] **T14** — 91 sorts JSON générés (101 total), registre + test all-101. Commit `feat(data): 101 spell JSONs` + +#### Wave 5 : T15, T16, T17, T18 +- [ ] **T15** — JSON compagnons : harvie, ierlieth, jeanne, monture esha. Commit `feat(data): companions and mounts` +- [ ] **T16** — Channel energy Oni (30ft, 2d6, ressource 6/jour) + hexes Nairda (Evil Eye, Slumber, Cackle). Commit `feat(combat): channel energy and hexes` +- [ ] **T17** — Deeds Tammara (Up Close and Deadly +1d6≤30ft coûte 1 grit, Quick Clear) + Misty (Spell Combat = sort+full attack, Inspire Courage 30ft). Commit `feat(combat): class action patterns` +- [ ] **T18** — Liche : `data/monsters/lich.json` (Fear Aura shaken Will DC22, Paralyzing Touch → PARALYZED 1d4+1r, DR 15/bludgeoning+magic). Commit `feat(data): lich monster` + +#### Wave 6 : T19, T20 +- [ ] **T19** — `tests/test_pc_combat_golden.py` : 8 PJ vs gobelin + tammara vs liche, ScriptedRng épinglés. Commit `test(combat): PC golden transcripts` +- [ ] **T20** — README à jour + gate finale verte (~500 tests) + retirer références obsolètes ("377 tests", "Manœuvres non modélisées"). Commit `docs: finalize engine documentation` + +#### Vérification finale +- [ ] **F1** — Audit conformité plan (20 todos + evidence présentes) +- [ ] **F2** — Qualité code (ruff + basedpyright propres, pas de suppression de type) +- [ ] **F3** — QA CLI manuelle : `pf1e-sim --side players data/pcs/tammara.json --side monsters data/monsters/lich.json --runs 100 --seed 1` et oni vs gobelins (vérifier sorts/summon/manœuvres/ressources) +- [ ] **F4** — Fidélité scope (8 PJ jouables, pas de modif fiches_personnages/.omo/.codegraph, pas de LLM) + +## Matrice de dépendances (ordre d'exécution) + +- T2 ← T1 ; T6 ← T2 ; T5 ← T2, T6 +- T11 ← T10 ; T12 ← T11, T10 +- T13 ← T2, T10 ; T14 ← T7, T8, T9 +- T16 ← T4 ; T17 ← T4, T7, T14 ; T18 ← T14 +- T19 ← 2,5,12,13,14,15,16,17,18 ; T20 ← 19 +- T1, T3, T4, T9, T7, T8, T10, T15 : indépendants (mais exécution séquentielle pour éviter conflits sur `combat.py`) + +## Matériel pour la reprise sur un autre ordinateur + +- Besoin du repo cloné : `git clone pf1e-simulator && cd pf1e-simulator && uv sync --dev` +- Fichiers de contexte à conserver : `.omo/plans/finalize-engine.md`, `.omo/drafts/finalize-engine.md` +- Lancer le plan depuis `.omo/plans/finalize-engine.md` (il contient tous les references par todo) +- Le processus d'exécution attendu : délégation séquentielle par todo via subagent, TDD, gate verte, un commit par todo, puis vérification finale F1-F4