The kernel's flagship functions take the creature they act on as object or Any: 36 such parameters in core/combat.py, 43 in core/spells.py, 15 in core/effects.py, and 2 in core/items.py (attacker, defender, target, combatant, caster, reader, carrier). After the docstring pass (#87 to #99) every one says in prose that a Character or a MonsterInstance satisfies it, but the signature still says nothing: pyright cannot check a caller, the reference cannot link the type, and a reader has to trust prose over the annotation. The pre-Phase-7 audit named this and it was never done.
Proposed shape: a Combatant protocol in osrlib.core listing the attributes the kernel reads through those parameters (id, armour_class, current_hp, max_hp, hit_dice, level, the ability-modifier accessors, and whatever the getattr chains pull), with narrower protocols where a function needs more (a caster with spell slots, a reader with an inventory). Character and MonsterInstance satisfy them structurally, so no class changes. Annotate the parameters with the protocol and keep the runtime behaviour identical.
Contract-first chunk: write the protocol stubs and a test that asserts Character and MonsterInstance satisfy each one and that pyright accepts every existing call site, then annotate. Widening an object parameter to a protocol is not a breaking change under semver.
The kernel's flagship functions take the creature they act on as
objectorAny: 36 such parameters incore/combat.py, 43 incore/spells.py, 15 incore/effects.py, and 2 incore/items.py(attacker, defender, target, combatant, caster, reader, carrier). After the docstring pass (#87 to #99) every one says in prose that aCharacteror aMonsterInstancesatisfies it, but the signature still says nothing: pyright cannot check a caller, the reference cannot link the type, and a reader has to trust prose over the annotation. The pre-Phase-7 audit named this and it was never done.Proposed shape: a
Combatantprotocol inosrlib.corelisting the attributes the kernel reads through those parameters (id,armour_class,current_hp,max_hp,hit_dice,level, the ability-modifier accessors, and whatever thegetattrchains pull), with narrower protocols where a function needs more (a caster with spell slots, a reader with an inventory).CharacterandMonsterInstancesatisfy them structurally, so no class changes. Annotate the parameters with the protocol and keep the runtime behaviour identical.Contract-first chunk: write the protocol stubs and a test that asserts
CharacterandMonsterInstancesatisfy each one and that pyright accepts every existing call site, then annotate. Widening anobjectparameter to a protocol is not a breaking change under semver.