fix: [no-ticket] harbor - selection/finalize integrity (idempotency, tree pooling, retries, fail-safe floor)#33
Open
shehabyasser-scale wants to merge 2 commits into
Conversation
…retries, fail-safe floor) Five gaps in the champion-selection/finalize path, all observed or provoked live: 1. Idempotent finalize: the first completed result is cached and replayed on any retry. Re-running would re-rank against a DB that now contains the first finalize's own admin evals, so a retried finalize could crown a different champion than the one already reported. 2. Pooled shortlisting: recorded evals of the same commit average (not max), and commits with identical git TREES collapse into one candidate group. Max-over-rows made every re-measurement an independent lottery draw; one live optimizer farmed empty re-commits as 'clean independent lottery tickets', another refused to re-measure its champion to protect a lucky draw. Pooling makes re-measurement variance-reducing and stops identical content from stuffing the top-K shortlist. 3. Every reward-critical finalize eval (targets, shortlist re-scores, floor, baseline) retries transient failures; targets that persistently fail are floored WITH a durable target_errors marker instead of aborting finalize (a trial that ships no reward.json loses its result: happened live to an 8-hour run on a disk-full host). 4. All-error evals (score(fill_score=None) is None) retry like exceptions: an outage must never quietly become a measured 0.0. 5. Fail-safe floor: when the baseline itself cannot be measured, revert to the seed rather than shipping an unverified candidate (the floor exists to stop shipped regressions; skipping it re-opens that hole). Plus: verifier_timeout build field sizes Harbor's [verifier] timeout_sec for the whole finalize battery (the old value covered ~one eval, so Harbor could kill finalize mid-flight), and GitWorkspace.tree_hash() resolves commit content identity for the pooling. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…the loop (review follow-up) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
f047e22 to
fdef2d2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Hardens the champion-selection/finalize path against the failure modes and exploits observed in the Pawn campaign:
GitWorkspace.tree_hash). Two live optimizers Goodharted max-over-rows selection in opposite directions: one farmed empty re-commits as "clean independent lottery tickets for max-based selection" (its words), the other refused to ever re-measure its champion to protect a lucky 0.3 draw. Pooling makes re-measurement variance-reducing, and tree-dedup stops identical content from stuffing the top-K shortlist or collecting several admin re-score draws.target_errorsmarker in the finalize payload instead of aborting finalize: a trial that ships no reward.json loses its result entirely, which happened live to an 8-hour run on a disk-full host.score(fill_score=None) is None) is an outage, indistinguishable from infrastructure failure, and is retried like an exception.verifier_timeoutbuild field: Harbor's[verifier] timeout_secmust cover the whole finalize battery (up to top-K re-scores + floor + targets + baseline attempts, each a full nested eval in Mode B), not one eval. Defaults totimeoutfor backward compatibility; docstring gives the sizing formula.Tests
tests/test_harbor_verifier.py: 34 pass, including 7 new pins: idempotent replay, same-commit pooling to mean, identical-tree dedup (no shortlist stuffing, one re-score per tree), floor fail-safe, target retry-then-floor-with-marker, transient recovery, all-error-retries. Build/serve suites green (43 pass).Stack
Based on #32 (
harbor-5-scoring-integrity). Part of the pre-paper hardening series.🤖 Generated with Claude Code
Greptile Summary
This PR hardens the Harbor champion-selection and finalize path against failure modes observed in the Pawn campaign, introducing idempotency, pooled shortlisting, bounded retries for every reward-critical eval, and a fail-safe floor.
asyncio.Lock+ instance-level cache ensures a replayed/finalizecall returns the first completed result verbatim, preventing re-ranking against a DB already polluted by the first run's admin evals.GitWorkspace.tree_hash, preventing tree-stuffing of the top-K shortlist._admin_eval_scoreretry helper: all reward-critical evals (targets, shortlist re-scores, floor, baseline) now share a bounded-retry path that treats all-error evals as outages rather than silently zeroing them; persistent failures floor the target reward with a durabletarget_errorsmarker soreward.jsonalways ships.Confidence Score: 5/5
Safe to merge; all changed paths have targeted test coverage and the fail-safe defaults are conservative.
The finalize logic is thoroughly tested with 7 new pins covering every new invariant. The idempotency cache is correctly scoped to the Verifier instance and serialized with an asyncio.Lock. Tree pooling and per-commit mean averaging are straightforward pandas operations with correct groupby semantics. The two non-blocking observations do not affect correctness.
vero/src/vero/harbor/verifier.py — the _maybe_score_baseline retry loop is the only path that does not use the new _admin_eval_score helper, leaving it with subtly different retry semantics; worth unifying in a follow-up.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[finalize call] --> B{_finalize_result cached?} B -- yes --> C[Return cached result idempotent] B -- no --> D[Acquire asyncio.Lock] D --> E[_select_commit] E -- submit mode --> F[Read submission.json] E -- auto_best mode --> G[get_experiments_df] G --> H[Filter split / dataset / exclude base_commit] H --> I[Per-commit mean pooling] I --> J[Resolve tree hashes] J --> K[Tree dedup pooling] K --> L[Rank and shortlist top-K] L --> M[Admin re-score each shortlisted commit] M --> N{auto_best_baseline_floor?} N -- yes --> O[Score baseline with retry] O -- None returned --> P[Fail-safe: return base_commit] O -- score returned --> Q{best_score > base_score?} Q -- no --> P Q -- yes --> R[Return best_commit] N -- no --> R F --> S[sha = selected commit] R --> S P --> S S --> T[Score each target on sha with retry] T -- score returned --> U[rewards key = score] T -- None after all retries --> V[rewards key = floor + target_errors marker] U --> W[_maybe_score_baseline optional] V --> W W --> X[Cache and return result]%%{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[finalize call] --> B{_finalize_result cached?} B -- yes --> C[Return cached result idempotent] B -- no --> D[Acquire asyncio.Lock] D --> E[_select_commit] E -- submit mode --> F[Read submission.json] E -- auto_best mode --> G[get_experiments_df] G --> H[Filter split / dataset / exclude base_commit] H --> I[Per-commit mean pooling] I --> J[Resolve tree hashes] J --> K[Tree dedup pooling] K --> L[Rank and shortlist top-K] L --> M[Admin re-score each shortlisted commit] M --> N{auto_best_baseline_floor?} N -- yes --> O[Score baseline with retry] O -- None returned --> P[Fail-safe: return base_commit] O -- score returned --> Q{best_score > base_score?} Q -- no --> P Q -- yes --> R[Return best_commit] N -- no --> R F --> S[sha = selected commit] R --> S P --> S S --> T[Score each target on sha with retry] T -- score returned --> U[rewards key = score] T -- None after all retries --> V[rewards key = floor + target_errors marker] U --> W[_maybe_score_baseline optional] V --> W W --> X[Cache and return result]Reviews (2): Last reviewed commit: "fix(harbor): a None aggregate score cons..." | Re-trigger Greptile