PM 리뷰 — STAGE 2 PASS + STAGE 2.5 wiring

2026-05-06 · 8 commit 검증 PASS · 단 wiring 미완 발견 · main 머지 전 STAGE 2.5 필수

⚠️ STAGE 2 자체는 PASS, 단 main 머지 보류

8 commit 모두 룰 준수, 새 endpoint 7개 + 모달 2개 + 페이지 2개 + 섹션 4개 모두 박힘. 그러나 DiagnoseFlow2.tsx 본체에 모달 wiring이 없어 사용자가 실제 v3 흐름을 안 탐. 이건 PM 작업지시서 룰 작성 책임 (가드레일이 너무 엄격해서 본체 수정 자체를 막음). STAGE 2.5로 fix.

01.STAGE 2 검증 결과 (PASS)

8 commit sha 모두 staging에 박힘
bf9a8ec ~ 7c94f84
색깔 룰 (admin + 모달 2개)
grep #009090 ... → 0 hits
새 endpoint 7개 디렉토리
request-otp / verify / admin/{pending,fallback,dispatch/all-hold} / cron/{recheck,escalate}
신규 모달 2개
AgentGateModal.tsx / OtpVerifyModal.tsx
tsc / eslint / build
all PASS
untracked 13개 보존
CLAUDE.md / catchrank / landing / context 등 그대로
Vercel preview 7 URL 200
admin / admin/pending / admin/fallback / admin/leads/<id> / api 3개
⚠️ DiagnoseFlow2 wiring
grep AgentGateModal/OtpVerifyModal/request-otp → 0 hits

02.Critical 발견 — wiring 미완

코덱스 잘못 X — PM 작업지시서 룰 작성 실수

작업지시서 #3 가드레일에 박힌 표현:

