docs(rules): add the SRD conformance audit journal

Consolidates the round-1 audit: 15 fixed divergences (one commit each,
312f9fd..ea60b2e), the deliberate out-of-scope rules, the assumed
residuals, and the verified-conformant guard rails. Linked from README.
This commit is contained in:
2026-09-16 18:10:05 +02:00
parent ea60b2e4b3
commit 99e9203d8c
2 changed files with 149 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
# 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.