sync: agent-ops from agentic-framework v1.1.189
This commit is contained in:
parent
270c724664
commit
be6d9d6bce
3 changed files with 113 additions and 27 deletions
|
|
@ -1 +1 @@
|
||||||
1.1.188
|
1.1.189
|
||||||
|
|
|
||||||
|
|
@ -426,11 +426,50 @@ def epic_cycle_script(workspace: Path) -> Path:
|
||||||
|
|
||||||
|
|
||||||
def dispatcher_script(workspace: Path) -> Path:
|
def dispatcher_script(workspace: Path) -> Path:
|
||||||
root = workspace / "agent-ops" / "skills" / "common" / "orchestrate-agent-task-loop"
|
skills_root = workspace / "agent-ops" / "skills"
|
||||||
path = root / "scripts" / "dispatch.py"
|
project_root = skills_root / "project" / "orchestrate-agent-task-loop"
|
||||||
if not path.is_file():
|
project_dispatcher = project_root / "scripts" / "dispatch.py"
|
||||||
raise PreparationError(f"dispatcher script not found: {path}")
|
if project_dispatcher.is_file():
|
||||||
return path
|
private_root = skills_root / "private" / "orchestrate-agent-task-loop"
|
||||||
|
private_dispatcher = private_root / "scripts" / "dispatch.py"
|
||||||
|
if (private_root / "SKILL.md").is_file() and private_dispatcher.is_file():
|
||||||
|
return private_dispatcher
|
||||||
|
return project_dispatcher
|
||||||
|
common_dispatcher = (
|
||||||
|
skills_root / "common" / "orchestrate-agent-task-loop" / "scripts" / "dispatch.py"
|
||||||
|
)
|
||||||
|
if not common_dispatcher.is_file():
|
||||||
|
raise PreparationError(f"dispatcher script not found: {common_dispatcher}")
|
||||||
|
return common_dispatcher
|
||||||
|
|
||||||
|
|
||||||
|
def dispatcher_command(
|
||||||
|
*,
|
||||||
|
workspace: Path,
|
||||||
|
dispatcher: Path,
|
||||||
|
task_group: str,
|
||||||
|
execution_catalog: str,
|
||||||
|
) -> list[str]:
|
||||||
|
command = [
|
||||||
|
sys.executable,
|
||||||
|
str(dispatcher),
|
||||||
|
"--workspace",
|
||||||
|
str(workspace),
|
||||||
|
"--task-group",
|
||||||
|
task_group,
|
||||||
|
]
|
||||||
|
common_dispatcher = (
|
||||||
|
workspace
|
||||||
|
/ "agent-ops"
|
||||||
|
/ "skills"
|
||||||
|
/ "common"
|
||||||
|
/ "orchestrate-agent-task-loop"
|
||||||
|
/ "scripts"
|
||||||
|
/ "dispatch.py"
|
||||||
|
)
|
||||||
|
if dispatcher.resolve() == common_dispatcher.resolve():
|
||||||
|
command.extend(["--execution-catalog", execution_catalog])
|
||||||
|
return command
|
||||||
|
|
||||||
|
|
||||||
def epic_cycle_command(
|
def epic_cycle_command(
|
||||||
|
|
@ -883,18 +922,15 @@ def coordinate_batch(
|
||||||
dispatcher = dispatcher_script(workspace)
|
dispatcher = dispatcher_script(workspace)
|
||||||
task_group = f"m-{milestone_slug}"
|
task_group = f"m-{milestone_slug}"
|
||||||
if not state.get("dispatcher_dry_run_done"):
|
if not state.get("dispatcher_dry_run_done"):
|
||||||
|
dry_run_command = dispatcher_command(
|
||||||
|
workspace=workspace,
|
||||||
|
dispatcher=dispatcher,
|
||||||
|
task_group=task_group,
|
||||||
|
execution_catalog=args.execution_catalog,
|
||||||
|
)
|
||||||
|
dry_run_command.append("--dry-run")
|
||||||
dry_run = run(
|
dry_run = run(
|
||||||
[
|
dry_run_command,
|
||||||
sys.executable,
|
|
||||||
str(dispatcher),
|
|
||||||
"--workspace",
|
|
||||||
str(workspace),
|
|
||||||
"--task-group",
|
|
||||||
task_group,
|
|
||||||
"--execution-catalog",
|
|
||||||
args.execution_catalog,
|
|
||||||
"--dry-run",
|
|
||||||
],
|
|
||||||
cwd=workspace,
|
cwd=workspace,
|
||||||
check=False,
|
check=False,
|
||||||
capture=False,
|
capture=False,
|
||||||
|
|
@ -907,16 +943,12 @@ def coordinate_batch(
|
||||||
atomic_json(state_path, state)
|
atomic_json(state_path, state)
|
||||||
emit("DISPATCHER_DRY_RUN_FINISHED", task_group=task_group)
|
emit("DISPATCHER_DRY_RUN_FINISHED", task_group=task_group)
|
||||||
|
|
||||||
command = [
|
command = dispatcher_command(
|
||||||
sys.executable,
|
workspace=workspace,
|
||||||
str(dispatcher),
|
dispatcher=dispatcher,
|
||||||
"--workspace",
|
task_group=task_group,
|
||||||
str(workspace),
|
execution_catalog=args.execution_catalog,
|
||||||
"--task-group",
|
)
|
||||||
task_group,
|
|
||||||
"--execution-catalog",
|
|
||||||
args.execution_catalog,
|
|
||||||
]
|
|
||||||
if resume_blocked_dispatcher and args.retry:
|
if resume_blocked_dispatcher and args.retry:
|
||||||
command.append("--retry-blocked")
|
command.append("--retry-blocked")
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,60 @@ class PrepareWorkspaceTest(unittest.TestCase):
|
||||||
Path("/tmp/example/sample-feature-worktree"),
|
Path("/tmp/example/sample-feature-worktree"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_dispatcher_prefers_project_override_and_private_pair(self) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as raw:
|
||||||
|
workspace = Path(raw)
|
||||||
|
common = (
|
||||||
|
workspace
|
||||||
|
/ "agent-ops/skills/common/orchestrate-agent-task-loop/scripts/dispatch.py"
|
||||||
|
)
|
||||||
|
project = (
|
||||||
|
workspace
|
||||||
|
/ "agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py"
|
||||||
|
)
|
||||||
|
private_root = (
|
||||||
|
workspace / "agent-ops/skills/private/orchestrate-agent-task-loop"
|
||||||
|
)
|
||||||
|
private = private_root / "scripts/dispatch.py"
|
||||||
|
for path in (common, project, private):
|
||||||
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
path.touch()
|
||||||
|
|
||||||
|
self.assertEqual(MODULE.dispatcher_script(workspace), project)
|
||||||
|
|
||||||
|
(private_root / "SKILL.md").touch()
|
||||||
|
self.assertEqual(MODULE.dispatcher_script(workspace), private)
|
||||||
|
|
||||||
|
project.unlink()
|
||||||
|
self.assertEqual(MODULE.dispatcher_script(workspace), common)
|
||||||
|
|
||||||
|
def test_dispatcher_command_injects_catalog_only_for_common_runtime(self) -> None:
|
||||||
|
workspace = Path("/repo")
|
||||||
|
common = (
|
||||||
|
workspace
|
||||||
|
/ "agent-ops/skills/common/orchestrate-agent-task-loop/scripts/dispatch.py"
|
||||||
|
)
|
||||||
|
project = (
|
||||||
|
workspace
|
||||||
|
/ "agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py"
|
||||||
|
)
|
||||||
|
|
||||||
|
common_command = MODULE.dispatcher_command(
|
||||||
|
workspace=workspace,
|
||||||
|
dispatcher=common,
|
||||||
|
task_group="m-sample",
|
||||||
|
execution_catalog="/runtime/catalog.json",
|
||||||
|
)
|
||||||
|
project_command = MODULE.dispatcher_command(
|
||||||
|
workspace=workspace,
|
||||||
|
dispatcher=project,
|
||||||
|
task_group="m-sample",
|
||||||
|
execution_catalog="/runtime/catalog.json",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIn("--execution-catalog", common_command)
|
||||||
|
self.assertNotIn("--execution-catalog", project_command)
|
||||||
|
|
||||||
def test_epic_document_range_is_one_based_and_inclusive(self) -> None:
|
def test_epic_document_range_is_one_based_and_inclusive(self) -> None:
|
||||||
epics = MODULE.parse_epics(
|
epics = MODULE.parse_epics(
|
||||||
"""## 기능
|
"""## 기능
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue