Skip to content

refactor(kernel): decompose GEAK settlement and GEMM validation in the KERNEL phase - #1960

Merged
chaojhou merged 50 commits into
mainfrom
feature/chenyi/cc-kernel-phase
Oct 22, 2026
Merged

chaojhou merged 50 commits into
mainfrom
feature/chenyi/cc-kernel-phase

Conversation

@chennyiiis

@chennyiiis chennyiiis commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

What this changes

Decomposes the GEAK handoff/settlement, GEMM candidate validation and rewrite-controller sequencing inside orchestrator/phases/kernel.py. The recipe benchmark block is parsed once for the handoff, duplicate GEMM timeline-row construction is removed, and the long phase operations are expressed as named steps. No sibling module is introduced: all production changes remain in kernel.py (net production lines remain negative against main).

GeakRunnerFacts and GeakRunnerOutcome keep process timing separate from the GEAK candidate. Only candidate content and its existing return-code/validation annotations are persisted in geak_result, so a later AgentX KERNEL entry still recognizes a candidate that was already adjudicated. The malformed-term-grace check runs before process dispatch, snapshot adoption retains all three existing signals, and controller-owned cycle/budget/handoff facts retain precedence over subprocess output.

Observable effect

  • A missing, unreadable or non-object GEAK result is handled as no usable candidate through the shared JSON reader; an unreadable result no longer aborts this recovery read.
  • Successful controller-result envelopes supply patch_count, task_count and output_dir defaults when the subprocess omits them. Phase-owned macro_cycle, handoff_dir, budget_minutes and hard_timeout_sec cannot be overridden by the subprocess.
  • Each kept GEMM E2E row now carries its tuned_file explicitly. The final campaign still attributes the last adopted file and validates gains through the existing writeback path.
  • The GEAK handoff's invalid_env_spec handler covers env-spec serialization only, as it did before this PR: build_env_spec() raises EnvSpecUnserializable and _open_geak_handoff catches that alone. Every other fault raised while the handoff is built -- a native-AgentX proposal refusing an unverified Magpie server launch, a malformed MAX_MODEL_LEN/GPU_MEMORY_UTILIZATION, a non-serializable extra_envs, a SharedState field that is not a number -- propagates and fails the kernel_agent task rather than being reported as a serialization failure on a phase that returns success. No operator-visible change against main.
  • Logger names remain under hyperloom.orchestrator.phases.kernel. Some orchestration diagnostics are consolidated with their owning step; there is no logger migration to a sibling module.

Candidate promotion, timeout cleanup, rollback/retry policy and the measured KEEP gate are preserved. This refactor introduces no new CLI flag or default.

Tests and scope

Five test files change: test_geak_config.py, test_geak_late_settlement.py, test_geak_revalidation_dispatch.py, test_controller_patch_integration.py and test_gemm_e2e_validation_offloads_io.py; test_geak_revalidation_dispatch.py also adds test_native_geak_proxy_refusal_is_not_an_invalid_env_spec, pinning that a refusal from seed_native_geak_proxy reaches the caller instead of being classified invalid_env_spec. They follow moved internal entry points and pin recognition of a settled AgentX candidate on re-entry, preservation of phase-owned controller facts, and offloading of GEMM coverage I/O. Linux Python 3.10: 728 tests passed across the GEAK suites, prelude roofline, kernel timeline, controller integration and GEMM I/O-offload tests.

The metrics baseline retires 21 entries and reduces the existing module-length allowance. The phase module remains large, and the unchanged seven-parameter delegation-timeline interface remains existing debt. The size-exception covers the in-place decomposition of this single phase owner: the production diff changes 3123 lines while reducing its size and preserving the coupled delegation/settlement contracts. There is no unrelated production-file change.

Linked issue: none. Current GitHub lint, code-metrics, test and coverage checks pass; GPU E2E jobs are skipped and do not establish a GPU execution result.

…here its spec comes from

_run_geak_kernel_phase derived the reference-verification status through a
four-armed chain and rebuilt the launch spec from a recipe's server log inline,
both before the measurement it guards is even read. Give each a name.

_run_geak_kernel_phase: 608 -> 594 lines, cognitive 122 -> 109, cyclomatic 49 -> 42

Observable effect: none -- pure refactor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chennyiiis chennyiiis added the baseline-raise Waives new, worse and baseline-growth code-metrics findings; reason required in the PR description label Oct 17, 2026
@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Code metrics gate: PASSED

Judged: the 7 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 11 -3
Cognitive complexity fails > 30 0 0 0 75 -5
Function length (lines) fails > 80 0 0 0 204 -9
Nested block depth fails > 5 0 0 0 8 +0
Parameter count fails > 5 0 0 0 83 -4
Module length (lines) warns > 800, fails > 1200 0 0 0 47 +0
Duplicated lines per file fails > 0 0 0 0 95 +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

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

Unit Dimension Base Now
src/hyperloom/orchestrator/phases/kernel.py:2587 KernelPhase._record_geak_delegation_timeline Parameter count 7 7
src/hyperloom/orchestrator/phases/kernel.py:1 Module length (lines) 4241 4199
58 findings in files this change does not touch

Outside this change (informational): 58

Unit Dimension Baseline Now
src/hyperloom_kb/collect/mapping.py _experience Cognitive complexity absent 31
src/kernelforge/cli/forge_loop_command.py forge_loop Cognitive complexity absent 31
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/inference_optimizer/cli/__init__.py _reexport_session_environment Function length (lines) absent 107
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/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 84
src/hyperloom_kb/sync.py GlobalSync.pull Function length (lines) absent 84
src/kernelforge/loop/runner.py IterationLoop._close_iteration Function length (lines) absent 117
src/kernelforge/loop/runner.py IterationLoop._collect_session_candidate Function length (lines) absent 185
src/kernelforge/loop/runner.py IterationLoop._implementer_history Function length (lines) absent 112
src/kernelforge/loop/runner.py IterationLoop._rule_on_attempt Function length (lines) absent 102
src/kernelforge/loop/runner.py IterationLoop._source_attempt Function length (lines) absent 175
src/hyperloom_kb/http_service.py Module length (lines) absent 1525
src/kernelforge/cli/forge_loop_command.py Module length (lines) absent 2003
src/hyperloom/orchestrator/actions/executors/_kv_metrics.py port_from_server_log Nested block depth absent 6
src/hyperloom/orchestrator/specialists/dispatch.py SpecialistDispatchCollaborator.warm_specialist_params Cognitive complexity 93 94
src/hyperloom/agents/quantization/driver/retry.py quantize_via_prompt Function length (lines) 102 103
src/hyperloom/inference_optimizer/breakdown/exporter.py build Function length (lines) 132 136
src/hyperloom/inference_optimizer/cli/__init__.py _run_optimize Function length (lines) 273 279
src/hyperloom/inference_optimizer/cli/preflight.py _preflight Function length (lines) 203 208
src/hyperloom/inference_optimizer/multi_node/scripts/kernel_patch_multinode.py _do_apply Function length (lines) 98 99
src/hyperloom/orchestrator/actions/executors/_multi_node_server_lifecycle.py restart_server_for_round Function length (lines) 135 138

