diff --git a/docs/phase-2-llm-architecture.md b/docs/phase-2-llm-architecture.md index 9c198dd..31ac7db 100644 --- a/docs/phase-2-llm-architecture.md +++ b/docs/phase-2-llm-architecture.md @@ -357,10 +357,35 @@ hex/deed si ressource présente, défense totale, ready/delay) ; `swift`/`free`/ `full_round` exclus ; liste bornée et triée. **Pas de second moteur de légalité** : le moteur reste l'arbitre. Test : `tests/test_phase2_candidates.py`. +### Runner et rejeu (P0.4 + P0.5 — commits 334f8f9, 6c6de16) + +`run_encounter` accepte désormais `llm_policy=` (observation-only ou legacy, +transmise à chaque `CombatEngine`) et `policy_factory=` (constructeur de +`Policy` **par run**, appelé une fois par indice dans l'ordre ; `None` pour un +run = repli `default_policy`). Les trois entrées (`policy`, `llm_policy`, +`policy_factory`) sont mutuellement exclusives (erreur claire). Test : +`tests/test_phase2_runner.py` (factory 1 appel/run dans l'ordre, repli, +exclusivité, une bataille complète pilotée par `llm_policy`). + +`replay.py` : `ReplayRecord(call_seq, observation, action)` + `ReplayLog` +(spec + seed + round_cap + appels) sérialisé en **JSONL** (clé `call_seq`), +`observation_key()` (digest déterministe, insensible à la collapse +tuple/liste de JSON), et `replay(log) -> LLMPolicy` qui renvoie les actions +enregistrées dans l'ordre des `call_seq`, avec gardes : observation +divergente (`ReplayMismatchError`), log épuisé (`ReplayExhaustedError`), +`call_seq` dupliqué ou hors ordre, JSONL invalide. **Un run enregistré se +rejoue à l'identique** (wins/draws/rounds/transcript — aussi depuis un log +dégelé du JSONL). Test : `tests/test_phase2_replay.py`. Le rejet de replay +par défaut est documenté (D3 : pas de mémoire inter-tours ; `delay`/ready +rejoués par les actions enregistrées elles-mêmes, sans ré-invocation). + ### État du plan -- **P0.0 (D1)** ✅, **P0.1 (`schema_version`)** ✅, mesures ✅, `objectives.py` ✅, - `collect_runs` ✅, **P0.2** (vue compacte) ✅, **P0.3** (`candidates`) ✅. -- Restent : **P0.4** (`llm_policy` sur le runner + `policy_factory`), **P0.5** - (replay `call_seq`) → puis P1 (client + politiques), P3 (optimiseur), P4 - (cartographie complète), P5 (fournisseur + rapport). +- **P0.0 (D1)** ✅ · **P0.1 (`schema_version`)** ✅ · mesures ✅ · + `objectives.py` ✅ · `collect_runs` ✅ · **P0.2** (vue compacte) ✅ · + **P0.3** (`candidates`) ✅ · **P0.4** (`llm_policy` + `policy_factory`) ✅ · + **P0.5** (ReplayLog `call_seq` + `replay()` identique) ✅. +- **Frontière P0 atteinte** : gate verte (2022 tests) ; un run enregistré se + rejoue à l'identique. +- Suite : **P1** (client + politiques), P3 (optimiseur), P4 (cartographie + complète), P5 (fournisseur + rapport).