sync: agent-ops from agentic-framework v1.1.192

This commit is contained in:
toki 2026-08-08 13:26:03 +09:00
parent c573bad4f0
commit ee7b4cbb32
6 changed files with 38 additions and 19 deletions

View file

@ -1 +1 @@
1.1.191
1.1.192

View file

@ -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 UTC time, task artifact, plan loop, role, attempt, selected agent/model display, result, and locator.
- Append chronological `START` and `FINISH` rows with KST (`Asia/Seoul`) 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`.

View file

@ -17,9 +17,10 @@ import subprocess
import sys
import uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
from datetime import datetime
from pathlib import Path
from typing import Any
from zoneinfo import ZoneInfo
_OBSERVATION_MODULE_NAME = "agent_task_dispatcher_observation"
@ -152,7 +153,8 @@ 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
DISPATCHER_TIMEZONE_NAME = "Asia/Seoul"
KST = ZoneInfo(DISPATCHER_TIMEZONE_NAME)
DEFAULT_MAX_PARALLEL = 3
@ -245,11 +247,11 @@ class ExecutionDecisionError(RuntimeError):
def now_iso() -> str:
return datetime.now(timezone.utc).isoformat()
return datetime.now(KST).isoformat()
def work_log_now_utc() -> str:
return datetime.now(UTC).strftime("%y-%m-%d %H:%M:%SZ")
def work_log_now_kst() -> str:
return datetime.now(KST).strftime("%y-%m-%d %H:%M:%S KST")
def sha256_file(path: Path | None) -> str:
@ -452,7 +454,7 @@ def append_work_log_event(
return str(value).replace("|", r"\|").replace("\n", " ")
stream.write(
f"| {sequence} | {work_log_now_utc()} | {cell(event)} | "
f"| {sequence} | {work_log_now_kst()} | {cell(event)} | "
f"{cell(task_name)} | "
f"{loop} | {cell(role)} | {attempt} | {cell(model)} | {cell(result)} | "
f"{cell(locator.resolve())} |\n"
@ -1773,7 +1775,7 @@ def select_execution_decision(
transition = "resume" if prior_decision is not None else "initial"
return selector.select_execution_target(
_decision_file(task, stage), stage=stage,
evaluated_at=evaluated_at or datetime.now(UTC),
evaluated_at=evaluated_at or datetime.now(KST),
catalog_path=EXECUTION_CATALOG_PATH,
transition=transition,
prior_decision=prior_decision,
@ -1842,7 +1844,7 @@ def synthesized_official_review_decision(
task: Task, *, evaluated_at: datetime | None = None
) -> dict[str, Any]:
lane, grade, work_unit_id = official_review_source_identity(task)
evaluated = evaluated_at or datetime.now(UTC)
evaluated = evaluated_at or datetime.now(KST)
if evaluated.tzinfo is None or evaluated.utcoffset() is None:
raise ExecutionDecisionError(
"official review evaluated_at이 timezone-aware가 아니다"
@ -2836,7 +2838,7 @@ def external_active_is_live(
for path in root.glob("**/*.jsonl")
]
native = max(sessions, key=lambda path: path.stat().st_mtime_ns) if sessions else None
now = datetime.now(timezone.utc).timestamp()
now = datetime.now(KST).timestamp()
runtime = locator.get("runtime")
monitor_native_session = bool(
isinstance(runtime, dict) and runtime.get("native_session_monitor")
@ -3098,7 +3100,7 @@ async def invoke(
resume_locator: Path | None = None,
) -> tuple[int, str | None, Path]:
attempt, identity = next_execution_identity(store, task, role)
attempt_dir = store.runs / f"{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')}__{identity}"
attempt_dir = store.runs / f"{datetime.now(KST).strftime('%Y%m%dT%H%M%S%z')}__{identity}"
attempt_dir.mkdir(parents=True, exist_ok=False)
locator_path = attempt_dir / "locator.json"
stream_path = attempt_dir / "stream.log"
@ -5845,7 +5847,7 @@ async def dispatch_with_store(
):
ready.append((task, stage))
admission_time = datetime.now(UTC)
admission_time = datetime.now(KST)
if args.dry_run:
candidates, deferred, _ = select_dispatch_candidates(
store,

View file

@ -13,8 +13,9 @@ import json
import os
import re
import sys
from datetime import datetime, timezone
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo
SCHEMA_VERSION = "2.0"
@ -24,7 +25,8 @@ DEFAULT_CATALOG_PATH = (
/ "assets"
/ "default-execution-catalog.json"
)
TIMEZONE_NAME = "UTC"
TIMEZONE_NAME = "Asia/Seoul"
KST = ZoneInfo(TIMEZONE_NAME)
_FILENAME_RE = re.compile(r"^(PLAN|CODE_REVIEW)-(local|cloud)-G(\d{2})\.md$")
_MILESTONE_TASK_ID_PATTERN = r"[A-Za-z0-9]+(?:[-_+=][A-Za-z0-9]+){0,3}"
_MILESTONE_TASK_ID_RE = re.compile(rf"\A{_MILESTONE_TASK_ID_PATTERN}\Z")
@ -317,7 +319,7 @@ def _base_decision(
"rule_id": route.rule_id,
"policy_priority": route.policy_priority,
"reason_codes": list(route.reason_codes),
"evaluated_at": evaluated_at.astimezone(timezone.utc).isoformat(),
"evaluated_at": evaluated_at.astimezone(KST).isoformat(),
"timezone": TIMEZONE_NAME,
"time_window": route.time_window,
"pinned": pinned,
@ -448,7 +450,7 @@ def select_execution_target(
stage=inferred_stage,
lane=lane,
grade=grade,
evaluated_at=evaluated_at or datetime.now(timezone.utc),
evaluated_at=evaluated_at or datetime.now(KST),
catalog_path=catalog_path,
transition=transition,
prior_decision=prior_decision,

View file

@ -7,7 +7,7 @@ import stat
import subprocess
import sys
import unittest
from datetime import datetime, timezone
from datetime import datetime, timedelta, timezone
from pathlib import Path
from tempfile import TemporaryDirectory
from unittest import mock
@ -98,6 +98,16 @@ class RuntimeCatalogDispatcherTests(unittest.TestCase):
def tearDown(self):
dispatch.EXECUTION_CATALOG_PATH = self.previous_catalog
def test_dispatcher_default_timestamps_use_kst(self):
timestamp = datetime.fromisoformat(dispatch.now_iso())
self.assertEqual(dispatch.DISPATCHER_TIMEZONE_NAME, "Asia/Seoul")
self.assertEqual(timestamp.utcoffset(), timedelta(hours=9))
self.assertRegex(
dispatch.work_log_now_kst(),
r"^\d{2}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} KST$",
)
def test_agent_spec_is_loaded_from_persisted_catalog_evidence(self):
with TemporaryDirectory() as tmp:
root = Path(tmp)

View file

@ -4,7 +4,7 @@ import os
import subprocess
import sys
import unittest
from datetime import datetime, timezone
from datetime import datetime, timedelta, timezone
from pathlib import Path
from tempfile import TemporaryDirectory
from unittest import mock
@ -189,6 +189,11 @@ class SelectorTests(unittest.TestCase):
self.assertEqual(result["selected"]["model"], "model-one")
self.assertEqual(result["catalog"]["source"], str(catalog.resolve()))
self.assertEqual([item["target_id"] for item in result["candidates"]], ["first", "second"])
self.assertEqual(result["decision"]["timezone"], "Asia/Seoul")
self.assertEqual(
datetime.fromisoformat(result["decision"]["evaluated_at"]).utcoffset(),
timedelta(hours=9),
)
self.assertNotIn("quota", result)
self.assertTrue(all("quota_status" not in item for item in result["candidates"]))