Problem
The "context-aware controller" capability is detected at runtime via getattr:
episode.py:56-63 does getattr(controller, "set_context", None) to decide whether to feed a context bucket.
scheduler.py:348 types the noise controller as object | list[object] | None because BaseController (Protocol at controller.py:12-22) does not express the set_context capability.
Proposal
Split the protocol:
class BaseController(Protocol):
def select(...) -> ...: ...
def update(...) -> ...: ...
class ContextualController(BaseController, Protocol):
def set_context(self, context: Hashable) -> None: ...
Then:
- Use
isinstance(controller, ContextualController) (with @runtime_checkable) instead of getattr probing.
- Replace
object | list[object] | None typing in scheduler.py:348 with the real protocol union.
Acceptance criteria
Problem
The "context-aware controller" capability is detected at runtime via
getattr:episode.py:56-63doesgetattr(controller, "set_context", None)to decide whether to feed a context bucket.scheduler.py:348types the noise controller asobject | list[object] | NonebecauseBaseController(Protocol atcontroller.py:12-22) does not express theset_contextcapability.Proposal
Split the protocol:
Then:
isinstance(controller, ContextualController)(with@runtime_checkable) instead ofgetattrprobing.object | list[object] | Nonetyping inscheduler.py:348with the real protocol union.Acceptance criteria
ContextualControllerprotocol introduced.getattr(..., "set_context", None)calls inepisode.pyreplaced withisinstancechecks.objecttyping inscheduler.pyreplaced with the proper protocol type.