feat(curator): make skill consolidation opt-in (prune stays default-on) (#47840)

The curator now defaults to prune-only: the deterministic inactivity pass
(mark stale / archive long-unused skills) still runs whenever the curator is
enabled, but the opinionated LLM umbrella-building consolidation fork is OFF
by default.

- agent/curator.py: add DEFAULT_CONSOLIDATE=False + get_consolidate(); gate
  the forked aux-model review in run_curator_review behind it (new consolidate
  param, None=read config). When off, the LLM pass is skipped entirely (no
  aux-model cost); the run is still recorded and reported.
- config.py: add curator.consolidate (default false); v29->v30 migration seeds
  the key for existing installs without clobbering a user-set value.
- hermes_cli/curator.py: 'hermes curator run --consolidate' override; status
  shows consolidate state; prune-only notice on run.
- docs + tests.
This commit is contained in:
Teknium 2026-06-17 05:20:32 -07:00 committed by GitHub
parent e48803daec
commit 7bbffceb9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 217 additions and 11 deletions

View file

@ -520,7 +520,7 @@ def test_dry_run_injects_report_only_banner(curator_env, monkeypatch):
"tool_calls": [], "error": None}
monkeypatch.setattr(c, "_run_llm_review", _stub)
c.run_curator_review(synchronous=True, dry_run=True)
c.run_curator_review(synchronous=True, dry_run=True, consolidate=True)
assert "DRY-RUN" in captured["prompt"]
assert "DO NOT" in captured["prompt"]
@ -571,7 +571,11 @@ def test_run_review_synchronous_invokes_llm_stub(curator_env, monkeypatch):
monkeypatch.setattr(c, "_run_llm_review", _stub)
captured = []
c.run_curator_review(on_summary=lambda s: captured.append(s), synchronous=True)
c.run_curator_review(
on_summary=lambda s: captured.append(s),
synchronous=True,
consolidate=True,
)
assert len(calls) == 1
assert "skill CURATOR" in calls[0] or "CURATOR" in calls[0]
@ -595,6 +599,69 @@ def test_run_review_skips_llm_when_no_candidates(curator_env, monkeypatch):
assert any("skipped" in s for s in captured)
def test_consolidate_default_off(curator_env):
"""Consolidation (the LLM umbrella pass) is OFF by default — only the
deterministic inactivity prune runs unless the user opts in."""
c = curator_env["curator"]
assert c.get_consolidate() is False
def test_consolidate_enabled_via_config(curator_env, monkeypatch):
c = curator_env["curator"]
monkeypatch.setattr(c, "_load_config", lambda: {"consolidate": True})
assert c.get_consolidate() is True
def test_run_review_skips_llm_when_consolidate_off(curator_env, monkeypatch):
"""With consolidation off (the default), a run does the deterministic
prune but never spawns the LLM consolidation fork even with candidates
present. The run is still recorded and a 'consolidation off' summary is
surfaced."""
c = curator_env["curator"]
u = curator_env["usage"]
skills_dir = curator_env["home"] / "skills"
_write_skill(skills_dir, "a")
u.mark_agent_created("a")
calls = []
monkeypatch.setattr(
c, "_run_llm_review",
lambda prompt: (calls.append(prompt), "never-called")[1],
)
captured = []
c.run_curator_review(on_summary=lambda s: captured.append(s), synchronous=True)
assert calls == [] # LLM consolidation fork not invoked
assert any("consolidation off" in s for s in captured)
# The run is still recorded (deterministic prune happened).
state = c.load_state()
assert state["last_run_at"] is not None
assert state["run_count"] >= 1
def test_run_review_consolidate_override_runs_llm(curator_env, monkeypatch):
"""Passing consolidate=True overrides the config default (off) and drives
the LLM consolidation pass mirrors `hermes curator run --consolidate`."""
c = curator_env["curator"]
u = curator_env["usage"]
skills_dir = curator_env["home"] / "skills"
_write_skill(skills_dir, "a")
u.mark_agent_created("a")
calls = []
monkeypatch.setattr(
c, "_run_llm_review",
lambda prompt: (calls.append(prompt), {
"final": "", "summary": "s", "model": "", "provider": "",
"tool_calls": [], "error": None,
})[1],
)
c.run_curator_review(synchronous=True, consolidate=True)
assert len(calls) == 1
def test_maybe_run_curator_respects_disabled(curator_env, monkeypatch):
c = curator_env["curator"]
monkeypatch.setattr(c, "_load_config", lambda: {"enabled": False})