...and 33 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.

chennyiiis and others added 5 commits October 19, 2026 11:38
…k named units

Every unit of phases/kernel.py now names a complete job rather than a stretch of
one of five long methods.

Two sibling modules take the work that is not the phase's own sequencing:

* `_kernel_timeline.py` records what each settled campaign did. The four
  recorders read their result through one `SettledRun`, which answers for the
  fact wanted rather than the key a producer happened to write it under -- the
  rule all four were spelling out inline, once per field.
* `_geak_handoff.py` owns the payload the phase hands GEAK: the same-config
  reference it must reproduce, the devices it runs on, and the recipe facts the
  handoff advertises.

`RunnerRun`, `_GemmE2ERun`, `_GemmE2ELedger`, `_AcceptedLaunch`, `_RooflineAnchor`
and their kin replace the parallel keyword lists and the names the old blocks
wrote back into their enclosing scope.

No observable change: internal structure only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chennyiiis chennyiiis removed the baseline-raise Waives new, worse and baseline-growth code-metrics findings; reason required in the PR description label Oct 19, 2026
Test and others added 2 commits October 19, 2026 18:23
…t might hold

``SettledRun`` grows a method per fact the timeline reports -- status, start,
end, failure class, failure reason, evaluation directory -- so each key a
producer might have used is named once rather than at every call site, and
``geak_dir`` names the directory a delegation reads and writes under.

No observable change: internal structure only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chennyiiis chennyiiis added the size-exception Waives the PR diff budget; added by a maintainer with a reason in the PR description label Oct 19, 2026
Test and others added 2 commits October 19, 2026 18:54
`_aiter_config_merge.py` takes the CSV reading, column union, dispatch-key
derivation and de-duplication that turned a GEMM candidate's rows into the
complete table the runtime serves. The phase keeps the question it can answer:
which table this env var names, and whether the candidate is on disk.

The fusion retry caps, the last-fusion record, the bus announcement and the
per-kernel adoption writer become module functions of what they read rather than
methods, which is how the phase they serve is already faked where they are tested.
Three tests follow the symbols they exercise: the GEAK runner is a method rather
than a closure, the AgentX proxy note is logged by the handoff builder, and the
GEMM validator's two off-loop reads now sit in the methods it runs through.

No observable change: internal structure only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@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 #1960 -- refactor(kernel): name how a GEAK reference launch is verified, and where its spec comes from

What it does: PR #1960 is a pure-refactor split of src/hyperloom/orchestrator/phases/kernel.py. It breaks 16 KernelPhase methods (the GEAK delegation, the GEMM e2e validation, the aiter CSV merge, GEAK promotion/adoption and the timeline rows) into named helpers and NamedTuples, and moves the work that isn't phase sequencing into three new sibling modules: _geak_handoff.py, _kernel_timeline.py and _aiter_config_merge.py. Differential scripts that drove the base and head code with identical fakes found no behaviour difference. The only blocking item is that the PR description no longer matches the diff at head.

Blocking issues: 1

  1. [desc-stale-third-sibling] The PR description no longer matches the diff at head: a third new module, the module length and the test count are all wrong [verified]
    Problem: The newest commit (52115c5, 'let the aiter dispatch table own its own merge') added _aiter_config_merge.py, 213 lines that now own the aiter CSV merge. The body was not updated. It still says 'Nothing outside phases/kernel.py and its two new siblings changes except four tests', its 'Two sibling modules' section lists only _geak_handoff.py and _kernel_timeline.py, and it gives the module length as '4227 -> 4013', while the head baseline entry is 3820 (scripts/code_metrics_baseline.txt:964). 'Three existing test files move' is also wrong: six test files change, and the caplog logger change, the to_thread qualname change and the widened offload-source check are not mentioned. The body also says 'Observable effect: none', but every log line that moved into the siblings (for example the log.error 'geak handoff: %s is set but empty; the run has no visible GPUs', the 'workload_spec: observed replay shape', the 'GEAK could not use these backends' warning and all 'gemm E2E: merged/cannot derive dispatch keys/duplicate' lines) now carries the logger name hyperloom.orchestrator.phases._geak_handoff, _kernel_timeline or _aiter_config_merge. The operator log format includes that name ('%(asctime)s %(name)s [%(levelname)s] %(message)s', inference_optimizer/cli/init.py:2796), and test_geak_handoff_workload_contract.py had to widen caplog to 'hyperloom.orchestrator.phases' for this reason. src/hyperloom/orchestrator/phases/_aiter_config_merge.py:1
    Impact: The release notes are assembled from PR descriptions, so this one will describe a module split and an effect that are not what lands. Anyone who greps or filters operator logs by the phases.kernel logger name silently loses the GEAK handoff and GEMM merge lines.
    Action: Update the description. Name _aiter_config_merge.py and its job, correct the module length to 3820, list all six test files, and in the observable-effect section state that log lines from the moved code now carry the sibling modules' logger names. Alternatively keep log = _logging.getLogger('hyperloom.orchestrator.phases.kernel') in the siblings, but stating the change is the simpler fix.

