3. Operator policy
The second decision of every step, which operator is applied to the chosen node.
The operator policy (πop) gets what the selector policy chose and names the operator to apply to it. The base class already does the plain mapping:
| the selector chose | the operator |
|---|---|
| nothing | draft, a fresh attempt |
| a failing candidate | debug |
| several candidates to combine | ensemble |
| a scored candidate | improve |
So a policy of your own only has to say where it differs:
# drafts_only.py
from hillclimb.sdk import Action, OperatorPolicy
class DraftsOnly(OperatorPolicy):
"""Never improves: whatever was chosen, draft (but repair a failing candidate first)."""
def propose(self, state, selection):
node = state.journal.candidates[selection.target_id] if selection is not None else None
if node is not None and node.status in ("failing", "buggy"):
return Action(operator="debug", target_id=node.candidate_id)
return self.draft_action(state)hillclimb climber check --climber drafts_only.py
hillclimb run circle-packing --climber drafts_only.pypropose(state, selection) returns one Action (an operator and the candidate it starts from),
or None to wait for a result in flight. selection is None for a root step. state is
everything the policy sees: the history (with holdout scores hidden), what is in flight, the
budget left, and which direction is better. draft_action and expand_action(state, selection, operator=...) build the usual actions, and observe(state, candidate) is called for every
result.
The built-in one
greedy is the plain mapping plus one decision of its own: before improving a chosen
candidate that declared tunable parameters, it spends a few tune trials on it. Its settings go in
params:
| setting | default | what it does |
|---|---|---|
tune_budget | 8 | extra trials per candidate, 0 turns tuning off |
tune_gate | band | which candidates get tuned: within the noise band of the best, only the best, or always |
tune_parallel | 1 | tune trials in flight per candidate |
tune_burst | 2 | tune trials between agent attempts, so tuning does not crowd out improving |
Three rules
- A policy only ever reads. It must not write to the history or to a candidate's folder.
- Every decision must follow from the history: compute it from
state, or rebuild your caches inobserve. That is what makeshillclimb resumework. - A setting the block passes must be one the class declares in
DEFAULTS, so a typo is an error before anything is spent.