Repository navigation
refactor(inference-optimizer): delete what the CLI session start, the multi-node restart and the KB field declaration re-checked or did twice - #2085
Conversation
…ready makes
_short_poll returns a job only when its status is SUCCEEDED; FAILED and STOPPED raise
RuntimeError("<label> terminal failure: ...") and a timeout raises TransientFailure. Five
of its seven callers nevertheless re-tested the returned status against
_TERMINAL_FAIL_STATUSES (and _submit_and_collect_pod_json also against
_TERMINAL_OK_STATUSES), so those branches could never run. Deleted from
_restart_multinode (the fail-fast block and its message), _start_pd_router (the "router
job ended" exit), _restart_single_node, cmd_verify and _submit_and_collect_pod_json;
--print-logs still prints the logs.
Safe: no production code path and no test returns a non-SUCCEEDED job from _short_poll
(the only test that replaces it, test_external_multi_node.py:941, returns SUCCEEDED).
Co-authored-by: Cursor <cursoragent@cursor.com>
_restart_multinode wrote the launch identity twice: the early checkpoint before the poll, then _persist_multinode_restart rewrote the same pid/log dirs, submission id, framework, model, tp, ep, pd_mode and topology from a _MultinodeLaunch bag built for that one call. Deleted the bag and the rewrites; _persist_multinode_restart now writes only what the checkpoint leaves until the poll returns (the unconditional last_kill_submission_id and, under PD, the per-field pd_* keys, router submission and leg URLs) and saves. The checkpoint takes tp, ep, pd_mode and extra_args from the topology record, which normalizes them with the same expressions, instead of recomputing them; pd_mode is no longer recomputed after the poll or inside _resume_prior_launch, whose derivable prev_sub parameter it now reads from state itself. Dropped a comment that narrated the next line. Safe: a fake-driven run of every path (aggregated and PD, fresh, resumed RUNNING, resumed serving, router without leg URLs, poll timeout, --print-logs) saves the same state snapshots, logs the same lines and returns the same codes on base and branch. The one difference is out of contract: a non-int args.tp used to end in state.json as the raw value and now stays int(args.tp) from the checkpoint; restart-server parses --tp with type=int (multi_node/cli.py:1536) and the in-process caller passes tp=tp_int (_multi_node_server_lifecycle.py:446). Co-authored-by: Cursor <cursoragent@cursor.com>
…ning its next leg
_resume_session was 170 lines. Deleted first:
- the __import__("...session.paths", fromlist=[...]) hop to reach _SESSION_SKELETON,
now a plain import next to the other session.paths names it already imported;
- the hand-written relative_to/ValueError containment check, now session.paths'
is_path_within, which owns that rule;
- the one-element gated_terminal set and the force_resume re-test in the override
note (a gated stop without --force-resume has already exited by then);
- (state.current_best or {}) twice: SharedState.from_dict always yields a dict;
- four hand-written "state value, else manifest value" copies for the model gate,
now one comprehension over the same keys in the same order;
- a comment repeating the docstring, one narrating the mkdir loop and a "no
backfill needed" tombstone.
What is left splits along its two jobs: _open_resumed_session validates, pins, locks
and guards the --resume-from directory and loads its manifest; _open_next_leg refuses
a target_reached stop without --force-resume, then begins the leg, grants
--extend-hours and prints the budget. Multi-line messages are joined at their line
breaks; every message is byte-identical.
Safe: a fake-driven run of 13 paths (plain, target_reached with and without
--force-resume, other stop, --extend-hours, outside the workspace, missing dir, resume
blocked, no manifest, no state.json, stale AgentX, resume conflict, null current_best)
prints the same stdout and stderr, exits with the same codes, makes the same calls in
the same order and leaves the same state.json on base and branch. One difference is
out of contract: a workspace root whose resolve() raises OSError or RuntimeError (a
symlink loop) now gets the "is not under $USER_DATA_PATH" error and exit 2 instead of
a traceback.
Co-authored-by: Cursor <cursoragent@cursor.com>
…U and creating the session _seed_fresh_session was 164 lines. The GPU resolution (probe over the --gpu-type hint, the Magpie runner export, the custom-entrypoint refusal and the gfx-arch check) moves into _resolve_target_gpu, and the session creation (directory, lock, benchmark binding, manifest, install event, Langfuse marker, launch info, partition shape, GPU power, seeded state) into _create_fresh_session. Both run where the code ran before, so the side-effect order is unchanged. Deleted guards nothing reaches: - the second args.gpu_type = None in the no-runner branch, and the "runner_gpu_type or gpu_type" fallback: the resolved type is a known board tag or "" (_resolve_amd_gpu_type, _autodetect_gpu_type and remote_autodetect_gpu_type return a tag or None), and gpu_runner_type maps every tag to a non-empty runner, so that branch only runs for "" and args.gpu_type is already None there. LaunchInfo now reads args.gpu_type, which holds the same value. - the empty-model fallbacks for LaunchInfo and PartitionModel: the function exits when args.model is empty, and the quantization prelude only replaces it with the exported checkpoint path. The three preflight exits become one condition; "or" keeps the short circuit, so a failing check still stops the later ones. Their comments restated the callees' docstrings; the ordering constraint stays as one comment. The "B3:" note above the atom check is dropped. Out of contract: a GPU type that is non-empty but blank used to leave args.gpu_type None and reach the custom-entrypoint check as the blank string; it now stays in args.gpu_type and the check gets "". No resolver produces one. Co-authored-by: Cursor <cursoragent@cursor.com>
…ne to 1804 The two multi-node commits removed 66 lines from multi_node/cli.py, so its module-lines entry is out of date. Written by scripts/code_metrics.py --update-baseline; no other entry changed. Co-authored-by: Cursor <cursoragent@cursor.com>
…ts from from_dict FieldDeclaration.__post_init__ re-checked three things its only external producer had already settled: that kind is a FieldKind, that required, sensitive, many and group are booleans, and that role is a FieldRole. Every declaration that comes from outside Python code (declaration YAML or JSON through load_declaration, KB database rows, HTTP request bodies, remote KB responses and spool files) reaches the dataclass only through ExperienceDeclaration.from_dict -> FieldDeclaration.from_dict, which converts kind with FieldKind(...) and role with FieldRole(...) and runs _boolean on each flag before it constructs, raising the same SchemaValidationError messages as before. The only other constructors are RATIONALE_DEFAULTS and the tests, which pass enum members and bool literals. The package ships py.typed, so the annotations state the contract for any other Python caller. Every check on a value from_dict passes through raw stays: the name, description and declared values, the search weight (still normalized to a float, which keeps the schema_ref of a declaration with an integer weight stable across a round trip), and every combination rule. Cognitive complexity 32 -> 28, cyclomatic 16 -> 13, 36 -> 30 lines. Co-authored-by: Cursor <cursoragent@cursor.com>
Code metrics gate: PASSEDJudged: the 4 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.
Module length warning: over 800 lines, within the 1200-line failure limit (not failing): 1
Already over at the base (backlog, not this change's): 5
98 findings in files this change does not touchOutside this change (informational): 98
...and 73 more; the job log lists every row.
Thresholds and sources: |
ZhengGong-amd
left a comment
There was a problem hiding this comment.
Reviewed: gate numbers re-measured (ruff C901, complexipy, function lines) and match the description; each removed guard / "producer always sets it" claim checked against its producers and call sites; base vs head differential runs show no undisclosed behaviour change; tests covering the touched modules pass.
LGTM.
_default_grid_for_framework() seeds a curated grid for atom and xdit and returns [] for everything else. For an LLM framework that is fine: the model reads the framework's source and emits default_grid variants. A `custom` workload has no framework source to read, so the model cannot infer its knobs and EXPLORE has nothing to sweep -- the workload runs and benchmarks, but is not autonomously tunable. optimize-custom-workload.md already promises "leave the rest for exploration" with nothing behind it. Let the adapter declare its knob space in <benchmark-scripts-dir>/levers.json. The scripts dir is the adapter's contract directory and is already persisted and re-exported on --resume, so the declaration travels with the session. Reshaped per review on AMD-AGI#1546: - Gate on `framework == "custom"`, not framework_is_scriptable() -- the latter is {xdit, custom}, so xdit would not have been untouched. - One source only. The inline env vars (HYPERLOOM_DECLARED_LEVERS, HLHPC_LEVERS), the legacy hlhpc_levers.json filename, and the {"levers"} / {"variants"} wrapper object forms are all gone. - Validate at the launch boundary (next to _require_custom_entrypoint) and exit 2 on a malformed declaration. The executor no longer warns and continues: every silent path surfaced later as empty_grid, so an operator's typo read as "the model proposed nothing". - Seed only declarations absent from the ledger's name_index. A re-proposal is still benchmarked, so re-prepending the full set each round re-spent the round's wall-clock budget ahead of the model's proposals. - Validity is is_executable(normalize_proposal(v)), not "has a name" -- a name-only variant is benchmarked as a duplicate of the baseline. - Folded into the existing framework-lever merge rather than a second copy of the prepend/dedupe block. - provenance defaults to default_grid, so the session breakdown attributes these to the seed grid rather than to the orchestration agent. The redundant note setdefault is gone. Tests cover behaviour rather than the parser: declarations sweep when the model proposes nothing; vllm/xdit/sglang/atom are unaffected; an already-benchmarked declaration is not re-seeded; a model proposal wins a name collision; and each malformed shape raises rather than degrading to an empty grid. Closes AMD-AGI#1544 Re-applied onto current main (2026-10-27) after AMD-AGI#2085 reworked CLI session start and _resolve_grid moved into _ExploreRound. The merge point is now the existing _lever_seeded_payload() call, so the declared levers are appended to lever_payload rather than inlined into a second prepend/dedupe block. The per-round ledger filter moved with them into declared_lever_payload(). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
What this does
Four functions were over the code-metrics gate at base, and none had a baseline entry, so the gate excused them as backlog:
_resume_session(170 lines) and_seed_fresh_session(164) incli/__init__.py,_restart_multinode(107) inmulti_node/cli.py, andFieldDeclaration.__post_init__(cognitive 32) inhyperloom_kb/schema.py. This PR brings each one under every limit, mostly by deleting what it did twice: status checks on a_short_pollresult that is alwaysSUCCEEDED, a second write of every restart key from a bag built for that one call, guards on values the function had already settled, and three type checks thatFieldDeclaration.from_dictalready makes. What is left of the two session functions splits along the jobs they ran in sequence.Production code: +180 / −278 (net −98) across 3 files. Tests: none changed. The code-metrics baseline lowers one entry,
module-linesofmulti_node/cli.py(1,867 → 1,804), and gains none.This continues the complexity cleanup of #2024, #2026, #2052, #2056 and #2057. The 6 commits are grouped by module and read one at a time; each commit message lists what it deleted and why that is safe.
multi_node/cli.pymulti_node/cli.pycli/__init__.pycli/__init__.pyhyperloom_kb/schema.pycli/__init__.pyandmulti_node/cli.pyare undersrc/hyperloom/inference_optimizer/;schema.pyis undersrc/hyperloom_kb/.Gate numbers per function
Measured with the gate's own collectors (ruff C901, complexipy, function lines), origin/main → this branch. Limits are 20, 30 and 80; bold values were over the limit at base.
cli/__init__.py_resume_session_seed_fresh_sessionmulti_node/cli.py_restart_multinodeschema.pyFieldDeclaration.__post_init__Also changed, never over at base (cyclomatic / cognitive / lines), all in
multi_node/cli.py:_persist_multinode_restart2 / 8 → 7 / 25 → 17;_resume_prior_launch6 / 7 / 44 → 41;_start_pd_router5 → 4 / 5 → 3 / 39 → 35;_submit_and_collect_pod_json5 → 3 / 4 → 2 / 32 → 27;_restart_single_node4 / 5 → 4 / 41;cmd_verify3 / 2 → 1 / 36.New functions, cyclomatic / cognitive / lines, all in
cli/__init__.py:_open_resumed_session7 / 6 / 50,_open_next_leg6 / 7 / 44,_resolve_target_gpu6 / 13 / 34,_create_fresh_session2 / 1 / 55. Each takes one to three parameters.Deleted: the
_MultinodeLaunchdataclass inmulti_node/cli.py.Across the 3 touched production files: C901 total 743 → 737, cognitive total 1,025 → 1,008, functions 196 → 200. Functions over a gate limit: cognitive 2 → 1, length 9 → 6. What is left is outside this work: in
cli/__init__.py,_run_optimize(279 lines, cognitive 43),_reexport_session_environment(111),_export_partition_shape(101),_resolve_run_topology(92) and_resolve_agent_lineup(89); inmulti_node/cli.py,build_parser(266). Open PRs change_run_optimize(#1450, #1546, #2062),_reexport_session_environment(#1979, #2055) and_resolve_run_topology(#1924), so this PR leaves them alone.Module lengths:
cli/__init__.py2,778 → 2,752,multi_node/cli.py1,870 → 1,804,schema.py1,014 → 1,008.What changes in each module
Multi-node restart (
multi_node/cli.py)Removed:
_short_pollcallers made on the job it returned: a fail-fast block in_restart_multinode, a "router job ended" exit in_start_pd_router, twoEXIT_TRANSIENTreturns in_submit_and_collect_pod_json, and a log print in_restart_single_nodeand incmd_verify._MultinodeLaunchdataclass and the second write of the restart keys it carried._persist_multinode_restart(state, args, *, kill_sub, router_sub, router_state)now writes only what the checkpoint leaves until the poll returns: the kill submission and, for a disaggregated launch, the PD fields, the router submission and the leg URLs. It takes five parameters instead of three; the bag's other four fields (the PID and log directories, the launch submission and the topology record) were keys the checkpoint had already written.tp,ep,pd_modeandextra_argsfromargsat each use. The checkpoint writes them from the topology record.prev_subparameter of_resume_prior_launch, which now reads the prior submission id from thestateit already takes.Session start (
cli/__init__.py)New names:
_open_resumed_session: check that--resume-fromnames a session under the workspace root, pin it, take its lock, run the resume guard, and load its manifest oncestate.jsonis known to exist._open_next_leg: refuse atarget_reachedstop without--force-resume, then begin the leg, grant--extend-hours, record the model-gate skip and print the restored budget._resolve_target_gpu: settleargs.gpu_typefrom the probe and the--gpu-typehint, export the Magpie runner, refuse a framework that runner cannot serve, and check that provenance can name the ISA._create_fresh_session: create and lock the session directory, then write its manifest, launch info, partition shape, GPU power settings and seeded state._resume_sessionnow runs the first two in order, and_seed_fresh_sessionthe last two.Removed:
__import__hop to reach_SESSION_SKELETON; the hand-written containment check, nowsession.paths.is_path_within, which owns that rule; a one-element stop-reason set and a--force-resumere-test that could not fail; two(state.current_best or {}); and four hand-written "state value, else manifest value" copies, now one comprehension over the same keys in the same order.args.gpu_type = Noneand arunner_gpu_type or gpu_typefallback for a board without a Magpie runner; the empty-model fallbacks in the launch info and the partition model; and three copies of "exit 2 if this preflight fails", now one condition whoseorkeeps the short circuit.except Exceptionmoved into_create_fresh_sessionunchanged; no new handler was added.KB field declaration (
hyperloom_kb/schema.py)FieldDeclaration.__post_init__no longer re-validates the three attributesFieldDeclaration.from_dictalready converts or rejects before it constructs:kindas aFieldKind,roleas aFieldRole, andrequired,sensitive,manyandgroupas booleans. Every check on a value thatfrom_dictpasses through raw stays: the name, the description, the declared values, every combination rule, and the search weight. The weight is still normalized to a float, so a declaration with an integer weight keeps oneschema_refacross a round trip.Kept at base:
sweep_casesweep_case(src/kernelforge/evaluation/benchmark/runner.py:581) is over the parameter limit with seven keyword parameters and stays at seven; itsfunction-argsbaseline entry is unchanged. Its callers:src/:runner.py:707-716, thepython3 -m kernelforge.evaluation.benchmark.runnerCLI (_sweep_cli). It passes all seven, takingwarmup_iters,bench_itersandtimeout_secfrom--warmup,--itersand--timeout(:694-696).kernelforge/orchestrator/specialist_probe.py:394-400and:450(probe_variant, then_measure_on_device). They passdriver_script,case_id,constants,prefix_constantsandtimeout_sec. The probe resolvessweep_caseby name (:41-43,:197-200), and it refuses a primitive that does not accept those five keywords (:45-51,:218-231).scripts/: none.kernelforge/tests/evaluation/benchmark/test_sweep_case.py:42-44, whose call sites passcase_id,constants,timeout_secandprefix_constants; and the probe stand-ins inkernelforge/tests/test_specialist_probe.py:401,:414and:740, which have the same five-keyword signature.No parameter can be derived from another, and
warmup_itersandbench_itersare the only two that the probe and the tests never pass. The only way to five parameters without bundling arguments therefore deletes the--warmupand--itersCLI flags, a CLI interface change this PR does not make. Passing the existingBenchRunin place ofdriver_script,warmup_itersandbench_iterswould only bundle arguments, and the probe's keyword check requiresdriver_scriptby name.Why the deletions are safe
Line numbers are on this branch.
Status re-checks after
_short_poll._short_poll(multi_node/cli.py:267-300) returns a job only when its status is in_TERMINAL_OK_STATUSES, which is{"SUCCEEDED"}(:60, returned at:290-291).FAILEDandSTOPPEDraiseRuntimeError(:292-293), and a timeout raisesTransientFailure(:295-299), so no caller can see another status. AFAILEDorSTOPPEDjob'sRuntimeErrorreaches the multi-node CLI's dispatcher handler,except (RuntimeError, ValueError)inmain(multi_node/cli.py:1786); in-process, the orchestrator catches it aroundcmd_restart_serverandcmd_apply_tracelens_patch(orchestrator/actions/executors/_multi_node_server_lifecycle.py:507,:400). So the job logs those deleted branches meant to print on a failure were never printed, before or after this PR. Printing them would be a behaviour change, which this refactor does not make.Restart keys. The early checkpoint (
multi_node/cli.py:1302-1318) writes the PID and log directories, the launch submission, the framework, the model,tp,ep,pd_mode,extra_argsand the topology record before the poll.tp,ep,pd_modeandextra_argscome from the record_rayjob_topology_fingerprintbuilds (:1120-1128), with the expressions the checkpoint used at base._persist_multinode_restart(:1258-1274) runs after the poll and the PD router, and writes only the keys the checkpoint cannot have yet._resume_prior_launch(:1174-1214) reads the prior submission id fromstate(:1187), the value the deleted parameter carried.Resume.
is_path_within(session/paths.py:220-226) is the containment rule the hand-written check copied.SharedState.from_dictalways yields a dict forcurrent_best(orchestrator/state/shared_state.py:1103-1104).Fresh session. Only an empty GPU type reaches the branch for a board without a Magpie runner, and
args.gpu_typeis alreadyNonethere. The function has exited on an empty model before it writes the launch info and the partition model, and the quantization prelude only replacesargs.modelwith aPath(cli/quantization.py:80). The preflight condition (cli/__init__.py:2197-2202) runs the three checks in the order the three copies did and stops at the first that fails.KB field declaration.
FieldDeclaration.from_dict(schema.py:468-495) converts or rejectskind(:476),role(:481) and the four flags (:488-491) before it constructs. Every declaration that comes from outside Python code reaches the dataclass only throughExperienceDeclaration.from_dict(schema.py:700) and thenFieldDeclaration.from_dict, which is the path for:hyperloom_kb/config.py:35, also behindcollect/mapping.py:162);http_service.py:449);http_service.py:1262);remote.py:506);remote.py:567).In the repo, the only direct constructors are
RATIONALE_DEFAULTS(schema.py:500-502) and tests, and they passFieldKindandFieldRolemembers and bool literals.Observable effect
Nothing an operator sees changes. For every input the repo's own callers produce, these are the same, in the same order:
state.json, the session manifest and the launch info;to_dictoutput and theirschema_ref. An invalid kind, role or flag in a declaration document still raises the sameSchemaValidationErrorfromfrom_dict, before anything is constructed.Fake-driven differential runs compared base and head with no difference:
_restart_multinode: 12 scenarios (aggregated and PD, fresh, resumed RUNNING, resumed serving, router without leg URLs, poll timeout,--print-logs). Saved state snapshots, log lines and return codes match._resume_session: 13 scenarios (plain,target_reachedwith and without--force-resume, other stop reasons,--extend-hours, outside the workspace, missing directory, resume blocked, no manifest, nostate.json, stale AgentX state, resume conflict, nullcurrent_best). Output, exit codes, call order andstate.jsonmatch._seed_fresh_session: 20 scenarios (missing model, model fromMODEL_PATH, local and remote probe, a probe that disagrees with the hint, no GPU, atom, framework fromFRAMEWORK, quantized model, AgentX,--profile-osl, no framework version, each preflight failing, Langfuse failing, a family-runner board, R9700 with vLLM). Output, exit codes, call order, exports andargsmatch.FieldDeclaration: 1,456 cases, every combination of kind, role, flags, values and search weight built in Python, plus valid and invalidfrom_dictdocuments. Results and error messages match.The differences below are reachable only by input that no caller in the repo produces:
tpthat is not an int.last_restart_tpin the multi-node state ended as the raw value, because the persist step overwrote the checkpoint'sint(args.tp).int(args.tp).--tpwithtype=int(multi_node/cli.py:1536). The in-process caller passestp=tp_int(orchestrator/actions/executors/_multi_node_server_lifecycle.py:446). Tests passtp=8._short_pollthat returns a job whose status is notSUCCEEDED. Only a test double can; the real one raises instead (multi_node/cli.py:288-299).FAILEDorSTOPPED,_restart_multinodeand_start_pd_routerprinted the job logs, logged anERRORline and failed with exit code 1, andcmd_verifyand_restart_single_nodeprinted the job logs;SUCCEEDED,_submit_and_collect_pod_jsonreturnedEXIT_TRANSIENT.SUCCEEDEDjob, and prints logs only with--print-logs._short_pollreturnsSUCCEEDED(inference_optimizer/tests/test_external_multi_node.py:941).--resume-fromunder a workspace root whoseresolve()raisesOSErrororRuntimeError.is_path_withinanswers no, so the operator gets the existing "is not under $USER_DATA_PATH" error and exit 2.$USER_DATA_PATHthroughworkspace_root()(session/paths.py:85). No caller or test in the repo sets one that cannot be resolved.args.gpu_typebecameNone, and the custom-entrypoint check received the blank string.args.gpu_type, and the check receives"".probed or user_specified(gpu_types.py:122). Each comes from a resolver that returns a known board tag orNone:_resolve_amd_gpu_type(gpu_types.py:153-162),_autodetect_gpu_type(gpu_types.py:125-150) andremote_autodetect_gpu_type(multi_node/_internal/gpu_probe.py:56-70, through_parse_gpu_typeat:41-53).FieldDeclarationdirectly with a value its annotations forbid (the package shipspy.typed):kindthat is not aFieldKind, for example the string"number". Before:SchemaValidationError("field 'x' has an invalid kind"). Now: a later combination rule may raise its ownSchemaValidationError, for example "only a string field declares values" for"string"with values; otherwise the object is built, andto_dict()andschema_refraiseAttributeError.required="yes"ormany=1. Before:SchemaValidationError("field 'x' required must be a boolean"). Now: the value is accepted and read for its truthiness, andto_dict()emits it as given.from_dictrejects it, so the declaration cannot survive a round trip.rolethat is not aFieldRole, for example the string"decision". Before:SchemaValidationError("field 'x' has an invalid role"). Now: withmany=True, "a field with a role holds one value" is raised; otherwise the object is built, andto_dict()andschema_refraiseAttributeError.Checklist
breakdown/andkernelforge/rewrite_by_flydsl/that changes none of the three files or the tests below.-n 2, give 1,041 passed and 11 skipped on both sides, and the same 121 failed and 76 errors, with no failing id on one side only. These fail on base too, on Windows only, mostly intest_apply_gpu_power_settings.py,test_multi_node_scripts.pyandtest_external_multi_node.py._restart_multinode, becauseos.getuidis missing instate_file_safe_to_read. A local shim for that one call, used the same way on both sides, lets five multi-node files run it: 266 passed and the same 15 failures (POSIX group checks) on base and head.hyperloom_kb/testsplustest_experience_collect.py,test_experience_kb_integration.pyandtest_experience_kb_service.py) give 280 passed, 1 skipped and 19 failed on head. The same 19 fail on base: local-service startup (an SSL error on this host), SIGTERM, directory fsync and file-path tests.cli/__init__.pyandmulti_node/cli.pyare on the coverage omit list (pyproject.toml,[tool.coverage.run] omit,:360-361), so diff coverage has no countable statements for them, and theschema.pychange only deletes lines.size-exception._persist_multinode_restarttakes five parameters, at the limit (three before, one of them the_MultinodeLaunchbag); the new functions take one to three._restart_multinode(74, from 107) is the one restart sequence, in its order: kill or resume, launch, the early checkpoint, the poll, the PD router and the persist._seed_fresh_session(69, from 164) is the fresh-session steps in their fixed order (model, quantization, framework, target GPU, workload exports, session creation, model gate and preflights), each a call or the environment exports it publishes for subprocesses.FieldDeclaration.__post_init__(13, from 16), oneifper declaration rule thatfrom_dictdoes not already enforce.cli/__init__.py(2,752) andmulti_node/cli.py(1,804) stay over the 1,200-line module limit they were already over; each gets shorter, andcli/__init__.pystays over its 2,569 baseline entry, as at base.schema.py(1,008) is on the report-only 800-line warning list.from_dictraises._persist_multinode_restart,_resume_prior_launch) belong to private functions whose only callers are in the same file.FieldDeclarationkeeps its fields, andfrom_dictkeeps its errors.Known merge interactions
Each open PR that changes one of these files was trial-merged with
git merge-tree --write-tree, once into origin/main alone and once into this branch. A conflict that also occurs against main alone is not introduced by this branch.cli/__init__.py: feat(trace): per-LLM-call attribution, and the reports that read it #1450, feat(explore): let a scriptable adapter declare its own lever grid #1546, xDiT profiling and TraceLens analysis support #1762, Migrate from the deprecated rocm-smi to amd-smi #1924, feat(specialist): add a speculative decoding specialist type #1979, [fix] close the defects behind the MuseSpark run shortfall and delete state nothing reads #2055 and Run the orchestration role on the DeepSeek Harness #2062. None gains a conflict from this branch, in this file or anywhere else.cli/__init__.pyagainst main, and the block is identical against this branch. feat(trace): per-LLM-call attribution, and the reports that read it #1450 conflicts in 23 files and feat(explore): let a scriptable adapter declare its own lever grid #1546 in 2, either way.cli/__init__.pycleanly. Its 4 conflict blocks in the baseline file occur either way; in one of them, this branch's side readsmulti_node/cli.py 1804where main's reads 1867.cli/__init__.pytheir 25, 49 and 63 blocks are identical either way. Inmulti_node/cli.pythey have 22, 29 and 29 blocks either way, three of which this branch's restart changes reshape in place.hyperloom_kb/schema.py.scripts/code_metrics_baseline.txt. 14 of them conflict there against main alone, with the same blocks against this branch; in fix: judge explore variants and warm recipes by what they change about the running launch #1908, one block's side reads 1804 here instead of 1867. One conflicts only against this branch: refactor(inference-optimizer): delete the dead and repeated code in ten over-gate CLI, roofline and multi-node functions #2077 (cc20,lean/shafeng/cc20-io-misc-pr), whose lowered module-lines rows formodel_gate.py,preflight.pyandroofline_ceiling.pysit next to this PR'smulti_node/cli.pyrow. Keeping both sides' rows resolves it.Gate
python scripts/code_metrics.py --base-ref origin/main(origin/main at e25cb2d) passes on the 4 touched files: 0 new, 0 worse and 0 out-of-date entries in every dimension, no new or worse duplication or dead code, and 0 findings from the comment, English-only, test-import, repeated-literal and A1–A12 checks. Baseline: one module-length entry lowered (multi_node/cli.py1,867 → 1,804), +0.The gate lists under Already over at the base:
_run_optimize(279 lines),_reexport_session_environment(111),_resolve_run_topology(92) and_resolve_agent_lineup(89), andcli/__init__.pymodule length 2,778 → 2,752 (baseline entry 2,569).schema.py(1,008 lines) is on the report-only module-length warning list.ruff checkandruff format --checkare clean on the three files.