fix(agent-ops): 구현 체크리스트 완료 판정을 완화한다
This commit is contained in:
parent
8d782e8fc3
commit
c6ea3bdc87
3 changed files with 19 additions and 43 deletions
|
|
@ -99,7 +99,7 @@ Keep control prompts in English, insert absolute paths only, and do not expand t
|
|||
|
||||
Never ask a worker, self-check, or review model to create, edit, or summarize `WORK_LOG.md`.
|
||||
|
||||
Do not treat Pi self-check exit code `0` as success by itself. Set `selfcheck_done=true` only when the implementation-completion table, `구현 체크리스트`, `계획 대비 변경 사항`, `주요 설계 결정`, `검증 결과`, and final CODE_REVIEW synchronization item in `CODE_REVIEW_PATH` all contain real content. If any field is empty, retry with the same prompt. After 10 consecutive incomplete results, block that task and continue draining independent work.
|
||||
Do not treat Pi self-check exit code `0` as success by itself. Set `selfcheck_done=true` only when `## 구현 체크리스트` in `CODE_REVIEW_PATH` contains at least one Markdown list checkbox and every `[...]` checkbox value has at least one non-whitespace character. Accept any non-empty value, including `x`, `v`, and `✅`. Do not inspect `## 구현 항목별 완료 여부`, `계획 대비 변경 사항`, `주요 설계 결정`, `검증 결과`, or final CODE_REVIEW synchronization text. If the checklist condition fails, retry with the same prompt. After 10 consecutive incomplete results, block that task and continue draining independent work.
|
||||
|
||||
For Pi recovery attempts, pass only `Read {PLAN_PATH}. Continue.` without a locator explanation. For other CLI escalation attempts, pass `Continue from {LOCATOR_PATH}. Check the saved context and current workspace. Final in Korean.` Preserve the collaboration prohibition and next-state-materialization sentence in official-review escalation and recovery prompts. Do not ask the model to write a separate handoff summary.
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,9 @@ VERDICT_BLOCK_RE = re.compile(
|
|||
PLAN_IDENTITY_RE = re.compile(
|
||||
r"<!--\s+task=(?P<task>\S+)\s+plan=(?P<plan>\d+)\s+tag=(?P<tag>\S+)\s+-->"
|
||||
)
|
||||
FINAL_CHECK_RE = re.compile(r"^-\s+\[[xX]\]\s+CODE_REVIEW-\*-G\?\?\.md", re.MULTILINE)
|
||||
IMPLEMENTATION_CHECKBOX_RE = re.compile(
|
||||
r"^-\s+\[([^\]\r\n]*)\]", re.MULTILINE
|
||||
)
|
||||
WORK_LOG_NAME = "WORK_LOG.md"
|
||||
WORK_LOG_ARCHIVE_RE = re.compile(r"^work_log_(\d+)\.log$")
|
||||
AGENT_PROCESS_MARKER_ENV = "IOP_AGENT_TASK_EXECUTION_ID"
|
||||
|
|
@ -1459,8 +1461,6 @@ def task_stage(task: Task, state: dict[str, Any]) -> str:
|
|||
if task_requires_selfcheck(task, state) and not state.get("selfcheck_done"):
|
||||
return "selfcheck"
|
||||
return "review"
|
||||
if task.review and task.review.exists() and FINAL_CHECK_RE.search(text):
|
||||
return "review"
|
||||
return "worker"
|
||||
|
||||
|
||||
|
|
@ -1477,23 +1477,11 @@ def implementation_review_errors(task: Task) -> list[str]:
|
|||
if task.review is None or not task.review.is_file():
|
||||
return ["CODE_REVIEW 파일 없음"]
|
||||
text = task.review.read_text(encoding="utf-8", errors="replace")
|
||||
errors: list[str] = []
|
||||
checklist = markdown_section(text, "구현 체크리스트")
|
||||
if not checklist or re.search(r"^-\s+\[\s\]", checklist, re.MULTILINE):
|
||||
errors.append("구현 체크리스트 미완료")
|
||||
completion = markdown_section(text, "구현 항목별 완료 여부")
|
||||
if not completion or re.search(r"\|\s*\[\s\]\s*\|", completion):
|
||||
errors.append("구현 항목 완료 여부 미작성")
|
||||
for heading in ("계획 대비 변경 사항", "주요 설계 결정"):
|
||||
body = markdown_section(text, heading)
|
||||
if not body or re.fullmatch(r"_[^_]*_", body, re.DOTALL):
|
||||
errors.append(f"{heading} 미작성")
|
||||
verification = markdown_section(text, "검증 결과")
|
||||
if not verification or re.search(r"_미실행(?:[^_]*)_", verification):
|
||||
errors.append("검증 결과 미작성")
|
||||
if not FINAL_CHECK_RE.search(text):
|
||||
errors.append("CODE_REVIEW 동기화 체크 미완료")
|
||||
return errors
|
||||
checkbox_values = IMPLEMENTATION_CHECKBOX_RE.findall(checklist)
|
||||
if not checkbox_values or any(not value.strip() for value in checkbox_values):
|
||||
return ["구현 체크리스트 미완료"]
|
||||
return []
|
||||
|
||||
|
||||
def classify_failure_with_evidence(output: str) -> tuple[str, str | None]:
|
||||
|
|
|
|||
|
|
@ -254,14 +254,14 @@ class TaskStageTest(unittest.TestCase):
|
|||
task.recovery = True
|
||||
self.assertEqual(dispatch.task_stage(task, {}), "blocked")
|
||||
|
||||
def test_completed_implementation_checklist_starts_review(self):
|
||||
def test_completed_implementation_checklist_does_not_bypass_worker(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root = Path(temporary)
|
||||
task = self.make_task(
|
||||
root,
|
||||
"- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채운다.\n",
|
||||
)
|
||||
self.assertEqual(dispatch.task_stage(task, {}), "review")
|
||||
self.assertEqual(dispatch.task_stage(task, {}), "worker")
|
||||
|
||||
def test_pi_worker_success_requires_selfcheck_before_review(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
|
|
@ -329,41 +329,29 @@ class TaskStageTest(unittest.TestCase):
|
|||
self.assertIs(created, semaphores["pi:laguna-s:2.1"])
|
||||
self.assertEqual(created._value, 64)
|
||||
|
||||
def test_selfcheck_requires_all_implementation_review_fields(self):
|
||||
def test_selfcheck_requires_nonempty_checklist_values(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
task = self.make_task(
|
||||
Path(temporary),
|
||||
"## 구현 항목별 완료 여부\n\n"
|
||||
"| 항목 | 완료 여부 |\n|---|---|\n| TEST-1 | [ ] |\n\n"
|
||||
"## 구현 체크리스트\n\n- [ ] TEST-1\n"
|
||||
"- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채운다.\n\n"
|
||||
"## 계획 대비 변경 사항\n\n_미작성_\n\n"
|
||||
"## 주요 설계 결정\n\n_미작성_\n\n"
|
||||
"## 검증 결과\n\n결과:\n_미실행_\n",
|
||||
"## 구현 체크리스트\n\n- [ ] TEST-1\n",
|
||||
)
|
||||
self.assertEqual(
|
||||
dispatch.implementation_review_errors(task),
|
||||
[
|
||||
"구현 체크리스트 미완료",
|
||||
"구현 항목 완료 여부 미작성",
|
||||
"계획 대비 변경 사항 미작성",
|
||||
"주요 설계 결정 미작성",
|
||||
"검증 결과 미작성",
|
||||
"CODE_REVIEW 동기화 체크 미완료",
|
||||
],
|
||||
["구현 체크리스트 미완료"],
|
||||
)
|
||||
|
||||
def test_selfcheck_accepts_filled_implementation_review_fields(self):
|
||||
def test_selfcheck_accepts_any_nonempty_checklist_values(self):
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
task = self.make_task(
|
||||
Path(temporary),
|
||||
"## 구현 항목별 완료 여부\n\n"
|
||||
"| 항목 | 완료 여부 |\n|---|---|\n| TEST-1 | [x] |\n\n"
|
||||
"## 구현 체크리스트\n\n- [x] TEST-1\n"
|
||||
"- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채운다.\n\n"
|
||||
"## 계획 대비 변경 사항\n\n계획과 동일하게 구현했다.\n\n"
|
||||
"## 주요 설계 결정\n\n상태 전이를 직렬화했다.\n\n"
|
||||
"## 검증 결과\n\n결과: PASS (exit 0)\n",
|
||||
"| 항목 | 완료 여부 |\n|---|---|\n| TEST-1 | [ ] |\n\n"
|
||||
"## 구현 체크리스트\n\n"
|
||||
"- [x] TEST-1\n"
|
||||
"- [v] TEST-2\n"
|
||||
"- [✅] TEST-3\n",
|
||||
)
|
||||
self.assertEqual(dispatch.implementation_review_errors(task), [])
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue