diff --git a/agentdescent/cli.py b/agentdescent/cli.py index 2aa24f42..378cea62 100644 --- a/agentdescent/cli.py +++ b/agentdescent/cli.py @@ -309,10 +309,12 @@ def algorithm_override(name: Optional[str], params: Optional[Mapping[str, Any]], if params: params = algorithms._canonical_param_map(alg, params, where="algorithm.params") resolved = algorithms.resolve_params(alg, params) # types/unknowns/choices + # Write the registered name, not the alias the user typed (`GEPA`, + # `archive-selection`), so the spec reads the same as `algorithms list`. if not params: - return name + return alg.name # Store user-supplied keys only, with coerced (folded) values (#888). - return {"name": name, "params": {k: resolved[k] for k in params}} + return {"name": alg.name, "params": {k: resolved[k] for k in params}} def missing_target_message(path: str) -> str: diff --git a/changelog.d/894-init-algorithm-canonical-name.md b/changelog.d/894-init-algorithm-canonical-name.md new file mode 100644 index 00000000..cda846cd --- /dev/null +++ b/changelog.d/894-init-algorithm-canonical-name.md @@ -0,0 +1,5 @@ +Fixed +`init --algorithm` writes the registered algorithm name into the spec, so +`--algorithm GEPA` or `archive-selection` is saved as `gepa` / +`archive_selection` (matching `algorithms list` and `algorithms run`) instead +of the alias as typed (#894). diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py index 542c2806..cc5e85e0 100644 --- a/tests/test_algorithms.py +++ b/tests/test_algorithms.py @@ -474,6 +474,27 @@ def test_init_writes_the_algorithm_and_only_the_params_given(tmp_path, capsys): assert "wrote" in capsys.readouterr().out +def test_init_writes_the_registered_name_for_an_alias(tmp_path, capsys): + # #894: `init --algorithm GEPA` / `archive-selection` validated but wrote + # the alias verbatim into the spec. + prompt = tmp_path / "prompt.txt" + prompt.write_text("be terse\n") + assert cli.main(["init", str(prompt), "--out", str(tmp_path / "a.json"), + "--algorithm", "GEPA", "--model", "m"]) == 0 + assert json.loads((tmp_path / "a.json").read_text())["algorithm"] == "gepa" + assert cli.main(["init", str(prompt), "--out", str(tmp_path / "b.json"), + "--algorithm", "RRSI", "--param", 'protected=["s"]', + "--model", "m"]) == 0 + assert json.loads((tmp_path / "b.json").read_text())["algorithm"] == { + "name": "rrsi", "params": {"protected": ["s"]}} + skill = tmp_path / "skill" + skill.mkdir() + (skill / "SKILL.md").write_text("---\nname: s\ndescription: d\n---\nbody\n") + assert cli.main(["init", str(skill), "--out", str(tmp_path / "c.json"), + "--algorithm", "archive-selection", "--agent", "opencode"]) == 0 + assert json.loads((tmp_path / "c.json").read_text())["algorithm"] == "archive_selection" + + def test_init_refuses_an_unknown_algorithm_or_wrong_kind(tmp_path, capsys): prompt = tmp_path / "prompt.txt" prompt.write_text("be terse\n")