Skip to content

fix: [no-ticket] harbor - honest measurement signals (summary qualifiers, versioned re-evals, dead-attempt causes)#35

Merged
varunursekar merged 13 commits into
harbor-7-access-tiersfrom
harbor-8-honest-signals
Jul 17, 2026
Merged

fix: [no-ticket] harbor - honest measurement signals (summary qualifiers, versioned re-evals, dead-attempt causes)#35
varunursekar merged 13 commits into
harbor-7-access-tiersfrom
harbor-8-honest-signals

Conversation

@shehabyasser-scale

@shehabyasser-scale shehabyasser-scale commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #34 (harbor-7-access-tiers). Part of the pre-paper hardening series; this is the "recorded numbers say what they are" group.

Changes

1. summary.json qualifies its mean (server.py). The agent-visible aggregate now carries n_scored, n_errored, and score_se beside mean_score. A mean over 3-of-18 scored samples, or one dominated by errored zero-fills, is a different measurement than a clean full-split mean; the agent (deciding whether a delta is real) and any auditor (reading the run afterward) should both see that without per-sample access. All three are label-safe aggregates, so they ride the same non_viewable-safe summary as before.

2. Enum value, not repr (server.py). "status": str(experiment.result.status) serialized as "ExperimentResultStatus.SUCCESS" (str-mixin enums lost their value-__str__ in py3.11). Now writes .value -> "success".

3. Versioned result dirs (server.py). Result dirs were keyed on (split, commit[:12]) and wiped on re-eval, so a repeat measurement of the same commit erased the agent's earlier evidence; repeat measurements (multifidelity confirms, champion re-evals under a noisy scorer) are exactly the ones worth comparing. Each metered eval now gets {split}__{commit12}__eN and result_path in the response names the dir for that eval. The stale-file hazard the wipe used to prevent is gone structurally: no two evals share a dir.

4. Dead-attempt causes (runner.py). Mean-mode collation zero-fills attempts that died before scoring and counts them in n_dead; it now also records dead_exception_types (a per-cause count) in the sample output. Causes are not interchangeable: rate-limit deaths are infra noise that retunes with capacity, crashes point at the candidate, and deaths cluster hard by cause in practice (measured on 46 historical runs: 110 of 129 UnicodeDecodeError deaths sat on two never-solved tasks). Kept out of metrics (float-valued by contract) and rides the same viewable-only exposure path as attempt detail.

Tests

  • summary qualifiers: n_scored/n_errored/score_se/mean asserted on a known score vector; status == "success".
  • versioned re-evals: two evals of one commit -> __e1 and __e2, both dirs alive.
  • dead causes: recorded exception type + no-exception death bucketed as no_rewards_recorded; all-clean samples carry no key.
  • 81 passed on the affected files; existing dest assertions updated to the versioned naming.

🤖 Generated with Claude Code

Greptile Summary

This PR hardens the measurement-signal layer across four independent axes: summary.json now carries n_scored, n_errored, and mean_score_se so a thin-sample or error-dominated mean is legible without per-sample access; metered result dirs gain a per-eval __eN ordinal so repeat measurements (multifidelity confirms, champion re-evals) coexist on disk rather than the second erasing the first; dead attempt causes are recorded per exception type in dead_exception_types so n_dead is no longer opaque to infra vs candidate failures; and a bounded, off-by-default infra-retry path lets trusted-candidate evaluations recover from transient outages without exposing an adversarial re-roll surface.

  • summary.json qualifiers: n_scored/n_errored/mean_score_se are zero-fill–consistent (SE is of the same population as mean_score), and status now serialises as .value fixing the ExperimentResultStatus.SUCCESS string regression on Python 3.11+.
  • Versioned result dirs + restart resume: _eval_seq is scanned from surviving __eN dirs on the first route call after a restart, so old-session evidence is never silently wiped.
  • Dead-attempt causes + infra retry: the conservative allowlist, adversarial opt-out (infra_retry_rounds = 0 by default), sibling-dir isolation, and per-recovery audit markers (infra_retry) are all in place; key-budget exhaustion is correctly classified as non-retryable infra.
  • Additional clean-ups: free-baseline claim-before-await race fix, ledger fail-closed on corrupt file, k_anonymity_floor default aligned to sidecar enforcement, transfer-target model override wired end-to-end, instruct_exhaust_budget ablation lever.

