Files
pf1e-simulator/docs/conformance-srd.md
T
ctan 99e9203d8c 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.
2026-09-16 18:10:05 +02:00

146 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.