# 실행 계획 — 조용한 실패 4건 수습 (2026-08-25)

> 회장 요구: **"진행하면 어떤 결과를 예측할 수 있는지"** 를 명시할 것.
> 그래서 각 단계마다 **바뀌는 것 / 안 바뀌는 것 / 위험 / 검증법** 을 분리해 적는다.
> ★ "안 바뀌는 것"을 빼면 계획서가 아니라 희망사항이 된다.

## 배경 — 왜 이 순서인가

2026-08-25 조사에서 **같은 유형의 장애 4건**이 드러났다. 공통점은 버그 자체가 아니라
**"실패가 조용해서 아무도 모른다"** 는 구조다.

| 사고 | 은폐 수단 | 방치 기간 | 상태 |
|---|---|---|---|
| 복합설계 저장 중단 | `ok:true, FLAG_DISABLED_STORED` → **"저장됐어요"** | 4주(7/30~8/25) | **복구 완료** (확장 재빌드) |
| 트렌드 파이프라인 | `CHAT_ID` 미설정 + `--force-alert` 인자 없음 + 0건인데 exit=0 | **4개월**(4/30~) | 미해결 |
| InsuWiki 백업 | 실패해도 보는 사람 없음 (GCP 인증 실패) | **3개월**(5/18~, 98회 전건 실패) | 미해결 |
| 배포 드리프트 감시 | `EnvironmentFile` 주석 + 파일 부재 → 탐지해도 발송 불가 | 설치 이래 | 미해결 |

Codex 지적(2026-08-25): *"핵심은 개별 버그가 아니라 **조용히 실패 + 성공처럼 보이는 UI +
수동 게이트 + 무관측**의 조합이다. **'성공 기대 이벤트 대비 실제 수신 0건' 같은 부재형 알림이
아예 없다.** 원인 버그를 고쳐도 관측이 비어 있으면 같은 급 사고가 형태만 바꿔 재발한다."*

---

## 1단계 — `run_policy_analysis` 타임아웃 현실화

### 실측 근거
```
현재 타임아웃 : 120초
스캔이미지 5p 실소요 : 85~139초   (dev7 task-3005 실측)
결과 : 실호출 3/3 타임아웃 실패
```

### 바뀌는 것
- **회장이 스캔 증권(이미지 PDF)을 올렸을 때 분석이 완주할 확률이 오른다.**
  현재는 139초짜리가 120초에서 잘려 실패한다.
- 실패했을 때 사용자가 원인을 알 수 있게 된다(타임아웃 사유 노출 개선을 함께 요구).

