feat(sbd-v6): declare the axis a session graded on, and grade the conc sweep on it too - #1520
Merged
Merged
Conversation
…re it is resolved Nothing in session_breakdown.json said which axis its gains were on. An AgentX replay is ranked on the slow-tail interactivity percentile with total throughput held as a guard; a synthetic run is ranked on output throughput alone. Every throughput field in the document is the output axis by construction, benchmark_mode reaches reports/final.json but never this document, and on the canonical corpus the two axes differ by roughly two orders of magnitude -- so a consumer had no way to keep an AgentX session out of a synthetic leaderboard. It would sort one against the other and every number would look plausible. metadata.grading declares what the session was configured to rank on: benchmark_mode, objective, and the tput_guard that rides along with the interactivity objective. outcome.validation.graded_on is the separate fact of what the run actually decided its last promotion on, read off that promotion; the two differ whenever a comparison could not supply the axis pair and fell back to output. Neither resolves the other. Publishing the configured axis in outcome would put an interactivity label on an output number, and resolving metadata from outcome would erase what the session asked for. The axis is resolved once at seed and recorded in SharedState.grading, next to benchmark_mode and agentx_epoch, which live there for exactly the same reason. Resolution reads HYPERLOOM_PERF_METRIC and HYPERLOOM_PERF_NOISE_PCT, and every later reader of those is somewhere the environment is not evidence: a resume is a new process, a re-baseline runs in a subprocess, and CLOSE drives the breakdown export from one that frequently never inherited them. resolve_graded_comparison was re-deriving on every comparison, so a shell that lost the metric var would flip a session back to the output axis mid-run and a lost noise var would silently widen a 3.5% guard band to the 5% default -- the KEEP and REVERT rule has to be the one the session started with. Both now come from resolved_grading, and the recorded band reaches passes_intvty_gate and passes_tput_guard, which were taking the ambient default. Sessions seeded before the field derive as before and report a null band, because the band they applied was never recorded and today's default is not evidence of it. Recording it rather than projecting it also removes the environment from the export path entirely: the metadata collector projects nothing for this block, so it lands from the spool alone through the existing leaf overlay. The four graded axes now land beside the output figures instead of being parsed and discarded. They are recorded where the measurement is taken -- the baseline round's own measurement block, which is already where outcome.baseline comes from, and the stack validation row that produced the gain -- rather than read off state.baseline_perf and current_best at export. A second source for one baseline is a second answer, and a revalidation moves the cumulative gain without re-promoting the recipe, so current_best can be a later measurement than the gain beside it. An unmeasured axis is an explicit null: absent would be indistinguishable from an axis the framework failed to report, and zero reads as "measured, and it was zero". TPOT p90 was already parsed out of the benchmark report and dropped at five call sites; it now lands. An adoption that fell off the configured axis is reported as a validation.notes finding naming the count and the reason. Its contribution sits in the same sum as the axis-graded ones, so the total is not single-axis and the reader has to be told. The reason is recorded on the adoption because that is the only comparison that can degrade: the whole-stack validation is recorded only when the orchestrator found the comparison comparable, so its axis is always the configured one. Additive to the wire shape. throughput_tok_s_per_gpu, output_throughput and every other existing key keep their output-axis meaning, so nothing downstream has to change to keep working. Cortex has no column for the interactivity or total axes yet, so the immediate value is that an AgentX session becomes identifiable, which is the prerequisite for keeping it off a leaderboard it is not comparable on. Co-authored-by: Cursor <cursoragent@cursor.com>
…and report the guard beside it The sweep resolved its own axis through the environment-derived graded_metric_key, which diverges from the KEEP path two ways now that KEEP reads the axis recorded at seed. A resume whose shell lost HYPERLOOM_PERF_METRIC would redraw the curve on output throughput while the promotions in the same session were decided on interactivity, and graded_metric_key has no scriptable carve-out, so an image framework -- which reports no interactivity axis at all -- would rank every rung on a field it never measures and report the whole sweep as failed. Both call sites now go through resolved_grading, which is the one place the answer is settled. Each pair also carries the throughput axis beside the objective, and whether it held within the band the session grades under. Reported, not enforced: InferenceX publishes a 2-D frontier with no fixed interactivity target, so a rung that traded throughput for interactivity moved along that frontier rather than violating a constraint -- and drawing that frontier is what a sweep is for. Gating here would drop half the curve. best_conc stays the best rung on the objective alone, with best_conc_guard_holds saying whether the session's own KEEP rule would have taken it. The pair columns are renamed off throughput accordingly: baseline_tput / optimized_tput held an interactivity percentile whenever the session graded on one, and the V6 timeline renamed them further to baseline_throughput / optimized_throughput. They are now *_value, with result.metric naming the axis. Also fixes V6ConcSweepPoint declaring intvty_p90 where the recorded curve has always written e2e_norm_intvty_p90, and lifts bool_or_none into the shared event_fields leaf rather than leaving a second copy in warm_start_event. Co-authored-by: Cursor <cursoragent@cursor.com>
…rites it The fixture called itself a flattened rung "shaped the way the sweep's own report writes it" while keying interactivity as intvty_p90, which is the VariantResult dataclass field and not what _point_from_variant emits. That is why V6ConcSweepPoint could declare the same stale name for as long as it did: the only test reading the key agreed with the schema instead of with the producer. Co-authored-by: Cursor <cursoragent@cursor.com>
8 tasks
lishuoshuo-amd
added a commit
that referenced
this pull request
Sep 16, 2026
Picks up the three reverts (#1523, #1524, #1525), which drop #1520, #1511 and #1495 out of the release. #1525 is the one that changes this branch. #1495 both re-keyed the Recipe KB on compute-partition shape and removed --recipe-kb-strict-fingerprint, so reverting it restores the option: the parser carries it again, and the CLI delta against v1.1.0 is now one removal (--breakdown-include-transcripts) and one addition (--extend-hours). Its two changelog entries, which main deleted from [Unreleased], are dropped from the v1.1.1 section here -- the conflict was the whole section against an emptied [Unreleased], resolved by keeping the section and deleting those two. The --breakdown-include-transcripts entry loses the comparison it drew against the option that is now back. The release notes lose the same two claims: the partition-key highlight, the warm-start clause in the opening paragraph, and the upgrade-table row, leaving one removed option rather than two. #1520 and #1511 needed no entries because neither ships. The environment contract is untouched by the reverts: the seven variables are still absent from .env.template, and the five collective ones still have no read site.
xiaofei-zheng
pushed a commit
that referenced
this pull request
Sep 17, 2026
…c sweep on it too (#1527) * feat(sbd-v6): declare the axis a session graded on Reapply the changes from #1520 after the temporary revert in #1523. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(sbd-v6): address review feedback on grading axis wiring Allow degraded output-axis lifts to promote and record degrade_reason, read grading from resolved_grading instead of the environment in explore and report paths, thread session grading into GEAK axis selection, and sync docs/docstrings with the V6 outcome wire shape. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(sbd-v6): fail closed when AgentX grading cannot apply AgentX sessions no longer KEEP on the diagnostic output figure when the interactivity axis pair is missing. Explore and _lift_to_current_best refuse the promotion instead of degrading to throughput grading. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(sbd-v6): remove remaining AgentX throughput fallback paths Drop ANCHOR_DEGRADED, force REVERT at the grading resolver when degraded, check comparability before stack validation verdicts, and align explore tests with fail-closed AgentX grading. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(sbd-v6): thread the seeded grading into the AgentX workload spec ``build_agentx_workload_spec`` grew a ``grading`` parameter so the spec it publishes to GEAK would name the axis and band the session was seeded with, but nothing passed it: the sole call site is inside ``apply_agentx_switch``, which had no way to reach the live state. ``metric_basis`` and ``intvty_p90_veto_pct`` therefore still came from whichever process happened to render the recipe, which is the defect the seeded block exists to fix -- a resume or a re-baseline subprocess can have lost HYPERLOOM_PERF_METRIC, or hold a different one. ``grading`` now rides the config-materialization chain the way ``agentx_mode`` already does, and the three callers that hold the live state pass it: the baseline executor, explore, and the conc sweep. A caller that cannot reach the state passes nothing and keeps the environment-derived fallback byte for byte, so no existing recipe moves. Also drops the ``framework_is_scriptable`` import explore no longer uses after the switch to ``resolved_grading``; it was failing ruff's F401. Co-authored-by: Cursor <cursoragent@cursor.com> * test(sbd-v6): a degraded pair asserts REVERT, not a throughput KEEP These two suites still encoded the fallback the previous commits removed: ``test_report_performance_comparison_snapshots_effective_axes`` expected KEEP on every missing-axis case, and the stack-validation matrix expected KEEP whenever the output figure happened to move up. ``resolve_graded_comparison`` now returns REVERT on a degraded pair whichever way output moved, so both assertions were pinning the old rule and failing CI. The output figures are still recorded beside the refusal, which is what these cases check -- they are a diagnostic, not a verdict that could promote. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(sbd-v6): state the fail-closed rule where the degrade narrative was The code has been fail-closed since the two previous commits, but the wire contract still described degradation as a working path: the ``V6Grading`` and ``_grading`` docstrings and two sections of the breakdown reference all said a session configured for interactivity "still grades an individual comparison on output whenever either side cannot supply the axis pair". That is no longer true at any layer -- such a comparison returns REVERT at the resolver. ``_degraded_adoptions`` and the ``validation.notes`` finding it fed go with them. The note counted adoptions carrying a ``degrade_reason``, and no adoption can carry one now: a REVERT never reaches the adoption writeback, so the finding could not fire. ``test_a_degraded_agentx_lift_is_refused`` already pins that invariant on the refusal itself, which is where it belongs, so the note's own test is dropped rather than rewritten against a condition the orchestrator cannot produce. CHANGELOG gains the entries this PR's own wire-shape changes were missing: the ``conc_sweep`` pair-column rename, flagged breaking because no alias is kept and the old names read a latency percentile as a throughput, plus ``metadata.grading``, the ``perf`` axis blocks and the ``graded_on`` fields. Co-authored-by: Cursor <cursoragent@cursor.com> * style(sbd-v6): ruff format the degraded-lift assertion ``ruff format --check`` is a separate CI step from ``ruff check``, and the assertion added with the fail-closed commit was never run through it. Formatting only. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: kong.haishuo <Kong.Haishuo@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Reopens the work from #1480, rebased onto current
main. That PR was closed by request with its commits kept on the branch; this is that branch, brought forward. One conflict, an import line inconc_sweep.py, resolved by keeping both sides.I re-checked the three defects it claims to fix against today's
mainbefore reopening, because #1380 and #1445 were both invalidated bymainmoving underneath them. All three are still live -- see Still current at the end.Why
Nothing in
session_breakdown.jsonsaid which axis its gains were on. An AgentX replay is ranked on the slow-tail interactivity percentile (e2e_norm_intvty_p90) with total throughput held as a guard; a synthetic run is ranked on output throughput alone. Every throughput field in the breakdown is the output axis by construction, andbenchmark_modereachesreports/final.jsonbut never this document.A consumer therefore had no way to keep an AgentX session out of a synthetic leaderboard. It would sort one against the other and every number would look plausible -- on the canonical corpus the two axes differ by roughly two orders of magnitude.
The two blocks, and why they are two
metadata.gradingis what the session was configured to rank on.outcome.validation.graded_onis what the run actually decided its last promotion on, read off that promotion. The two differ whenever a comparison could not supply the axis pair and fell back to output.Neither resolves the other, deliberately. Publishing the configured axis in
outcomewould put an interactivity label on an output number; resolvingmetadatafromoutcomewould erase what the session asked for. There is a test on each direction.Recorded, not re-derived
Resolution reads
HYPERLOOM_PERF_METRIC/HYPERLOOM_PERF_NOISE_PCT. Every later reader of those is somewhere the environment is not evidence: a resume is a new process, a re-baseline runs in a subprocess, and CLOSE frequently drives this export from one that never inherited them. So the axis and its band are resolved once at seed and recorded inSharedState.grading, besidebenchmark_modeandagentx_epoch, which live there for the same reason.This fixes a live defect rather than only serving the export.
resolve_graded_comparisonre-derives on every comparison, and the recorded band now reachespasses_intvty_gateandpasses_tput_guard-- both already acceptnoise_pct, and neither call site passes it, so a lostHYPERLOOM_PERF_NOISE_PCTsilently widens a configured 3.5% guard band back to the 5% default. The KEEP/REVERT rule has to be the one the session started with.Sessions seeded before the field derive as before and report a null band: the band they actually applied was never recorded, and today's default is not evidence of it.
The conc sweep, grading on the same axis
The sweep resolved its own axis through
graded_metric_key, which diverges from the KEEP path two ways now that KEEP reads the axis recorded at seed.A resume whose shell lost
HYPERLOOM_PERF_METRICwould redraw the curve on output throughput while the promotions in the same session were decided on interactivity. Andgraded_metric_keyhas no scriptable carve-out whereintvty_serving_grading_enableddoes, so an image framework -- which reports no interactivity axis at all -- would rank every rung on a field it never measures and report the whole sweep as failed. Both call sites now go throughresolved_grading.Each pair also carries the throughput axis beside the objective, and whether it held within the session's band. Reported, not enforced: InferenceX publishes a 2-D frontier with no fixed interactivity target, so a rung that traded throughput for interactivity moved along that frontier rather than violating a constraint -- and drawing that frontier is what a sweep is for. Gating here would drop half the curve.
best_concstays the best rung on the objective alone, withbest_conc_guard_holdssaying whether the session's own KEEP rule would have taken it.The pair columns are renamed off throughput accordingly:
baseline_tput/optimized_tputheld an interactivity percentile whenever the session graded on one. They are now*_value, withresult.metricnaming the axis. Also fixesV6ConcSweepPointdeclaringintvty_p90where the recorded curve has always writtene2e_norm_intvty_p90.The axes themselves
outcome.baseline.perf,outcome.final.perfandoutcome.validation.perfcarry the four AgentX axes, recorded where the measurement is taken rather than read offstate.baseline_perfandcurrent_bestat export. A second source for one baseline is a second answer to the same question, and a revalidation moves the cumulative gain without re-promoting the recipe, socurrent_bestcan be a later measurement than the gain published beside it.An unmeasured axis is an explicit null. Absent would be indistinguishable from an axis the framework failed to report, and zero reads as "measured, and it was zero", so a synthetic run publishes four nulls. TPOT p90 was already parsed out of the benchmark report and dropped at five call sites; it now lands.
Degrade is a finding, not a field
An adoption that fell off the configured axis is reported through
validation.notes, naming the count and the reason. Its contribution sits in the same sum as the axis-graded ones, so the total is not single-axis and the reader has to be told.The reason is recorded on the adoption, because that is the only comparison that can degrade. The whole-stack validation is recorded only after the orchestrator found the comparison comparable, so a
degrade_reasonon that row would never be non-empty -- a field that reads as protection the document does not have.Still current
Verified against
mainatff7c235e4before reopening:maintodayresolve_graded_comparisonre-derives the axis from the environment on every callshared_state.pycallsintvty_serving_grading_enabled(...)per comparisonnoise_pct, so both fall back toparse_intvty_noise_pct()conc_sweep.pycallsgraded_metric_keyat two sites; it has noscriptableparameterRollout
Additive to the wire shape.
throughput_tok_s_per_gpu,output_throughputand every other existing key keep their output-axis meaning, so nothing downstream has to change to keep working.Cortex has no column for the interactivity or total axes yet, so the immediate value is that an AgentX session becomes identifiable -- the prerequisite for keeping it off a leaderboard it is not comparable on.
Test
25 cases in
test_sbd_v6_grading.py: both axes at seed, the scriptable carve-out, recorded-beats-environment in both directions, the null band on a legacy session, the block surviving the recorder and the overlay end to end, the metadata/outcome separation locked in both directions, the axis fallback for a session that adopted but never validated, explicit nulls, and the degrade finding. Plus the conc-sweep cases for the renamed pair columns and the guard column.ruff checkis clean onsrc/hyperloomafter the rebase. The suites here importfcntltransitively, so they are POSIX-only and I could not run them on Windows -- CI is the check that matters for this one, and I will follow up on anything it turns up.