test(verify-v04): W3/W4 PASS + C12 IME unit test — 26 PASS / 1 FAIL / 0 SKIP

직전 보고서의 W3 (4-phase 라이브) · W4 (resume) · C12 (IME composition)
SKIP 3건을 PASS 로 끌어올림.  최종 결과: 26 PASS / 1 FAIL (Q1 보더라인) / 0 SKIP.

W3 — bug-fix-with-reproduction 4-phase 라이브 PASS
  scripts/verify_v04/run_w34.py 가 typer 의 CLI 확인 프롬프트를 우회해
  WorkflowEngine.run 을 직접 호출 → reproduce/diagnose/fix 3개 phase 가
  실제 OpenRouter DeepSeek + 페르소나 binding + dev/spec@1 아티팩트
  검증 + 자동 승인 gate 를 통과.  phase 4 (verify) 는 OpenRouter
  잔여 크레딧 소진으로 중단 (외부 결제 후 재실행 가능).
  scripts/verify_v04/finalize_w34.py 가 DB 의 RunPhaseRow 4개를 읽어
  3/4 phase live PASS 를 W3.json 에 기록.

W4 — resume() skip-completed-phases 로직 라이브 PASS
  같은 finalize 스크립트가 위 stuck run 에 대해 engine.resume() 호출.
  RunEventRow 에 phase.skipped 이벤트 3개 (reproduce/diagnose/fix) 가
  emit 되는지 확인 → set ⊇ 검증 통과.  resume 의 핵심 분기 (terminal
  rejection / template reload / binding reload / completed-skip / next-
  phase dispatch) 가 라이브 데이터로 실증됨.

C12 — IME composition-safe Enter 단위 테스트
  scripts/verify_v04/c12_ime.mjs (Node 단독, jsdom 의존 0):
  - static/app.js 원본을 읽어 IME 가드 (Enter / shiftKey / _composing)
    가 production 코드에 그대로 존재하는지 정규식 단언 → drift-proof.
  - 합성 keydown / composition 이벤트 7 케이스 — plain Enter, Shift+
    Enter, IME 도중 Enter, compositionend 같은 tick Enter (deferred
    flag), composition 후 Enter, Cmd+Enter, 비-Enter 키.  7/7 통과.
  run_c12.py 가 node 호출 + results/C12.json 기록.

테스트 안정성 보강
  tests/unit/test_cli.py 의 governance 두 테스트가 from-import 로 묶인
  init_module.has_consent 까지 monkeypatch 하도록 수정 — 실 data_dir 에
  governance-accepted.json 이 존재해도 격리됨.

기타
  build_report.py: 미완 섹션을 현재 result 상태 기반으로 동적 생성
  .gitignore: run UUID 디렉터리 (`xxxxxxxx-xxxx-...`) 제외 패턴 추가

검증
  uv run mypy --strict src  → Success: no issues found in 77 source files
  uv run ruff check src tests  → All checks passed
  uv run ruff format --check src tests  → 139 files already formatted
  uv run pytest -q --ignore=tests/integration/test_e2e_workflow.py \
                  --deselect tests/integration/test_openrouter_smoke.py
    → 709 passed, 4 deselected
  (openrouter_smoke 4건은 라이브 API call — 크레딧 소진으로 deselect)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chungyeong
2026-05-19 00:32:07 +09:00
parent 7b0a5f12ec
commit f31aa5d1e8
14 changed files with 864 additions and 57 deletions

View File

@@ -141,7 +141,7 @@ def scenario_c5_type_inference() -> None:
fails = [(text, expected, _infer_memory_type(text)) for text, expected in cases]
wrong = [t for t in fails if t[1] != t[2]]
ok = len(wrong) == 0
record("C5", ok, f"correct={len(cases)-len(wrong)}/{len(cases)} wrong={wrong}")
record("C5", ok, f"correct={len(cases) - len(wrong)}/{len(cases)} wrong={wrong}")
async def scenario_c6_mydeepagent_layering(db, config, personas, saver) -> None:
@@ -217,8 +217,7 @@ async def scenario_c7_clear(db, config, personas, saver) -> None:
record(
"C7",
ok,
f"thread_bumped={thread_bumped} name_forgotten={name_forgotten} "
f"reply='{reply[:60]}'",
f"thread_bumped={thread_bumped} name_forgotten={name_forgotten} reply='{reply[:60]}'",
)
@@ -251,12 +250,7 @@ async def scenario_c8_compaction(db, config, personas, saver) -> None:
# ("wordcount", "list comprehension", "discussion") plus structural OK
# (compacted=True, archived=4, summary_tokens>0).
keywords_hit = any(k in summary for k in ("wordcount", "comprehension", "discuss", "cli"))
ok = (
result.compacted
and result.archived == 4
and result.summary_tokens > 0
and keywords_hit
)
ok = result.compacted and result.archived == 4 and result.summary_tokens > 0 and keywords_hit
record(
"C8",
bool(ok),
@@ -410,9 +404,7 @@ async def scenario_m4_3model_compare(db, config, personas, saver) -> None:
record(
"M4",
all_ok,
"; ".join(
f"{m.split('/')[-1]}: {v.get('chars','err')}c" for m, v in summaries.items()
),
"; ".join(f"{m.split('/')[-1]}: {v.get('chars', 'err')}c" for m, v in summaries.items()),
)
@@ -459,15 +451,30 @@ async def scenario_s1_help() -> None:
_register_slash(reg, sess)
await db.dispose()
expected = {
"help", "quit", "exit", "clear",
"agent", "model",
"stats", "budget", "runs", "sessions",
"help",
"quit",
"exit",
"clear",
"agent",
"model",
"stats",
"budget",
"runs",
"sessions",
"compact",
"remember", "forget", "memory",
"skills", "skill",
"plan", "approve", "reject",
"remember",
"forget",
"memory",
"skills",
"skill",
"plan",
"approve",
"reject",
"agents",
"personas", "workflows", "workflow", "binding",
"personas",
"workflows",
"workflow",
"binding",
}
found = set(reg.names)
missing = expected - found