Skip to content

fix: judge explore variants and warm recipes by what they change about the running launch - #1908

Open
lishuoshuo-amd wants to merge 6 commits into
mainfrom
fix/restated-baseline-variants
Open

lishuoshuo-amd wants to merge 6 commits into
mainfrom
fix/restated-baseline-variants

Conversation

@lishuoshuo-amd

@lishuoshuo-amd lishuoshuo-amd commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator
  • Description: what and why

    On a MiniMax-M3-MXFP4 AgentX release-qualification run (hyperloom:14-31-g6-054c763634, 720 min budget, Hyperloom 123ad18), three hour-long AgentX measurements launched exactly the baseline:

    Measurement Flags it added Already in the baseline launch
    PRELUDE warm replay --kv-cache-dtype fp8 --enable-prefix-caching both
    explore maxseqs-64-tight-graphs --max-num-seqs 64 yes
    explore kv-cache-fp8 --kv-cache-dtype fp8 yes

    Each run's launch_evidence.json records the same observed_server_config (22 settings) and env as the baseline's. Together they cost about 3.4 h; the session closed with 0 KEEP, and its closing summary listed "kv-cache fp8 (flat)" as a known dead end. The 2026-10-15 top-model-daily MiniMax-M3 run (hyperloom:2-19-g16-293468d5b2) ran the same baseline-identical warm replay.

    Root cause. Candidates were identified by the text of their own delta (extra_server_args / extra_envs) over a base nothing looked at:

    • explore's round dedup fingerprinted the delta alone, so two rows with the same effect were two experiments, and a row restating the stack was not recognised as the measurement the stack already is;
    • the baseline-noop probe (feat(explore): skip baseline-noop variants  #1647), the one place that compared launches, diffed only the settings a variant names. It could not see removals or list order, so it exempted both, and it kept every empty delta because it could not tell "no change" from "a change it cannot see";
    • a warm recipe's best_config is a delta from the session that produced it, replayed onto whatever baseline the current session has;
    • the orchestration state rendered the baseline as an empty delta, and its sandbox cannot read the session config, so the model proposed the operator's own flags as new levers ("default max-num-seqs is far above that").

    Fix. Judge every candidate by what it changes about the base it is composed onto, composed the same way the launch composes it.

    1. compose_variant_args: the removal and replace rules a grid variant launches with move out of _build_variant_yaml into one function, which the launch and the judgement both call.
    2. launch_change (new actions/executors/launch_change.py) composes a candidate onto a BaseLaunch and returns the full difference: settings set, rewritten or removed; envs set; every unset (the image env a launch inherits is not recorded). A setting the engine resolved to the requested value is not a change, which keeps feat(explore): skip baseline-noop variants  #1647's engine-record rule, but only on the launch the engine was observed running.
    3. explore: round_base builds the base from the round's own inputs, config_path envs plus base_extra_args, base_extra_envs and the base controls, and takes the engine record of the current best's launch (launch_evidence, written from the materialized config by every executor) only when the round base launches the same args. plan_round drops a variant with no change (baseline_noop) and benches variants with the same change once (round_dup). It replaces the probe, its two exemptions, its empty-delta branch and the text-based round dedup. The re-measure exemption named resume_stack_revalidate, a source [Refactor] Orchestration layer: retire delegation mechanism and converge ownership #1739 renamed to stack_revalidate, so it never matched; stack revalidations only ran because empty deltas were kept. It now names STACK_REVALIDATE_SOURCE.
    4. warm replay: phases/warm_recipe_change.replay_skip_code judges the recipe with the same function against the current best's recorded launch, which is the baseline's (the replay is enqueued from the baseline's own writeback), and skips it as recipe_already_in_baseline when it changes nothing. A recipe carrying patches or kernel work always replays; an empty recipe still skips as best_config_empty.
    5. orchestration state: to_prompt_summary carries current_launch_flags(already passed by the running stack)=, the deduplicated requested args of that same record, so the model proposes against the base the system judges against. The explore contract in the prompt now states the rule: a row changing nothing is skipped, rows with one change are benched once.
  • Linked issue(s): none. Found through Dispatron problem Lack of dedicated resources impacts the analysis result #56 (plan hyperloom-release-qualification).

  • Tests: added/updated? commands run?

    • Regression tests that fail at base and pass at head:
      • test_explore_executor.py::test_explore_benches_only_what_changes_the_running_launch (base: all four rows benched, including two restatements and a duplicate effect)
      • test_explore_executor.py::test_explore_judges_rows_on_the_round_base_not_the_current_best (the review repro: a round with its own base_extra_args must bench lpm and lpm-plus-mem; fails at 48d8bf3)
      • test_explore_executor.py::test_a_stack_revalidation_runs_although_it_restates_the_stack (fails with the stale resume_stack_revalidate exemption)
      • test_warm_replay.py::test_warm_replay_skips_a_recipe_the_baseline_already_launches_with (base: replay enqueued)
      • test_shared_state_renderers.py::test_prompt_summary_renders_the_flags_the_current_best_launched_with (base: no such line)
    • Ported: the probe's cases (_ALREADY_THE_STACK, _MUST_RUN, removals, the engine-record and engine-rewrote tests) now assert exact LaunchChange values. Two cases changed meaning on purpose: removing a flag the stack does not pass is no change (the argv is fully recorded), and removing one it passes is a change; an unset env is always a change.
    • Guard: test_warm_replay.py::test_warm_replay_runs_a_recipe_that_changes_the_baseline_launch. Unit: test_the_engine_record_describes_a_round_base_only_when_it_launches_the_recorded_args.
    • Commands run at head:
      • pytest .../test_explore_executor.py (117 passed)
      • the 20 other test files that create or run explore tasks, plus test_warm_replay.py, test_sbd_v6_warm_replay_timeline.py and test_shared_state_renderers.py (1084 passed)
      • pytest .../test_grid_runner.py src/hyperloom/orchestrator/actions/executors/tests/ (809 passed; the two test_kernel_tool_entry_points cases that import TraceLens fail locally without it, at base too)
      • the 9 test files that build the orchestration prompt (284 passed; one TraceLens-import case as above)
      • pytest .../test_shared_state_renderers.py plus the six files asserting the prompt summary (196 passed)
      • python scripts/code_metrics.py --base-ref origin/main (PASSED), ruff check, ruff format --check, lint-imports (7 contracts kept), mypy on the new modules (clean)
    • Real-data check against both sessions' own records and round inputs: run 542's round-1 grid benches aiter-kernel-path-enable, gpu-mem-95-more-kv, multi-step-scheduler and skips maxseqs-64-tight-graphs and kv-cache-fp8; its five later rounds bench every row; its warm recipe returns recipe_already_in_baseline. Run 543 (Kimi-K3): all five explore rounds bench every row and the warm recipe replays.
  • Size/complexity triggers crossed: split, or why the shape is right: production code +371/-305 across 8 files; tests +268/-100. explore.py loses the probe (1905 -> 1692 lines) and ExploreExecutor.__call__ gets simpler (length 1124 -> 1088, cognitive 369 -> 365, cyclomatic 76 -> 73); _build_variant_yaml shrinks (138 -> 133 lines) by calling compose_variant_args. prelude.py is over its recorded module length, so the warm-replay judgement is a 41-line module and prelude.py swaps its 12-line empty-recipe check for one of the same length (maybe_enqueue_warm_replay cognitive 190 -> 189). The baseline is lowered for exactly these units.

  • If this simplifies or refactors: removed filter_baseline_noop_variants, observed_launch_from_state, _launch_delta, _settings_already_hold, _envs_already_hold, _subtracts_from_the_launch, _touches_a_multi_value_flag and the throwaway base materialization. Preserved: a variant whose settings the engine already resolved to the requested value is not benched (engine-record and engine-rewrote tests, ported); revalidations and GEAK config replays always run; an empty recipe skips with best_config_empty and persists the one-shot guard (test_warm_replay_skips_when_best_config_empty, test_every_refusal_persists_the_one_shot_guard, unchanged).

  • Observable effect: what an operator will now see, and what the old behaviour cost them:

    • A row restating the running launch appears in skipped_dup as baseline_noop; a row repeating another row's change appears as round_dup. Before, each cost one full measurement.
    • A recipe already in the baseline launch settles as skipped / recipe_already_in_baseline instead of a measured drift.
    • The orchestration prompt carries one more line (about 0.9 KB on this MiniMax-M3 config).
    • The old behaviour cost about 68 min per restated AgentX measurement and taught the model a false negative about a flag that was already on.
  • Breaking changes: no config, flag or env var changes. New values only: the warm-replay skip code recipe_already_in_baseline. skipped_dup rows for baseline_noop now carry fingerprint and no detail.

  • PR addresses single concern: yes. One finding, a measurement that launches what the stack already runs, fixed by judging candidates on the stack's recorded launch at the two places that launch them and showing that record to the model that proposes them.

  • Root cause is upstream (Magpie/TraceLens/GEAK/IntelliKit/AgentKernelArena), ticket filed: no, all changes are in Hyperloom.

@lishuoshuo-amd
lishuoshuo-amd requested a review from a team as a code owner October 15, 2026 07:44
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Code metrics gate: PASSED

Judged: the 12 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.

Dimension Limit New Worse Baseline out of date Baseline entries vs base
Cyclomatic complexity fails > 20 0 0 0 72 +0
Cognitive complexity fails > 30 0 0 0 284 +0
Function length (lines) fails > 80 0 0 0 449 -1
Nested block depth fails > 5 0 0 0 27 +0
Module length (lines) warns > 800, fails > 1200 0 0 0 53 +0
Duplicated lines per file fails > 0 0 0 0 118 +0
Dead code (vulture) fails > 0 0 0 0 2 +0
Check Findings Effect
Comments 0
English only 0
Production code imports test code 0
Repeated literals 0
A1 Private name of another package 0
A8 Flag argument 0
A9 Tuple return 0
A10 Raw vocabulary comparison 0
A11 Hidden global write 0
A12 Pure forwarder 0
A12 Single-implementation abstraction 0

Module length warning: over 800 lines, within the 1200-line failure limit (not failing): 1

Module Lines Limit
src/hyperloom/orchestrator/state/_shared_state/render.py 957 warns > 800

Already over at the base (backlog, not this change's): 6

Unit Dimension Base Now
src/hyperloom/orchestrator/state/_shared_state/render.py:732 _RenderMixin._format_variant_line Cognitive complexity 40 40
src/hyperloom/orchestrator/phases/prelude.py:2081 PreludePhase._settle_warm_replay Function length (lines) 390 390
src/hyperloom/orchestrator/prompts/prompt_builder.py:577 _section_decision_framework Function length (lines) 104 104
src/hyperloom/orchestrator/prompts/prompt_builder.py:1044 build_orchestration_prompt Function length (lines) 142 142
src/hyperloom/orchestrator/phases/prelude.py:1 Module length (lines) 2656 2656
src/hyperloom/orchestrator/prompts/prompt_builder.py:1 Module length (lines) 1226 1226
86 findings in files this change does not touch

Outside this change (informational): 86

Unit Dimension Baseline Now
src/hyperloom/orchestrator/enablement/recipe/projections.py project_launch_evidence Cognitive complexity absent 36
src/hyperloom/orchestrator/state/shared_state.py resolve_graded_comparison Cognitive complexity absent 34
src/hyperloom_kb/collect/mapping.py _experience Cognitive complexity absent 31
src/hyperloom_kb/schema.py FieldDeclaration.__post_init__ Cognitive complexity absent 32
src/kernelforge/cli/forge_loop_command.py _prepare_measurement_driver Cognitive complexity absent 36
src/kernelforge/cli/forge_loop_command.py _read_kb_warm_start Cognitive complexity absent 40
src/kernelforge/loop/runner.py IterationLoop._collect_session_candidate Cognitive complexity absent 44
src/kernelforge/loop/runner.py IterationLoop._rule_on_attempt Cognitive complexity absent 38
src/hyperloom_kb/tests/test_cli.py Duplicated lines per file absent 18
src/hyperloom_kb/tests/test_global_identity_and_pull.py Duplicated lines per file absent 13
src/hyperloom_kb/tests/test_local_state.py Duplicated lines per file absent 18
src/hyperloom/inference_optimizer/cli/__init__.py _reexport_session_environment Function length (lines) absent 112
src/hyperloom/inference_optimizer/cli/__init__.py _resolve_agent_lineup Function length (lines) absent 89
src/hyperloom/inference_optimizer/cli/__init__.py _resolve_run_topology Function length (lines) absent 92
src/hyperloom/inference_optimizer/cli/__init__.py _resume_session Function length (lines) absent 171
src/hyperloom/inference_optimizer/cli/__init__.py _seed_fresh_session Function length (lines) absent 183
src/hyperloom/inference_optimizer/multi_node/cli.py _restart_multinode Function length (lines) absent 107
src/hyperloom/inference_optimizer/multi_node/scripts/launch_multinode.py _build_parser Function length (lines) absent 84
src/hyperloom/inference_optimizer/multi_node/scripts/launch_multinode.py _spawn_rank_actors Function length (lines) absent 90
src/hyperloom_kb/http_service.py RequestHandler._dispatch Function length (lines) absent 91
src/hyperloom_kb/sync.py GlobalSync.pull Function length (lines) absent 86
src/kernelforge/cli/forge_loop_command.py _RunReporter.build_result Function length (lines) absent 91
src/kernelforge/cli/forge_loop_command.py _prepare_measurement_driver Function length (lines) absent 140
src/kernelforge/cli/forge_loop_command.py _read_kb_warm_start Function length (lines) absent 154
src/kernelforge/loop/knowledge_integration.py _measure_warmstart_field Function length (lines) absent 83

...and 61 more; the job log lists every row.

Verdict computed by the gate scripts in /home/runner/work/_temp/code-metrics-gate, not by this tree's copy.

Thresholds and sources: [tool.hyperloom.code_metrics] in pyproject.toml. Tools: complexipy 8.0.1, jscpd 5.4.1, ruff 0.16.2, vulture 2.16.

@lishuoshuo-amd lishuoshuo-amd changed the title Fix/restated baseline variants fix: stop re-measuring the baseline when a variant or warm recipe restates its flags Oct 15, 2026
Comment thread src/hyperloom/orchestrator/phases/prelude.py Fixed
Comment thread src/hyperloom/orchestrator/phases/warm_replay_noop.py Fixed
@lishuoshuo-amd
lishuoshuo-amd marked this pull request as draft October 15, 2026 07:53
…unch

Explore identified a variant by the text of its own delta, over a base it
never looked at: the round dedup fingerprinted extra_server_args alone,
and the baseline-noop probe diffed only the settings a variant names, so
it exempted removals and list-valued flags and kept every empty delta.
On a MiniMax-M3 AgentX run two variants restating the operator's
--max-num-seqs 64 and --kv-cache-dtype fp8 each re-measured the baseline
for about 68 minutes.

launch_change composes a candidate onto the current best's recorded
launch (launch_evidence, written from the materialized config by every
executor) and returns the full difference: settings set, rewritten or
removed, envs set or unset, minus settings the engine already resolved to
the requested value. plan_round drops a variant with no change and
collapses variants with the same change, replacing the probe, its
exemptions and the text-based round dedup.

The re-measure exemption named resume_stack_revalidate, a source #1739
renamed to stack_revalidate, so it never matched; stack revalidations
only ran because empty deltas were kept. It now names
STACK_REVALIDATE_SOURCE.
A recipe's best_config is a delta from the session that produced it, and
the replay laid it onto whatever baseline the current session has. When
the operator config had since adopted those flags, the replay
re-measured the baseline for a full AgentX round and settled as drift.

Judge the recipe with launch_change against the current best's recorded
launch and skip it as recipe_already_in_baseline when it changes
nothing. A recipe carrying patches or kernel work still replays; an
empty recipe still skips as best_config_empty.
…with

The tick summary carried current_best without its measurement, and the
orchestration sandbox cannot read the session config, so the model never
saw the operator's server flags. It proposed --max-num-seqs 64 and
--kv-cache-dtype fp8 as new levers on a baseline that already passed
both. Render the deduplicated requested server args from the current
best's launch evidence, the record launch_change judges candidates
against.
@lishuoshuo-amd
lishuoshuo-amd force-pushed the fix/restated-baseline-variants branch from 90c61a7 to 48d8bf3 Compare October 15, 2026 08:14
@lishuoshuo-amd lishuoshuo-amd changed the title fix: stop re-measuring the baseline when a variant or warm recipe restates its flags fix: judge explore variants and warm recipes by what they change about the running launch Oct 15, 2026
@lishuoshuo-amd
lishuoshuo-amd marked this pull request as ready for review October 15, 2026 08:19
@lishuoshuo-amd

Copy link
Copy Markdown
Collaborator Author

PR #1908 -- fix: judge explore variants and warm recipes by what they change about the running launch

What it does: Restatements of the running launch were measured as experiments. A warm recipe and two explore rows that only re-passed the baseline's own flags cost about 3.4 h on a MiniMax-M3 run, because candidates were identified by the text of their delta and never compared with the launch the stack runs. This PR adds launch_change, which composes a candidate onto current_best's recorded launch_evidence.requested_server_args and returns what differs. explore's plan_round uses it to skip no-change rows (baseline_noop) and to bench rows with the same effect once (round_dup), replacing the old probe and the text-based dedup. Warm replay uses the same function to skip a recipe already in the baseline (recipe_already_in_baseline), and the orchestration summary now shows the running launch's flags.

Blocking issues: 2

  1. [D7] round_dup now merges rows that are different launches on the round's own base [verified]
    Problem: plan_round keys round dedup on LaunchChange.key, computed by composing each variant onto current_best's recorded requested_server_args (src/hyperloom/orchestrator/actions/executors/explore.py:455-462, called at :755-762; launch_change.py:47-75). The round launches each variant on its own base instead: config_path plus params["base_extra_args"] / base_extra_envs and the base controls (explore.py:619-623). plan_round receives only base_remove_args / base_unset_envs / base_args_mode, and uses them for the fingerprint only. At base, the round dedup keyed on the variant's own fingerprint, which no base could merge. The two bases differ whenever Orchestration supplies base_extra_args, which the explore schema offers (prompt_builder.py:496). inject_stack_base_params only setdefaults the stack (shared_state.py:219-241), and the executor refreshes the base from current_best only on an upward anchor drift (explore.py:610-618). The new executor test runs in exactly this mismatch, a base YAML with no server args against a current_best record of --mem-fraction-static 0.9 --max-running-requests 512, and asserts the merge.
    Impact: Repro through the real ExploreExecutor, with a current_best record of --mem-fraction-static 0.9 (resolved config included), a round with base_extra_args="--chunked-prefill-size 8192", and grid rows lpm and lpm-plus-mem. Head benches lpm only and skips lpm-plus-mem as round_dup, although the latter would launch --watchdog-timeout 1800 --chunked-prefill-size 8192 --schedule-policy lpm --mem-fraction-static 0.9, a different argv. Base benches both. The skipped launch is never measured, and no warning is logged.
    Action: Compute the change against the base the round composes variants onto (inherited args plus base_extra_args/base_extra_envs/base controls, already composed for apply_compatibility_filter at explore.py:777-782), or key round_dup on the variant's composed launch. Add an executor test on a round whose base differs from current_best.

  2. [X3] The Orchestration prompt still promises that only exact same-grid duplicates are collapsed [verified]
    Problem: src/hyperloom/orchestrator/prompts/prompt_builder.py:500-502 ("only exact duplicates within the same submitted grid are collapsed, so any prior fingerprint may be re-proposed") and :769-770 ("only exact same-grid duplicates are collapsed") are unchanged. At base the round dedup keyed on the variant's own fingerprint (args+envs+controls), so the sentence held for round_dup. At head, plan_round collapses rows by their effect on the running launch (LaunchChange.key), for example --x and --mem-fraction-static 0.9 --x when the stack already passes 0.9.
    Impact: The agent proposing the grid is told that two rows with different text are two measurements, but one of them is skipped as round_dup, so the agent's model of the grid contract is wrong.
    Action: Update both prompt copies to state the new rule: rows are judged by what they change about the running launch (now shown on current_launch_flags), restatements are skipped, and rows with the same change are benched once.

SKIPPED: V4 -- openprs.txt compared #1032, #1154, #1276, #1675 and #1869 on their first 100 changed files only, so overlap beyond those files was not checked.

Checked: launch_change.py, explore.py (plan_round, __call__ base resolution and filters, provenance stamping, _CARRIED_VARIANT_ATTRS), _grid_base.py GridVariant, _grid_runner.py overlay/runtime overrides, writeback.py (current_best / _stamp_current_best_measurement writers, warm replay call order, both revalidation producers), _launch_evidence.py (requested_server_args source), baseline.py evidence attachment, prelude.py maybe_enqueue_warm_replay and replay params, warm_recipe_change.py, render.py, shared_state.py inject_stack_base_params, prompt_builder.py explore contract. Tree-wide git grep for skipped_dup, baseline_noop, skip-code readers, overlay_pythonpath producers, and base_extra_args sanitizers. | Ran: an ExploreExecutor repro with a non-stack round base and a full current_best record, head vs base, re-checked by an independent reader; the PR's regression tests on base (explore behaviour test appended to the base test file; warm-replay and render test files overlaid), all of which fail at base for the stated reason, while the guard passes on both; pytest at head: test_explore_executor.py (115), test_warm_replay.py (111), test_shared_state_renderers.py (16); ruff C901 base/head (__call__ 76 -> 73); CI at 48d8bf3 green, including 12/12 test shards | Base: e67763d | Head: 48d8bf3

The removal and replace rules a grid variant launches with lived inline in
_build_variant_yaml. Move them to compose_variant_args so the launch and
the judgement of what a variant changes compose the same way.
…onto

Review of #1908: plan_round composed every variant onto current_best's
recorded launch, but a round launches its variants on its own base,
config_path plus base_extra_args, base_extra_envs and the base controls.
When Orchestration supplies base_extra_args the two differ, and round_dup
merged rows that launch different argv.

round_base builds the base from the round's own inputs and the same
composition the launch uses (compose_variant_args). The engine's resolved
settings describe the recorded launch only, so they apply to a round base
only when it launches the same args. Warm replay keeps the current best's
record as its base: it is enqueued from the baseline's own writeback.
Review of #1908: both copies of the explore contract still said only
exact same-grid duplicates are collapsed. A row is now judged by what it
changes about the round's base launch: one changing nothing is skipped,
and rows with one change are benched once.
@lishuoshuo-amd

Copy link
Copy Markdown
Collaborator Author

Both blocking issues are addressed at 00bf8560b.

  1. [D7] Fixed by judging each row against the base its round composes it onto (04c2a4b18, with the refactor f551ec0fd before it).

    • compose_variant_args now holds the removal and replace rules that _build_variant_yaml applied inline, so the launch and the judgement compose a variant the same way.
    • round_base builds the round's base from its own inputs: the config_path envs plus base_extra_args, base_extra_envs, base_remove_args, base_unset_envs and base_args_mode.
    • The current best's engine record (observed_server_config minus rewritten settings) applies only when that round base launches the same args as the recorded launch.
    • Your repro is now test_explore_judges_rows_on_the_round_base_not_the_current_best. A round with base_extra_args="--chunked-prefill-size 8192" against a --mem-fraction-static 0.9 record benches both lpm and lpm-plus-mem; this test fails at 48d8bf3.
    • The earlier executor test now uses a base config that matches its record.
    • Warm replay keeps the current best's record as its base, because the replay is enqueued from the baseline's own writeback, right after the baseline is stamped as the current best.
  2. [X3] Fixed in 00bf8560b: both copies of the explore contract now say that a row is judged by what it changes about the round's base launch, a row changing nothing is skipped, and rows with one change are benched once.

The two CodeQL cyclic-import notes are on warm_replay_noop.py, which no longer exists. CodeQL is green at the current head, with no open alerts.

@chaojhou chaojhou left a comment

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.

PR #1908 -- fix: judge explore variants and warm recipes by what they change about the running launch

What it does: Restatements of the running launch were measured as experiments, costing about 3.4 h on a MiniMax-M3 run. This PR adds launch_change, which composes a candidate onto its round's base launch with the same compose_variant_args the launch uses and returns what differs. explore's plan_round uses it to skip rows that change nothing (baseline_noop) and to bench rows with the same change once (round_dup), replacing the #1647 probe and the text-based dedup. Warm replay uses it to skip a recipe already in the baseline (recipe_already_in_baseline), and the orchestration summary now shows current_launch_flags. Both items from the earlier self-review are fixed at this head. It also fixes a live bug on main: stack revalidation rows are exempt again (STACK_REVALIDATE_SOURCE replaces the dead resume_stack_revalidate name).

Blocking issues: 3

  1. [X6] The PR conflicts with main, and #1903 has since landed a second restatement mechanism there; the rebase must leave one [verified]
    Problem: #1903 (8c4b640) added actions/executors/_launch_restatement.py (named_settings, _opposite_setting, restates_the_baseline, config_launch_restated_by), a changed empty-delta branch in filter_baseline_noop_variants, and PreludePhase._skip_restated_replay with skip code restates_baseline, judged against baseline_config_path. git merge-tree origin/main 00bf8560b conflicts in five files but merges prelude.py cleanly, with both checks in one flow: replay_skip_code (recorded launch, recipe_already_in_baseline) at :1314 and _skip_restated_replay (config file, restates_baseline, exempt under reference launch controls) at :1365. The tree also carries two byte-identical argv parsers, launch_change._named_settings and _launch_restatement.named_settings. src/hyperloom/orchestrator/phases/prelude.py:1312
    Impact: one decision made twice against two bases with two skip codes. Warm-replay event consumers see either code for the same outcome, and the next parser fix (item 2) lands in one copy and is missed in the other.
    Action: when rebasing, launch_change owns the decision. Delete _skip_restated_replay and its call, restates_the_baseline / config_launch_restated_by / _opposite_setting and the duplicate named_settings, and keep one warm skip code. Keep config_envs (with #1903's None filter) and the helpers #1904's filter_context_copy_drafts / _materialized_launch still read. Port #1903's tests onto plan_round / replay_skip_code, and state in the description any expectation that deliberately flips (the reference-launch-controls warm replay case).

  2. [D] launch_change sees no change when the candidate flips a negated flag the base already names [verified]
    Problem: _named_settings keys --enable-prefix-caching as enable_prefix_caching and --no-enable-prefix-caching as no_enable_prefix_caching, two independent settings, and composition appends without folding them. When the base passes both spellings (the engine takes the last one, so the cache is off) and a row appends --enable-prefix-caching (the cache is now on), the base and composed dicts are equal. src/hyperloom/orchestrator/actions/executors/launch_change.py:178
    Impact: reproduced at head through plan_round. A config with --enable-prefix-caching plus base_extra_args="--no-enable-prefix-caching", and a row --enable-prefix-caching, is skipped as baseline_noop although it changes the engine's setting. replay_skip_code skips such a warm recipe as recipe_already_in_baseline the same way. main pins the opposite in #1903's test_restates_the_baseline[negated_spelling_in_the_baseline].
    Action: have the one parser resolve what the engine resolves. Fold a negated boolean spelling onto its positive setting with a value (x=True / x=False, last occurrence wins), so the two dicts differ exactly when the effective setting differs, with no separate opposite-spelling exemption. Carry #1903's case over as a launch_change test.

  3. [D] Rows skipped as baseline_noop / round_dup never count as decided, so the FRAMEWORK_AGENT auto-bench stalls behind them [verified]
    Problem: skipped rows go only to per_variant_outcomes / last_round.skipped_dup (explore.py:1578-1588). explore_search.tested is written only for benched and warmup-failed variants (:1022, :1270). FrameworkPhase._maybe_bench_untested_proposals (framework.py:2813-2852) takes untested_proposal_rows()[:4], where "untested" means "not in explore_search.tested", and keys its task on those four fingerprints through create_or_return_existing, which returns a finished task with the same key. At the PR base, a row restating the stack produced an empty delta that the probe kept and benched, so it left the queue. #1903 has since dropped that class on main as well, so main stalls the same way, and this PR, which replaces that mechanism, is where it gets fixed. src/hyperloom/orchestrator/actions/executors/explore.py:1578
    Impact: reproduced with the real FrameworkPhase, plan_round and untested_proposal_rows. A specialist round proposes four rows the stack already passes, then lpm. The first auto-bench skips all four, the second computes the same key auto-explore-c0-7d0008f798fa and enqueues nothing, and lpm is never benched in that macro cycle. This is the restatement pattern the description reports from the MiniMax-M3 run, so the PR saves the hour-long runs but can starve the real proposals behind them.
    Action: a row skipped because it changes nothing, or the same thing another row measures, is a decision. Record it in the explore ledger under its own fingerprint with its skip reason (and the twin's fingerprint for round_dup), so benched_fingerprints / untested_proposal_rows and the negative-ledger renderer read "decided" from one source. Do not add a guard in the auto-bench path. Pin it with a test: an all-restatement head followed by a real row, where the next auto-bench grid contains the real row.

Non-blocking:

  • launch_change does not model overlay_pythonpath or runtime_override. Today only the geak_revalidate and stack_revalidate rows carry an overlay, and both are exempt, so this is latent. #1903's test_restated_args_with_a_kernel_overlay_are_not_a_restatement should still be ported or retired explicitly.
  • round_base stringifies config envs, so a null config env becomes 'None' and a row requesting FOO='None' is skipped. #1903's config_envs drops None; carry that filter over.
  • The engine-record gate in round_base compares args but not env against the recorded launch.
  • _named_settings returns {} when shlex fails, which judges every row a no-op. Unreachable today because seal_server_argv validates with posix shlex, but it fails toward skipping; raise instead.

Checked: launch_change.py; explore.py plan_round, _remeasures_on_purpose, base resolution, skip handling into per_variant_outcomes / tested_update; _grid_runner.py _build_variant_yaml / compose_variant_args; grid_server_args and launch_log_evidence flag naming; prelude.py maybe_enqueue_warm_replay and warm_recipe_change.py; writeback current_best stamping and the revalidation producers; framework.py auto-bench and task_registry idempotency; render.py untested_proposal_rows / current_launch_flags; prompt_builder explore contract; #1903 (8c4b640) and #1904 on origin/main; base filter_baseline_noop_variants | Ran: git merge-tree origin/main 00bf8560b; repro scripts on archives of head and origin/main (same-effect rows, removals, list flags, round-base mismatch, stack revalidation, overlay, negated spelling, null env, shlex failure); FrameworkPhase._maybe_bench_untested_proposals with plan_round at head (same idempotency key, real row never enqueued). Not run: pytest (not installed here) | Base: e67763d | Head: 00bf856

@chaojhou

Copy link
Copy Markdown
Collaborator

Heads-up from the directory-size gate that landed in #2136 (code-metrics now fails a directory with more than 50 entries, counting the production modules directly in it and its subdirectories that hold one).

This PR adds launch_change.py directly to src/hyperloom/orchestrator/actions/executors, which is already over the limit and recorded in scripts/code_metrics_baseline.txt. Once the branch is updated with main, code-metrics will report that entry as worse and fail.

To pass, either move the new module(s) into a subpackage of src/hyperloom/orchestrator/actions/executors grouped by responsibility, or keep them where they are and add the baseline-raise label with the reason stated in the PR description (the gate then also expects the baseline entry raised to the new count).

This branch has not been deployed

No deployments
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.

3 participants