Skip to content
19 changes: 18 additions & 1 deletion vero/src/vero/evaluation/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,22 +193,39 @@ async def evaluate_admin(
split: str,
commit: str,
sample_ids: list[int] | None = None,
model: str | None = None,
) -> Experiment:
"""Admin/verifier evaluation: explicit ``task``, no budget, no allowlist.

Unlike :meth:`evaluate` (which is bound to ``default_task`` and metered),
this scores an arbitrary ``(task, dataset_id, split)`` — including held-out
tasks/splits the agent never had access to. Used by the verifier to score
the selected commit on its configured targets.

``model`` overrides the executor model for this one eval (rides
``task_params`` so the eval strategy can honor it; the Mode-B
HarborRunner does, the Mode-A vero-task path ignores it). Used for
transfer targets: scoring the champion under a model it was NOT
optimized on.
"""
params = self.run_constraints
if model is not None:
params = params.model_copy(
update={
"task_params": {
**(params.task_params or {}),
"harbor_model_override": model,
}
}
)
return await self.evaluator.evaluate(
commit=commit,
dataset_id=dataset_id,
split=split,
task=task,
sample_ids=sample_ids,
db=self.db,
evaluation_parameters=self.run_constraints,
evaluation_parameters=params,
)

def status(self) -> dict[tuple[str, str], SplitBudget]:
Expand Down
1 change: 1 addition & 0 deletions vero/src/vero/harbor/build/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ def _serve_config(
"split": t.split,
"reward_key": t.reward_key,
"sample_ids": t.sample_ids,
"model": t.model,
}
for t in config.targets
],
Expand Down
6 changes: 6 additions & 0 deletions vero/src/vero/harbor/build/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ class TargetSpec(BaseModel):
split: str
reward_key: str = "reward"
sample_ids: list[int] | None = None
# Executor-model override for this target (transfer probe; Mode B only):
# score the selected commit AND the baseline under a model it was not
# optimized on, so model-specific couplings (measured live: hardcoded
# temperature=0 crashing 72/72 on an executor that rejects it) surface at
# finalize instead of one substrate away.
model: str | None = None


class _BuildConfigBase(BaseModel):
Expand Down
38 changes: 38 additions & 0 deletions vero/src/vero/harbor/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,32 @@ class HarborConfig:
# without running anything). None keeps the current behavior: the
# candidate env supplies harbor, and is trusted to.
harbor_requirement: str | None = None
# Bounded within-eval retry for infra-destroyed samples. A sample whose
# EVERY attempt died of a transient infrastructure cause (connection,
# timeout, rate limit, 5xx) was never measured at all: re-run it after a
# backoff instead of booking the outage as a permanent error. Measured
# live: a 65-second host DNS blip killed 44 of 72 attempts of one eval
# with ConnectionError, and nothing in the record distinguished the blip
# from a bad candidate.
#
# OFF BY DEFAULT, and it must stay off when the candidate is an
# adversarial optimizer. The qualifying predicate is built from exception
# types raised inside candidate code, and agents are stochastic: a
# candidate that raises an allowlisted exception whenever an attempt is
# going badly loses nothing on partially-good samples (its fakes
# zero-fill like honest failures) but converts every all-bad sample from
# a booked 0.0 into a fresh re-roll. That is one-sided selection over
# attempt sets, exactly what the zero-fill invariant exists to prevent.
# Enable only for trusted-candidate evaluations (frozen agents,
# operator-run matrices), where re-measuring an outage is pure signal
# recovery. Candidate crashes and exhausted key budgets never retry
# regardless (a crash is a result; a spent key cannot recover by
# waiting), and recovered samples carry an ``infra_retry`` audit marker.
infra_retry_rounds: int = 0
# Backoff before retry round N is N times this many seconds. Transient
# infra needs time, not immediacy: instant retries burned 6 of 8 run
# attempts inside one live DNS blip.
infra_retry_delay_s: float = 30.0
extra_args: list[str] = field(default_factory=list) # passthrough harbor run flags

def __post_init__(self) -> None:
Expand All @@ -50,6 +76,18 @@ def __post_init__(self) -> None:
f"aggregate_attempts must be 'best' or 'mean', got "
f"{self.aggregate_attempts!r}"
)
if self.infra_retry_rounds < 0:
raise ValueError(
f"infra_retry_rounds must be >= 0, got {self.infra_retry_rounds}"
)
# A zero (or negative) delay silently nullifies the backoff, and an
# instant retry re-enters the same outage: 6 of 8 run attempts once
# burned inside a single live DNS blip for exactly this reason.
if self.infra_retry_rounds > 0 and self.infra_retry_delay_s <= 0:
raise ValueError(
f"infra_retry_delay_s must be > 0 when infra retries are "
f"enabled, got {self.infra_retry_delay_s}"
)

@property
def is_registry(self) -> bool:
Expand Down
Loading