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

10 KiB
Raw Blame History

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.