Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ All notable changes to AgentDescent are documented here. The format follows

### Fixed

- `agentdescent init selection --kind policy_slot` now finishes successfully and
explains how to provide the `data.problems` ref and its import allowlist.
It previously wrote the starter spec and then crashed looking for `data.path`,
which policy slots do not use. The clean-wheel smoke check now exercises this
installed command as well as the offline demo and MCP server.

- **Onboarding, published-version guidance and CI support coverage.** Lead with
the packaged offline demo on current `main`, distinguish it from the v0.5.0
source-example path, name NumPy as a runtime dependency, exercise Python 3.10
Expand Down
18 changes: 12 additions & 6 deletions agentdescent/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,17 @@ def cmd_init(a: argparse.Namespace) -> int:
with open(out, "w", encoding="utf-8") as fh:
json.dump(spec, fh, indent=2)
print(f"wrote {out}")
data = spec["data"]["path"]
if not os.path.exists(os.path.expanduser(data)):
print(f"next: create {data} -- one JSON object per line, "
'{"prompt": "...", "gold": "..."}')
print(" (`agentdescent demo` runs a complete example, offline, to copy from)")
if spec["kind"] == "policy_slot":
problems = spec["data"]["problems"]
print(f"next: provide {problems} -- a zero-argument builder "
"returning a mapping of inner problems")
print(" make its module importable and include its package in the spec's allow list")
else:
data = spec["data"]["path"]
if not os.path.exists(os.path.expanduser(data)):
print(f"next: create {data} -- one JSON object per line, "
'{"prompt": "...", "gold": "..."}')
print(" (`agentdescent demo` runs a complete example, offline, to copy from)")
print(f"then: agentdescent plan {out}")
return 0

Expand Down Expand Up @@ -785,7 +791,7 @@ def build_parser() -> argparse.ArgumentParser:
s.add_argument("path")
s.add_argument("--kind", choices=("text", "skill_dir", "agent_dir", "agent_code",
"plugin", "policy_slot"))
s.add_argument("--data", help="cases file to point the spec at")
s.add_argument("--data", help="cases file, or a module:attribute problems ref for policy_slot")
s.add_argument("--agent", default="claude_code", help="worker agent short name")
s.add_argument("--out", help="where to write the spec")
s.set_defaults(fn=cmd_init)
Expand Down
6 changes: 4 additions & 2 deletions docs/meta-evolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,10 @@ nature of the thing rather than an inconsistency:
artifact is a decision rule of the optimiser, and there is no file to point at.
* **`data` holds refs, not rows.** An inner problem is a callable
`(value, seed) -> MetaOutcome`; no row format can express one. `data.problems`
is a `module:attribute` ref to a mapping of them (or a zero-argument builder),
inside the spec's import allowlist.
is a `module:attribute` ref to a zero-argument builder returning a mapping of them,
inside the spec's import allowlist. Pass `--data mypkg.problems:build` to
`init` to choose that ref; make the module importable and include its package
in the generated spec's `allow` list before planning.

`score` takes `auc` (the default), `final_reward`, `rollouts_to`, or a ref to
any `MetaOutcome -> float`.
Expand Down
19 changes: 19 additions & 0 deletions scripts/smoke-installed-wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,25 @@ def main() -> None:
env.pop(name, None)
env["AGENTDESCENT_HOME"] = str(workdir / "home")

policy_init = subprocess.run(
["agentdescent", "init", "selection", "--kind", "policy_slot"],
cwd=workdir, env=env, text=True, capture_output=True, timeout=30,
)
assert policy_init.returncode == 0, (
f"installed policy-slot init failed ({policy_init.returncode})\n"
f"{policy_init.stdout}\n{policy_init.stderr}"
)
policy_spec = json.loads(
(workdir / ".agentdescent" / "selection.evolve.json").read_text(encoding="utf-8")
)
assert policy_spec["target"] == "selection", policy_spec
assert policy_spec["data"] == {"problems": "mypkg.problems:build", "seeds": [0]}, (
policy_spec
)
assert "mypkg.problems:build" in policy_init.stdout, policy_init.stdout
assert "allow list" in policy_init.stdout, policy_init.stdout
assert "JSON object per line" not in policy_init.stdout, policy_init.stdout

demo = subprocess.run(
["agentdescent", "demo"], cwd=workdir, env=env,
text=True, capture_output=True, timeout=120,
Expand Down
35 changes: 35 additions & 0 deletions tests/test_cli_init_policy_slot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Policy-slot onboarding uses problem refs rather than row-data paths."""

import json

import pytest

from agentdescent import cli
from agentdescent.evolvespec import load_spec


@pytest.mark.parametrize("problems", [None, "custom.problems:build"])
def test_init_policy_slot_writes_spec_and_explains_problem_ref(
tmp_path, monkeypatch, capsys, problems
):
monkeypatch.chdir(tmp_path)
args = ["init", "selection", "--kind", "policy_slot"]
if problems is not None:
args.extend(["--data", problems])
assert cli.main(args) == 0

spec_path = tmp_path / ".agentdescent" / "selection.evolve.json"
payload = json.loads(spec_path.read_text(encoding="utf-8"))
expected = problems or "mypkg.problems:build"
assert payload["kind"] == "policy_slot"
assert payload["target"] == "selection"
assert payload["data"] == {"problems": expected, "seeds": [0]}
assert payload["allow"] == ["mypkg"] # init does not expand import permissions
assert load_spec(str(spec_path)).target == "selection"

out = capsys.readouterr().out
assert expected in out
assert "zero-argument builder returning a mapping of inner problems" in out
assert "importable" in out and "allow list" in out
assert "then: agentdescent plan .agentdescent/selection.evolve.json" in out
assert "JSON object per line" not in out
Loading