anatomy / operatorshillclimb

Operators

How one attempt is made. The draft, debug, improve, ensemble scaffolds.

Operator registry: name -> class, mirroring policies and tuners.

The registry is the harness's operator vocabulary: candidate.OPERATORS, policy check's known operators and the route preflight all derive from it, so registering an operator is the only step that makes it usable.

src/hillclimb/modules/operators/base.py
class Operator(ABC):
    """One way of making an attempt. Subclasses set `name` and `role` and
    implement `prepare`; `params` is the operator's own configuration."""

    name: str
    role: str
    needs_target: bool = False

    def __init__(self, params: Mapping | None = None):
        self.params = dict(params or {})

    def valid_target(self, target: Candidate | None) -> str | None:
        """Why this operator cannot run on `target`, or None when it can.
        Checked by the harness before anything is created or spent."""
        if self.needs_target and target is None:
            return f"{self.name} without a target_id"
        return None

    @abstractmethod
    def prepare(self, ctx: OperatorContext) -> Preparation: ...