Repository navigation
refactor(orchestrator): delete the dead and repeated code in nine over-gate specialist, policy and prompt functions - #2078
Conversation
…-checks _section_roofline_evidence rendered a "Kernel roofline" table from roofline_evidence["kernel_roofline_top15"]. The only writer of roofline_evidence, warm_specialist_params (specialists/dispatch.py:264), copies analysis_md_path, roofline_snapshot_id, executive_summary and hot_kernels_top15 out of last_trace_analyze and never that key, so the table and its per-row guards could not render from any in-repo producer. The kernel_roofline_top15 list inside last_trace_analyze is untouched. The isinstance checks on roofline_evidence, executive_summary, hot_kernels_top15 and its rows are deleted as well. The field is a typed dict the runner builds with dict(...) (specialists/runner.py:660); the warmer writes executive_summary as extract_workload_summary()'s dict and hot_kernels_top15 as a list of the dict rows record_trace_analyze stores (state/shared_state.py:2135, rows built at 2209). The str(... or "") around analysis_md_path is gone because the f-string formats the value the same way. Only an agent-written params.roofline_evidence, which the warmer leaves in place and no prompt asks for, can carry other shapes: a kernel_roofline_top15 list no longer renders a table, and a non-dict executive_summary or a hot_kernels_top15 that is not a list of dicts now raises while the prompt is built instead of being skipped. The coverage test's kernel_roofline_top15 fixture and its `k1` assertion only exercised the deleted table and are removed with it. Co-authored-by: Cursor <cursoragent@cursor.com>
…ady normalized
_section_mandate re-derived values its producer already guarantees:
- scope: (inp.scope or "domain").lower() is gone. The runner passes
profile.scope (specialists/runner.py:714), which
resolve_specialist_profile lowercases into SCOPE_VALUES
(specialists/profile.py:125-126); _section_identity already reads
inp.scope raw. An empty scope still takes the non-freeform branch.
- task_description / gap_canonical_id / task_kind: the ternary and the
`or ""` fallbacks are gone. They are typed str fields the runner fills
with str(...) (runner.py:592, 653, 731), and "".split("\n")[0] is "".
- framework: the `or None` alias is gone; format_primary_metric resolves
"" and None to the same spec (framework_registry._spec_or_default).
- prior_attempts: the per-entry isinstance(dict) skip is gone; the runner
keeps only dict entries (runner.py:732).
The run-status block (its own header, gated on a baseline or a KEEP
threshold) moves unchanged into _mandate_run_status, which is the one
job left over once the redundant normalization is deleted.
Only a SpecialistPromptInputs built directly, never through the runner,
can tell the difference: a mixed-case scope is compared as written,
None in task_kind (or in task_description with scope "freeform") raises
AttributeError, and a non-dict prior_attempts entry raises instead of
being skipped.
Co-authored-by: Cursor <cursoragent@cursor.com>
_validate_warm_replay_targets re-read and re-parsed a patch for every
plan entry that named it, then denied the dispatch ("changed during
validation") when two reads of the same realpath disagreed. The patch is
now read once per validation, keyed by its realpath, so the comparison
and its denial are gone. KB downloads land in a fresh mkdtemp generation
directory (knowledge/remote_recipe/client.py:377-383) and nothing in the
tree rewrites a downloaded patch in place, so two reads inside one
validation could only differ under an outside writer racing the gate;
that case is now judged against the first read.
Also deleted:
- `not declared_targets or` before declared_targets.isdisjoint(...): an
empty frozenset is disjoint from everything, so the denial is the same.
- `or []` after entry.get("resolved_patch_targets"): the next line's
`if not raw_targets: continue` already skips None.
The patch_path checks (non-empty string, inside the session KB download
root, resolvable) move unchanged, in the same order, into
_warm_replay_patch_key. Every rule, message and raise order on
in-contract input is the same; the shape checks on the persisted plan
stay because validate_dispatched_task re-validates coordinator.db rows.
Co-authored-by: Cursor <cursoragent@cursor.com>
… dispatch _validate_specialist_dispatch handed off to the freeform gate from two places, once for scope='freeform' and once for a bare dispatch with no scope and no tags, each with its own call and return. It is now one condition. normalize_dispatch_tags only reads params and the domain catalogue (specialists/domains.py:253-274: no logging, no mutation, and nothing in it raises on JSON-shaped params), so computing the tags before the freeform check changes nothing a caller can see. The gap check tested `if not gap` twice around the ledger backfill; it is now `if not gap and not self._autofill_gap_from_ledger(...)`, which calls the backfill exactly when the old code did and denies exactly when it returns no id. The single-use max_turns_raw local is inlined. The tag / scope coherence logging, which the docstring already describes as logged rather than denied, moves unchanged into _log_specialist_dispatch_coherence. Every raise and log line happens in the same order with the same text on every path. Co-authored-by: Cursor <cursoragent@cursor.com>
…e entries now under the gate Removes the seven code_metrics_baseline.txt entries of the four functions this branch brought under every gate limit: - cognitive-complexity: PolicyGate._validate_warm_replay_targets (39), _section_mandate (49), _section_roofline_evidence (70) - function-lines: PolicyGate._validate_specialist_dispatch (91), PolicyGate._validate_warm_replay_targets (102), _section_mandate (92), _section_roofline_evidence (119) and lowers the module-lines entries of policy/gate.py (1588 -> 1580) and prompts/specialist_prompt_builder.py (2571 -> 2556) to the gate's own count. No other entry changes, and none is added or raised. Co-authored-by: Cursor <cursoragent@cursor.com>
…elper Delete three copies of the resolve-plus-is_dir pattern, the seen-set loop that dict.fromkeys replaces, the empty-parsed guard that an early empty-texts return makes redundant, and the two comments that only restated the docstring. Safe because every in-repo caller already passes str patch texts and directory candidates; duplicate candidates only gain an extra is_dir stat. Co-authored-by: Cursor <cursoragent@cursor.com>
Delete the getattr chain around shared_state.phase, the redundant or-[] normalisation of proposal_set and notes, and the duplicate proposal_set / domain locals in record_specialist_result. Lift the nine carried keys into _ROUND_CARRIED_KEYS and the post-save harvest / gap / autosubmit chain into _follow_up_specialist_round so each names one job. Safe because Task.params is always a dict, every production caller has a Coordinator-backed shared_state, and the one test that built a bare Coordinator for the round_id fallback now uses the fixture that supplies one. Co-authored-by: Cursor <cursoragent@cursor.com>
Delete RunOutcome and RunHarvest: the only production caller already held a SpecialistSubprocessResult, and the bags only restated fields it already owns. Decode the backend error once, admit a done payload through one method that keeps the reconcile/stamp/vet order, and fold the ledger writes plus the finished heartbeat into _record_subprocess_run. Safe because every in-repo path reaches finalize after a workspace is set and with the dataclass defaults for patches/patch_roots/turn_usages; the unit tests that built the bags now pass a SpecialistSubprocessResult instead. Co-authored-by: Cursor <cursoragent@cursor.com>
The five target functions are under every limit they violated; remove their baseline rows and lower the runner.py module-lines entry to match. Co-authored-by: Cursor <cursoragent@cursor.com>
Pin the backend-error tokens, recovered-partial note, multi-turn ledger rows, run_error/notes round entry fields, and the explicit-root empty-texts path so the changed statements clear the diff-coverage gate. Co-authored-by: Cursor <cursoragent@cursor.com>
The finalize rework removed the only place that set a run's status to tool_violation, which fired on a RunOutcome field every caller left empty. Delete the TOOL_VIOLATION failure type and its classify branch, and the two test rows that fed the classifier a status the runner can no longer emit. Co-authored-by: Cursor <cursoragent@cursor.com>
…on main #2041 removed five lines from SpecialistRunner._prepare after the specialist rework had lowered this entry to 1462, so on top of main the module is 1457 lines and the gate reports the entry as out of date. Written by scripts/code_metrics.py --update-baseline, which changed only this row. Co-authored-by: Cursor <cursoragent@cursor.com>
Code metrics gate: PASSEDJudged: the 11 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): 2
Already over at the base (backlog, not this change's): 1
108 findings in files this change does not touchOutside this change (informational): 108
...and 83 more; the job log lists every row.
Thresholds and sources: |
…cated one Every comment the earlier commits removed was checked against the head code. The constraints they stated are still written where the code does the job: the freeform-lane and gap-backfill notes in PolicyGate._validate_specialist_dispatch, the create-only and no_candidate_roots rules in resolve_patch_apply_root's docstring, the leading-token rule in _subprocess_backend_error's docstring and the scope stamp in _admit_done_payload. None needed restoring, and comments that only narrated the code stay deleted. This drops the second of two consecutive comments in SpecialistRunner._record_subprocess_run that stated the same ledger rule, and lowers runner.py's module-length entry to 1455. dd02809's message says warm_specialist_params copies executive_summary out of last_trace_analyze; it parses executive_summary from the analysis file with extract_workload_summary and copies only analysis_md_path, roofline_snapshot_id and the hot-kernel rows. Co-authored-by: Cursor <cursoragent@cursor.com>
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.
What this does
Nine functions in the orchestrator's specialist, policy and prompt code were over the code-metrics gate: the policy gate's specialist-dispatch and warm-replay checks, the specialist prompt's mandate and roofline sections, patch-root resolution, the runner's subprocess run and finalize, and the dispatch collaborator's round entry and its bookkeeping. This PR brings each one under every limit it broke, mostly by deleting what it did twice or what no producer feeds: a "Kernel roofline" prompt table built from a key nothing writes into the specialist's evidence; shape checks and re-normalisation of prompt inputs the runner already builds in those shapes; the repeat read of a warm-replay patch, with the "changed during validation" denial that only compared the reads; a duplicated hand-off to the freeform dispatch gate; three copies of a resolve-and-
is_dircheck; twoNamedTuplebags (RunOutcome,RunHarvest) that the one caller filled with constants and copies of theSpecialistSubprocessResultit already held; thetool_violationstatus nothing sets; and, in the round bookkeeping, agetattrfallback for a collaborator without shared state and locals that recomputed what the round entry already holds.Production code: +268 / −392 (net −124) across 5 files. Tests: +104 / −35. The code-metrics baseline loses 17 entries, lowers 3 module-length entries and gains none.
This continues the complexity cleanup of #2024, #2026, #2052, #2056, #2057 and #2067. The 13 commits are grouped by module and read one at a time; each refactor commit's message says what it deleted and why that is safe. Line numbers in the commit messages are from the base those commits were written against; the ones below are re-checked on this branch. One correction to dd02809's message: the warmer does not copy
executive_summaryout oflast_trace_analyze, it parses it from the analysis file (see Why the deletions are safe); 00a49d9's message records the same correction.prompts/specialist_prompt_builder.pyprompts/specialist_prompt_builder.pypolicy/gate.pypolicy/gate.pyspecialists/patch_safety.pyspecialists/dispatch.pyspecialists/runner.pyspecialists/runner.pyspecialists/runner.pyAll five production files are under
src/hyperloom/orchestrator/.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.
specialist_prompt_builder.py_section_roofline_evidence_section_mandategate.pyPolicyGate._validate_warm_replay_targetsPolicyGate._validate_specialist_dispatchpatch_safety.pyresolve_patch_apply_rootrunner.pySpecialistRunner._finalizeSpecialistRunner._run_via_subprocessdispatch.pySpecialistDispatchCollaborator.build_specialist_round_entrySpecialistDispatchCollaborator.record_specialist_resultAlso changed, never over at base:
classify_specialist_failure11 → 10 / 16 → 15 / 46 → 44.New functions, cyclomatic / cognitive / lines:
specialist_prompt_builder.py:_mandate_run_status7 / 9 / 21.gate.py:PolicyGate._warm_replay_patch_key4 / 5 / 20,PolicyGate._log_specialist_dispatch_coherence6 / 10 / 22.patch_safety.py:_resolved_dir2 / 2 / 7.runner.py:_subprocess_backend_error5 / 9 / 16,SpecialistRunner._record_subprocess_run5 / 8 / 61,SpecialistRunner._admit_done_payload12 / 21 / 71.dispatch.py:SpecialistDispatchCollaborator._follow_up_specialist_round5 / 7 / 52.Deleted:
runner.RunOutcome,runner.RunHarvest, and theSpecialistFailureType.TOOL_VIOLATIONmember.Across the 5 touched production files: C901 total 790 → 773, cognitive total 1,316 → 1,219, functions 157 → 165. Functions over a gate limit: cyclomatic 3 → 1, cognitive 9 → 3, length 20 → 11. What is left is functions this PR does not touch:
SpecialistDispatchCollaborator.warm_specialist_params(cyclomatic 39, cognitive 93, 170 lines),maybe_force_stalled_domain_specialist(cognitive 33, 108 lines) andmaybe_auto_retry_specialist(134 lines);PolicyGate._validate_payload_paths(cognitive 39) andPolicyGate._validate_specialist_gpu_request(82 lines);SpecialistRunner._prepare(193 lines);vet_patches(107 lines); and five builder functions that are mostly prompt text:_focus_framework_rewrite_specialist,_focus_kernel_switch_specialist,_focus_serving_specialist,_focus_static_recon_specialistand_section_output_protocol.Module lengths:
specialist_prompt_builder.py2,597 → 2,556,gate.py1,588 → 1,580,runner.py1,500 → 1,455,patch_safety.py1,175 → 1,152,dispatch.py1,042 → 1,035.What changes in each module
Specialist prompt builder (
specialist_prompt_builder.py)New name:
_mandate_run_status— §0's read-only run status (baseline, current best, validated gain, KEEP threshold, applied stack), empty until a baseline or a KEEP threshold exists. Lifted out of_section_mandateunchanged, except that it passesinp.frameworkstraight toformat_primary_metric, which resolves""andNoneto the same spec (inference_optimizer/framework_registry.py:105-108).Removed:
_section_roofline_evidence: the "Kernel roofline" table and its per-cell fallbacks, built from akernel_roofline_top15key that no producer writes intoroofline_evidence; theisinstancechecks onroofline_evidence,executive_summary,hot_kernels_top15and each hot-kernel row, with theor {}/or []defaults that fed them; and thestr(… or "")aroundanalysis_md_path._section_mandate: the(scope or "domain").lower()re-normalisation, thetask_descriptionternary, theor ""fallbacks ongap_canonical_idandtask_kind, theframework or Nonealias, and the per-entryisinstance(dict)skip onprior_attempts.Policy gate (
gate.py)New names:
PolicyGate._warm_replay_patch_key: checks a warm-replay entry'spatch_path(a non-empty string, inside the session KB download root, resolvable) and returns its realpath, which keys the one read of that patch. The three checks keep their order, rules and messages.PolicyGate._log_specialist_dispatch_coherence: the five log lines for a tag / scope combination thatresolve_specialist_profilewill re-infer, which the docstring already describes as logged rather than denied. Moved verbatim.Removed:
_validate_warm_replay_targets: the repeat read and parse of a patch that several entries name, the "changed during validation" comparison with itswarm_replay_patch_target_mismatchdenial, thenot declared_targets orguard in front ofisdisjoint(an empty set is disjoint from everything), and anor []in front of a falsiness check._validate_specialist_dispatch: the second call-and-return into the freeform gate, the secondif not gaparound the ledger backfill, and a single-usemax_turns_rawlocal.Patch-root resolution (
patch_safety.py)New name:
_resolved_dir— a path resolved, orNonewhen it cannot be resolved or is not a directory.Removed from
resolve_patch_apply_root: three copies of that resolve-and-is_dircheck (explicit root, candidates, default root), the seen-set loop thatdict.fromkeysreplaces, the empty-parsedguard that an early empty-texts return makes redundant, thestr(text or "")coercion of each patch text, and two inline comments that restated the docstring's create-only andno_candidate_rootsparagraphs (patch_safety.py:344-358).Specialist runner (
runner.py)New names:
_subprocess_backend_error: the error a reaped subprocess reports, led by the token the failure classifier keys on (subprocess_timeout,subprocess_stale_heartbeat,subprocess_error:,subprocess_exit_code:), or""when it ended cleanly. Moved out of_run_via_subprocessunchanged.SpecialistRunner._record_subprocess_run: the transcript line, the per-turn token-ledger rows, the intel calls, the conversation turn and the finished heartbeat, in that order.SpecialistRunner._admit_done_payload: reconciles claimed and scanned patches, stamps each proposal with the dispatch scope and normalises its citations, and runs the patch-safety gate, in the order_finalizeran them.Removed:
RunOutcomeandRunHarvest;_finalize'sspecialist_done_payload,outcomeandharvestparameters (now onesub_result); a second loop over the proposals; thetool_violationsnotes and status branch, and with themSpecialistFailureType.TOOL_VIOLATION, itsclassify_specialist_failurebranch and theNamedTupleimport; and, in the ledger writes, a second computation of the latency_subprocess_call_outcomealready puts on the call span, plus thelist(…)/dict(…)copies ofturn_usages,patchesandpatch_roots.Specialist dispatch (
dispatch.py)New names:
_ROUND_CARRIED_KEYS: the nine optional round-row fields taken from the done payload or else the dispatch params.SpecialistDispatchCollaborator._follow_up_specialist_round: what runs after a round is recorded and saved — findings harvest, static-recon consumption, research evidence, the gap attempt, the gap refresh, a requested enablement build, and the two autosubmits.Removed: the
getattrchain aroundshared_state.phase, theround_idandtagslocals,or []defaults in front ofisinstancechecks, abool(…)around a condition, and inrecord_specialist_resultthedomain,proposalsandis_emptylocals that recomputed what the round entry already holds.Why the deletions are safe
Line numbers are on this branch unless marked otherwise. Paths are under
src/hyperloom/orchestrator/, except those starting withinference_optimizer/, which are undersrc/hyperloom/.roofline_evidenceiswarm_specialist_params, which writes four keys (specialists/dispatch.py:269-274):analysis_md_path,roofline_snapshot_idand the first eighthot_kernels_top15rows fromlast_trace_analyze, plus anexecutive_summaryparsed from the analysis file (dispatch.py:256-267). It never copieskernel_roofline_top15, althoughlast_trace_analyzecarries that key (state/shared_state.py:2122), and it leaves a value already in the dispatch params alone (dispatch.py:253). The deleted table read the key atspecialist_prompt_builder.py:1775on main. No prompt, skill or doc undersrc/hyperloomnamesroofline_evidence, so no agent is asked to write one.dict(…)(specialists/runner.py:654).extract_workload_summaryalways returns a dict (inference_optimizer/roofline_snapshot.py:84-106), and the hot-kernel rows are thelist[dict]that_build_hot_kernel_summariesbuilds forrecord_trace_analyze(state/shared_state.py:2063,:2161-2168).SpecialistPromptInputs(runner.py:643);SpecialistRunner.runalso accepts pre-built inputs (runner.py:537), but no caller passes any. It passesprofile.scope(:708), whichresolve_specialist_profilelowercases and forces intoSCOPE_VALUES(specialists/profile.py:128-131);gap_canonical_idandtask_descriptionas stripped strings (runner.py:582,:586, passed at:647,:711);task_kindas a string (:725); and only the dict entries ofprior_attempts(:726).mkdtempgeneration directory (knowledge/remote_recipe/client.py:377-382) rather than rewriting files in place, so two reads in one validation could differ only if another process rewrote the file between them._remote_recipe_files_rootonly resolves the session directory and returnsNoneon failure (policy/gate.py:1231-1238), so resolving it per entry has no side effect. The plan's shape checks stay, becausevalidate_dispatched_taskre-validates rows read back fromcoordinator.dband runs this check (gate.py:326-360; called fromloop/dispatcher.py:522andloop/sub_agent_runner.py:295).normalize_dispatch_tagsand the_tag_to_kb_anchorit calls only read the params and the domain catalogue (specialists/domains.py:255-276,get_domainat:316-321), so computing the tags before the freeform check has no effect a caller can see._autofill_gap_from_ledgerreturns""when nothing matches (gate.py:1021-1043), soif not gap and not self._autofill_gap_from_ledger(…)calls it and denies exactly when the old two-step check did.resolve_patch_apply_rootpasses strings:ground_patch_textpasses the one patch text it already ran throughis_unified_diffandpatch_escapes_tree(specialists/patch_safety.py:688-705);vet_patchespasses file text fromread_text(:978, call at:1020-1025);integrate_patchpassesstr(text)and_read_patch_textsoutput (actions/executors/integrate_patch.py:613-614,_read_patch_textsat:500-514, call at:626-631).RunOutcome/RunHarvest. Neither was exported, and on main nothing outsiderunner.pyand its unit test named them (bypass_report.RunOutcomeis an unrelated class). The one construction (runner.py:1073on main) filled them from thesub_resultin scope and always passedturns_used=1andtool_violations=[]._finalize's only caller is_run_via_subprocess(runner.py:984), which returns before it when there is no workspace (:926-942)._subprocess_call_outcomecomputes the sameint(float(elapsed_seconds) * 1000)and leaves the key out on the sameTypeError/ValueError(runner.py:108-111);_run_via_subprocesshands it over with.get, so a missing latency is stillNone(:983).SpecialistSubprocessResultdeclarespatchesandturn_usagesas lists andpatch_rootsas a dict, all with defaults (specialists/subprocess_.py:547,:553,:578). Every producer passes those types: the reaper's early returns use the defaults (:924,:993,:1011,:1023,:1063), its full return (:1171-1185) takespatchesandpatch_rootsfrom_collect_patches(:1139, typedtuple[list[str], dict[str, str]]at:1655-1662) andturn_usagesfrom the two parsers (:1155,:1166;inference_optimizer/trace/parse_usage.py:195-197,:441-443), and the rehearsal stub buildspatchesas a list (rehearsal/specialist.py:147-163).tool_violation. On main its only producer was_finalize'sif outcome.tool_violations(runner.py:1220-1221on main), fed by that oneRunOutcomewithtool_violations=[], so the status never fired.classify_specialist_failureis called only bymaybe_auto_retry_specialiston the live result (dispatch.py:356), which returns before using the failure type when the attempt is not retry-eligible (:357-358);tool_violationwas never retry-eligible, and afailure_typeis written only past that point (:364-477). Nothing converts a stored value back intoSpecialistFailureType, and notool_violationstring is left in the tree.build_specialist_round_entry's only production caller isrecord_specialist_result(dispatch.py:854), whose only caller is the dispatcher, through the Coordinator's collaborator (loop/dispatcher.py:1185-1190); a collaborator'sshared_stateis the Coordinator's (collaborator.py:56-58). The scorer call and the follow-up readdomain,proposal_setandproposals_totalfrom the round entry, which builds them from the same expressions the deleted locals used (dispatch.py:743-745,:759,:763-764). Nothing in between changes those fields:record_specialist_roundstores a copy (state/_shared_state/phase_state.py:254) and_record_specialist_round_productonly reads the entry (dispatch.py:797-833).ProposalScorer, built by_build_proposal_scorer(inference_optimizer/cli/backends.py:186-203) and handed to the collaborator atloop/coordinator.py:322. Itsscorereturns a dict on every path (scoring/proposal_scorer.py:411,:417-421,:448) and rebindsproposalsto a filtered copy before using it (:409).Observable effect
Nothing an operator sees changes. Specialist prompts; policy decisions on specialist dispatches and warm-replay plans, with their rules, messages and hints; specialist round rows,
specialist_done.json, run results and notes; ledger rows; retry decisions; and log lines are the same for every input the repo's own callers produce.The differences below are reachable only by input that no caller in the repo produces:
_section_roofline_evidencegiven aroofline_evidencethe warmer did not write, reachable only through dispatch params that already carry one, which the warmer then leaves alone (specialists/dispatch.py:253):kernel_roofline_top15list no longer renders a "Kernel roofline" table.executive_summarythat is not a dict, or a non-emptyhot_kernels_top15that is not a list of dicts, raises while the prompt is built instead of being skipped._preparebuilds the prompt outside anytry(specialists/runner.py:731) and the specialist executor does not catch it (inference_optimizer/cli/executors.py:166);SubAgentRunner.run_taskrecords the task as failed with the exception's repr and class name (loop/sub_agent_runner.py:359-369), and the session continues.roofline_evidencethat is not a dict raises as well, but only in aSpecialistPromptInputsbuilt directly; the runner always passes a dict (runner.py:654)._section_mandategiven aSpecialistPromptInputsbuilt directly: a mixed-casescopeis compared as written, so"Freeform"or"Domains"gets the deliverable line and anchor of a single-domain dispatch;Noneintask_kind, or intask_descriptionwith scope"freeform", raisesAttributeError; a non-dictprior_attemptsentry raises instead of being skipped._validate_warm_replay_targetswhile another process rewrites a KB file: a patch that several plan entries name is read once per validation, through its realpath. If the file is rewritten between what used to be two reads, the plan is judged against the one read instead of being denied withwarm_replay_patch_target_mismatch("changed during validation"). The session KB root is resolved per entry rather than once per plan, so a session directory re-pointed during the validation is seen by the later entries.resolve_patch_apply_rootgiven a patch text that is not a string:NoneraisesAttributeErrorinstead of being skipped, and other values are no longer passed throughstr()first.build_specialist_round_entryon a collaborator withoutshared_state: raisesAttributeErrorinstead of reading the phase as empty.record_specialist_resultwith a proposal scorer whosescorereturnsNone: raisesAttributeErrorinstead of recording no ensemble scores.SpecialistSubprocessResultwithNonein a list or dict field:Noneturn_usagesnow raisesTypeErrorwhile the run is recorded, where it used to give one cumulative ledger row.Nonepatchesorpatch_rootsused to raiseTypeErroron every run; now they raise only where finalize reads them, so a run without a done payload finalizes as empty, andNonepatch_rootswith an empty patch set or a declaredframework_source_rootreads as empty.tool_violationrunner status:classify_specialist_failurereturnsUNKNOWNinstead ofTOOL_VIOLATION. Neither is retry-eligible, so the only caller does the same with either.Two changes are not observable at all:
_resolved_dirprobesis_dirbefore de-duplicating, so a candidate root listed twice is probed twice, with the same resolution; and the proposal scorer now receives the round entry's copy of the proposal list instead of the done payload's, whichProposalScorer.scoredoes not mutate.Checklist
test_build_rich_user_prompt_sections(test_specialist_prompt_builder_coverage_unit.py:132) loses itskernel_roofline_top15fixture entry and the`k1`assertion, which pinned only the deleted table; its other assertions still run.test_build_specialist_round_entry_round_id_falls_back_to_task_id(test_specialist_lifecycle.py:264) uses thecoordfixture instead of a bareCoordinator.__new__; itsround_idassertion is unchanged, and what it no longer exercises is the deletedgetattrfallback for a collaborator withoutshared_state. The_finalizehelper (test_specialist_runner_helpers_unit.py:237-246) andtest_a_harvested_diff_is_the_whole_round_and_the_specialists_copy_of_it_is_not_added(:249) pass aSpecialistSubprocessResultinstead of the deleted bags; their assertions are unchanged.test_classify_tool_violation(test_specialist_runner_helpers_unit.py) and thetool_violationrow of the classifier table intest_specialist_auto_retry.py. Both pinned only the deletedTOOL_VIOLATIONmember. An unrecognised status still classifies asUNKNOWNand is not retried, pinned bytest_classify_unknown(test_specialist_runner_helpers_unit.py:117-118) and theweird_statusrow (test_specialist_auto_retry.py:44). No remaining production path carries the deleted contract, so the replaced-test rule does not stop this PR.test_build_specialist_round_entry_records_run_error_and_notes(test_specialist_lifecycle.py:275: a run error setsstatus,errorandrun_error, and notes are stringified);test_explicit_root_without_patch_texts_is_accepted(test_specialist_patch_safety_unit.py:616); and intest_specialist_runner_helpers_unit.py,test_subprocess_backend_error_leading_tokens(:499),test_finalize_notes_a_recovered_partial_payload(:516),test_finalize_skips_non_dict_proposals_and_drops_scanned_patches_outside(:527) andtest_record_subprocess_run_emits_one_ledger_row_per_turn(:544).size-exception. It stays one PR because it is one concern, the over-gate functions in the orchestrator's specialist, policy and prompt code, and a specialist round runs through all of them: the policy gate admits the dispatch, the prompt builder and the runner build the prompt and finalize the result, patch-root resolution grounds the patches, and the dispatch collaborator records the round. Split, it would be two PRs on the same baseline file that need the same review context. Net −124.resolve_patch_apply_rootsits at the 80-line limit, 38 of those lines its docstring;_section_roofline_evidence,_finalizeand_run_via_subprocessare at 79._validate_warm_replay_targets(14, from 17) andresolve_patch_apply_root(14, from 21) are each one validation ladder whose branches are its denial or failure reasons;_section_mandate(13, from 20) and_section_roofline_evidence(11, from 17) are one prompt section each;build_specialist_round_entry(12, from 13) builds one row in place;_admit_done_payload(12, new) is the admission sequence_finalizecarried, in the same order.resolve_patch_apply_root(80),_section_roofline_evidence,_finalizeand_run_via_subprocess(79),_validate_warm_replay_targets(76),record_specialist_result(75),build_specialist_round_entry(74),_admit_done_payload(71),_section_mandate(68),_validate_specialist_dispatch(64) and_record_subprocess_run(61). Each is one ladder, one prompt section, one record, or one call with its keyword arguments.specialist_prompt_builder.py(2,556),gate.py(1,580) andrunner.py(1,455) stay over the 1,200-line module limit they were already over;patch_safety.py(1,152) anddispatch.py(1,035) stay over the 800-line warning. Each gets shorter.RunOutcome,RunHarvestand_finalize's parameters are private torunner.py.SpecialistFailureTypeis exported, and losingTOOL_VIOLATIONbreaks only code that names the member: nothing in the tree does after this PR, no code path on main could produce the value, and nothing converts a stored value back into the enum.Known merge interactions
Trial merges used
git merge-tree --write-tree, against this branch and against origin/main at 9da8788, with each open PR's head at the time. A conflict that also exists against main alone is not introduced by this branch.gate.pymoved ([fix]Enablement: run later phases on the promoted runtime checkout; add a ROCm install path for SGLang checkouts #2050: an import and the specialist delegate ingress, +3 / −3), and this branch merges with origin/main at 678ba07 cleanly.runner.py,specialist_prompt_builder.pyand the baseline file. Threerunner.pyhunks conflict only against this branch: theSpecialistRunResult.statuscomment, theSpecialistFailureTypemembers andclassify_specialist_failure, where fix(specialist): keep specialists out of shared trees and dependencies — prompt guardrails plus post-run checks #2068 addsSHARED_TREE_VIOLATIONnext to theTOOL_VIOLATIONthis PR deletes. fix(specialist): keep specialists out of shared trees and dependencies — prompt guardrails plus post-run checks #2068 does not producetool_violationeither; resolution is to keep its new member and branch and drop thetool_violationlines. Its prompt-builder hunks (_section_iron_rules) merge cleanly, and its baseline conflict exists against main too.gate.pyand the baseline file.gate.pymerges cleanly (its hunks are theextend_leasebound, a role hint and__all__). The baseline conflicts only against this branch, still so at 678ba07, on thegate.pymodule-length row both lower (1,580 here, 1,585 there); whichever lands second re-runspython scripts/code_metrics.py --update-baseline.prelude.pyandgate.pymodule-length rows; [fix] close the defects behind the MuseSpark run shortfall and delete state nothing reads #2055 in three hunks, twodispatch.pyrows where each side removes the other's neighbour, and the module-length block. Neither baseline conflicted with main at 9da8788; at 678ba07 refactor(prelude): read a warm-start prior into named values before PRELUDE acts on it #1958's still does not, and [fix] close the defects behind the MuseSpark run shortfall and delete state nothing reads #2055's now conflicts with main too. [fix] close the defects behind the MuseSpark run shortfall and delete state nothing reads #2055 also editsdispatch.py(maybe_force_stalled_domain_specialistand a newrefuse_patch_without_source) andtest_specialist_lifecycle.py; both merge cleanly with this branch, and its other conflict (test_coverage_margin3_unit.py) is with main only. Resolve the rows by key, then re-run--update-baseline.runner.pyandtest_specialist_runner_helpers_unit.py. Itsrunner.pyconflicts exist against main too. The test file conflicts only against this branch, where both sides append tests at the end of the file; keep both.warm_specialist_paramsand adds a focus section; fix(prompts): point native AgentX agents at Magpie's launch protocol #2058 edits_section_hardware; Migrate from the deprecated rocm-smi to amd-smi #1924 (draft) edits focus sections and_section_source_hint; fix(integrate_patch): end a patch set the tree already holds as already_present #1845 editspatch_file_targetsandpatch_targets_missing; fix(agentx): serve the replay with its prefix cache, and stop reading an unmeasured round as a result #1867 edits_git_apply_checkandground_patch_text. The other conflicts of Migrate from the deprecated rocm-smi to amd-smi #1924 and fix(agentx): serve the replay with its prefix cache, and stop reading an unmeasured round as a result #1867 exist against main too.scripts/code_metrics_baseline.txtwith this one, among them refactor(breakdown, kernelforge): delete what eleven over-gate functions re-checked #2052 and refactor(fusion): delete the dead and repeated code in thirteen over-gate functions #2067; none of them conflicts only against this branch. Four of them, refactor: delete the dead and repeated code in fourteen over-gate functions #2024, refactor(kernelforge): delete the dead and repeated code in twelve over-gate functions #2026, refactor(knowledge): delete the dead and repeated code in seven over-gate functions #2056 and refactor(trace-analysis): delete the dead and repeated code in eleven over-gate functions #2057, have since merged.Gate
scripts/code_metrics.py --base-ref origin/main(base 931af8a) passes on the 11 touched files: 0 new, 0 worse and 0 out-of-date entries, no new or worse duplication or dead code, and 0 findings from the comment, English-only, production-imports-test-code and repeated-literal checks and the A1 and A8–A12 design checks. Baseline: −17 entries, 3 module-length entries lowered (specialist_prompt_builder.py2,571 → 2,556,gate.py1,588 → 1,580,runner.py1,500 → 1,455), +0. On mainspecialist_prompt_builder.pywas already 26 lines over its entry (2,597 against 2,571).The gate lists under Already over at the base
SpecialistRunner._prepareat 193 lines (baseline entry 192), which this PR does not touch, and warns thatpatch_safety.py(1,152) anddispatch.py(1,035) are over 800 lines.ruff checkandruff format --checkare clean on the touched files.