diff --git a/orchestrator.py b/orchestrator.py index 89c7a03..0e6b671 100644 --- a/orchestrator.py +++ b/orchestrator.py @@ -900,7 +900,22 @@ AUDIT-ONLY MODE (this issue is an integration audit / report-only): status_integrity / report_assembly / followup_proposal). Each unit's tests: field MUST list verification commands or report artifacts (NOT pytest tests:[] which the orchestrator rejects). - Stage 5 commit = only audit report files. pipeline run artifacts under data/runs/ or .orchestrator/ - are evidence-only and must NOT be staged for commit.""" + are evidence-only and must NOT be staged for commit. +- COMMENT FORMAT (CRITICAL — orchestrator detect_agent is first-line strict, P0-1): + The FIRST non-empty line of every Gitea comment MUST be exactly one of: + [Claude #] + [Codex #] + Audit anchor citation, banners, prefaces of any kind MUST appear AFTER the first line + (line 2 or later). If you put `Audit anchor:` or any other preface BEFORE the [Claude #N] / + [Codex #N] header, the orchestrator will fail to detect the agent and the stage cannot + advance — your work will be discarded and re-attempted with token waste. + Correct example: + [Codex #14] Stage 4 test-verify — INTEGRATION-AUDIT-02 + + Audit anchor: This audit verifies pipeline contracts... + ... + FINAL_CONSENSUS: YES +""" def build_context_pack(n, title, body, sid, agent, rnd, start_cnt, compact=None): @@ -1273,6 +1288,19 @@ def run_stage(n, title, body, sid): is_codex = detect_agent(last) == "codex" if not is_codex: log(" Codex 응답 미감지 — continuing") + # P5 (2026-05-20) — audit-mode 에서 detect_agent None 의 흔한 원인 = + # agent 가 audit anchor / preface 를 첫 줄에 박아서 P0-1 strict 가 못 찾음. + # 자동 supplement 로 format 교정 요청 → 무한 루프 자동 break. + if _audit_mode(title): + try: gitea(f"issues/{n}/comments", "POST", {"body": + "⚠️ **[Orchestrator]** Codex 응답 미감지 — `detect_agent` 가 첫 줄에서 " + "`[Codex #N]` 또는 `[Claude #N]` 패턴을 찾지 못함.\n\n" + "AUDIT-ONLY mode 의 흔한 원인: `Audit anchor:` 같은 preface 가 첫 줄에 있음.\n\n" + "다음 round 부터 모든 comment 의 **FIRST non-empty line 은 반드시**:\n" + " `[Codex #N] ` 또는 `[Claude #N] `\n" + "Audit anchor / banner / preface 는 line 2 이후 에만. 안 그러면 orchestrator 가 " + "stage 진행 못 함 (P0-1 first-line strict)."}) + except: pass continue status, target = parse_consensus(last) diff --git a/tests/orchestrator_unit/test_orchestrator_core.py b/tests/orchestrator_unit/test_orchestrator_core.py index 1460071..af88d89 100644 --- a/tests/orchestrator_unit/test_orchestrator_core.py +++ b/tests/orchestrator_unit/test_orchestrator_core.py @@ -97,6 +97,37 @@ Addressing [Codex #2] findings ... assert detect_agent("[Codex#1] hello") == "codex" assert detect_agent("[Claude#5] hi") == "claude" + def test_audit_anchor_preface_breaks_detection(self): + """P5 (2026-05-20) — regression: AUDIT-ONLY mode 의 'Audit anchor:' preface 가 + 첫 줄에 박히면 detect_agent 는 None 반환 (P0-1 strict 의도된 동작). + 이게 #56 (INTEGRATION-AUDIT-02) 의 Stage 4 Round #14 infinite loop 의 직접 원인. + 해결책 = detect_agent 완화 X, AUDIT_ONLY_NOTE 가 agent header 를 first line 으로 강제.""" + body_anchor_first = ( + "Audit anchor: This audit verifies pipeline contracts...\n" + "It does not implement runtime code.\n" + "\n" + "[Codex #14] Stage 4 (test-verify) Round #14 - INTEGRATION-AUDIT-02\n" + "\n" + "Verdict: PASS. Stage 3 satisfies all criteria.\n" + "FINAL_CONSENSUS: YES\n" + ) + assert detect_agent(body_anchor_first) is None, ( + "audit anchor preface as first line MUST cause detect_agent None " + "(P0-1 strict). Fix path: comment format, not detect_agent." + ) + + def test_audit_anchor_after_header_works(self): + """P5 (2026-05-20) — 올바른 format: agent header first line, anchor line 2+.""" + body_header_first = ( + "[Codex #14] Stage 4 (test-verify) Round #14 - INTEGRATION-AUDIT-02\n" + "\n" + "Audit anchor: This audit verifies pipeline contracts...\n" + "\n" + "Verdict: PASS.\n" + "FINAL_CONSENSUS: YES\n" + ) + assert detect_agent(body_header_first) == "codex" + # ───────────────────────────────────────────────────────────────── # parse_consensus — YES/NO + rewind_target