sync: agent-ops from agentic-framework v1.1.189
This commit is contained in:
parent
f6e65b31c0
commit
9fcff70704
5 changed files with 45 additions and 23 deletions
|
|
@ -112,7 +112,7 @@ Accept self-check completion only when `## Implementation Checklist` or its supp
|
|||
## Work log
|
||||
|
||||
- Keep one dispatcher-owned `WORK_LOG.md` per task group.
|
||||
- Append chronological `START` and `FINISH` rows with KST time, task artifact, plan loop, role, attempt, selected agent/model display, result, and locator.
|
||||
- Append chronological `START` and `FINISH` rows with UTC time, task artifact, plan loop, role, attempt, selected agent/model display, result, and locator.
|
||||
- Archive the group log as the next `work_log_N.log` only after every observed task in the group is verified complete and idle.
|
||||
- Work-log write or archive failure is a retryable control-plane failure and prevents exit `0`.
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import subprocess
|
|||
import sys
|
||||
import uuid
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
|
@ -153,7 +153,6 @@ DISPATCHER_CHILD_BOUNDARY_PROMPT = (
|
|||
REPOSITORY_LANGUAGE_PROMPT = "Follow the repository's language and output rules."
|
||||
SELF_CHECK_PROMPT_PREFIX = REPOSITORY_LANGUAGE_PROMPT
|
||||
UTC = timezone.utc
|
||||
KST = timezone(timedelta(hours=9))
|
||||
DEFAULT_MAX_PARALLEL = 3
|
||||
|
||||
|
||||
|
|
@ -249,8 +248,8 @@ def now_iso() -> str:
|
|||
return datetime.now(timezone.utc).isoformat()
|
||||
|
||||
|
||||
def work_log_now_kst() -> str:
|
||||
return datetime.now(KST).strftime("%y-%m-%d %H:%M:%S")
|
||||
def work_log_now_utc() -> str:
|
||||
return datetime.now(UTC).strftime("%y-%m-%d %H:%M:%SZ")
|
||||
|
||||
|
||||
def sha256_file(path: Path | None) -> str:
|
||||
|
|
@ -453,7 +452,7 @@ def append_work_log_event(
|
|||
return str(value).replace("|", r"\|").replace("\n", " ")
|
||||
|
||||
stream.write(
|
||||
f"| {sequence} | {work_log_now_kst()} | {cell(event)} | "
|
||||
f"| {sequence} | {work_log_now_utc()} | {cell(event)} | "
|
||||
f"{cell(task_name)} | "
|
||||
f"{loop} | {cell(role)} | {attempt} | {cell(model)} | {cell(result)} | "
|
||||
f"{cell(locator.resolve())} |\n"
|
||||
|
|
|
|||
|
|
@ -261,13 +261,6 @@ class RuntimeCatalogDispatcherTests(unittest.TestCase):
|
|||
|
||||
|
||||
class GenericDispatcherContractTests(unittest.TestCase):
|
||||
def test_work_log_timestamp_uses_compact_kst_format(self):
|
||||
fixed_kst = datetime(2026, 7, 26, 7, 40, 15, tzinfo=dispatch.KST)
|
||||
with mock.patch.object(dispatch, "datetime") as datetime_mock:
|
||||
datetime_mock.now.return_value = fixed_kst
|
||||
self.assertEqual(dispatch.work_log_now_kst(), "26-07-26 07:40:15")
|
||||
datetime_mock.now.assert_called_once_with(dispatch.KST)
|
||||
|
||||
def test_parallel_limit_contract(self):
|
||||
self.assertEqual(dispatch.validated_max_parallel(0), 0)
|
||||
self.assertEqual(dispatch.validated_max_parallel(3), 3)
|
||||
|
|
|
|||
|
|
@ -426,14 +426,17 @@ def epic_cycle_script(workspace: Path) -> Path:
|
|||
|
||||
|
||||
def dispatcher_script(workspace: Path) -> Path:
|
||||
skills_root = workspace / "agent-ops" / "skills"
|
||||
project_root = skills_root / "project" / "orchestrate-agent-task-loop"
|
||||
project_dispatcher = project_root / "scripts" / "dispatch.py"
|
||||
if project_dispatcher.is_file():
|
||||
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 = (
|
||||
workspace
|
||||
/ "agent-ops"
|
||||
/ "skills"
|
||||
/ "common"
|
||||
/ "orchestrate-agent-task-loop"
|
||||
/ "scripts"
|
||||
/ "dispatch.py"
|
||||
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}")
|
||||
|
|
@ -455,7 +458,17 @@ def dispatcher_command(
|
|||
"--task-group",
|
||||
task_group,
|
||||
]
|
||||
command.extend(["--execution-catalog", execution_catalog])
|
||||
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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class PrepareWorkspaceTest(unittest.TestCase):
|
|||
Path("/tmp/example/sample-feature-worktree"),
|
||||
)
|
||||
|
||||
def test_dispatcher_uses_common_runtime_only(self) -> None:
|
||||
def test_dispatcher_prefers_project_override_and_private_pair(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as raw:
|
||||
workspace = Path(raw)
|
||||
common = (
|
||||
|
|
@ -70,15 +70,24 @@ class PrepareWorkspaceTest(unittest.TestCase):
|
|||
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_always_injects_catalog(self) -> None:
|
||||
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,
|
||||
|
|
@ -86,7 +95,15 @@ class PrepareWorkspaceTest(unittest.TestCase):
|
|||
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:
|
||||
epics = MODULE.parse_epics(
|
||||
|
|
|
|||
Loading…
Reference in a new issue