Confidence Score: 5/5

Safe to merge; all changes add measurement-signal qualifiers, fix a serialisation regression, and introduce an opt-in recovery path — none touch any critical hot path without proper guards.

The four axes of change (summary qualifiers, versioned dirs, dead-cause classification, infra retry) are each independently scoped and extensively tested (81 assertions on affected files). Security-sensitive properties — adversarial opt-out for infra retry, zero-score-fill regardless of classification, conservative exception allowlist, sibling-dir isolation — are correct and explicitly verified. Two minor audit-record issues exist but neither affects scores, recovery correctness, or the agent-visible data.

runner.py _retry_transient_infra — the discard_history accumulation timing is worth a follow-up in a multi-round, no-output-round scenario.

Important Files Changed

Filename Overview
vero/src/vero/harbor/server.py Versioned result dirs (__eN), summary.json qualifiers (n_scored/n_errored/mean_score_se), enum .value fix, free-baseline claim-before-await race fix — logic all correct and well-tested.
vero/src/vero/harbor/runner.py Dead-attempt cause classification, n_dead_infra metric, key-budget alarm, and bounded infra retry — infra vs candidate distinction is sound; security guards (adversarial-opt-out, no score excusal, no key-budget retry) are all in place.
vero/src/vero/harbor/serve.py Ledger fail-closed on corrupt file (was: silent refund), instruct_exhaust_budget field added — both changes are correct and the fail-closed behaviour is now tested.
vero/src/vero/harbor/verifier.py _admin_eval_score now returns (score, cause) tuple, _dominant_sample_errors produces a task-normalised frequency summary, transfer-target model override wired through baseline and champion evals.
vero/src/vero/evaluation/engine.py evaluate_admin gains model= parameter — correctly uses model_copy to produce a one-shot params object without mutating shared run_constraints.
vero/src/vero/harbor/config.py infra_retry_rounds / infra_retry_delay_s added with correct validation (negative rounds rejected; zero delay forbidden when retries enabled).
vero/src/vero/harbor/protocol.py k_anonymity_floor default corrected from 1 to 5 to match EvaluationSidecar's enforcement default — prevents agents seeing a laxer floor than the sidecar enforces.
vero/src/vero/harbor/build/config.py TargetSpec.model and instruct_exhaust_budget fields added; both are nullable/defaulted correctly and flow through to serve.json.
vero/tests/test_harbor_runner.py Comprehensive TestInfraResilience suite covering off-by-default, sibling-dir invariant, no-trial-round persistence, partial recovery, multi-round backoff, mixed-cause guard, and candidate-crash/key-budget non-retry.
vero/tests/test_harbor_server.py TestHonestSummary covers qualifiers + SE value, versioned re-eval dirs, and volume-reuse ordinal resume; TestFreeBaselineEval gains the claim-before-await concurrency test.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[produce_sample_results] --> B[_run_harbor\ninitial jobs_dir]
    B --> C[_collate\njobs_dir]
    C --> D{infra_retry_rounds > 0?}
    D -- No --> Z[Done]
    D -- Yes --> E[_retry_transient_infra]
    E --> F{for each round}
    F --> G[_transient_infra_dead\nper sample]
    G --> H{any retryable?}
    H -- No --> Z
    H -- Yes --> I[asyncio.sleep\nN x delay_s]
    I --> J[_run_harbor\nsibling round_dir]
    J --> K[_load_trials\nround_dir]
    K --> L{any produced?}
    L -- No --> F
    L -- Yes --> M[_collate\nround_dir only]
    M --> N[_mark_recovered\nadd infra_retry audit marker]
    N --> F
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[produce_sample_results] --> B[_run_harbor\ninitial jobs_dir]
    B --> C[_collate\njobs_dir]
    C --> D{infra_retry_rounds > 0?}
    D -- No --> Z[Done]
    D -- Yes --> E[_retry_transient_infra]
    E --> F{for each round}
    F --> G[_transient_infra_dead\nper sample]
    G --> H{any retryable?}
    H -- No --> Z
    H -- Yes --> I[asyncio.sleep\nN x delay_s]
    I --> J[_run_harbor\nsibling round_dir]
    J --> K[_load_trials\nround_dir]
    K --> L{any produced?}
    L -- No --> F
    L -- Yes --> M[_collate\nround_dir only]
    M --> N[_mark_recovered\nadd infra_retry audit marker]
    N --> F
