Skip to content

hill-climb: multi-objective (Pareto/epsilon-constraint) support + intake objectives config - #686

Merged
OsherElhadad merged 1 commit into
mainfrom
hill-climb-moo-684
Oct 8, 2026
Merged

OsherElhadad merged 1 commit into
mainfrom
hill-climb-moo-684

Conversation

@OsherElhadad

Copy link
Copy Markdown
Collaborator

Summary

Part of #684 (item 8 only). Extends hill-climb's per-iteration gate call to support
multi-objective acceptance, and adds objectives as an intake-interview config
parameter — fully additive, zero behavior change for existing single-objective
configs.

  • core/cap_evolve/harness.py: run_step's gate call now builds metrics_candidate/
    metrics_current/their stderrs (new _pareto_metrics_kwargs() helper, per-task
    cost_usd) and forwards them to gate.decide() only when gate_kwargs["mode"] == "pareto". Every other mode's call is byte-identical to before.
  • skills/algorithms/hill-climb/scripts/run.py: new --objectives flag (JSON, mirrors
    gate_check.py's own flag) forwarded into gate_kwargs["objectives"].
  • core/cap_evolve/cli.py: forwards capevolve.yaml's objectives: block to
    hill-climb's --objectives only when algorithm_skill == hill-climb and
    objectives is set — absent key ⇒ no flag added, no behavior change.
  • skills/phases/intake/inputs/INPUTS.md: new RECOMMENDED objectives input —
    default is unset (single-objective, today's behavior); if the user wants
    multi-objective and doesn't name objectives, default to the standard
    reward-maximize + cost-minimize pair (gate._DEFAULT_PARETO_OBJECTIVES), with the
    per-objective significance/honesty-cost note (ParetoObjectiveError).
  • templates/project/capevolve.yaml: comment update — gate_mode: pareto is no
    longer agent-optimize-only.

Known integration point (read before merging)

Issue #684's item 1/2 (a persistent ParetoArchive module at
core/cap_evolve/pareto_archive.py, plus epsilon_constraint as a gate mode) has
not landed on main and has no open PR as of this writing — checked
gh pr list and the merged-PR history (#667: "gate: multi-objective Pareto
acceptance mode", on main). This PR wires hill-climb against gate.py's
existing single-pairwise mode="pareto" (from #667) instead of duplicating that
work. _pareto_metrics_kwargs() + the gate_kwargs plumbing are structured so that
swapping in the persistent archive later is a follow-up rebase (reuse
ParetoArchive/the real epsilon_constraint mode once it lands), not a redesign.
epsilon_constraint itself does not exist as a gate mode anywhere yet, so it is not
wired here — a config declaring gate_mode: epsilon_constraint today gets gate.py's
own unknown gate mode ValueError, same as before this PR.

Out of scope (owned by sibling #684 workstreams, untouched here): round.py's own
agent-optimize gate cascade/screening, merge_search.py, plan_round.py's ownership
logic, the dashboard, cost-reduction guidance.

Test plan

  • core/tests/test_hill_climb_pareto_gate.py — gate dispatches to pareto mode
    (accept on dominance, reject when dominated) when declared; regression test
    confirms the pre-existing single-metric call path is unchanged when no
    objectives/pareto mode is set.
  • core/tests/test_hill_climb_objectives_cli.py — end-to-end
    capevolve.yaml → cli.py → hill-climb/run.py → the gate, driven through a
    real toy_calc run (cli.main(["run", ...])), confirming the whole chain
    actually reaches pareto mode (and that an absent objectives: key never adds
    the flag).
  • core/tests/test_intake_objectives_default.py — pins the documented default
    objectives (INPUTS.md + the template) against gate._DEFAULT_PARETO_OBJECTIVES.
  • python -m pytest core/tests -q: 1921 passed, 1 skipped, 2 failed — the two
    known pre-existing test_tau2_airline_eval_cost.py failures (unrelated to this
    change: a cost_source label mismatch, nothing to do with the gate).

Closes partially #684 (item 8 only).

🤖 Generated with Claude Code

@skillberry-bot skillberry-bot added algorithm Optimization algorithms: GEPA / SkillOpt / hill-climb enhancement New feature or request documentation Improvements or additions to documentation priority-p1 High impact labels Oct 8, 2026
@skillberry-bot

Copy link
Copy Markdown
Contributor

🏷️ Automatic Labeling

I've analyzed this pull request and added the following labels:

  • algorithm - enhancement - documentation - algorithm - enhancement - documentation - priority-p1

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.

Comment on lines +71 to +74
"import sys, pathlib\n"
"d = pathlib.Path(sys.argv[1])\n"
"(d / 'cfg.txt').write_text('A B')\n"
"(d / 'cost.txt').write_text('1.0')\n",
Comment on lines +93 to +95
"import sys, pathlib\n"
"d = pathlib.Path(sys.argv[1])\n"
"(d / 'cost.txt').write_text('4.0')\n",
Comment on lines +114 to +117
"import sys, pathlib\n"
"d = pathlib.Path(sys.argv[1])\n"
"(d / 'cfg.txt').write_text('A B')\n"
"(d / 'cost.txt').write_text('999.0')\n", # cost would dominate-reject in pareto mode
… config

Extends hill-climb's per-iteration gate call (harness.run_step) to dispatch to
cap_evolve.gate's existing mode="pareto" when the project declares objectives/
gate_mode: pareto, mirroring agent-optimize's gate_check.py --mode pareto
semantics. hill-climb's own per-iteration data can resolve one secondary
objective today -- per-task cost_usd -- via a new _pareto_metrics_kwargs()
helper; any other declared objective name is left for gate.py's own
ParetoObjectiveError to refuse rather than fabricate a value.

Wiring: hill-climb/scripts/run.py gets --objectives (JSON, mirrors
gate_check.py's flag); cli.py forwards capevolve.yaml's objectives: block to
it only when algorithm_skill == hill-climb and objectives is set, so an
existing spec with no objectives key builds a byte-identical argv and gate
call.

Also adds objectives as a RECOMMENDED intake-interview input (INPUTS.md),
defaulting to the standard reward-maximize + cost-minimize pair
(gate._DEFAULT_PARETO_OBJECTIVES) when the user wants multi-objective but
doesn't name objectives, and updates templates/project/capevolve.yaml's
comments to say hill-climb supports gate_mode: pareto natively now.

Known integration point: issue #684's sibling item (1/2, a persistent
ParetoArchive module at core/cap_evolve/pareto_archive.py for round.py/
agent-optimize) has not landed on main and has no open PR as of this
writing -- checked `gh pr list` and the merged #667 history. This wires
hill-climb against gate.py's existing single-pairwise mode="pareto" (from
follow-up rebase of _pareto_metrics_kwargs/gate_kwargs plumbing, not a
redesign.

round.py's own agent-optimize gate cascade, screening, merge_search.py,
plan_round.py ownership, the dashboard and cost-reduction guidance are out
of scope for this PR (owned by sibling #684 workstreams).

Tests: core/tests/test_hill_climb_pareto_gate.py (gate dispatch + no-objectives
regression), core/tests/test_hill_climb_objectives_cli.py (end-to-end
capevolve.yaml -> cli.py -> hill-climb run.py -> gate, via a real toy_calc
run), core/tests/test_intake_objectives_default.py (default-objectives
parity between gate.py, INPUTS.md and the template).

python -m pytest core/tests -q: 1921 passed, 1 skipped, 2 failed (the two
known pre-existing test_tau2_airline_eval_cost.py failures, unrelated to
this change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Osher Elhadad <Osher.Elhadad@ibm.com>
@OsherElhadad
OsherElhadad merged commit e99752d into main Oct 8, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

algorithm Optimization algorithms: GEPA / SkillOpt / hill-climb documentation Improvements or additions to documentation enhancement New feature or request priority-p1 High impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants