$ uv run tools/flow.py test cases/lab02-challenge.json -v
✔ before: POST http://localhost:8800/ops-reset · HTTP 200 · 0.0s · {"seeded_at": "2026-11-04T08:30:00", "note": "79일차 운영 아침 상태 (연습 데이터, seed 79)", "counts": {"alerts": 6, "dlq": 3, "quali
    → request  POST http://localhost:8800/ops-reset
      {}
    ← response HTTP 200
      {
        "seeded_at": "2026-11-04T08:30:00",
        "note": "79일차 운영 아침 상태 (연습 데이터, seed 79)",
        "counts": {
          "alerts": 6,
          "dlq": 3,
          "quality_log": 4,
          "quarantine": 1,
          "usage": 192
        }
      }
POST http://localhost:5678/webhook/my-ops-ask · 5 case(s) × 1
✘ 낱말이 없는 비용 질문 · 0.0s · area = unknown (expected cost); router = keyword (expected llm); refused = true (expected false); numbers_ok = null (expected true)
    → request  POST /webhook/my-ops-ask
      {
        "question": "이번 주에 AI 쓰는 데 얼마나 나갔어?"
      }
    ← response HTTP 200
      {
        "question": "이번 주에 AI 쓰는 데 얼마나 나갔어?",
        "area": "unknown",
        "router": "keyword",
        "refused": true,
        "reason": "out_of_scope",
        "answer": "운영 데이터(실행 · 알림 · DLQ · 품질 · 비용 · 릴리스)로 답할 수 있는 질문만 받습니다."
      }
✘ 낱말이 없는 DLQ 질문 · 0.1s · area = unknown (expected dlq); router = keyword (expected llm); numbers_ok = null (expected true); answer has none of ['3']
    → request  POST /webhook/my-ops-ask
      {
        "question": "어젯밤에 접수 못 하고 남은 건 몇 건이야?"
      }
    ← response HTTP 200
      {
        "question": "어젯밤에 접수 못 하고 남은 건 몇 건이야?",
        "area": "unknown",
        "router": "keyword",
        "refused": true,
        "reason": "out_of_scope",
        "answer": "운영 데이터(실행 · 알림 · DLQ · 품질 · 비용 · 릴리스)로 답할 수 있는 질문만 받습니다."
      }
✘ 낱말이 없는 릴리스 질문 · 0.0s · area = unknown (expected release); router = keyword (expected llm); numbers_ok = null (expected true); answer has none of ['62.5']
    → request  POST /webhook/my-ops-ask
      {
        "question": "새 판을 일부 사용자에게 먼저 내보낸 거 어떻게 되고 있어?"
      }
    ← response HTTP 200
      {
        "question": "새 판을 일부 사용자에게 먼저 내보낸 거 어떻게 되고 있어?",
        "area": "unknown",
        "router": "keyword",
        "refused": true,
        "reason": "out_of_scope",
        "answer": "운영 데이터(실행 · 알림 · DLQ · 품질 · 비용 · 릴리스)로 답할 수 있는 질문만 받습니다."
      }
✘ LLM도 운영 밖이면 거절 · 0.1s · router = keyword (expected llm)
    → request  POST /webhook/my-ops-ask
      {
        "question": "점심 메뉴 추천해 줘"
      }
    ← response HTTP 200
      {
        "question": "점심 메뉴 추천해 줘",
        "area": "unknown",
        "router": "keyword",
        "refused": true,
        "reason": "out_of_scope",
        "answer": "운영 데이터(실행 · 알림 · DLQ · 품질 · 비용 · 릴리스)로 답할 수 있는 질문만 받습니다."
      }
✔ 낱말이 있으면 LLM을 부르지 않음 · 1.0s
    → request  POST /webhook/my-ops-ask
      {
        "question": "지난주 오류율이 얼마야?"
      }
    ← response HTTP 200
      {
        "question": "지난주 오류율이 얼마야?",
        "area": "runs",
        "router": "keyword",
        "refused": false,
        "reason": null,
        "answer": "지난주 오류율은 5.88%였습니다. 총 357회 실행 중 21회 오류가 발생했습니다.",
        "numbers_ok": true,
        "unknown_numbers": []
      }
Passed 1/5
report: runs/lab02-challenge-20261101-185310.json