Loading

Reviews (2): Last reviewed commit: "Merge pull request #36 from scaleapi/har..." | Re-trigger Greptile

…alue, versioned re-evals, dead-attempt causes)

Four small fixes so recorded numbers say what they are:

1. summary.json now carries n_scored, n_errored, and score_se beside
   mean_score: a mean over 3-of-18 scored samples, or one dominated by
   errored zero-fills, is a different measurement than a clean
   full-split mean, and both the agent and any auditor should see that
   without per-sample access. All three are label-safe aggregates.

2. summary.json status now writes the enum VALUE ("success"), not
   str(enum) ("ExperimentResultStatus.SUCCESS").

3. Result dirs are versioned per eval ({split}__{commit12}__eN instead
   of wipe-and-rewrite keyed on (split, commit)): repeat measurements of
   one commit (multifidelity confirms, champion re-evals) are exactly
   the evidence worth comparing, and the second eval erased the first.

4. Mean-mode collation records dead_exception_types per sample: n_dead
   alone hides WHY attempts died, and cause matters (rate-limit deaths
   are infra noise, crashes point at the candidate; measured live,
   110/129 UnicodeDecodeError deaths sat on two never-solved tasks).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread vero/src/vero/harbor/server.py Outdated
Comment thread vero/src/vero/harbor/server.py Outdated
…fails closed on corrupt restore)

Two operational fixes:

1. instruct_exhaust_budget (default True): the instruction's "unspent
   budget is wasted" persistence bullet becomes a build-config lever,
   like instruct_multifidelity. On preserves current behavior; off makes
   stopping-early the agent's own choice, which is the ablation arm for
   measuring what the exhortation itself contributes to optimizer
   persistence. The "scores are noisy" fact stays unconditional.

2. _load_or_build_ledger fails CLOSED on a persisted ledger that exists
   but cannot be parsed: metered budgets restore with zero remaining,
   and the unreadable file is preserved as ledger.corrupt for the
   operator. The old fallback restored the CONFIGURED budgets, which
   refunded the agent everything already spent, so any crash that
   corrupted the flush minted budget. A missing file is still a fresh
   boot; admin and finalize are unaffected either way.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
shehabyasser-scale and others added 11 commits July 9, 2026 02:39
Two Greptile P2s on #30, fixed at the stack tip:

- An empty transcript file no longer surfaces as "" feedback: empty
  candidates are skipped (an empty pane falls through to the
  trajectory), and if everything is empty the search moves to the next
  failed attempt rather than short-circuiting on "".

- The no-verifier-rewards error branch (agent died before scoring) now
  attaches the failure transcript like any failed sample: a candidate
  edit that crashes the agent lands exactly here, and the transcript is
  the only way the optimizer can see the crash it caused.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… floor default, ordinal resume, SE naming)

- Free-baseline flag is claimed BEFORE the eval await and refunded on
  failure: setting it only after success reopened a window where two
  concurrent baseline evals both resolved free (asyncio interleaves at
  await points). Claim-then-refund keeps both properties: concurrent
  callers see the claim, and a failed eval does not burn the freebie.

- build_status defaults k_anonymity_floor to 5, matching the sidecar's
  enforcement default: a caller that forgets to pass the floor must not
  advertise a laxer one than gets enforced.

- _route_results resumes the eval ordinal past surviving __eN dirs on a
  reused volume: a restarted sidecar started back at e1 and silently
  wiped the prior session's evidence, the exact erasure the versioned
  dirs exist to prevent.

- score_se renamed to mean_score_se and documented: it is the SE of the
  zero-filled mean_score over n_samples, not of the n_scored subset.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e crash vs infra outage)

Measured live in the transfer matrix: three champions scored 0/72 on an
off-model executor because their optimizers hardcoded temperature=0,
which that provider rejects. The harness handled it safely (rewards
floored, finalize shipped) but the durable record could not say WHY, and
"why" decides opposite actions: a deterministic candidate crash is a
real, reportable portability failure; an infra outage means invalidate
and re-run. Two changes:

