fix(agent-ops): 자동 실행기의 공통 역의존을 제거한다

This commit is contained in:
toki 2026-07-28 10:34:42 +09:00
parent 0565d2be66
commit 5924629a40
2 changed files with 13 additions and 17 deletions

View file

@ -112,7 +112,7 @@ When recovering a KST-night `local-G07``local-G08` Laguna locator or a termin
- Keep exactly one `agent-task/{task_group}/WORK_LOG.md` per task group. Do not create one in a split-subtask directory.
- Allow only the dispatcher to modify this file. Worker/self-check/review models need not read or update it, and success must not depend on its prose.
- Append chronological `START`/`FINISH` rows with time, task, role, attempt, model, result, and locator. Record time in KST (`UTC+09:00`) as `YY-MM-DD HH:MM:SS`, for example `26-07-26 07:40:15`. Use this single timeline to inspect parallel execution order.
- When code-review moves a PASS task, do not move, copy, or delete the task-group `WORK_LOG.md`. For a single task, create the archive destination and move every artifact except `WORK_LOG.md`, preserving the path where the dispatcher writes the final `FINISH`.
- Do not require the common code-review skill to preserve `WORK_LOG.md`. For split work the group log normally remains in the parent because review moves only the selected subtask. For a single task review may move the log with the task archive; after review exits, resolve exactly one source from the active group path or verified completed archive and normalize it to `work_log_N.log`.
- After every observed task in a task group has a verified complete archive and no active/running task remains, append the final `FINISH` and move the generated `WORK_LOG.md` under the final completed archive's group root as `work_log_N.log`. If an archive exists after restart but the last `START` lacks `FINISH`, do not terminate or archive while any PID/start token, per-attempt process marker, or pidless stream/native evidence remains live. Track it until execution evidence has ended and the complete archive is verified, then append `FINISH` with `reconciled:verified-complete-archive` and move the log. Use `agent-task/archive/YYYY/MM/{task_group}/` for split tasks and the actual suffix-bearing archive destination for a single task. Set `N` to one more than the maximum suffix for the same task group across all months, starting at `0`.
- If `WORK_LOG.md` archiving fails or multiple active/archive sources exist, drain other independent work and return non-terminal exit `3` for retry. Return successful exit `0` only after a completed group that generated a log has no active `WORK_LOG.md` and its `work_log_N.log` is verified. Keep an incomplete group's `WORK_LOG.md` active for blocker or exit `3` recovery.
- Split each attempt locator into `stream.log` for model stdout/stderr and `heartbeat.log` for dispatcher state. Determine health only from the newest progress in `stream.log` and native session events; never use heartbeat mtime as progress evidence. Do not copy either log into `WORK_LOG.md`.
@ -192,7 +192,7 @@ When recovering a KST-night `local-G07``local-G08` Laguna locator or a termin
- Send an already completed review stub with no dispatcher execution record to review. Never send dispatcher-recorded Pi worker success to review before self-check completes.
- Start official review and worker/self-check together when they belong to different dependency-ready tasks. Do not wait for another task's checkout or write-set.
- Let the dispatcher record every worker/self-check/review attempt start and finish in the task-group `WORK_LOG.md`.
- Archive `WORK_LOG.md` as `work_log_N.log` only after the final task review process exits, the dispatcher appends `FINISH`, and a complete scan finds no active/running task in that group. The code-review process must not move it first.
- Archive `WORK_LOG.md` as `work_log_N.log` only after the final task review process exits, the dispatcher appends `FINISH`, and a complete scan finds no active/running task in that group. Accept the log at either the active group path or the verified completed single-task archive; do not impose either location contract on common plan/code-review.
3. **Escalate and recover context.**
- Escalate `agy -> Claude -> Codex` or `Claude -> Codex` only on terminal provider error events or stderr evidence of context/output limits, provider quota/rate limits, unavailable models, or confirmed provider transport errors. For AGY, accept top-level `error`, `fatal`, `request.failed`, or `turn.failed` events; failed/rejected status with a top-level error/code; stderr; or strong `RESOURCE_EXHAUSTED`, HTTP 429, quota, or rate-limit evidence in `agy-cli.log`. For Claude, classify a `rate_limit_event` with `rate_limit_info.status=rejected`, an error `result` with `api_error_status=429` or `error=rate_limit`, or a `You've hit your session limit · resets ...` terminal diagnostic as `provider-quota`. Never escalate from an assistant message, source text, tool/test output, or a plain quota-configuration string in an AGY log.

View file

@ -3353,7 +3353,8 @@ class ReviewControlTest(unittest.TestCase):
self.assertEqual(priority.count("Allow `final`"), 2)
self.assertIn("unless at least one of the two titled permissions", priority)
self.assertNotIn("unless exactly one of the two titled permissions", priority)
self.assertNotIn("`final`", lower_contract)
self.assertNotIn("### `final` Permission", lower_contract)
self.assertNotIn("Allow `final`", lower_contract)
self.assertIn(
"### Every Other User-Visible Message Must Use `commentary`",
priority,
@ -3477,7 +3478,7 @@ class ReviewControlTest(unittest.TestCase):
skill,
)
def test_work_log_archive_ownership_is_consistent_across_skills(self):
def test_work_log_archive_ownership_stays_project_local(self):
skills_root = Path(__file__).parents[3]
dispatcher_skill = (
Path(__file__).parents[1] / "SKILL.md"
@ -3503,19 +3504,14 @@ class ReviewControlTest(unittest.TestCase):
dispatcher_skill,
)
self.assertIn(
"task-group `agent-task/{task_group}/WORK_LOG.md`는 "
"이동·복사·삭제·이름 변경하지 않는다",
review_skill,
)
self.assertIn(
"review process 종료 뒤 dispatcher가 마지막 `FINISH`를 append",
review_skill,
)
self.assertIn(
"dispatcher가 마지막 `FINISH` 기록과 group 완료 확인 뒤 "
"`work_log_N.log`로 archive한다",
plan_skill,
"Do not require the common code-review skill to preserve "
"`WORK_LOG.md`",
dispatcher_skill,
)
self.assertNotIn("WORK_LOG", review_skill)
self.assertNotIn("work-log", review_skill)
self.assertNotIn("WORK_LOG", plan_skill)
self.assertNotIn("work-log", plan_skill)
class ProcessTerminationTest(unittest.IsolatedAsyncioTestCase):
@ -5137,7 +5133,7 @@ class WorkLogArchiveTest(unittest.TestCase):
[],
)
def test_normalizes_legacy_work_log_already_moved_by_review(self):
def test_normalizes_single_task_work_log_moved_by_generic_review(self):
with tempfile.TemporaryDirectory() as temporary:
workspace = Path(temporary)
archive = self.complete_archive(workspace, "single")