# Conformité SRD Pathfinder 1re édition — journal d'audit Ce document consigne, de façon exhaustive et vérifiable, l'écart entre les règles implémentées par le moteur et le SRD Pathfinder 1re édition. Il complète le `README.md` (qui décrit le comportement attendu) en traçant **ce qui a été corrigé**, **ce qui est hors périmètre**, et **ce qui reste assumé comme résiduel**. ## Méthode 1. **Audit read-only par domaine** : chaque passe couvre un domaine étanche (sorts, mouvement, morts-vivants, dons, manœuvres, conditions, données de sorts, bestiaire). L'auditeur croise le code avec la base de connaissances SRD (`pathfinder-kb`, collection `pathfinder-1e-rules`) et cite la règle. 2. **Classement** : `High` (change matériellement l'issue d'un combat), `Med` (clairement faux, impact étroit), `Low` (donnée / cosmétique). 3. **Correction** : un commit par correctif, gate complète verte avant chaque commit (`ruff` `select=ALL`, `basedpyright` strict, `pytest`). 4. **Non-régression** : le transcript doré (`tests/test_pc_combat_golden.py`) n'est réécrit que lorsqu'un correctif SRD change légitimement la sortie, avec justification ligne à ligne. 5. **Traçabilité** : tout constat non corrigé est classé ici en *hors périmètre* (choix délibéré) ou *résiduel assumé* (écart connu, non corrigé). ## État de la gate | Contrôle | Résultat | |---|---| | `uv run pytest -q` | 1491 tests verts | | `uv run ruff check src tests` (`select=ALL`) | conforme | | `uv run basedpyright src` (strict) | 0 erreur | | Transcript doré depuis la passe 1 | inchangé | --- ## 1. Écarts corrigés ### Passe 1 — sorts de buff, mouvement/grille, morts-vivants, armes naturelles | # | Constat | Sév. | Correctif (commit) | |---|---|---|---| | B1 | *Bless* affectait **toutes** les créatures d'un burst de 20 ft (y compris les ennemis) ; le SRD cible « you and each of your allies within a 50-ft. burst » | **High** | `e43066e` — filtre `AreaEffect.targets` (`all`/`allies`/`enemies`), burst 50 ft, alliés + lanceur | | B2 | *Bane* était un sort mono-cible ; le SRD cible les **ennemis** dans un burst de 50 ft | Med | `5e3505d` — conversion en `AreaEffect` 50 ft, `targets: "enemies"` | | B3 | *Bladed Dash* : niveau 1 (au lieu de 2), portée `close` (au lieu de `personal`) | Med | `312f9fd` | | B4 | *Aid* : niveau 1 (au lieu de 2), portée `long` (au lieu de `touch`) | Med | `312f9fd` | | B5 | *Doom* : portée `close` (au lieu de `medium`) | Med | `312f9fd` | | B6 | *Bull's Strength* : sauvegarde `fort` (le SRD n'accorde pas de jet ; valeur neutre corrigée en `will`) | Med | `312f9fd` | | B7 | *Bear's Endurance* : sauvegarde `fort` (idem) | Med | `312f9fd` | | B8 | *Mage Armor* absent du registre | Low | `3e19d29` — sort ajouté (niveau 1, conjuration, contact, +4 CA d'**armure**, RM non) + type de bonus `"armor"` non cumulable (registre porté à 108 sorts) | | C1 | Budget de déplacement : parité des diagonales figée à 0 dans les descentes gloutonnes → la règle 5-10-5 était **violée** (un marcheur vitesse 30 parcourait 6 cases en diagonale au lieu de 4) | Med | `2cc2ba1` — parité propagée (`_best_move_step`, `_descend_path`, `_run_line_path`, `_flee_path`) + `_path_cost_5105` pour la charge | | C2 | Pas de placement (5 ft) autorisé depuis/vers un terrain difficile | Med | `437507e` — rejet si le footprint courant **ou** la destination touche du terrain difficile | | C3 | Entrer dans la case d'une créature hostile ne provoquait pas d'AoO | Med | `9a16ca0` — `_check_enter_aoo` câblé dans `_walk_steps` | | C4 | Traversée d'une case hostile à ≥ 3 catégories d'écart de taille non modélisée | Med | `9a16ca0` — `_can_pass_through` (porte « traversée ») distincte de `_blocked` (porte « fin de mouvement ») | | C5 | Un combattant **fatigué/épuisé** pouvait courir (seule la charge était bloquée) ; le SRD : « can neither run nor charge » | Low | `7fc7a1d` — garde `cant_charge` ajoutée à `_execute_run` | | D1 | Les morts-vivants (et les créatures artificielles) mouraient à `hp ≤ -CON` au lieu d'être détruits à `hp ≤ 0` | Med | `59caf67` — seuil de mort à 0, pas de bande d'agonie | | D2 | Fiche du liche non conforme au bloc SRD : BAB 8 (au lieu de +5), contact +10 (au lieu de +5), `damage_bonus` 5 doublant la formule (dégâts réels 1d8+10 au lieu de 1d8+5), CA 25/14/23 (au lieu de 23/14/21), jets 10/10/14 (au lieu de 6/7/11), `con_score` 14 alors qu'un mort-vivant n'a pas de Constitution | Low | `881f2ef` | | D3 | Les sorts de soin (`cure`) soignaient les morts-vivants au lieu de les **blesser** | Low | `cad35e2` — dégâts (Volonté pour moitié), ni RD ni stabilisation | | A1 | Armes naturelles : itératifs de BAB accordés à tort et absence de la pénalité −5 des attaques secondaires | Med | `e5691bd` — champs `natural`/`secondary` (une seule attaque, secondaires −5, Multiattack −2) | ### Passe 2 — dons/capacités, manœuvres, conditions, données de sorts > En cours. Les constats de la passe 2 sont ajoutés ci-dessous à mesure que les > audits reviennent (voir § 6, journal des audits). | # | Constat | Sév. | Correctif (commit) | |---|---|---|---| | — | *(à compléter)* | — | — | --- ## 2. Hors périmètre (choix délibéré, documenté) Ces règles sont **volontairement** non modélisées et signalées dans le `README.md` (sections « Limites hors périmètre » et « Limites de sorts ») pour ne pas être confondues avec des bogues. | Sujet | Raison | |---|---| | **Squeeze** (se serrer dans un passage étroit) | La largeur de couloir n'est pas comparée à la taille ; une créature bloquée par son footprint l'est sans option de squeeze payante | | **Acrobatics « Tumbling »** (traverser une case ennemie sans AoO) | Le moteur n'a pas de système de compétences | | **Créature inerte très grande = obstacle à 2 cases** | Les cases d'une créature inactive ne bloquent pas et ne coûtent pas double | | **Échec d'incantation du personnage sourd (20 %)** | `SpellSpec` n'a pas de composante verbale ; il faudrait encoder les composantes sur les 108 sorts | | **Incantation multi-round (`long` : 3 rounds, 10 min, 1 h)** | Non planifiable dans un tour ; jetée du budget (`downgraded:cast_time`) plutôt qu'approximée | | **Co-occupation au-delà de Tiny vs plus grand** | Deux créatures Small+ ne partagent jamais une case ; plusieurs Tiny sur une case non gérés | | **Combat à deux armes / arme en main secondaire (×0,5 Force)** | Hors du modèle d'attaque actuel | | **Vol 3D / vol par-dessus les obstacles** | La vitesse de vol est un budget au sol ; pas de maniabilité ni de survol du terrain | | **`markers`** (points nommés de la carte) | Validés à la lecture, non appliqués à la résolution | --- ## 3. Résiduels assumés (écart connu, non corrigé) | Sujet | Nature de l'écart | Décision | |---|---|---| | **Course — durée liée à la Constitution, don Run, armure lourde (×3)** | Le SRD limite la course à `CON` tours puis impose un jet de Constitution DD 10, et réduit la vitesse de course en armure lourde ; le moteur applique toujours ×4 | Assumé : aucun scénario courant ne course plus de quelques tours | | **Dégâts non létaux** | Absents du modèle de PV | Assumé : aucune source de dégâts non létaux dans les données | | **Armes à éclaboussures / dégâts d'éclaboussure** | Absents (pas d'objet concerné dans les données) | Assumé | | **Sort « harmless » sur cible consentante** | Le moteur roule tout de même une sauvegarde (consommation de RNG) ; l'issue du soin n'en dépend pas | Assumé : fidélité de déterminisme, pas d'effet observable | | **Réglages du liche non SRD** (pv 100 vs 111, Force, Intelligence, initiative, aura de terreur 30 ft/DD 22 vs 60 ft/DD 18) | Réglages d'équilibrage du scénario doré | Assumé : conservés pour ne pas casser la fiche dorée | | **Dés énergétiques non multipliés par un critique** (contact du liche) | Règle moteur globale (exceptions CRB « Critical Hits ») | Assumé : cohérent avec les autres attaques | | **`selective_channeling` non modélisé** | Le canal d'énergie affecte toutes les créatures du type visé dans la zone | Assumé (documenté au README) | --- ## 4. Conformités vérifiées (garde-fous anti-régression) Constats explicitement contrôlés et **confirmés conformes** : - **Résistance aux sorts** : le drapeau `spell_resistance` est respecté et `_check_spell_resistance` court-circuite sans consommer de RNG quand la cible n'a pas de RM. - **Description de *Lightning Bolt*** (ligne 120 ft, 1d6/niveau plafonné 10d6, Réflexes demi) : conforme. - **Diagonale entrant en terrain difficile** : coût plat de 3 cases, convention de table explicite dans `grid.py`, cohérente entre Dijkstra et descentes. - **Terrain mixte** : les coûts de pas s'additionnent sans double comptage. - **Déplacement d'un personnage aveuglé** : le SRD n'interdit ni le déplacement, ni la course, ni la charge à un aveugle (seuls la touche, la CA et les compétences sont affectés) — le constat d'audit initial ne visait pas une vraie règle. --- ## 5. Journal des audits | Passe | Domaines | Sessions | Constats | Statut | |---|---|---|---|---| | 1 | Sorts (B), Mouvement/grille (C), Morts-vivants (D), Armes naturelles (A) | `bg_9e9e22e6`, `bg_cda707bf`, `bg_ba80396a`, `bg_6a621d4c` | 1 High, 12 Med, ~20 Low | Corrigés + documentés (ci-dessus) | | 2 | Dons/capacités (E), Manœuvres (F), Conditions (G), Données de sorts (H) | `bg_e764efda`, `bg_9fdc610e`, `bg_f5829c68`, `bg_b289533d` | en cours | En cours de triage | | 3+ | Bestiaire/PJ (données), résolution d'attaque/défense, économie d'action, sorts restants | — | à lancer | À faire | --- ## 6. Relancer une passe d'audit Chaque passe suit le même protocole : 1. Choisir un domaine **non encore audité** (voir le journal § 5). 2. Lancer un auditeur read-only (agent `oracle`) avec la base SRD `pathfinder-kb` et l'exigence de citer la règle et la ligne de code fautive. 3. Trier chaque constat : **corriger** (si l'impact est matériel et le correctif propre) ou **classer** en hors périmètre / résiduel assumé (ce document). 4. Un commit par correctif ; gate complète verte ; transcript doré préservé ou justifié ligne à ligne. 5. Reporter les constats dans les tableaux ci-dessus et la ligne de journal.