- Collation's no-verifier-rewards error string now names the dead
  attempts' exception types ("attempts died: UnsupportedParamsError
  x6"). The error string is the one field that flows to the DB, the
  per-sample files, and the verifier.

- _admin_eval_score returns (score, failure_cause); a floored target's
  target_errors entry carries the dominant per-sample causes (frequency
  summary, top 3). Diagnostics are fail-safe: any surprise result shape
  degrades to a fixed string, never fails finalize.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ask crashes cluster

Greptile follow-up on #37: _dominant_sample_errors keyed on the raw error
string, which embeds the task name, so identical exceptions across a
multi-task slice landed in 1x singletons instead of one dominant cause.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… finalize

Home-model evals cannot see model-specific couplings the optimizer bakes
in. Measured live in the wave-1 transfer matrix: three of five champions
independently hardcoded temperature=0 (a variance trick on their home
model, gpt-4.1-mini) and scored 0/72 on claude-opus-4-8, which rejects
it, while looking healthy on every eval the optimization loop ever ran.
The portability failure was invisible until a separate, manual,
after-the-fact probe.

VerificationTarget gains `model`: a target with an executor override
scores the selected commit under a model it was NOT optimized on, in the
same finalize battery as its home-model reward. The baseline is scored
under the same override so the comparison stays like-for-like. Plumbing:
build.yaml TargetSpec -> compiler -> serve.json _TargetCfg ->
VerificationTarget -> engine.evaluate_admin(model=...) -> task_params
["harbor_model_override"] -> HarborRunner -m flag. The override rides
task_params, so Mode A ignores it and the runner needs no new state; the
shared run_constraints are copied, never mutated.

Test fakes of evaluate_admin widened to accept the new kwarg (a strict
signature turned the new call into a retried TypeError, flooring
rewards, which is itself a nice demonstration of the floor's fail-safe).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…utage retry, key-budget alarm

Three infra failure modes measured live in the E5 matrix runs, fixed at
the measurement layer:

- Dead attempts are classified infra vs candidate (conservative
  exception-type allowlist + the litellm key-budget message signature).
  Labels flow into dead_exception_types, error strings, and a new
  n_dead_infra metric. Classification never moves a score: every dead
  attempt still zero-fills, or faking infra would excuse failures.
  Exception type names are candidate-authored, so brackets are
  neutralized before labeling (a class named 'XError[infra]' cannot
  walk in pre-suffixed).

- An OPT-IN, bounded, backoff-spaced within-eval retry re-measures
  samples whose every attempt died of a transient infra cause (the
  65-second DNS blip that killed 44/72 attempts of one eval). Off by
  default: against an adversarial optimizer the qualifying predicate is
  a re-roll lever, since a stochastic candidate that raises allowlisted
  exceptions on failing attempts converts all-bad rounds into fresh
  draws. Retry rounds run in fresh sibling jobs dirs (nesting would
  pool dead attempts into later resumed means) and recovered samples
  carry an infra_retry audit marker naming the discarded attempts.

- An ERROR-level alarm names key-budget exhaustion (a spent key fails
  every later call identically; two matrix cells of budget-exceeded
  zeros were nearly booked as a portability finding), with hedged
  wording since the signature reads candidate-process exceptions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d audit history

- HarborConfig rejects infra_retry_delay_s <= 0 when retries are enabled
  (a zero delay silently nullified the backoff) and negative
  infra_retry_rounds.
- The infra_retry audit marker now lists EVERY discarded round in order
  (discarded_rounds), not just the one immediately before recovery;
  recovered_round names when the sample finally measured.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat: [no-ticket] harbor - infra resilience (dead-attempt classification, opt-in outage retry, key-budget alarm)
feat: [no-ticket] harbor - transfer targets (per-target executor-model override at finalize)
fix: [no-ticket] harbor - floored rewards name their cause (candidate crash vs infra outage)
fix: [no-ticket] harbor - ops integrity (exhaust-budget instruction lever, fail-closed ledger restore)
# try one more variant) instead of stopping early. On by default (current
# behavior); off makes stopping-early a choice the agent arrives at itself,
# which is the ablation arm for measuring what the exhortation contributes.
instruct_exhaust_budget: bool = True

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems tied to the particular instruction template? should this be in a general config?

@varunursekar
varunursekar merged commit d4e0513 into harbor-7-access-tiers Jul 17, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants