diff --git a/agentdescent/cli.py b/agentdescent/cli.py index bdd1f24c..109814d0 100644 --- a/agentdescent/cli.py +++ b/agentdescent/cli.py @@ -1671,6 +1671,21 @@ def _key_sent_to_openai(block: Any, key_env: str, url_env: str) -> Optional[str] f"`default_base_url` (the endpoint URL) or export {url_env}") +def _propose_fail_rounds_text(spec: EvolveSpec) -> str: + """How many rounds a run with a dead reflector lasts, in words. + + The evolve loop stops after ``PROPOSE_FAIL_ROUNDS`` rounds in which every + proposal raised, but a spec asking for fewer rounds ends sooner: the quick + trial ``--rounds 1`` was told its run "fails after two rounds". + """ + from .evolution import PROPOSE_FAIL_ROUNDS + n = PROPOSE_FAIL_ROUNDS + rounds = (spec.evolve or {}).get("rounds") if isinstance(spec.evolve, dict) else None + if isinstance(rounds, int) and not isinstance(rounds, bool) and 0 < rounds < n: + n = rounds + return {1: "one round", 2: "two rounds"}.get(n, f"{n} rounds") + + def _unusable_refs(spec: EvolveSpec) -> List[str]: """Warnings for agents this machine cannot actually run. @@ -1735,9 +1750,9 @@ def _unusable_refs(spec: EvolveSpec) -> List[str]: "for its endpoint" if present else "") out.append(f"{field}: `openai_compatible` needs {key_env} (and " f"{url_env} for a non-OpenAI endpoint){route}; without it " - + ("every proposal raises and the run fails after two rounds " - "with nothing learned" if field == "reflect" - else "every call raises")) + + (f"every proposal raises and the run fails after " + f"{_propose_fail_rounds_text(spec)} with nothing learned" + if field == "reflect" else "every call raises")) else: leak = _key_sent_to_openai(block, key_env, url_env) if leak: diff --git a/changelog.d/872-dead-reflector-rounds.md b/changelog.d/872-dead-reflector-rounds.md new file mode 100644 index 00000000..79122ac2 --- /dev/null +++ b/changelog.d/872-dead-reflector-rounds.md @@ -0,0 +1,4 @@ +Fixed +The missing-reflector-key warning from `plan` / `algorithms run` now names how +long the run really lasts: "fails after one round" on a `--rounds 1` quick +trial instead of always "two rounds" (#872). diff --git a/tests/test_key_preflight.py b/tests/test_key_preflight.py index 3cff5371..9fcf0354 100644 --- a/tests/test_key_preflight.py +++ b/tests/test_key_preflight.py @@ -155,3 +155,14 @@ def test_claude_without_api_key_env_is_unchanged_and_unset_key_is_clear(monkeypa assert seen["init"] == {"max_retries": 0} with pytest.raises(RuntimeError, match="set X_KEY"): claude("m", api_key_env="X_KEY", retries=1)("p") + + +@pytest.mark.parametrize("rounds,words", [(1, "one round"), (2, "two rounds"), + (8, "two rounds")]) +def test_dead_reflector_warning_names_the_rounds_the_spec_will_run(tmp_path, rounds, words): + # #872: a --rounds 1 quick trial was told it "fails after two rounds". + spec = _spec(tmp_path, reflect={"ref": "openai_compatible", "model": "m", + "api_key_env": "X_UNSET_KEY"}) + spec.evolve = {"rounds": rounds} + [warning] = _unusable_refs(spec) + assert warning.endswith(f"the run fails after {words} with nothing learned"), warning