feat(phase2): observation-only LLMPolicy entry (D1) + pinned schema_version (P0.1)
- LLMPolicy is now Callable[[Observation], tuple[Action, ...]]: the snapshot is the policy's sole information source; the live CombatantState is no longer passed to new-style policies. - CombatEngine._llm_adapter keeps a compatibility path for pre-D1 two-argument callables: the signature is inspected ONCE at wrap time (_accepts_state) and legacy policies still receive (observation, state). - Observation.to_dict() carries schema_version (OBSERVATION_SCHEMA_VERSION=1) as its first pinned key; bump only on removal/meaning change. - Pinned keyset tests updated (test_observation, test_observation_enriched); new tests: observation-only e2e, legacy 2-arg e2e through the adapter, schema_version pin + JSON-serializability. - README: documents the new entry signature and the schema_version key.
This commit is contained in:
@@ -1051,7 +1051,8 @@ def test_observation_enriched_terrain_semantics_no_callable_no_share() -> None:
|
||||
def test_observation_enriched_e5_existing_keys_preserved() -> None:
|
||||
"""Given: an Observation built for the L5 fields
|
||||
When: the top-level dict is inspected
|
||||
Then: all pre-existing top-level keys remain (round, acting_combatant,
|
||||
Then: all pre-existing top-level keys remain (schema_version, round,
|
||||
acting_combatant,
|
||||
combatants, pairs, terrain, elevation) and the new keys
|
||||
(threatened, ranged_threatened, terrain_semantics,
|
||||
condition_definitions, initiative_order) are added — exact key set."""
|
||||
@@ -1063,6 +1064,7 @@ def test_observation_enriched_e5_existing_keys_preserved() -> None:
|
||||
|
||||
obs = _snapshot(engine, hero_state)
|
||||
assert set(obs) == {
|
||||
"schema_version",
|
||||
"round",
|
||||
"acting_combatant",
|
||||
"combatants",
|
||||
@@ -1138,7 +1140,7 @@ def _best_usable_attack(obs: Observation) -> dict[str, object] | None:
|
||||
return best
|
||||
|
||||
|
||||
def _enriched_llm_policy(obs: Observation, state: CombatantState) -> tuple[Action, ...]:
|
||||
def _enriched_llm_policy(obs: Observation) -> tuple[Action, ...]:
|
||||
"""Read ONLY the observation — never state.combatant.* nor engine.*."""
|
||||
cast = _castable_spell_action(obs)
|
||||
if cast is not None:
|
||||
|
||||
Reference in New Issue
Block a user