Repository navigation
agent-optimize: rewrite SKILL.md loop to use graph/Pareto/targeted-eval/model-routing - #671
Conversation
…al/model-routing Issue #665's forensic run showed the SKILL.md prose never used the new machinery (plan_round.py, EvaluationPlan, gate_mode: pareto, model_routing, merge_search.py) even though it already existed: 8 rounds, 1 candidate each, full-val paid regardless of hypothesis scope, two mergeable accepts never merged. - Step 2 now calls plan_round.py for a branch plan (varying N, not a fixed bucket-A/B default) before any candidate dir is created. - Step 5 builds and persists an EvaluationPlan per candidate and screens at its stage (affected_tasks/regression_sentinels), escalating only on evidence, instead of defaulting to full val. - Merging disjoint-cluster accepted candidates before sealing is now an explicit REQUIRED step, framed as a visible protocol violation to skip. - Added model-routing guidance (resolve_model per decision role) and Pareto-gate guidance (gate_mode: pareto via gate_check.py/commit.py, reading a frontier result instead of a single accept/reject). - references/algorithm.md updated to match: new "Branch planning", "Evaluation plans", "Model routing" and "Pareto acceptance" sections, and the old "default to N>=3" framing corrected to point at plan_round.py. - gate_check.py: wired --mode pareto end to end (new GATE_MODES entry, --objectives/--metrics-*/--metrics-stderr-* flags reaching gate.decide). - templates/project/capevolve.yaml: documented gate_mode: pareto + objectives. - core/tests/test_skill_code_claims.py: new tests pinning plan_round.py's JSON shape, the model_routing roles table, gate_check.py's pareto wiring, the evaluation_plan stage constants, and merge_search's finalize-time hook against SKILL.md's claims. SKILL.md stayed under the repo's 500-line/5000-token body budget by moving rationale into references/algorithm.md and trimming prose elsewhere; mechanics are unchanged. Signed-off-by: Osher Elhadad <Osher.Elhadad@ibm.com>
|
🏷️ Automatic Labeling I've analyzed this pull request and added the following labels:
These labels were selected based on the PR title, description, and changed files. If you believe any labels are incorrect or missing, feel free to adjust them manually. |
round.py's --mode choices were gate_check.GATE_MODES verbatim, so adding "pareto" to that shared list (this PR) made `round.py --mode pareto` CLI-acceptable. But round.py's _gate() never forwards --objectives/--metrics-* to gate_check.py, so `--mode pareto` always crashed at runtime (gate_check.py raises ParetoObjectiveError and exits 2, round.py raises GateCheckFailed) -- after a wasted gate_check.py subprocess call. round.py now derives its own choices as gate_check.GATE_MODES minus "pareto", so this is rejected at argument-parsing time instead, with a comment explaining why pareto is excluded on purpose. Also fixes test_round_mode_choices_are_exactly_gate_check_mode_choices (now test_round_mode_choices_are_gate_check_mode_choices_minus_pareto), which asserted the two lists were identical -- that invariant is now "identical except for pareto" by design, and the comment in gate_check.py describing round.py's --mode as "restricted to the single-metric modes" is now accurate instead of aspirational. Adds a regression test confirming `round.py --mode pareto` is rejected at argument-parsing time, before any gate_check subprocess call. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Osher-Elhadad <Osher.Elhadad@ibm.com>
|
Fixed a confirmed review finding: Fix (
Test results: |
argparse's invalid-choice error quotes each choice individually on some Python versions (3.12+) and not others (3.11), so the single literal substring 'paired, significant, strict, threshold' matched locally but not in CI's Python 3.11. Check each mode name independently instead. Signed-off-by: Osher Elhadad <Osher.Elhadad@ibm.com>
Summary
Issue #665's final workstream (ws3). A forensic run (
run_20261003_184253) showed that theagent-optimizeSKILL.md's loop was being followed in prose but not in behavior, because theengine pieces that landed on
mainfrom PRs #666-#669 (model_routing,gate_mode: pareto,CandidateGraph/plan_round.py/EvaluationPlan) were never wired into the instructions an agentactually reads:
merge_compliance_warningfiring twice.
This PR rewrites the loop so following it mechanically produces the new behavior:
plan_round.pyon the diagnose output to get a branch planbefore any candidate dir is created, and creates exactly the number of slots it proposes (varies
with root-cause structure — not a fixed bucket-A/B default).
EvaluationPlan(
build_evaluation_plan()/persist_evaluation_plan()) per candidate and screens at its own stage(
affected_tasks/regression_sentinels), escalating only on evidence of a wider footprint — insteadof defaulting straight to full val.
acceptedcandidates is now an explicit,non-optional step framed as a visible protocol violation to skip, not advisory prose.
resolve_model(role, spec)at each judgment point (plan,root-cause, propose, implement, evaluation-analysis, merge, synthesis) and report the resolved model.
capevolve.yamldeclaresobjectives, usegate_mode: paretoviagate_check.py/commit.py(neverround.py, whose screen/merge machinery stays single-metric) andread the result as a frontier (
better/worse/tiedper objective), not a disguised boolean.references/algorithm.mdupdated to match — new "Branch planning", "Evaluation plans", "Modelrouting" and "Pareto acceptance" sections, and the old "default to N≥3" framing corrected to point at
plan_round.pyinstead of asserting a fixed constant.Backward compatible by construction: a single-objective run with no
objectivesdeclared, or nomodel_routingblock, behaves exactly as before;plan_round.pylegitimately proposes N=1 for atrivial single-cluster round (that is not the anti-pattern — never running it, or overriding its
estimate unstated, is).
Code changes beyond the two docs
skills/algorithms/agent-optimize/scripts/gate_check.py:--mode paretowas declared in core(
cap_evolve.gate, PR gate: multi-objective Pareto acceptance mode #667) but never reachable from any script — added it toGATE_MODESplus--objectives/--metrics-candidate/--metrics-current/--metrics-stderr-candidate/--metrics-stderr-current, wired through togate.decide(), withParetoObjectiveErrorsurfaced asa clean JSON refusal (exit 2) rather than a traceback.
round.pystill forwards--modeverbatimfrom
gate_check.GATE_MODES, so a round that mistakenly tries--mode paretofails loudly(
GateCheckFailed) rather than silently — the SKILL.md explicitly documents pareto mode as reachableonly through
gate_check.py+commit.pyby hand, never throughround.py.templates/project/capevolve.yaml: documentedgate_mode: paretoand the optionalobjectives:block (comment-only; the spec loader already reads arbitrary keys generically, so no schema code
changes were needed).
core/tests/test_skill_code_claims.py: extended with tests pinning (a)plan_round.py's JSON outputshape against what SKILL.md's step 2 reads, (b)
model_routing.ROLESagainst the roles table inSKILL.md, (c)
gate_check.py's pareto-mode CLI flags existing and actually reachinggate.decide()'s kwargs (not just accepted and ignored), (d)evaluation_plan.py's stage constantsagainst SKILL.md's stage guidance, (e)
merge_search.check_merge_compliancestill being the functionmeasure.pycalls at finalize time, per SKILL.md's "Stop & seal" claim.Known limitation (left out of scope, by design)
Full automatic Pareto-gate wiring through
round.py's batched screen/merge/null-control cascade wasnot attempted — that machinery is built around one scalar delta end to end, and bolting a
multi-objective path onto it is a larger change than this workstream's scope (SKILL.md + the two scripts
it names). A pareto-gated candidate is always gated by hand, one tag at a time, through
gate_check.pydirectly — documented explicitly in both SKILL.md and
algorithm.mdrather than left as a silent gap.Test plan
python -m pytest core/tests -q— 1884 passed, 1 skipped, 2 failed (both pre-existing andenvironment-dependent —
test_tau2_airline_eval_cost.py'scost_sourceassertions failidentically on unmodified
mainin this environment, confirmed by stashing this PR's changes andre-running; unrelated to this change).
python skills/algorithms/agent-optimize/scripts/check.py→"ok": true.python skills/_registry/lint_skills.py skills→ noagent-optimizeerrors (SKILL.md body keptunder the repo's 500-line/5000-token budget).