Apply the PF1e per-turn action budget (CRB Actions in Combat: one
standard + one move, or one full-round in place of both, plus one
swift) unconditionally to every sequence a policy returns, not only
the immediate/staggered/nauseated specializations.
- _action_economy_filter: greedy in-order walk keeping the earliest
legal prefix-subsequence; a full-round action is only kept while
both the standard and move slots are still free.
- Exhaustive kind->slot classification (standard / move / swift /
full_round / free-conditional) over the Action.kind Literal,
documented and locked by tests.
- Surplus actions dropped and reported as
ActionResult(ok=False, reason="downgraded:action_economy") via a
multiset drop diff (reports duplicate-kind surplus the set-based
_dropped_kinds cannot).
- Composition order: general budget first, then the existing
immediate/staggered/nauseated filters (unchanged).
- default_policy untouched and its turns verified legal by the
validator; golden transcripts byte-identical.
Movement budget is now max(land, fly) speed. Updates the harvie golden: Harvie (land 10 / fly 80) is a flyer and now flies at 80 ft, so he wins vs the goblin instead of being pinned crawling at 10 ft.
- New Action kind activate_power_attack: free action that sets
state.power_attack = True when the combatant has the Power Attack feat;
logged no-op otherwise. Routed via _execute_movement (free-action branch)
to keep _execute under the C901 complexity ceiling.
- Consume deed_up_close_and_deadly in resolve_attack: the first attack
resolved while the flag is set gains +1d6 if it is a ranged attack within
30 ft; the flag is reset in all cases (benefit lost if out of range or
not ranged). Extracted into _consume_up_close_and_deadly helper.
- Document activate_power_attack, corrected deed semantics, and the
power_attack snapshot key in README.
- Add tests/test_engine_fixes.py (5 tests): PA activation, no-feat no-op,
deed+ranged within 30ft, deed out-of-range, deed melee.
Rename monster.py → combatant.py with _KNOWN_FEATS lookup table
that resolves feat name strings to AbilitySpec constants (Power Attack,
Iron Will, Toughness, Weapon Focus, etc.). Unknown feats produce
minimal AbilitySpec (data preserved for future implementation).
Migrate all 8 PC JSONs (data/pcs/) with complete feat/spell lists
extracted from Foundry sheets — including traits, racial abilities,
and all spells. Monsters (goblin, orc) unchanged (no feats/spells).
Update all imports, add backward-compatible aliases (load_monster,
MonsterLoadError), update CLI to use load_combatant.
Gate: 377 tests, ruff clean, basedpyright clean.