### 안 바뀌는 것 ★
- **LLM 이 담보명을 표준어휘로 리네임하는 것은 그대로다.**
  `종합입원형(질병)실손의료비(갱신형)` → `질병입원의료비` (3회 재현 확정).
  즉 담보명 경로(PR #249)는 여전히 기여 0이다. 실손 판정은 `category='실손'` 경로가 담당한다.
- 텍스트레이어 없는 스캔본의 **인식 정확도 자체**는 개선되지 않는다.
- 분석 시간이 줄지 않는다. 오래 걸리는 건 그대로고, 잘리지 않게만 한다.

### 위험
- 타임아웃을 늘리면 **실패를 늦게 알게 된다**(139초 기다렸다가 실패). 사용자 대기 시간 증가.
- 동시 요청이 몰릴 때 워커 점유가 길어진다. 현재 사용량(증권분석 12건 수준)에선 문제 없으나
  값을 무한정 늘리는 것은 답이 아니다. **실측 상한(139초)에 여유를 둔 값**으로 잡는다.

### 검증법
- 회장 제공 KB 증권(5p 스캔)으로 **실호출 3회 이상** — 전건 완주 확인
- 소요 시간 분포를 수치로 제시(평균·최대)
- 기존 텍스트레이어 증권 경로 무회귀 확인

---

## 2단계 — 알림 배선 복구

### 실측 근거 (고장 지점 3개, 전부 별개)
```
① COKACDIR_CHAT_ID / COKACDIR_KEY 미설정
   → daily_health_check.py:63  "설정되지 않아 Telegram 알림을 건너뜁니다"
② run_trend_pipeline.sh 가 --force-alert 로 호출하는데 스크립트에 그 인자가 없음
   → "error: unrecognized arguments: --force-alert"  → notify_failure() 자체가 항상 실패
③ insuro-drift-monitor.service 의 EnvironmentFile 이 주석 처리 + 대상 파일 자체 부재
   → CRITICAL 탐지해도 발송 경로 없음
```

### 바뀌는 것
- **앞으로 발생하는 고장을 회장/ANU 가 당일에 알게 된다.**
- 트렌드 파이프라인은 이미 매일 경고를 만들고 있다(수집 0건·랭킹 0건). 배선만 하면 **내일 아침부터 도달**한다.
- 배포 드리프트(`DRIFT_UNDEPLOYED`)도 즉시 도달한다.

### 안 바뀌는 것 ★★ (가장 중요)
- **트렌드 수집이 0건인 문제 자체는 안 고쳐진다.** 알림이 오기 시작할 뿐이다.
  즉 2단계 직후 *"트렌드 수집 0건"* 경고가 **매일 오게 된다.** 이건 정상이며,
  그 원인 규명은 **별도 작업**이다. (혼동하면 "알림 고쳤더니 오히려 문제가 생겼다"고 오해한다)
- InsuWiki 백업 실패(GCP 인증)도 안 고쳐진다. 그건 GitHub Actions 쪽이라 이 배선과 무관하다.
- **`exit=0` 인데 0건 수집인 조용한 성공**(Step 1)은 그대로다. 이것도 별도 수정 대상이다.

### 위험
- **알림 폭주**. 이미 고장난 것이 여럿이라 배선 즉시 매일 여러 건이 온다.
  → 대응: 첫날은 있는 그대로 받아 실태를 파악하고, **중복 억제(같은 사유 1일 1회)** 를 함께 넣는다.
- 알림에 **민감정보가 실리지 않도록** 확인 필요(엔드포인트 전체·키·고객정보 금지).

### 검증법
- 강제 실패를 만들어 **실제 텔레그램 수신** 확인 (로그 "발송함"만으로 판정 금지)
- `--force-alert` 인자로 호출했을 때 정상 동작 확인
- 드리프트 감시를 CRITICAL 상태로 만들어 수신 확인

---

## 3단계 — 상담이력 전송 플래그 정식 전환

### 현황
```
extension/background/ingest.js:38  INSURO_CONSULTATION_HISTORY_V1_ENABLED = false
scripts/build-pilot-extension.mjs  복사본에서만 true 치환 → "파일럿 빌드"
```
파일럿 빌드를 설치한 사용자만 서버 전송이 된다. **이 구조가 이번 4주 장애를 만들었다.**

### 이미 갖춰진 통제 (Codex 판단 근거)
2차 명시 동의 UI · consent 객체(granted_at/version/scope/source) · `consent!==true` fail-closed ·
단일 egress(`sendIngest` 하나만 fetch) · URL allowlist · JWT 검증 · 서버측 인카 소속 검증 · 멱등키

### 바뀌는 것
- **일반 릴리스 빌드로도 저장이 된다.** 확장을 고칠 때마다 파일럿 빌드를 다시 만들어
  전달해야 하는 절차가 사라진다 → **같은 사고의 재발 경로가 닫힌다.**
- 배포 경로가 하나로 단순해진다(파일럿/일반 이원화 폐기).

### 안 바뀌는 것 ★
- **자동 전송은 여전히 없다.** 사용자가 "인슈로에 동의하고 저장"을 눌러야만 전송된다
  (회장 결정 #4, task-2354/2800). 이 결정은 유지한다.
- 동의 없이는 전송되지 않는다(`consent!==true` fail-closed 유지).
- 회장은 **확장을 한 번 더 재설치**해야 한다(정식 빌드로 교체).

### 위험 · 전제
- ★ **전제**: 과거 "자동 push 차단" 결정이 *사용자가 직접 클릭하는 전송*까지 막으라는 뜻이었다면
  이 판단은 뒤집힌다. **회장 확인 필요.** 기록상으로는 "자동 push"만 대상이다.
- PII(상담이력) 전송이므로, 켠 뒤 **실제 전송량·수신 성공률을 관측**해야 한다.
  2단계(알림)가 먼저 되어 있어야 하는 이유다.

### 검증법
- 정식 빌드(파일럿 아님)로 캡처 → 저장 → `consultation_history_v1` 행 증가 확인
- 동의 없이 저장 시도 → 차단 확인
- 서버 ACK 없이 성공 문구가 뜨지 않는지 확인(task-3006 수정분 회귀)

---

## 4단계 — 백엔드 배포 (1·2 검증 후 1회)

### 배포 대상
```
서빙 eadd0c9  →  main(현재 edd7582 + 1·2 반영분)
포함: task-2956 실손 분쟁 판례 지식(1,512줄, 호출자 0건) · task-3004 실손 판정 · 1·2단계 수정
```

### 바뀌는 것
- 타임아웃 수정이 **실제로 적용된다**(그전까지는 머지만 되고 라이브 반영 0).
- 백엔드 코드가 4주 만에 최신화된다.

### 안 바뀌는 것 ★
- **PR #249(담보명 경로)는 배포해도 효과 없다.** 담보명 리네임이 확정됐기 때문이다.
  실손 판정은 배포 전에도 `category` 경로로 정상 작동 중이다.
- `task-2956` 판례 지식도 호출자가 0건이라 화면 변화 없다.
- 프론트는 이미 최신이라 변화 없다.

### 위험
- **백엔드 재기동**이 따른다. 짧은 중단 가능.
- 이 저장소는 `Restart=always` 라 크래시 시 디스크 HEAD 가 자동 라이브화되는 이력이 있었다.
  배포 원자화(task-2988)가 되어 있으나, **배포 중 크래시 시나리오**를 염두에 둔다.
- 미검증분이 함께 나간다 → 그래서 **1·2를 먼저 검증**한 뒤 한 번에 올린다.

### 검증법
- `/api/status` sha 가 목표 커밋과 일치
- 배포 직후 스캔 증권 실호출 1회 성공(1단계 효과 실증)
- 기존 화면 스모크(증권분석·실비계산기·복합설계 조회)

---

## 순서를 이렇게 잡은 이유

```
1 타임아웃  →  지금 사용자가 겪는 실패. 가장 체감이 크다
2 알림      →  3·4 를 안전하게 하려면 관측이 먼저 있어야 한다
3 플래그    →  재발 경로를 닫는다. 켠 뒤 관측이 필요하므로 2 이후
4 배포      →  1·2 를 실제로 적용. 검증 끝난 것만 한 번에
```

**2를 3보다 앞에 둔 것이 핵심이다.** 관측 없이 PII 전송을 켜면, 문제가 생겨도 또 몇 주 뒤에 안다.

---

## 이 계획으로 **해결되지 않는 것** (별도 과제로 남긴다)

1. **트렌드 수집 0건** — 4개월째. 2단계는 알림만 살린다. 원인 규명 별도 필요
2. **InsuWiki 백업 3개월 실패** — GCP 인증(`google-github-actions/auth@v2`). 별도
3. **`exit=0` 인데 0건인 조용한 성공** — 트렌드 Step 1. 성공 판정 기준 자체를 고쳐야 함
4. **부재형 알림 체계 부재** — "실패율"이 아니라 **"올 것이 안 왔다"** 를 감지하는 수단.
   Codex 가 지적한 근본 공백. 위 4건을 다 고쳐도 이게 없으면 다음 사고를 또 늦게 안다
5. **묵은 PR 4건**(#106·#105·#94·#92, 4~5월) — 회장이 "무시 가능"으로 분류
6. **dev5 M7**(웹푸시 payload 암호화) — 연구 산출물 보존됨, 재발사 대기
7. **InsuWiki PR #6**(유튜브 wiki 게시) — 요약 분량 상한 완화는 회장 승인 완료. 검사 후 머지 필요
8. **InsuRo PR #250**(3005 픽스처) — 검사 후 머지 필요

---

## 예측 요약 (한 장)

| 단계 | 하고 나면 달라지는 것 | 여전히 안 되는 것 |
|---|---|---|
| 1 타임아웃 | 스캔 증권 분석이 잘리지 않는다 | 담보명 리네임·인식 정확도는 그대로 |
| 2 알림 | **고장을 당일에 안다.** 트렌드 경고가 매일 도착 | 트렌드 수집 0건 자체는 미해결 |
| 3 플래그 | 정식 빌드로 저장됨. 재발 경로 차단 | 자동 전송은 여전히 없음(의도) · 확장 재설치 1회 필요 |
| 4 배포 | 1·2 가 실제로 적용됨 | PR #249·task-2956 은 효과 없음(호출자/기여 0) |

★ **가장 정직한 예측**: 이 4단계는 *"앞으로 생기는 고장을 빨리 알게 되는 것"* 까지다.
**이미 고장난 것(트렌드·백업)은 별도로 고쳐야 한다.** 2단계를 하면 그 고장들이
**매일 알림으로 드러나기 시작한다** — 그것이 이 계획의 의도된 결과다.
