Skip to content

[GDPVal] Make partial-calibration waivable failure classes configurable - #2704

Merged
Kh4L merged 1 commit into
spanev/gdpval-production-hardeningfrom
agronskiy/gdpval-partial-waivable-classes
Aug 24, 2026
Merged

[GDPVal] Make partial-calibration waivable failure classes configurable#2704
Kh4L merged 1 commit into
spanev/gdpval-production-hardeningfrom
agronskiy/gdpval-partial-waivable-classes

Conversation

@agronskiy

Copy link
Copy Markdown
Contributor

On top of #2588, PartialStagePolicy.waivable_failure_classes becomes a configurable tuple instead of being hardcoded to ("timeout_exceeded",). The default is unchanged, so existing configs behave identically.

Change

  • Commit: 36767c8052c76c7047551262e19fb1afd481233e, branched off 14125b7650317b1d1269987c840ea70deba7e5f4 (the tip of [GDPVal] Harden production rollout and judging paths #2588 at the time).
  • Files touched: resources_servers/gdpval/multistage_elo.py (new field), resources_servers/gdpval/multistage_orchestrator.py (7 edits: policy record now emits newly_waivable_failure_classes; the waiver check consults the configured set; _WAIVABLE_FAILURE_CLASSES validation frozenset is {"timeout_exceeded", "transient"}; snapshot revalidation maps the record key newly_waivable_failure_classes back to the config key waivable_failure_classes), plus 3 new tests in resources_servers/gdpval/tests/test_multistage_orchestrator.py (test_transient_omission_advances_when_explicitly_waivable, test_unknown_waivable_failure_class_is_rejected, test_empty_waivable_failure_classes_is_rejected).
  • Test status: 313 passed, 9 skipped across resources_servers/gdpval/tests/.

Motivation

The operative blocker was NOT the waiver scope: partial_completion is opt-in and was never set, so multistage_orchestrator.py:1090 took the strict branch and required every planned non-final rollout to carry battle evidence. That cost two GDPVal-AA-v2 runs their entire stage 2 (dc6c776f3af506df and 6c5ee8b81773b2b6, 44/220 each, eval_elo null). Once partial_completion is enabled, a transient judge failure still stopped the stage because the waiver set was hardcoded — which this PR fixes.

Production evidence

From Nemotron Super 3.5 run 590aeb2c72b1de1d on this commit:

  • stage 1 log line: [multistage-elo] stage 1/2 accepted partial calibration: success coverage 91.1%, omitted 4 rollout(s)
  • omitted rollouts and classes: task 78 timeout_exceeded, task 155 timeout_exceeded, task 159 timeout_exceeded, task 136 transient
  • success_fraction 0.9111, worst per-reference 0.8, multistage_state stage-0 status partial_complete
  • stage 2 then ran to completion: eval ELO = 737.5 over 4 refs. The predecessor run died at 44/220 with eval_elo null.
  • The run also spanned a slurm TIMEOUT resume, and the partial stage was correctly reloaded: [multistage-elo] resuming multi-stage run from cache (fingerprint match) and [multistage-elo] stage 1/2 reused partial result from cache: eval ELO = 627.4 (41 cached rollout(s)) — so the newly_waivable_failure_classes <-> waivable_failure_classes round-trip works across legs.
  • A second run on the same commit, Qwen3.5-122B-A10B df318339a24e9384, completed at eval ELO = 882.6 with a clean 45/45 stage-1 close (the waiver path did not fire there).

Pre-merge validation

Against 36767c80, with policy {min_success_fraction: 0.9, min_per_reference_success_fraction: 0.5, min_successful_rows_per_reference: 1, waivable_failure_classes: [timeout_exceeded, transient]} and a synthetic 45-rollout stage: an omission of class timeout_exceeded accepts at success_fraction 0.9778, transient accepts at 0.9778, and legitimate is rejected.

🤖 Generated with Claude Code

Partial calibration could only ever waive `timeout_exceeded`. A single
unresolved `transient` rollout — in practice one judge 400, e.g. the
empty-PDF "The document has no pages." from gemini-3.1-pro, which has no
model-group fallback — therefore held the whole run at stage 1.

Observed on invocation dc6c776f3af506df (Qwen3.5-122B-A10B, GDPVal-AA-v2):
45/45 stage-1 rollouts collected in 26 min, one `transient` row unresolved,
`stage 1/2 remains incomplete (1 rollout(s) retryable or undispatched)`,
stage 2 never planned, `comparison/eval_elo` null, 44 of 220 tasks delivered.

`PartialStagePolicy` gains `waivable_failure_classes`, defaulting to
`("timeout_exceeded",)` so existing behaviour is unchanged. The class is
validated against a closed set; `skipped` stays excluded because an unusable
sample is not evidence. The durable snapshot records the set and revalidation
compares it, so widening the policy correctly invalidates a frozen outcome.

A waived row is still omitted from the fit and still counts against every
coverage floor — this bounds which causes are acceptable, not how much
evidence may be missing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Alex Gronskiy <agronskiy@nvidia.com>
@Kh4L

Kh4L commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

LGTM! Thanks

@Kh4L
Kh4L merged commit d3f146d into spanev/gdpval-production-hardening Aug 24, 2026
26 checks passed
@Kh4L
Kh4L deleted the agronskiy/gdpval-partial-waivable-classes branch August 24, 2026 18:57
agronskiy added a commit that referenced this pull request Aug 27, 2026
The nemo_gym edit was redundant. The verify response already stamps the generic
_ng_failure_terminal flag, and _is_terminal_failure falls through to

    return bool(record.get(NG_TERMINAL_KEY))

so teaching it a GDPVal-specific class name changed nothing. Reverted
nemo_gym/rollout_collection.py and its test, moved
REFERENCE_MISSING_FAILURE_CLASS into resources_servers/gdpval/app.py, and
relocated the terminal/success tests alongside the rest of the gdpval suite.

Better layering on its own terms -- reference_missing is GDPVal vocabulary and
the harness only needs the generic flag -- and it also fixes CI. Touching
nemo_gym/ flipped unit-tests.yml from the server-only path onto a full run, whose
Test job enforces fail_under=96 while measuring the whole repo. The gdpval
package is ~2300 uncovered statements there because its tests run in the separate
sharded server-suite job, whose coverage is never merged in, so the gate reported
81.55% and failed with 356 passed and 0 failures. Confining the diff to
resources_servers/gdpval keeps the run on the server path, which is how PR #2704
passed.

Signed-off-by: Alex Gronskiy <agronskiy@nvidia.com>
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