Checked: KernelPhase._run_geak_kernel_phase (+ _open_geak_delegation, _refuse_geak_product, _wind_down_geak_to_sweep, _finish_gea (yes); KernelPhase._merge_gemm_candidate_with_runtime (+ _aiter_config_dirs, _aiter_config_merge.*) (yes); KernelPhase._validate_gemm_tuning_e2e (+ _close_gemm_tuning_with_no_candidates, _gemm_e2e_run, _validate_one_gemm_candid (yes); KernelPhase._gemm_e2e_candidates / _gemm_canonical_candidates (+ _rebuilt_gemm_candidates, _assumed_blockscale_candidate (yes); KernelPhase._geak_acceptance_specs (+ _acceptance_lane_rows, _stated_e2e_delta, _collapse_onto_twin) (yes); KernelPhase._record_gemm_tuning_timeline / _record_fusion_timeline / _record_kernel_rewrite_controller_timeline / _recor (yes); KernelPhase.promote_geak_from_candidate (+ _geak_promotion_measurement, _carry_measured_launch, _compose_promoted_launch (yes); KernelPhase._record_geak_candidate (+ _geak_claim, _geak_pending_record, _record_geak_claim_on_timeline, _warn_on_measur (yes); KernelPhase.record_geak_adopted_kernels (+ _relative_gain_pct, _geak_adoption_measurement, _write_geak_adoptions, _adopt (yes); KernelPhase._maybe_reprofile_for_kernel (+ _reprofile_decision, _roofline_anchor, _adopt_reprofiled_snapshot, _reprofile (yes); KernelPhase._run_kernel_rewrite_controller (+ _controller_attempt, _integrate_controller_patches, _record_controller_pat (yes); KernelPhase._handle_fusion_result (+ _fail_unreadable_fusion_envelope, _charge_fusion_retry_caps, _save_last_fusion, _an (yes) | Conflict with main none | Base: ef93838 | Head: 5ea9c36

…nel-phase

# Conflicts:
#	scripts/code_metrics_baseline.txt
@chennyiiis chennyiiis changed the title refactor(kernel): name how a GEAK reference launch is verified, and where its spec comes from refactor(kernel): name the KERNEL phase GEAK handoff, timeline rows and aiter table merge as their own units Oct 20, 2026
@ZhengGong-amd

Copy link
Copy Markdown
Collaborator

Requesting changes: this refactor relocates every mechanism of the 608-line function instead of removing any, introduces an orphaned-process regression, and is conflicting with main.

Production code: +2935/−2132 (net +803), with 3 new modules, 24 new types (22 NamedTuples, SettledRun, _GemmE2ELedger) and 100 → 224 functions; the summed cyclomatic complexity rises from 528 to 629. A complexity refactor should shrink production code. Bring this to net-zero or negative by deleting the mechanisms listed below. Moving them into new modules does not count.

Blocking

  1. GEAK_TERM_GRACE_S is now read after subprocess.Popen (phases/kernel.py:1736-1746), and the except Exception at :1587 swallows the error. With GEAK_TERM_GRACE_S=3m the runner is spawned, EnvValueError is raised, and the error is recorded as runner_crashed. Nothing calls communicate/killpg, so the GEAK process tree keeps holding the GPU. On base, this value was read before Popen. Resolve it next to _geak_timeouts() before asyncio.to_thread, pass it in as a parameter, and add a test asserting that Popen is never called when the value is invalid.
  2. mergeable: CONFLICTING in scripts/code_metrics_baseline.txt. Rebase and regenerate the file with scripts/code_metrics.py --update-baseline.
  3. The description does not match the diff. It omits _aiter_config_merge.py, and it gives 4013 lines for kernel.py (actual: 3820) and three changed test files (actual: six). The claim "every function at or below the ceiling" is wrong, because _fmoe_apply_verdict is still 131 lines and still in the baseline. Under Observable effect, state that the logger names of the moved log lines change.

Simplify instead of relocate

  • Delete read_recipe_bench_envs. _geak_handoff.py:258 and :271 parse the same YAML twice, contradicting the comment at :269. Have both callers (:258, kernel.py:2296) use recipe_benchmark(path).get("envs") or {}.
  • Replace SettledRun (_kernel_timeline.py:28-73) with a single _first_text(mapping, *keys). Only ended_at and failure_reason encode real rules.
  • Inline the single-use state bags: LaunchSpec, _GeakDelegation, _RunnerExit, _DispatchTable, _MergeSources, _ReprofileDecision, _IntegrateOutcome, _MergedCandidateEnvs, _ApplyCheck. Drop the bool-returning _refuse_*/_promote_* methods; once handoff construction moves out, GEAK settlement already fits the thresholds.
  • Narrow the 5 except Exception moved into _geak_handoff.py (:378,414,475,540,577) to yaml.YAMLError/OSError/ValueError/AttributeError.
  • Split into one PR per moved module, then the control-flow PR. Each step lowers the line count, so no size exception is needed.

Test and others added 3 commits October 20, 2026 10:56
Read inside the dispatched runner method, an unreadable GEAK_TERM_GRACE_S
raised with Popen already returned: nothing called communicate or killpg,
so the runner -> run_e2e -> vllm tree kept the GPUs while the phase
recorded runner_crashed and wound down to SWEEP.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…s it

The module-level import of the executors workload-env module joins an import
cycle CodeQL already reports on every other module-level importer of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chennyiiis

Copy link
Copy Markdown
Contributor Author

Thanks - all three blocking items are fixed, and the "simplify instead of relocate" points are answered below. Head is e9c48ace0; CI is 40 checks, 0 failures, MERGEABLE.

1. The term grace read after Popen. Confirmed and fixed in 8e5ac40f9. At the merge base term_grace = env_int("GEAK_TERM_GRACE_S", default=180) was kernel.py:1604, above the Popen at :1621; lifting the _run closure into _run_geak_runner turned it into an argument expression evaluated after the spawn. It is now resolved in the phase body before the asyncio.to_thread dispatch and passed in, which restores the base ordering exactly. test_an_unreadable_term_grace_is_refused_before_the_runner_is_dispatched sets GEAK_TERM_GRACE_S=3m, expects EnvValueError, and asserts the runner was never dispatched; it fails without the fix.

2. mergeable: CONFLICTING. origin/main merged (the five PRs of 2026-10-19 are now ancestors, and none of them touches any file in this diff), scripts/code_metrics_baseline.txt regenerated. Now MERGEABLE.

3. The description. Rewritten against head, with every number re-measured rather than recalled: three modules named with what each owns, kernel.py at 3820 lines, six changed test files plus one added test, the logger-name change stated as the one observable effect, and the complexity claim scoped to the cyclomatic ceiling it actually means (_fmoe_apply_verdict is pre-existing at 131 lines, still baselined, untouched here - it is called out explicitly).

On "relocates every mechanism instead of removing any". Fair, and the description now leads with the one real removal instead of the file inventory. The measurable part: the four _record_*_timeline methods carried 114 Cmetrics decision points at base, 76 of them boolean operands in the per-field "first non-empty of these keys" chains; SettledRun replaces that rule and the same responsibility now carries 48 and 10. On your summed-complexity number - I measure 544 to 629 across 105 to 225 functions. A split costs +1 per function by construction, so 120 new functions are worth +120 on that sum; the sum rose by 85, i.e. 35 decision points fewer than the split alone would have cost. That is the size of the removal, and the description now says so. It also says plainly that _geak_handoff.py and _aiter_config_merge.py are moves, not simplification - they exist because the module could not both absorb the split and stay under its own length baseline.

CodeQL cyclic import on _geak_handoff.py:34. Fixed in e9c48ace0: imported inside _handoff_payload, its single caller, as seven other imports in that module already are. The cycle is pre-existing (the same edge is an open accepted alert on phases/kernel.py:38, one of 199 open alerts of that rule), but this diff added one more importer of it. None of the three new modules imports phases/kernel.py or anything else under phases/, so the split creates no new cycle.

Not applied, with reasons:

  • Delete read_recipe_bench_envs (the double YAML parse). Both parses are pre-existing: at the merge base kernel.py:1328 called _read_recipe_bench_envs and kernel.py:1430 called _recipe_benchmark on the same handoff path, and both moved unchanged. The comment at _geak_handoff.py:269 claims only that the two resolvers below it share one parse, which is true. Collapsing them changes a path this PR does not otherwise touch, so it belongs in its own change.
  • Narrow the five except Exception in _geak_handoff.py. Those are moved bodies, not new handlers - the count over the changed production files is 24 at the merge base and 24 at head, none added. Narrowing them changes the "never raises" contract those resolvers' tests pin, which is a behaviour change and not a refactor.
  • Replace SettledRun with a single _first_text(mapping, *keys). That is the same mechanism with the rule pushed back to the call sites, which is what the four methods already did. SettledRun is 11 decision points for eight facts; the measurement above is what it bought.
  • Split into one PR per moved module. Each intermediate commit would leave phases/kernel.py over its own module-lines baseline, so each part would need baseline-raise. That is the trade the size-exception section states.

Test and others added 7 commits October 21, 2026 16:55
…rofiling settles

The rewrite controller recomputed its budget in minutes after having passed it,
guarded an ``integration`` slot every path assigns a dict to, and carried a
``baselines=None`` default only the test suite used. The kernel-entry reprofile
tested three independent signals for the one question of whether a snapshot
landed, and restated its own trigger in a log line the timeline row already carries.

No observable change beyond that one log line, which is the trigger the reprofile
row records.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…erge runs

The candidate merge read a CSV header through a second reader, counted duplicate
dispatch keys into a map it then threw away, and built its key tuple inline at
three sites. The search for the runtime table and the table-key derivation are
now each one named operation, and the legacy tuner candidates and the
a8w8-blockscale fallback no longer share a reader with the canonical path through
a copied env-normalisation comprehension.

No observable change: the merged CSV, its column order and its row order are the same.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The validator carried two loop-control flags for one retry outcome, a second
pair of locals for one merge refusal, a REVERT arm byte-identical to the one
below it, and two branches whose only difference was the sentence they logged.
Each step of a candidate's validation is now the operation it performs, and the
candidates it cannot run are refused by one named rule.

Observable effect: the per-tuner integrate-fault warning now always names the
attempt it is on, and a KEEP row in ``e2e_results`` carries the ``tuned_file``
that lift adopted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…atched

An unreadable GEAK_TERM_GRACE_S must be refused while nothing has been spawned:
resolved after the spawn, the raise leaves the runner -> run_e2e -> vllm tree
holding the GPUs with no handle left to signal it. Regenerates the metrics
baseline, which retires 21 entries for phases/kernel.py and lowers its module
length.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…r name the AgentX reference

The verification status branched on an AgentX flag into two bodies; AgentX has its
own status and the caller names it. The session's GEAK result.json is one path, and
the GEMM timeline row reads the applied artifact once instead of twice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@ZhengGong-amd ZhengGong-amd 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 #1960 -- refactor(kernel): name the KERNEL phase GEAK handoff, timeline rows and aiter table merge as their own units

What it does: At the current head the PR rewrites src/hyperloom/orchestrator/phases/kernel.py in place (+1453/-1622) to break the long KERNEL-phase methods (GEAK runner dispatch and settlement, GEMM e2e validation, rewrite-controller attempt, kernel-entry reprofile) into smaller KernelPhase methods, deletes _read_recipe_bench_envs so the recipe YAML is parsed once, and retires 21 of the 22 kernel.py entries in scripts/code_metrics_baseline.txt. It adds no new module.

Blocking issues: 5

  1. [free:geak-result-runner-facts] AgentX re-promotes and re-revalidates a GEAK result it already settled [verified]
    Problem: _settle_geak_runner_exit returns result | facts (src/hyperloom/orchestrator/phases/kernel.py:1694; the SIGTERM path does the same at :1743), and _run_geak_kernel_phase persists it with state.geak_result = result (:1880). facts holds runner_timeout_s, kill_timeout_s, started_at, duration_sec, which result.json never contains. geak_candidate_matches (src/hyperloom/orchestrator/phases/geak_rebench.py:97) ignores only _REVALIDATION_ANNOTATION_KEYS and returncode, so with harness_can_replay=False geak_candidate_is_adjudicated (geak_rebench.py:62) never matches the persisted result against the file it came from. At base these four values were keyword arguments to _record_geak_delegation_timeline and never entered the result.
    Repro: AgentX session; KERNEL entry 1 runs the runner with rc=0, result.json is written and revalidation returns incomparable; KERNEL entry 2 runs a runner that fails (or exits rc=0 leaving the same result.json). Base: revalidation reasons ['geak_e2e_win'], the settled candidate is left alone. Head: ['geak_e2e_win', 'geak_e2e_win_recovered']. _promote_crashed_geak_result (kernel.py:1783) re-promotes it, and the no_new_geak_product guard (kernel.py:1678) no longer fires.
    Impact: In every AgentX run that reaches a terminal GEAK verdict, the next KERNEL entry re-records an already rejected candidate and queues another full e2e revalidation. That spends GPU time and run budget on a settled result and can overwrite the settled verdict in session state.
    Action: Keep the runner facts out of the candidate. Have _dispatch_geak_runner/_settle_geak_runner_exit return the facts separately from the result, pass them to _record_geak_delegation_timeline as the started_at/duration_sec/runner_timeout_sec/kill_timeout_sec keyword arguments as base did, and persist only the result.json content to state.geak_result. Add a test that runs two AgentX KERNEL entries as in the repro and asserts that no geak_e2e_win_recovered revalidation is queued.

  2. [free:head-tests-red] Six tests fail at the PR head and pass at the merge base and on origin/main [verified]
    Problem: The diff changes these contracts without updating the tests that pin them:

    • src/hyperloom/inference_optimizer/tests/test_sbd_v6_kernel_timeline.py:1746 test_geak_runner_outcome_is_wired_to_geak_delegation and :1725 test_geak_absent_backends_land_on_the_result: TypeError, because _record_geak_delegation_timeline (kernel.py:2499) no longer accepts started_at.
    • src/hyperloom/inference_optimizer/tests/test_prelude_roofline.py:698 and :715 (test_kernel_entry_reprofiles_when_*_changed_at_same_tput): assert 64 == 128. _adopt_reprofile_snapshot (kernel.py:570) dropped the snapshot-count check, and the test stub appends a snapshot without bumping roofline_snapshot_id.
    • src/hyperloom/inference_optimizer/tests/test_geak_adoption_ledger.py:327 test_a_non_dict_result_records_no_candidate: AttributeError, because the non-dict guard was removed from _record_geak_candidate (kernel.py:1999).
    • src/hyperloom/orchestrator/phases/tests/test_gemm_e2e_validation_offloads_io.py:29 test_the_coverage_replay_runs_off_the_loop: the asyncio.to_thread(self._gemm_tuned_config_coverage, ...) call moved out of _validate_gemm_tuning_e2e into _gemm_apply_blockers (kernel.py:3405), and the test still inspects only the old method.
      Impact: CI run 39576811809 on head 83428fa has 9 test shards and both coverage jobs red, so the PR cannot merge. The two removed guards are also left without a test that states the new contract.
      Action: Fix the two timeline tests through the item 1 fix, which restores those keyword arguments. Make the roofline stub bump roofline_snapshot_id when it appends a snapshot, as the real writer does. Delete test_a_non_dict_result_records_no_candidate together with the guard and say so in the PR body, or restore the guard. Point _validator_source at the method set that now carries the GEMM e2e I/O. Then run these four files at head.
  3. [free:desync-observable-effect] The Observable effect section states a change that does not happen and omits two that do [verified]
    Problem:

    • Body: "one, and it is confined to the logger name on twelve log records". No sibling module exists at head (git ls-tree -r --name-only HEAD src/hyperloom/orchestrator/phases lists no _geak_handoff.py, _aiter_config_merge.py or _kernel_timeline.py), so every record still logs under hyperloom.orchestrator.phases.kernel.
    • Body: "Nothing else changes: no behaviour, interface, default, flag or artifact". In fact the persisted state.geak_result gains runner_timeout_s, kill_timeout_s, started_at, duration_sec (kernel.py:1694, :1743). On the success path kernel_rewrite_controller_result gains task_count: 0, and controller-returned keys now override the attempt fields (kernel.py:3687-3695, :3714 {**attempt, **result, ...}).
      Impact: The release notes would tell operators to widen log filters from phases.kernel to phases for a rename that never happens. They would also say session state is unchanged when geak_result and kernel_rewrite_controller_result now persist new keys.
      Action: Rewrite the Observable effect section. Delete the logger-name claim. Either state the two persisted-state changes, or (for geak_result) remove the change through the item 1 fix. Write the body against git diff origin/main...HEAD only; every name and number must be reproducible from that diff.
  4. [free:desync-names-absent-code] The title, the module and type list, the Tests section and the Review comments name code that is not in the diff [verified]
    Problem:

    • Title: "name ... as their own units", and the section "What each new module owns" describes _kernel_timeline.py, _geak_handoff.py, _aiter_config_merge.py. None of the three exists at head; all changes stay in kernel.py.
    • Body types SettledRun, RunnerRun, _GemmE2ERun, _GemmE2ELedger, _AcceptedLaunch, _RooflineAnchor: none of them appears in kernel.py at head.
    • Tests section: "Six existing test files". It names edits to test_geak_handoff_launch_server_script.py, test_geak_observed_replay_shape.py, test_geak_handoff_workload_contract.py, test_sbd_v6_kernel_timeline.py, test_gemm_e2e_validation_offloads_io.py, but none of these files is in the diff. The diff touches 3 test files: test_geak_config.py (calls _accepted_config_as_variant in place of the deleted KernelPhase._parse_geak_accepted_config), test_geak_revalidation_dispatch.py (adds test_an_unreadable_term_grace_is_refused_before_the_runner_is_dispatched), and test_controller_patch_integration.py (one line).
    • Review comments: "delete read_recipe_bench_envs ... Not applied". Head deleted _read_recipe_bench_envs (base kernel.py:921), and _recipe_benchmark (kernel.py:1075) is the only parse. The "five except Exception in _geak_handoff.py" reply refers to a module that does not exist.
      Impact: The release notes would describe a module split and new types that operators and plugin authors will not find. They would also say a requested deletion was declined when it actually landed.
      Action: Retitle the PR to describe an in-place decomposition of phases/kernel.py. Drop the new-module and new-type sections. List the 3 test files actually changed, and update the Review comments replies to match head. Write the body against git diff origin/main...HEAD only; every name and number must be reproducible from that diff.
  5. [free:desync-numbers] The size and baseline numbers in the body do not match the diff [verified]
    Problem:

    • "11 files, 2999 insertions, 2178 deletions": git diff --numstat 110ff0d65 83428faa9 gives 5 files, +1479/-1648.
    • "phases/kernel.py | 4249 → 3820" and "merge base ef93838a6": the actual change is 4273 → 4104 lines against merge base 110ff0d.
    • Baseline "at head it carries 2": it carries 1 (module-lines ... kernel.py 4102). "_fmoe_apply_verdict 131, unchanged": its entry is removed and the method is now about 91 lines. "module-lines ... 3820": the actual value is 4102.
    • Why size-exception: "this is ~5000": kernel.py actually changes by +1453/-1622.
      Impact: The release notes and the size-exception justification would cite line counts, a merge base and baseline entries that nobody can reproduce from the merged change.
      Action: Recompute every figure in "The final tree", "Effect on the metrics baseline" and "Why size-exception" from the current diff, or delete it. Write the body against git diff origin/main...HEAD only; every name and number must be reproducible from that diff.

Checked: src/hyperloom/orchestrator/phases/kernel.py (_dispatch_geak_runner, _settle_geak_runner_exit, _run_geak_kernel_phase, _promote_crashed_geak_result, _promote_recovered_geak_result, _record_geak_delegation_timeline, _record_geak_candidate, _adopt_reprofile_snapshot, _gemm_apply_blockers, _validate_gemm_tuning_e2e, _controller_attempt_result), src/hyperloom/orchestrator/phases/geak_rebench.py (geak_candidate_is_adjudicated, geak_candidate_matches), scripts/code_metrics_baseline.txt, the 4 failing test files, live PR title/body | Ran: gh pr view 1960 --json headRefOid,title,body; git diff --numstat/--stat 110ff0d 83428fa; git ls-tree at head; two-entry AgentX differential test at base and head; pytest on the 4 failing test files at base, head and head merged with origin/main b772e94; gh CI run 39576811809; trial merge with origin/main (clean); scripts/code_metrics --base-ref origin/main (PASSED) | Base: 110ff0d | Head: 83428fa

Test and others added 4 commits October 21, 2026 20:14
The dispatch's own facts -- runner_timeout_s, kill_timeout_s, started_at,
duration_sec -- were merged into the persisted geak_result. result.json
carries none of them, so under AgentX, where recovery must recognize the
result on disk, geak_candidate_matches never matched and the next KERNEL
entry promoted and re-measured a candidate this session had already
settled. They now travel beside the product as GeakRunnerFacts, to the
timeline recorder and the phase-entry evidence that are their only readers.

Two more values the rework left on the wrong side of a moved boundary:
_adopt_reprofile_snapshot lost the snapshot-count term from the three-way
"a snapshot landed" test, so a reprofile that reproduced the same tput
under the same snapshot id never adopted its workload; and
_record_geak_candidate lost the guard that makes a non-dict return record
nothing.

The GEMM e2e offload test reads the contract across the validator and the
apply-blocker helper it awaits, which is where the coverage replay moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nel-phase

# Conflicts:
#	scripts/code_metrics_baseline.txt
#	src/hyperloom/orchestrator/phases/kernel.py
The same shape as the GEAK timing merge, found by auditing the rewrite
against main function by function:

- promote_geak_from_candidate read the pre-GEAK anchor out of
  current_best after lift_to_current_best had already overwritten it with
  the measured tput, so every overlay_not_proven_loaded journey rejection
  recorded a zero delta against itself. The anchor is now read before the
  lift and handed to _settle_geak_lift.
- It also lost the guard that makes a non-dict result return False.
- _controller_attempt_result re-pinned only two of the four facts the
  phase owns; budget_minutes and hard_timeout_sec sat left of **result,
  where the Controller's own state.json could overwrite them.
- _record_gemm_tuning_timeline collapsed its two branches into one loop
  and five fields silently took the other branch's source. The campaign
  without per-tuner rows now enters the loop as a row that names those
  facts the way the campaign envelope does, so each keeps its own source;
  shapes_tuned drops the fallback a tuner row never had.

The runner's timeouts no longer ride the persisted no_result_json skip
record either, matching what the GeakRunnerFacts docstring promises.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Splitting the pre-GEAK anchor out took _settle_geak_lift to six
parameters. The writeback validation and the pending clear move back to
the caller that already holds the graded measurement, which leaves the
helper at five and names what it does: attribute the lift to the kernels
that can carry it.

The campaign row the GEMM timeline falls back to reads its fields
straight off the result rather than through _first_text, so the key names
stay out of value position.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chennyiiis chennyiiis changed the title refactor(kernel): name the KERNEL phase GEAK handoff, timeline rows and aiter table merge as their own units refactor(kernel): decompose GEAK settlement and GEMM validation in the KERNEL phase Oct 21, 2026

@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 #1960 -- refactor(kernel): decompose GEAK settlement and GEMM validation in the KERNEL phase

What it does: Breaks the long KernelPhase methods in src/hyperloom/orchestrator/phases/kernel.py into named steps, without adding a module. The affected methods are GEAK handoff/dispatch/settlement, GEMM e2e validation and the aiter CSV merge, GEAK promotion, the rewrite-controller attempt and the kernel-entry reprofile. Runner timing now travels in GeakRunnerFacts beside the GEAK result, so it is never persisted into it. The recipe benchmark block is parsed once, and 21 code-metrics baseline entries are retired. Base and head were compared unit by unit, and every moved unit behaves the same except for the effects the PR states and the one below.

Blocking issues: 1

  1. [S1] The GEAK handoff now catches every OSError/TypeError/ValueError raised while it is built, not only env-spec serialization failures, and reports them as invalid_env_spec. The PR does not state this. [verified]
    Problem: At the merge base, except (OSError, TypeError, ValueError) wrapped only self._coord.writeback.build_env_spec() (base kernel.py:1267-1276). _open_geak_handoff now wraps all of _build_geak_handoff in the same handler (src/hyperloom/orchestrator/phases/kernel.py:1834-1843). That includes seed_native_geak_proxy (kernel.py:1592), which raises ValueError("Native AgentX GEAK proposals require a Magpie-verified server launch") (inference_optimizer/agentx/geak_proxy.py:77, also :80), plus the AgentX env-spec rebuild, the GPU-pin, serving-fidelity and workload-spec resolvers. Trigger: an AgentX Magpie session whose current_best measurement has no launch_evidence.magpie_server_launch. A probe that makes seed_native_geak_proxy raise its own ValueError gives different results on the two trees. On base, _run_geak_kernel_phase raises. On head, it returns with state.geak_result = {"status": "error", "error_class": "invalid_env_spec", ...}. The body lists the observable effects and says the rest is preserved, but it does not mention this.
    Impact: On base the kernel_agent task ended state=failed, error_class=ValueError (loop/sub_agent_runner.py:359-368). On head run_agent returns {"status": "ok", "route": "geak"} (kernel.py:1026). It logs "geak: cannot serialize the accepted launch configuration", closes the timeline as invalid_env_spec and overwrites any non-terminal state.geak_result. A missing Magpie launch proof now reads as an env-spec serialization fault on a phase that reports success.
    Action: Narrow the handler back to build_env_spec(), for example by wrapping only _accepted_launch_spec or its first call, so other handoff errors still fail the task as they did on base. If catching them is intended, state the new behaviour in the Observable effect section, give the seed failure its own error_class, and add a test that drives _run_geak_kernel_phase with a raising seed_native_geak_proxy.

Checked: phases/kernel.py base vs head for _run_geak_kernel_phase (+_open_geak_handoff, _build_geak_handoff, _accepted_launch_spec, _same_config_reference, _geak_device_fields, _geak_serving_fidelity, _geak_workload_spec, _geak_session_fields, _promote_crashed_geak_result, _dispatch_geak_runner, _run_geak_runner, _settle_geak_runner_exit, _promote_recovered_geak_result, _finish_geak_skip, _settle_geak_skip, _refuse_geak_for_budget, _refuse_phantom_baseline_gain, _publish_geak_e2e_done), _record_geak_candidate (+_geak_pending_record, _record_geak_claim_and_product), promote_geak_from_candidate (+_geak_promotion_measurement, _compose_geak_launch_recipe, _record_geak_lift_attribution), _validate_gemm_tuning_e2e (+_gemm_candidate_skip_reason, _merged_candidate_envs, _integrate_gemm_candidate, _gemm_apply_blockers, _keep_gemm_candidate, _settle_gemm_e2e_verdict, _close_gemm_e2e_without_candidates), _gemm_e2e_candidates (+_legacy_tuner_candidates, _a8w8_blockscale_candidate), _merge_gemm_candidate_with_runtime (+_aiter_config_dirs, _aiter_source_tables, dispatch-row helpers), _fmoe_apply_verdict, _record_gemm_tuning_timeline (+_record_gemm_tuner_rows), _maybe_reprofile_for_kernel (+_adopt_reprofile_snapshot), _run_kernel_rewrite_controller (+_controller_attempt_result, _integrate_controller_output, _file_controller_integration_failure); agentx/geak_proxy.py seed_native_geak_proxy; geak_rebench.py; geak_native_revalidation.matching_review; common/jsonio.read_json; loop/sub_agent_runner.py | Ran: AST diff of defs and log calls base vs head; seed-raises probe via pytest on extracted base and head trees; pytest test_gemm_e2e_validation_offloads_io.py test_geak_config.py at head (30 passed) | Base: bb2da63 | Head: 3e60859

Test and others added 6 commits October 22, 2026 13:44
…nel-phase

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>
…nel-phase

Brings the branch up to origin/main; no path conflicted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nel-phase

main's #2051 replaced the inline Popen/killpg launch in the GEAK dispatch
with run_with_session_kill plus REAP_MARK_ENV, and removed GEAK_TERM_GRACE_S
with it. Resolved toward main: this branch's _run_geak_runner is gone and
_dispatch_geak_runner now awaits main's _launch_geak_runner, with the branch's
decomposition (_accepted_launch_spec, _dispatch_geak_runner, _facts,
_settle_geak_runner_exit, _refuse_geak_for_budget) re-applied on top. The test
that pinned GEAK_TERM_GRACE_S being resolved before the dispatch goes with the
mechanism main deleted; the rest of the dispatch's tests now assert main's
REAP_MARK_ENV contract.

scripts/code_metrics_baseline.txt 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>
…nel-phase

Brings the branch up to origin/main; no path conflicted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…on failure

_open_geak_handoff wrapped the whole _build_geak_handoff call in
except (OSError, TypeError, ValueError), so every fault raised anywhere in the
handoff build was reported as invalid_env_spec. A native-AgentX proposal
refusing an unverified Magpie server launch -- a deliberate ValueError from
seed_native_geak_proxy -- was swallowed and misreported as "cannot serialize
the accepted launch configuration".

The handler is back to the scope it had at the merge base: build_env_spec()
raises EnvSpecUnserializable, and that is the only thing _open_geak_handoff
classifies. Every other fault propagates to the caller as it did before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@chennyiiis

Copy link
Copy Markdown
Contributor Author

[S1] The GEAK handoff now catches every OSError/TypeError/ValueError raised while it is built, not only env-spec serialization failures, and reports them as invalid_env_spec.

Confirmed and fixed in 3395ce7 by narrowing the handler, not by stating the widened behaviour.

What I narrowed. self._coord.writeback.build_env_spec() -- the first call, in _accepted_launch_spec, the one the merge base wrapped -- now raises EnvSpecUnserializable on (OSError, TypeError, ValueError), and _open_geak_handoff catches only that. The invalid_env_spec classification, its log line, its finish_failed(stage="geak_handoff") timeline close and its _settle_geak_skip are reached by exactly one fault again: the accepted launch configuration could not be serialized.

What now propagates. Everything else _build_geak_handoff can raise reaches _run_geak_kernel_phase's caller and fails the kernel_agent task as it did on base. I walked the call tree rather than fixing only the one you named; what can escape is:

Source Exception
agentx/geak_proxy.py:77,:80 the two native-AgentX refusals -- ValueError("Native AgentX GEAK proposals require a Magpie-verified server launch") and "...server launch argv is missing"
agentx/geak_proxy.py:52 (_tunable_flags) ValueError("Native AgentX launch evidence has an unsupported server entry point") -- a non--m argv shape, or a wrapper in front
_gpu_pin.py:498,:508 via _geak_serving_fidelity EnvValueError (a ValueError subclass) from env_int("MAX_MODEL_LEN") / env_float("GPU_MEMORY_UTILIZATION") on a malformed value
writeback.py:3178 and kernel.py:302 TypeError from json.dumps(..., sort_keys=True) over a non-serializable extra_envs / source_snapshots / observed_server_identity
_geak_session_fields, _geak_serving_fidelity, _same_config_reference TypeError/ValueError from the unguarded casts on SharedState -- int(state.isl/osl/conc/macro_cycle), float(state.baseline_tput), int(max_model_len or 0), float(measurement["tput"])
kernel.py handoff body TypeError from sorted(server_identity.items()) and from dict(extra_envs) over a non-mapping
the AgentX env-spec rebuild -- build_env_spec(measurement=..., server_launch_flags=...) propagates, as it did at base: the base handler closed before that second call

No OSError can escape: every filesystem read in the tree is already guarded at its own call site, so the OSError arm only ever covered build_env_spec.

I did not widen a different handler to compensate and added no except Exception.

Test. test_native_geak_proxy_refusal_is_not_an_invalid_env_spec drives _run_geak_kernel_phase with a seed_native_geak_proxy that raises the refusal verbatim, and pins that it reaches the caller and that geak_result is not classified invalid_env_spec. Non-vacuity: with the production change reverted the test fails DID NOT RAISE ValueError, and the captured log is the misclassification you described -- ERROR hyperloom.orchestrator.phases.kernel: geak: cannot serialize the accepted launch configuration, with the refusal in the swallowed traceback. The existing test_invalid_handoff_configuration_never_launches_geak still passes on all four parametrizations, so the real invalid_env_spec path is unchanged.

Gates. Narrowing pushed nothing over a threshold: _accepted_launch_spec, _open_geak_handoff and _build_geak_handoff are all still under the cyclomatic ceiling of 20, and kernel.py is 4202 physical lines against the merge base's 4227. Net production lines are still negative. The PR description's Observable effect section now says the handler covers env-spec serialization only.

…nel-phase

# Conflicts:
#	scripts/code_metrics_baseline.txt
@chennyiiis

Copy link
Copy Markdown
Contributor Author

Addendum after re-merging origin/main (now b392e8d6f, which brought #1997, #1965, #2066).

The merge conflicted on scripts/code_metrics_baseline.txt only -- no production file conflicted -- and the ratchet resolved it entry by entry: 29 retired (18 by main, 11 by this branch), module-lines for phases/kernel.py kept at the lower 4102 and for phases/framework.py at main's 3059. Nothing came out stale, so no entry needed lowering.

Final numbers on 30d62230d:

  • Net production lines vs the merge base: -42 (src/*.py, tests excluded).
  • phases/kernel.py is 4202 physical lines against the merge base's 4244.
  • Nothing I changed crossed a gate threshold: _accepted_launch_spec, _open_geak_handoff and _build_geak_handoff are all still well under the cyclomatic ceiling of 20. The seven functions in this file at or over 20 are the same seven as before this fix, all pre-existing backlog.
  • subprocess.Popen in phases/kernel.py remains at 0: fix: reap every process a GEAK run starts when the KERNEL phase's launch returns #2051's _launch_geak_runner is untouched.
  • CI: 33 pass / 0 fail, 7 skipped (GPU e2e and the deploy/resolve jobs). Local Linux 3.10: 1837 passed across the GEAK, kernel and handoff suites.

@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 #1960 -- refactor(kernel): decompose GEAK settlement and GEMM validation in the KERNEL phase

What it does: Breaks the long KernelPhase methods in src/hyperloom/orchestrator/phases/kernel.py into named steps, without adding a module. The affected methods are GEAK handoff/dispatch/settlement, GEMM e2e validation and the aiter CSV merge, GEAK promotion, the rewrite-controller attempt and the kernel-entry reprofile. Runner timing now travels in GeakRunnerFacts beside the GEAK result, so it is never persisted into it. The recipe benchmark block is parsed once, and 21 code-metrics baseline entries are retired. Base and head were compared unit by unit, and every moved unit behaves the same except for the effects the PR states and the one below.

Blocking issues: 1

  1. [X2] The description says six test files change; the diff changes five [verified]
    Problem: The "Tests and scope" section says "Six test files change" and then names five (test_geak_config.py, test_geak_late_settlement.py, test_geak_revalidation_dispatch.py, test_controller_patch_integration.py, test_gemm_e2e_validation_offloads_io.py). git diff --stat origin/main...30d62230d shows exactly those five test files, plus kernel.py and scripts/code_metrics_baseline.txt.
    Impact: The release cut copies this description, so it would publish a count that matches neither its own list nor the diff.
    Action: Change "Six" to "Five".

Checked: phases/kernel.py base vs head for _run_geak_kernel_phase (+_open_geak_handoff, _build_geak_handoff, _accepted_launch_spec, _same_config_reference, _geak_device_fields, _geak_serving_fidelity, _geak_workload_spec, _geak_session_fields, _promote_crashed_geak_result, _dispatch_geak_runner, _run_geak_runner, _settle_geak_runner_exit, _promote_recovered_geak_result, _finish_geak_skip, _settle_geak_skip, _refuse_geak_for_budget, _refuse_phantom_baseline_gain, _publish_geak_e2e_done), _record_geak_candidate (+_geak_pending_record, _record_geak_claim_and_product), promote_geak_from_candidate (+_geak_promotion_measurement, _compose_geak_launch_recipe, _record_geak_lift_attribution), _validate_gemm_tuning_e2e (+_gemm_candidate_skip_reason, _merged_candidate_envs, _integrate_gemm_candidate, _gemm_apply_blockers, _keep_gemm_candidate, _settle_gemm_e2e_verdict, _close_gemm_e2e_without_candidates), _gemm_e2e_candidates (+_legacy_tuner_candidates, _a8w8_blockscale_candidate), _merge_gemm_candidate_with_runtime (+_aiter_config_dirs, _aiter_source_tables, dispatch-row helpers), _fmoe_apply_verdict, _record_gemm_tuning_timeline (+_record_gemm_tuner_rows), _maybe_reprofile_for_kernel (+_adopt_reprofile_snapshot), _run_kernel_rewrite_controller (+_controller_attempt_result, _integrate_controller_output, _file_controller_integration_failure); agentx/geak_proxy.py seed_native_geak_proxy; geak_rebench.py; geak_native_revalidation.matching_review; common/jsonio.read_json; loop/sub_agent_runner.py | Ran: AST diff of defs and log calls base vs head; seed-raises probe via pytest on extracted base and head trees; pytest test_gemm_e2e_validation_offloads_io.py test_geak_config.py at head (30 passed) | Re-review at 30d6223: the earlier [S1] is fixed by 3395ce7 -- EnvSpecUnserializable (kernel.py:441) is raised only around the first build_env_spec() in _accepted_launch_spec (kernel.py:1424-1427) and _open_geak_handoff catches only it (kernel.py:1845), the same scope as base kernel.py:1267-1277; seed_native_geak_proxy errors propagate again, pinned by test_native_geak_proxy_refusal_is_not_an_invalid_env_spec; merge 30d6223 resolved only the baseline file; CI green | Base: bb2da63 | Head: 30d6223

Test added 2 commits October 22, 2026 17:09
…nel-phase

# Conflicts:
#	scripts/code_metrics_baseline.txt
…nel-phase

# Conflicts:
#	scripts/code_metrics_baseline.txt

@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 #1960 -- refactor(kernel): decompose GEAK settlement and GEMM validation in the KERNEL phase

What it does: Breaks the long KernelPhase methods in src/hyperloom/orchestrator/phases/kernel.py into named steps, without adding a module. The affected methods are GEAK handoff/dispatch/settlement, GEMM e2e validation and the aiter CSV merge, GEAK promotion, the rewrite-controller attempt and the kernel-entry reprofile. Runner timing now travels in GeakRunnerFacts beside the GEAK result, so it is never persisted into it. The recipe benchmark block is parsed once, and 21 code-metrics baseline entries are retired. Base and head were compared unit by unit, and every moved unit behaves the same except for the effects the PR states and the one below.

Blocking issues: none

Checked: phases/kernel.py base vs head for _run_geak_kernel_phase (+_open_geak_handoff, _build_geak_handoff, _accepted_launch_spec, _same_config_reference, _geak_device_fields, _geak_serving_fidelity, _geak_workload_spec, _geak_session_fields, _promote_crashed_geak_result, _dispatch_geak_runner, _run_geak_runner, _settle_geak_runner_exit, _promote_recovered_geak_result, _finish_geak_skip, _settle_geak_skip, _refuse_geak_for_budget, _refuse_phantom_baseline_gain, _publish_geak_e2e_done), _record_geak_candidate (+_geak_pending_record, _record_geak_claim_and_product), promote_geak_from_candidate (+_geak_promotion_measurement, _compose_geak_launch_recipe, _record_geak_lift_attribution), _validate_gemm_tuning_e2e (+_gemm_candidate_skip_reason, _merged_candidate_envs, _integrate_gemm_candidate, _gemm_apply_blockers, _keep_gemm_candidate, _settle_gemm_e2e_verdict, _close_gemm_e2e_without_candidates), _gemm_e2e_candidates (+_legacy_tuner_candidates, _a8w8_blockscale_candidate), _merge_gemm_candidate_with_runtime (+_aiter_config_dirs, _aiter_source_tables, dispatch-row helpers), _fmoe_apply_verdict, _record_gemm_tuning_timeline (+_record_gemm_tuner_rows), _maybe_reprofile_for_kernel (+_adopt_reprofile_snapshot), _run_kernel_rewrite_controller (+_controller_attempt_result, _integrate_controller_output, _file_controller_integration_failure); agentx/geak_proxy.py seed_native_geak_proxy; geak_rebench.py; geak_native_revalidation.matching_review; common/jsonio.read_json; loop/sub_agent_runner.py | Ran: AST diff of defs and log calls base vs head; seed-raises probe via pytest on extracted base and head trees; pytest test_gemm_e2e_validation_offloads_io.py test_geak_config.py at head (30 passed) | Re-review at 30d6223: the earlier [S1] is fixed by 3395ce7 -- EnvSpecUnserializable (kernel.py:441) is raised only around the first build_env_spec() in _accepted_launch_spec (kernel.py:1424-1427) and _open_geak_handoff catches only it (kernel.py:1845), the same scope as base kernel.py:1267-1277; seed_native_geak_proxy errors propagate again, pinned by test_native_geak_proxy_refusal_is_not_an_invalid_env_spec; merge 30d6223 resolved only the baseline file; CI green | Re-review at 835b67b: the earlier [X2] is fixed -- the body now says "Five test files change", matching the five test files in git diff --stat origin/main...835b67b; its net-lines claim holds (kernel.py +1506/-1548); the non-baseline diff has the same patch-id as at 30d6223; merge with origin/main is clean; CI green | Head d739fe7 only merges main again: same non-baseline patch-id and body; clean against origin/main | Base: bb2da63 | Head: d739fe7

LGTM

@chaojhou
chaojhou merged commit e34bb7a into main Oct 22, 2026
40 checks passed
@chaojhou
chaojhou deleted the feature/chenyi/cc-kernel-phase branch October 22, 2026 09:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size-exception Waives the PR diff budget; added by a maintainer with a reason in the PR description

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants