Repository navigation
refactor(explore): let the explore round own the stack it builds and the ledger it writes - #1965
Conversation
The round record it compacts at the end, the seed grid a round with no LLM variants falls back to, the lever variants it puts in front of that grid, and the row one tested variant contributes. __call__: 1179 -> 1030 lines, cognitive 369 -> 304, cyclomatic 76 -> 61 vulture caught a real defect in the first cut of _seed_lever_variants: the lifted body kept its old indent and ended up under the early return, so lever seeding would have silently stopped happening. No test covered it. Observable effect: none -- pure refactor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Code metrics gate: PASSEDJudged: the 3 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.
71 findings in files this change does not touchOutside this change (informational): 71
...and 46 more; the job log lists every row.
Thresholds and sources: |
…f the grid This is the path vulture found broken in the first cut of _seed_lever_variants and no test covered. It now has three: the fresh levers go first, one the grid already names is not seeded twice, and a round with no levers keeps its grid. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The push that merged main fired only Secret Scan on this SHA; the rest of the workflows stayed on the previous one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lore # Conflicts: # scripts/code_metrics_baseline.txt
…er it writes ExploreExecutor.__call__ was one 1000-line method: a 561-line try whose loop read 45 names from the enclosing scope and wrote 5 back, with bare break/continue steering it. The round's state is now three records -- the stack each variant launches on, the ledger of what it decided, the budget it spends -- and the executor's methods operate on them, so the loop reads as a driver: admit the variant, stage it, measure it, record the verdict, tear down its server. A variant's rounds end in a named _RoundStep the driver dispatches on instead of a break the reader has to trace. The variant grid moves to _variant_grid.py beside it: where a round's variants come from and which of them the launch already satisfies is a job of its own, and explore.py was past the module-length ceiling with the round machinery in it. filter_baseline_noop_variants takes the materialization inputs the way its sibling filter does, which is what brings it back under the function-length limit. No observable change: internal structure only. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lore # Conflicts: # scripts/code_metrics_baseline.txt # src/hyperloom/inference_optimizer/tests/test_explore_executor.py # src/hyperloom/orchestrator/actions/executors/explore.py
…f their owner The baseline-noop filter asks whether a variant names a flag whose ordering a merged config cannot preserve. That question belongs to the module that knows the flags, so the tuple is `MULTI_VALUE_FLAGS` rather than a private name an orchestrator module reaches into. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
chaojhou
left a comment
There was a problem hiding this comment.
PR #1965 -- refactor(explore): let the explore round own the stack it builds and the ledger it writes
What it does: PR #1965 moves the round state of ExploreExecutor.call (CC 76 -> 2) into _ExploreRound / _VariantStack / _RoundLedger / _RoundBudget plus named helpers, moves the variant-grid code (payload, levers, seed grid, noop and context-copy filters) into a new executors/_variant_grid.py, and renames grid_server_args._MULTI_VALUE_FLAGS to MULTI_VALUE_FLAGS. At its merge base it behaves the same as before, which a 26-scenario differential script confirmed. It is still blocked: the branch conflicts semantically with main, which has since rewritten the same explore code in #1918 and #1984.
Blocking issues: 1
- [conflict-explore-1918-1984] CONFLICTING with main: a naive resolution reverts #1918 (per-turn restatement/dedup) and #1984 (eval opt-out removal) [verified]
Problem: The PR rewrote explore.py from merge base ef93838, but main has since changed the same logic. a8c4aac/#1918 now judges baseline-noop restatements and in-round duplicates at each variant's turn, against the stack as KEEPs have moved it. f7f64ac/#1984 removed the explore eval opt-out. At head, _runnable_variants (explore.py:1326) and _one_variant_per_fingerprint (1362) still run the old round-open noop filter and dedup, answered_its_grid (1115) still counts only ALREADY_MEASURED, and _explore_eval_disabled (154), _Workload.eval_disabled and the warmup_gv eval-off logic (1478-1500) are still there. src/hyperloom/orchestrator/actions/executors/explore.py:1326
Impact: #1918 is silently reverted: a variant that restates the stack only after a KEEP is launched and measured again, a duplicate whose twin was only skipped is dropped instead of run, and a round answered by restatements reports failed. #1984 is reverted too: params.disable_run_eval turns accuracy eval off for explore again. mergeable=CONFLICTING blocks on its own.
Action: Rebase onto origin/main. Port #1918's _launched_stack/_restates_stack (observed_launch_after), launched_fps dedup, leading-noop trimming and judged_without_measuring into _ExploreRound and _measure_the_grid. Drop _explore_eval_disabled, _Workload.eval_disabled, the RUN_EVAL override and warmup_gv as #1984 did. Regenerate code_metrics_baseline.txt. Then re-check equivalence against the new main.
Checked: ExploreExecutor.call (base explore.py:801-1987) vs head call/_open_round/_materialize_workload/_proposed_grid/_r (yes); filter_baseline_noop_variants (moved to _variant_grid.py:416, kwargs folded into **build via _materialized_launch) (yes); moved grid helpers (_action_reasoning, _grid_variants_from_payload, framework_lever_grid, _framework_lever_attributions, (yes); grid_server_args._MULTI_VALUE_FLAGS -> MULTI_VALUE_FLAGS (yes); consumers / monkeypatch targets of explore (yes) | Conflict with main semantic | Base: ef93838 | Head: 9029126
|
Requesting changes: behaviour is equivalent at the merge-base, but the branch conflicts with main in a way that would silently revert #1918 and #1984, and the refactor grows production code instead of shrinking it. Production code: +2064/−1616 (net +448, +22%) across Blocking
Simplify instead of relocate
|
…lore Carries #1918 and #1984 into the rewritten round by hand rather than by taking either side of the conflict. #1918 judges a baseline restatement against the stack it launches on, and the rewrite had restructured exactly that code. In the round's new shape: the grid keeps only the restatements ahead of every other variant (``_past_the_opening_restatements``), each later variant is judged at its turn against the stack a KEEP has moved (``_ExploreRound.restates_the_stack``, over ``observed_launch_after``), an in-round duplicate is one whose fingerprint this round actually launched (``launched_already`` / ``record_launch``), and a round whose variants were all answered without measuring succeeds. #1984 removes the accuracy-eval opt-out: ``_explore_eval_disabled``, the ``disable_run_eval`` task param, the ``SharedState.eval_disabled`` read and the forced ``RUN_EVAL=false`` warmup variant are gone; the decision round still skips eval when a warmup took the score. ``_variant_control_fields``, the fingerprint stamping and the baseline-noop skip record move to ``_variant_grid``, which owns the variant grid, keeping ``explore.py`` shorter than the base branch's. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…s it Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The grid helpers are imported from `_variant_grid` by every caller, so `explore` is no longer a second route to them: `intent_router`, the five test modules that still reached through it, and the two reference docs now name the owner. `_variant_grid` imports `_framework_switch_manifest` directly instead of through the package, which drops the `explore -> executors -> explore` module-level import cycle main has. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deletes four mechanisms rather than moving them: - the parallel `losers` list, a second row per refused variant carrying a subset of what `rejected` already holds. The result's `losers` key is that one list; refused rows now also carry `workspace` on the measured path; - `_RoundStep`, which restated in an enum what `_RoundBudget.stop` already records when a run stops the round; - `_ExploreRound.release_serving_lease`, with the lease now taken and closed by the one loop that uses it; - `_RoundGrid` and `_GradingAnchor`, two single-use pairs the round's own fields already name. `_stage_variant`, `_launch_round`, `_warm_the_server`, `_measure_variant`, `_judge_variant` and the grid loop were static methods whose first argument was the round; they are the round's methods, so the executor now opens a round, asks it to measure its grid, and reads the result. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tested, warmup-failure and refusal rows repeated the same six-key identity header; they now share one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Resolves the code_metrics_baseline.txt ratchet conflict with resolve_baseline.py. main's #1782 additive r9700/gfx1201 docs in SKILL.md and references/frameworks.md and its two new monkeypatch.setenv lines in test_custom_framework.py all survive alongside our explore/_variant_grid split. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The lever grid, observed launch, launch delta and multi-value-flag readers were never over a gated threshold, so they keep their own bodies, their docstrings and the module-qualified switch-manifest calls; grid_server_args goes back untouched with its private multi-value flag table. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lore # Conflicts: # scripts/code_metrics_baseline.txt
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ers it writes ExploreExecutor.__call__ was a 1159-line method holding the round's inputs, its accumulating stack, four closures over them and six ledger accumulators. The round is now an object that owns those, and each ledger row is a projection of the one row the round records per variant instead of a fourth restatement of it. Deleted on the way: the second reading of the shared state, the hand-written float/int parsing the common coercions already do, the duplicated materialization inside filter_baseline_noop_variants (it now calls _materialized_launch, as the speculative-decoding filter beside it already did), the per-call repetition of the launch and stack keywords, the dead "not_keep" fallback (every non-KEEP path sets a reason), and the "if runnable" guard around a grid that cannot be empty. Observable: a rejected-ledger row now carries the same fields whichever round produced it -- a warmup failure gains error_class and server_log_path, a decision rejection gains stage and workspace. A non-finite or boolean baseline_runtime_sec, baseline_warm_runtime_sec, base_tput or accuracy baseline now reads as missing rather than raising or being counted as 1.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…mapping _framework_of handed back the benchmark envs where the round expected the benchmark mapping, so the recipe's own server args and the agentx check both read empty and a KEEP persisted only the variant's own flags. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…settles Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…'s fields The loser view is the variant's fields plus the round's verdict, and the rejected view is the loser view plus where the round stopped, so each field name is written once. The winners-history row reads off the variant's row directly, and one reader answers for the canonical fingerprint a variant was stamped with. The warmup round now says whether it left a server to decide on, so its status is read where it is known rather than again by the caller. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ZhengGong-amd
left a comment
There was a problem hiding this comment.
PR #1965 -- refactor(explore): let the explore round own the stack it builds and the ledger it writes
What it does: Rewrites ExploreExecutor.__call__ in place in explore.py. The round now runs in a new _ExploreRound class, and __call__ only builds the params and output root before delegating to it. The diff against main touches 3 files (explore.py, test_explore_executor.py, scripts/code_metrics_baseline.txt) and is net -208 production lines.
Blocking issues: 4
-
[free:lever-seeding-dropped] Framework-rewrite lever variants are never seeded, so lever attribution stops happening [verified]
Problem:_resolve_gridsetsself.lever_payload = framework_lever_grid(self.ss)and then calls_lever_seeded_payload(payload, self.lever_payload). That call passes the already-built lever list where shared state is expected._lever_seeded_payloadcallsframework_lever_grid(ss)a second time, andgetattr(list, "authored_framework_levers", None)isNone, so it always returns[]. src/hyperloom/orchestrator/actions/executors/explore.py:1083-1084, :775-777, :252-256. The real writer isrecord_authored_framework_levers, called at src/hyperloom/orchestrator/loop/writeback.py:3000. On main, the seeding readextra["shared_state"]directly (explore.py:924 at base), so main does not have this bug. No existing test covers this path.
Repro: runExploreExecutorwith a shared state that holds pendingauthored_framework_levers, stubbing onlyrun_with_session_kill.- Empty
params.grid: base runs 5 additive (or 4 leave-one-out)fwlever_*variants, succeeds and returnsframework_lever_attributions. Head returnsstatus=failed, error_class=empty_grid. params.gridwith one LLM variant: base runs thefwlever_*variants plusv_llmand attributes a lever. Head runs onlyv_llmand returnsframework_lever_attributions=[].
Impact: Rewrite levers written by writeback never get an attributed gain, so the loop cannot tell which lever paid off. A round that relies only on levers fails withempty_gridinstead of running.
Action: Change the call to_lever_seeded_payload(payload, self.ss), or change_lever_seeded_payloadto take the precomputedself.lever_payloadand drop its innerframework_lever_gridcall. Then add an executor-level test that seedsauthored_framework_leversand asserts both thefwlever_*variants and a non-emptyframework_lever_attributions.
- Empty
-
[free:observable-effect-wrong] The Observable effect section names the wrong ledger and leaves out the fields that actually change [verified]
Problem: The live body says "each refused variant'slosersentry now carries the fields itsrejectedrow carries" and "no flag, env var, default, exit code or persisted field moves". At head the facts are different:losersis unchanged. It is still_ledger_view(row, _LOSER_FIELDS)(explore.py:837, :1600), and the recorded base-vs-head differential shows no difference inlosers.- The persisted
explore_search_update.rejectedrows do gain fields (_REJECTED_FIELDS, explore.py:838-846, written at :1599). Decision-stage rows gainstageandworkspace. Warmup-failure rows gainerror_classandserver_log_path(explore.py:1420-1421). The differential counted 71/71/22/22 occurrences. filter_baseline_noop_variantstakes a new keywordobserved=(config, env)and passes the remaining arguments through as**build(explore.py:577-581). Outside explore.py, only tests call it.
Impact: The release notes would tell operators thatlosersentries change and that no persisted field moves. In factlosersis unchanged and the persistedrejectedrows gain four fields, so anyone parsing the ledger is told to look in the wrong place.
Action: Rewrite Observable effect to say thatlosersis unchanged, list the four newrejectedfields and which row kinds get each one, and state thefilter_baseline_noop_variantssignature change. Write the body againstgit diff origin/main...HEADonly; every name and number must be reproducible from that diff.
-
[free:body-names-absent-code] The body describes an abandoned two-module design and lists deletions of code that never existed on main [verified]
Problem: The live body names code that is missing from both the head and main:- "
_variant_grid.py(671 lines)", "grid_server_args.MULTI_VALUE_FLAGS", "Every caller --intent_router, five test modules, two reference docs -- now reads the grid helpers from_variant_grid" and the removed "explore -> executors -> explore... import cycle". At head there is no_variant_grid.py,grid_server_args.pyis untouched, and the flag set is still the private_MULTI_VALUE_FLAGS(grid_server_args.py:360, imported at explore.py:93). - Under "What is gone": "
_RoundStep", "release_serving_lease", "_RoundGridand_GradingAnchor".git grepfor these names at origin/main (and at the merge-base 3a54819) returns nothing, so there was nothing to delete. - "the result's
loserskey is a view of it". At head,self.rejectedandself.losersare still two separate lists, both appended in_record(explore.py:1599-1600). - The methods "
_stage_variant,_launch_round,_warm_the_server,_measure_variant,_judge_variant" and the #1918 names "_past_the_opening_restatements", "_ExploreRound.restates_the_stack", "launched_already" do not exist at head. The actual methods include_drop_leading_noops(:1152),_restates_stack(:1226),_measure(:1341),_warmup(:1375) and_graded_verdict(:1490). - "
_RoundSetup,_RoundLaunchand_KeepComposition... are kept" and "_GradedVerdictand_AccuracyVerdict". None of these exist at head. The only classes are_RoundRefused,_ExploreRoundandExploreExecutor. - "the modules that imported a grid helper from
explorenow import it from_variant_grid, andtest_geak_revalidation_dispatchpatches ...". At head, the only test change is_noop_filter_kwargsin test_explore_executor.py, adapted to the newfilter_baseline_noop_variants(observed=...)signature.
Impact: The release notes would announce a new module, a newly publicgrid_server_argsname, an import-cycle fix and the removal of four mechanisms. None of them ship, so operators and downstream readers would look for code that does not exist.
Action: Replace "What is gone", "The new module", the #1918 paragraph and the test paragraph with the names the head actually contains (_ExploreRoundand its methods,_ledger_view/_REJECTED_FIELDS/_LOSER_FIELDS,_drop_leading_noops,_restates_stack). Delete every claim about_variant_grid,MULTI_VALUE_FLAGS, the import cycle and the five migrated test modules. Write the body againstgit diff origin/main...HEADonly; every name and number must be reproducible from that diff.
- "
-
[free:body-numbers-wrong] The size numbers, and the size-exception reason built on them, do not match the head diff [verified]
Problem: Several numbers in the live body disagree with the head:- "It is now eight lines" / "Function length (lines) ... 8". At head,
ExploreExecutor.__call__is 16 lines (explore.py:1805-1820). - "
explore.pyis 1798 lines" and "the two modules are 2469, a net +427". At head, explore.py is 1834 lines and is the only production module changed.git diff --numstat 3a548190f ad42c957fgives explore.py +1061/-1269, which is net -208. - "
size-exception: the diff is 2469 production Python lines across two modules, of which 671 are the new module's". That premise is false, yet thesize-exceptionlabel is still applied.
Impact: The release notes would report a +427-line, two-module change with a size waiver. The real change is a -208-line, single-module rewrite, and the waiver label rests on a reason that no longer holds.
Action: Restate the table and the line-count paragraph from the head diff (__call__16 lines, explore.py 1834 lines, net -208). Then either remove thesize-exceptionlabel or give a reason that holds for a 3-file, net -208 diff. Write the body againstgit diff origin/main...HEADonly; every name and number must be reproducible from that diff.
- "It is now eight lines" / "Function length (lines) ... 8". At head,
Checked: src/hyperloom/orchestrator/actions/executors/explore.py (framework_lever_grid, _lever_seeded_payload, _ExploreRound._resolve_grid, _record, _warmup_failed, _LOSER_FIELDS, _REJECTED_FIELDS, filter_baseline_noop_variants, ExploreExecutor.__call__), src/hyperloom/orchestrator/loop/writeback.py:3000, src/hyperloom/inference_optimizer/grid_server_args.py (_MULTI_VALUE_FLAGS), src/hyperloom/inference_optimizer/tests/test_explore_executor.py, live PR body | Ran: base-vs-head executor differential for lever seeding (run_with_session_kill stubbed); recorded base-vs-merged ledger differential over 13 explore test files; trial merge with origin/main b772e94 (clean); code_metrics gate on merged tree (PASSED); ruff check/format; pytest over 13 explore test files (770 passed); git grep of body-named symbols at head and at origin/main; git diff --numstat | Base: 3a54819 | Head: ad42c95
…er list ``_lever_seeded_payload`` took a ``shared_state`` and re-derived the lever grid from it, but the round had already computed that grid one line above and handed it the list instead. ``framework_lever_grid`` read ``authored_framework_levers`` off a list, found nothing, and returned ``[]``, so no ``fwlever_*`` variant was ever seeded: a round with only levers refused as ``empty_grid``, and a round with LLM proposals reported no lever attributions. The round owns the list -- it needs it again to attribute the outcomes -- so the helper now takes it rather than a second source to derive it from. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lore # Conflicts: # scripts/code_metrics_baseline.txt
Resolve scripts/code_metrics_baseline.txt by keeping main's entries and applying this branch's removals and lowered values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lore Only scripts/code_metrics_baseline.txt conflicted. Resolved with the ratchet rule: whichever side records the lower value wins, an entry the merge base had and a side dropped is retired, an entry absent from the base is kept. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lore Brings the branch up to origin/main; no path conflicted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…lore Brings the branch up to origin/main; no path conflicted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Resolve scripts/code_metrics_baseline.txt by keeping main's entries and applying this branch's removals and lowered values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve scripts/code_metrics_baseline.txt by keeping main's entries and applying this branch's removals and lowered values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chaojhou
left a comment
There was a problem hiding this comment.
PR #1965 -- refactor(explore): let the explore round own the stack it builds and the ledger it writes
What it does: ExploreExecutor.__call__ ran a whole explore round inline (1124 lines, cyclomatic 76) with the stack, budget, grading anchor and ledgers held as closure locals. The PR moves that state and the per-variant steps onto a private _ExploreRound in the same explore.py, and builds the rejected and loser rows from one recorded row through _REJECTED_FIELDS / _LOSER_FIELDS. The only observable change, which the description states, is that persisted explore_search_update.rejected rows gain stage/workspace (decision stage) and error_class/server_log_path (warmup failures). filter_baseline_noop_variants now takes observed=(config, env).
Blocking issues: none
SKIPPED: V4 -- openprs.txt is PARTIAL for #1869/#1276/#1154/#1032 (only their first 100 files were compared).
Checked: explore.py at base vs head, unit by unit: __call__ (base 794-2030) against ExploreExecutor.__call__, _ExploreRound.__init__, _materialized_round_config, _anchor_the_params, _framework_of, _resolve_grid, _lever_seeded_payload, _seed_grid_payload, _shape_runnable, _drop_leading_noops, _open_schedule, _refused, _restates_stack, _already_measured, _out_of_budget, _refuses_context_copy, _staged, _measure, _warmup, _warmup_failed, _settle, _graded_verdict, _apply_accuracy_gate, _record, _keep, _per_variant_outcomes/_outcome_row, _result, _budget_error and _last_round_summary; also filter_baseline_noop_variants and filter_context_copy_drafts (via _materialized_launch). Readers of the rejected rows: phase_state._merge_rejected, prelude.py:271 and prompt_builder.py:798. The callers and monkeypatch targets of explore names were grepped across the tree. The independent reader re-verified the prev-card fix and the updated callers. | Ran: pytest test_explore_executor.py on head (142 passed) and on base (140 passed). The same 8 tests fail on both because of the local Windows environment. The new lever tests also pass when run on the base tree. git merge-tree origin/main <head> is clean. | Base: bb2da63 | Head: ebbb11d | Branch head ab678b2 merges main into the reviewed head: baseline file only, non-baseline diff identical to the reviewed head
LGTM
What this does
Moves the explore round out of the long
ExploreExecutor.__call__body into_ExploreRound, in the sameexplore.pymodule. The round owns the accumulating launch stack, its budget, the measured fingerprints and the result ledgers._recordcentralizes the common variant-row fields and projects them through_REJECTED_FIELDSand_LOSER_FIELDS;rejectedandlosersremain separate lists.The production diff against
mainis confined toexplore.py: +1061/-1269, net -208 lines. The file is 1834 lines andExploreExecutor.__call__spans 16 lines. The other changed files aretest_explore_executor.pyandscripts/code_metrics_baseline.txt.The existing per-turn restatement and deduplication behavior is preserved:
_drop_leading_noopsremoves only leading restatements;_restates_stackevaluates later variants against the stack after prior KEEPs;launched_fpscontains variants actually launched. Authored framework levers are computed once and passed as a list to_lever_seeded_payload, so they are measured and attributed even when the LLM proposes no grid.Observable effect
The persisted
explore_search_update.rejectedrows gainstageandworkspacefor decision-stage failures/rejections, anderror_classandserver_log_pathfor warmup failures. Thelosersfield keeps its existing projection. Consumers that inspect the persisted rejected rows can now locate the failing stage and its artifacts.The internal helper
filter_baseline_noop_variantstakesobserved=(config, env)in place of separate observed-config and observed-env keywords; its test callers are updated. Grid helpers remain inexplore.py. No module is added and the private_MULTI_VALUE_FLAGSname remains unchanged.The eval opt-out removed on
mainstays removed. A warm decision round still uses the accuracy result collected by its warmup, while a cold round retains its own eval.Validation and size
The branch contains
mainatbb2da63332dcbc46bdf10702c516ef43eb80a016. The explore executor tests pass on Linux/Python 3.10. The lever regression cases drive the executor and the real grid runner on the forced-cold path, stubbing lifecycle eligibility and the benchmark subprocess and disabling grid warmup. They assert the seeded variant order, successful result and measured per-lever attribution both with and without an LLM proposal. Ruff check and format check pass.size-exception: this in-place rewrite changes 2330 production Python lines even though its net reduction is 208. The methods share one round's evolving stack and settlement order; this PR keeps the round refactor together so those invariants can be reviewed and tested as one change. Existing module-size debt stays recorded; the baseline is tightened, not raised.