From 2ee19d34e450555589554cef5e23e43f0b2f0808 Mon Sep 17 00:00:00 2001 From: ctan Date: Sat, 19 Sep 2026 17:09:08 +0200 Subject: [PATCH] fix(docs): correct the Observation 'frozen' claim - the dataclass is frozen at the top level but the inner per-combatant/pair dicts are mutable copies; wording softened in the docstring and README (deep-freeze rejected: it would change the LLM contract and require touching pinned observation tests; copies are never read back by the engine, so policy mutation is already inert) --- README.md | 7 +++++-- src/pf1e_simulator/combat.py | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1607fe3..762f4a7 100644 --- a/README.md +++ b/README.md @@ -1578,8 +1578,11 @@ Règles modélisées : `spell_slots` sans clés dupliquées. - Snapshot d'observation (contrat LLM, Phase 2) : avant chaque appel de politique, le moteur construit une `Observation` sérialisable (`to_dict()` → - JSON) figée — le snapshot est la SEULE source d'info de la LLM (aucune - référence `engine.*` exposée, collections copiées/figées). Une politique LLM + JSON) isolée du combat — le snapshot est la SEULE source d'info de la LLM + (aucune référence `engine.*` exposée, collections copiées, jamais relues en + retour : la politique peut les muter sans affecter le combat ; l'objet est un + dataclass figé au premier niveau, les dictionnaires internes sont des copies + mutables, non deep-freezés). Une politique LLM se branche via `CombatEngine(..., llm_policy=...)` (`LLMPolicy(observation, state) -> tuple[Action, ...]`). Schéma enrichi (E1-E5) : - Par combattant : id, side, pos, occupied_cells, hp/hp_max, active/dead, diff --git a/src/pf1e_simulator/combat.py b/src/pf1e_simulator/combat.py index cd491bc..58a6d32 100644 --- a/src/pf1e_simulator/combat.py +++ b/src/pf1e_simulator/combat.py @@ -929,8 +929,11 @@ class Observation: Built by the engine at the point of policy invocation; the snapshot is the ONLY source of information for an ``LLMPolicy`` — it never exposes - ``engine.*`` callables nor mutable references to live state. All collections - are copied (tuples/dicts) so the snapshot is frozen and JSON-serializable. + ``engine.*`` callables nor mutable references to live state. All + collections are copies (tuples/dicts), never read back by the engine, so a + policy may mutate them freely without touching combat; the snapshot is + JSON-serializable (the top level is a frozen dataclass; the inner dicts are + plain mutable copies, not deep-frozen). ``combatants`` holds one dict per combatant in ``self._states`` order; ``pairs`` holds one dict per (acting combatant x each other combatant) with