app/diagnose/* 격리 영역 절대 수정 금지
(예외: P3 신규 컴포넌트 AgentGateModal.tsx · OtpVerifyModal.tsx만 추가 OK)

"신규 컴포넌트 추가만 OK"로 해석되어 코덱스가 본체 (DiagnoseFlow2.tsx)를 안 건드림. 정확히 룰 따른 것. 결과: 모달은 박혔지만 호출 안 되는 dead code. 의도는 "본체 임의 수정 금지, 단 모달 wiring을 위한 최소 변경 OK"였어야 함. PM 작성 실수.

현재 흐름 vs 의도된 v3 흐름

단계현재 (production+staging)v3 의도 (미작동)
진단 폼 끝 [지원금 받기] POST /api/keeper-out/diagnose 즉시 발송 (hotfix 박혀있어 잔액/캡 체크) AgentGateModal → request-otp → OtpVerifyModal → verify → 24h 안내
1만원 발송 즉시 (잔액≥5만원 + cap 미달 시) 24h 휴먼 승인 후 (TM 1차 / fallback 2차)
admin 큐 (pending/fallback) 비어있음 (사용자가 v3 흐름 안 탐) 채워짐 (TM이 콜 outcome 입력)
★ INSIGHT — wiring 없으면 STAGE 2가 dead

03.코덱스 자체 판단 검토 (모두 합리적)

항목판단PM 평가
T+20~24h 정책 (c) 하이브리드 채택 — 작업지시서 기본값 OK 룰 따름
approved_by FK 안전 유효 UUID 있을 때만 저장, 없으면 NULL (auth.users 매핑 시점 미정) OK FK 실패 방지 합리적
/diagnose 본체 미수정 가드레일 룰 정확히 따름 PM 책임 룰 작성 실수, 코덱스 잘못 X
코덱스가 직접 짚은 것 "PM이 OTP 모달을 /diagnose 플로우에 연결하라고 판단하면 가드레일을 별도 해제해야 합니다" ★ EXCELLENT 자체 진단 정확

04.STAGE 2.5 — wiring 작업지시서

스코프 (1 commit, ~1-2시간)

변경파일역할
수정 app/diagnose/DiagnoseFlow2.tsx submit() 함수를 v3 흐름으로 교체. URL ?ref={code} 추출.

흐름 변경

// 현재 (line 172):
async function submit() {
  setStep("submitting");
  const photo_urls = await uploadPhotos();
  const res = await fetch("/api/keeper-out/diagnose", { ... });
  const data = await res.json();
  setStep(data.payback_token ? "reveal" : "done_basic");
}

// v3 흐름 (교체):
async function submit() {
  // ① AgentGateModal 띄움 (영업원 코드 + 이름)
  //    URL ?ref={code}가 있으면 prefill, 없으면 빈 input
  setShowAgentModal(true);
}

async function onAgentConfirm(agent_name, agent_code) {
  setShowAgentModal(false);
  setStep("submitting");
  const photo_urls = await uploadPhotos();
  // ② request-otp 호출
  const res1 = await fetch("/api/keeper-out/diagnose/request-otp", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      payload: { ...buildPayload(), photo_urls },
      agent_name, agent_code,
    }),
  });
  const { otp_session_id } = await res1.json();
  // ③ OtpVerifyModal 띄움 (5분 카운트다운)
  setOtpSessionId(otp_session_id);
  setShowOtpModal(true);
}

async function onOtpVerify(otp_code) {
  // ④ verify 호출
  const res2 = await fetch("/api/keeper-out/diagnose/verify", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      otp_session_id, otp_code, payload: buildPayload(), agent_code,
    }),
  });
  const data = await res2.json();
  setShowOtpModal(false);
  // ⑤ "본사 콜센터에서 곧 연락" 안내 화면
  setStep("review_pending");  // 새 step
}

05.코덱스 프롬프트 (복붙)

📋 같은 세션에 붙여넣기
STAGE 2 PM PASS 받음. 단 wiring 미완 — STAGE 2.5 진행.

[원인]
PM 작업지시서 #3 가드레일이 "신규 모달만 추가 OK"로 작성돼 본체 수정 차단됨.
의도는 "본체 임의 수정 금지, 단 모달 wiring은 OK"였음.
PM이 가드레일 명시적 해제 — 너는 잘못 X.

[STAGE 2.5 — 가드레일 해제]
app/diagnose/DiagnoseFlow2.tsx 본체 수정 OK. 단 다음 룰:
- 기존 /api/keeper-out/diagnose POST 호출 제거 (deprecated, hotfix 흐름)
- 모달 wiring + 새 흐름만 추가
- 색깔 청록 #009090 등 절대 X (이건 어차피 /diagnose 격리는 청록 OK이지만 본체엔 박지 말 것 — 기존 톤 유지)
- search/q1/q2/pain/q3/q4/contact/photo step 자체는 그대로

[작업 — 1 commit]

(1) URL ?ref={code} 추출
   - useSearchParams() 사용
   - DiagnoseFlow2 안에서 const ref = searchParams.get("ref")
   - AgentGateModal에 prefill로 전달

(2) state 추가
   - showAgentModal: boolean
   - showOtpModal: boolean
   - otpSessionId: string | null
   - agentName / agentCode: string

(3) submit() 함수 교체 (line 172부터)
   기존 fetch("/api/keeper-out/diagnose") 호출 제거.
   대신 setShowAgentModal(true)만.

(4) 새 함수 onAgentConfirm(agent_name, agent_code):
   - setShowAgentModal(false), setStep("submitting")
   - photo_urls = await uploadPhotos()
   - fetch("/api/keeper-out/diagnose/request-otp", body: { payload, agent_name, agent_code })
   - 응답 에러 처리 (잔액 미달, agent invalid, V4 unique 충돌, V8 place 검증 실패 등)
   - 성공: setOtpSessionId(data.otp_session_id), setShowOtpModal(true)

(5) 새 함수 onOtpVerify(otp_code):
   - fetch("/api/keeper-out/diagnose/verify", body: { otp_session_id, otp_code, payload, agent_code })
   - 응답 에러 처리 (OTP 만료, 시도 초과 등)
   - 성공: setShowOtpModal(false), setStep("review_pending") <- 새 step

(6) 새 step "review_pending" 렌더 추가
   기존 reveal/done_basic처럼 별도 화면.
   카피 (지시서 docs/keeper-out/anti-abuse/05-human-approval-queue.md §7 참조):
   ```
   ✅ 진단 완료!
   📞 본사 콜센터에서 곧 연락드릴 예정이에요
   💸 1만원 N페이 (입력하신 010-XXXX-XXXX)
      본사 확인 콜 종료 후 24시간 내 발송됩니다
   🔒 보안을 위해 자동 즉시 발송이 아닌 본사 확인 후
      발송하는 정책으로 운영하고 있어요
   ```

(7) JSX에 모달 2개 마운트
   {showAgentModal && }
   {showOtpModal && }

(8) 모달 props는 컴포넌트 시그니처 확인 후 맞추기
   grep -A 5 "interface.*Props" app/diagnose/components/AgentGateModal.tsx
   grep -A 5 "interface.*Props" app/diagnose/components/OtpVerifyModal.tsx

[검증]
1. npx tsc --noEmit → 0
2. npx eslint app/diagnose/DiagnoseFlow2.tsx → 0
3. npm run build → success
4. dev 서버 띄우고 /diagnose 접속 → search → q1 → ... → photo → [지원금 받기]
   ① AgentGateModal 등장 → 영업원 코드 입력 → confirm
   ② "submitting" → request-otp 호출 → otp_session_id 응답 (실제 OTP SMS 도착 확인)
   ③ OtpVerifyModal 등장 → SMS 받은 OTP 6자리 입력
   ④ verify 응답 → "본사 콜센터에서 곧 연락" 안내 화면
   ⑤ /keeper-out/admin/pending 큐에 신규 row 진입 확인 (Supabase MCP로 확인)

[커밋]
feat(keeper-out/diagnose): v3 OTP 흐름 wiring (AgentGate + OtpVerify 모달 호출 + review_pending step)

[보고 형식]

## STAGE 2.5 wiring 결과 보고

### 1. 커밋 sha (1개)
### 2. /diagnose 흐름 변경 — 어떤 step이 어떻게 바뀌었나
### 3. 검증 결과 (tsc/eslint/build/dev 실제 흐름 테스트)
### 4. URL ?ref={code} 동작 확인 (테스트한 ref)
### 5. admin/pending 큐에 row 진입 확인 (Supabase row count or screenshot)
### 6. 막힌 곳 / 의문

[가드레일 — 같은 룰 그대로]
- 청록 #009090 등 X (admin 영역에)
- 한화·키퍼·하나비전 텍스트 X
- next.config / package.json 수정 X
- staging만 push, main은 PM 검증 후 자동님 결정
- untracked 13개 보존

[시작]
지금 진행. 작업 끝나면 위 보고 형식으로 회신.

06.완료 후 흐름

  1. STAGE 2.5 PM 검증: wiring 동작 + admin 큐 채워지는지 확인
  2. 자동님 main 머지 결정: staging의 9 commit (8 P3 + 1 wiring) → main → production
  3. production 라이브 후: TM (이서경) 콜센터 운영 시작 + 자동님 fallback 큐 모니터링
  4. P3 후속: referio RE backfill / agent.code 발급 운영 / RBAC (Supabase Auth) 등

참조 흐름 (오늘 누적)

  1. P1 + 머지 — production live (4ab6eb4)
  2. v3 anti-abuse — 자동님 승인
  3. PM 컨텍스트 동기화 — 5/6
  4. Codex Brief #3 (STAGE 1+2) — 디스패치
  5. 이 문서 — STAGE 2 PASS + STAGE 2.5 brief