# task-3001 ANU 독립 검증 보고 — 인슈위키 유튜브 → wiki 자동 게시

- **검증자**: ANU (아누) · **검증일시**: 2026-08-24 09:56~10:20 KST
- **대상**: PR #6 `Jeon-Jonghyuk/InsuWiki` · head `e46ace50` · base `master`(`01b4360`) · **OPEN·미머지**
- **검증 방식**: 봇 자기보고 **미승계**. 전 항목 ANU 직접 재측정 + ANU 자작 프로브/변이.

---

## 0. 결론 요약

| 구분 | 판정 |
|---|---|
| 코드 품질·봉인·회귀 | **전항목 PASS** (재측정으로 재현) |
| 기존 경로 무손상(task-2989) | **PASS** (ANU 자작 3질의 × 2컬렉션 전건 HITS=5) |
| 프로덕션 오염 | **없음** (실행 산출물 3건 전부 프로덕션 404) |
| 머지 적합성 | **머지 가능** (CI 적색은 선재·무관 실증) |
| ★ **기능 발현** | ★ **머지해도 프로덕션 wiki 카드 = 0건** (3중 차단, 아래 G1) |

★ 핵심: **배선은 옳고 봉인도 단단하나, 실행 경로가 막혀 있어 회장 요구("자동 업로드")는 머지만으로 충족되지 않는다.**
t3000 "3단계 전까지 실효 0" · t2997 "죽은 필드" 와 같은 유형이다.

---

## 1. 회귀 기준선 — base 재측정 대조 (봇 수치 전건 일치)

`01b4360` 을 별도 워크트리로 체크아웃해 ANU 가 직접 측정했다.

| 스위트 | base 실측 | head 실측 | 봇 주장 | 일치 |
|---|---|---|---|---|
| pytest (youtube-pipeline) | **66 passed** | **104 passed** | 66→104 | ✅ |
| vitest (nextapp) | **1331 passed / 26 failed** | **1341 passed / 26 failed** | 동일 | ✅ |
| tsc `--noEmit` | **91 errors** | **91 errors** | 91→91 | ✅ |

tsc 91건 내역이 base·head **완전 동일**: `roles.test.ts` 49 · `useDocumentReviewListener.test.ts` 30 ·
`useReviewQueueListener.test.ts` 12. 신규 파일 기여 **0건**. → **F5 독립 확인.**

## 2. firestore_writer.py 순수 추가 — 확인

원격 PR API 기준 **+64 / −0**. diff 는 `save_insurance_chunk` 와 `update_last_crawled` 사이에
`save_wiki_document` 를 끼워 넣은 **단일 삽입 훅 1개뿐**. 기존 쓰기 함수 **무접촉**.
13개 변경파일 중 `youtube_knowledge`/`insurance_chunks` 쓰기 경로 삭제·변경 **0건**.

## 3. 프로덕션 벡터검색 — task-2989 무손상 (ANU 자작 질의)

봇의 질의 1개를 그대로 쓰지 않고 **ANU 가 3개 질의를 새로 만들어** REST(읽기전용)로 실행:

| 질의 | youtube_knowledge | insurance_chunks |
|---|---|---|
| 실손보험 보장 한도 | **HITS=5** | **HITS=5** |
| 암보험 진단금 | **HITS=5** | **HITS=5** |
| 자동차보험 대인배상 | **HITS=5** | **HITS=5** |

전 6건 `dim=768`, 반환 문서가 질의와 **의미적으로 정합**(수술비보험 한도 / 암치단비 / 통합상해).
→ task-2989 결과물 **무손상 실증**.

**프로덕션 미오염 확인**: run1 산출물 3건(`documents/youtube-Tvb1wqDwIB8`,
`youtube_knowledge/s0C3eaXm…`, `insurance_chunks/oBFnsWCa…`) 전부 프로덕션 **HTTP 404**.
→ "에뮬레이터로만 검증, 프로덕션에 쓰지 않았다"는 **사실**.

## 4. 봉인 — 봇 5종 재현 + ANU 자작 11종

봇 주장 5종을 재현하고, **ANU 가 11종을 새로 설계**해 추가 실행했다(전부 복원·잔여 diff 0).

**봇 주장 5종 — 전건 재현**
| 변이 | 결과 |
|---|---|
| visibility public→private | 2 failed / 102 |
| docType wiki→daily | 2 failed / 102 |
| 결정적 문서ID 파괴 | 23 failed / 81 |
| 분량 하한게이트 무력화 | 1 failed / 103 |
| SUMMARY_MIN_LENGTH→0 | 3 failed / 101 |

**ANU 자작 — 파이썬 7종 (6 검출 / 1 공백)**
| 변이 | 결과 |
|---|---|
| isDeleted False→True | 1 failed ✅ |
| title 빈값 가드 제거 | 3 failed ✅ |
| createdAt 보존 파괴 | 1 failed ✅ |
| category practice→general | 1 failed ✅ |
| merge=True→False | 1 failed ✅ |
| attachments 누락 | 2 failed ✅ |
| **WIKI_AUTHOR_ID→""** | **104 passed ★미검출** |

**ANU 자작 — 인증 프록시 4종 (전건 검출)**
| 변이 | 결과 |
|---|---|
| 인증검사 무력화 | 3 failed / 7 ✅ |
| fileId 정규식 무력화 | 1 failed / 9 ✅ |
| 등재확인 우회(404 제거) | 1 failed / 9 ✅ |
| 권한검사 제거(403 제거) | 1 failed / 9 ✅ |

복원 후 pytest **104 passed** · vitest **10 passed** · 잔여 diff **0 파일**.

★ **G3 봉인 공백**: `tests/test_wiki_document.py:328` 이
`assert data["authorId"] == config.WIKI_AUTHOR_ID` — **동어반복 단언**이다.
같은 상수를 양변에 두어 상수를 바꾸면 두 변이 함께 움직여 **영원히 FAIL 할 수 없다.**
영향도는 낮다(빈 문자열은 실제 uid 와 충돌 불가). 그러나 **테스트가 이빨이 없는 것은 사실**이므로
리터럴 기대값(`"system-youtube-pipeline"`) 고정으로 후속 교정 권고.

---

## 5. ★ ANU 자작 발견 — 봇 미보고 (가장 중요)

### G1. 머지해도 프로덕션 wiki 카드는 0건 — 3중 차단

회장 요구는 "유튜브 → wiki **자동** 업로드"다. 이 PR 은 배선을 옳게 만들었으나 **실행되지 않는다.**

**차단 ①: 러너 부트스트랩이 실행을 보류 중**
`insuwiki-youtube-pipeline.timer` 는 6시간마다 정상 발화한다(마지막 **2026-08-24 07:37:50 KST**, 다음 13:37 KST).
그러나 실행되는 `/home/jay/.local/bin/insuwiki-youtube-pipeline` 는 얇은 부트스트랩이며,
저장소의 `run_pipeline.sh` 가 **없으면 즉시 exit 0** 한다. 실제 저널:
```
[bootstrap] Phase 1 러너 미배포(.../run_pipeline.sh) — 구버전 실행 방지를 위해 보류
```
`run_pipeline.sh` 는 **디스크·origin/master·PR head 어디에도 없다**(3곳 전부 확인).
→ 파이썬 파이프라인은 프로덕션에서 **단 한 번도 실행된 적이 없다.** (task-2980 Phase 1 잔재)

**차단 ②: 배포 드리프트 — 러너가 구코드를 가리킴**
러너의 `WorkingDirectory` = `/home/jay/projects/insuwiki` 인데 이 체크아웃은 **`d5859a94`** 로
`origin/master`(`01b4360`)보다 **1커밋 뒤**다(= **task-2989 미반영**).
→ PR #6 을 머지해도 이 디렉터리에서 `git pull` 하지 않으면 **반영 0**. (머지 ≠ 배포)

**차단 ③: 클라우드 함수 경로는 wiki 카드를 만들지 않음**
Cloud Scheduler `firebase-schedule-crawlYoutubeChannels-asia-northeast3`(6시간 주기)는 **PAUSED**.
게다가 `functions/src/crawlYoutubeChannels.ts` 가 쓰는 컬렉션은 7종
(`pipeline_logs`·`youtube_channels`·`youtube_knowledge`·`insurance_chunks`·`youtube_summaries`·
`youtube_summary_queue`·`youtube_transcripts`)뿐이고 **`documents` 에는 쓰지 않는다**(PR head 기준 재확인).
→ 스케줄러를 재개해도 **wiki 카드는 생기지 않는다.** 그리고 `functions/**` 는 이 태스크 **금지 범위**다.

**정황 정합**: 활성 채널 2개 `lastCrawledAt` 이 **2026-04-11** 에 멈춘 것은 위와 모순 없이 설명된다.

> ⚠️ 자기정정: 검증 중 ANU 가 한때 "CF 가 3072차원 임베딩을 쓴다"고 판단했으나, 이는
> **구커밋(`d5859a94`) 워킹트리를 읽은 오독**이었다. task-2989 는 CF 의 `embedModel` 을 이미 제거했다.
> 해당 지적은 **철회**한다. 컬렉션 목록(=`documents` 미기록)은 PR head 에서 재확인해 유효하다.

### G2. 판정
코드는 **머지 가능**하나, 머지는 **기능 발현이 아니다.** 발현까지 필요한 순서는 §7 에 정리했다.

---

## 6. F1~F5 — ANU 독립 재현 및 판정

### F1. 요약 상한 초과 — **현 구현 유지. 규격 개정은 회장 상신** (머지 차단 아님)
ANU 재측정: `live-summary.md` = **2,480자**(봇 수치 일치) · run2 = 3,348자 · 4단 구조 **육안 확인 준수**.

★ **봇이 놓친 결정적 사실**: 전사 원문이 **2,087자**인데 요약이 **2,480~3,348자**다.
**요약이 원본보다 길다.** 즉 상한 초과는 "덜 압축해서"가 아니라 고정 규격(1,500~2,000자)이
**짧은 영상에 애초에 맞지 않아** 모델이 부풀린 결과다.

**금소법 위험 직접 검사**: 전사↔요약 숫자 전수 대조 실시.
요약에만 있는 토큰 4개는 ①§ 제목 번호 `2`,`3` ②전사의 `7,80만원`(Whisper 가 "칠팔십"을 표기)을
`70~80만원` 으로 **정규화**한 것 — **날조 수치 0건**. 초과분은 서술 장황함이지 환각이 아니다.

**판정 근거**
1. 압축 재시도는 회장 원칙 **"숫자·한도·기간·면책 축약 금지"** 와 **정면충돌**한다. 두 규칙은 실제로 충돌하며, 안전(금소법) 쪽이 우선한다.
2. 상한 하드실패는 안전한 구제책이 없어 파이프라인만 멎는다.
3. 초과가 수치 왜곡을 만들지 않음을 **실측으로 확인**했다(위).

→ **회장 상신**: 규격을 전사 길이 **상대값**(예: 전사의 60~80%, 바닥 하한 별도)으로 개정 제안.
회장 확정 규격이므로 ANU 가 임의 변경하지 않는다. **봇의 "임의 판단하지 않음"은 옳은 처신.**

### F2. 무효 키 — **운영 위험 주장은 과장 / 위생 문제는 실재**
- ANU 직접 호출: 커밋된 기본 키 → **HTTP 400 `INVALID_ARGUMENT`**. 무효인 것은 **사실**.
- ★ 그러나 **"프로덕션 크론이 돌면 요약이 항상 실패한다"는 성립하지 않는다.**
  러너 유닛이 `EnvironmentFile=/home/jay/.config/insuwiki/youtube-pipeline.env`(권한 **0600**)로
  `GEMINI_API_KEY` 를 주입한다. 커밋된 기본값은 **프로덕션에서 사용되지 않는다.**
- 실재 문제 = **평문 커밋 4종**(`GEMINI`/`YOUTUBE_API_KEY`/`DRIVE_CLIENT_SECRET`/`DRIVE_REFRESH_TOKEN`).
  **base 에도 동일 존재 = 선재**, 이 PR 무관. 범위 밖 판단 **승인**.
  repo 가 PRIVATE 이므로 **위생 등급**(t2980 판정 기준과 동일) — 긴급 아님, 별건 위임.

### F3. 자막 tier1 실패 — **재현됨. 영상 무관 = 계약 변경**
ANU 자작 4종(봇 영상 + 자막 확실한 대조군 3종)으로 `POST /youtubei/v1/get_transcript` 호출:
**4/4 전건 HTTP 400**. 특정 영상 문제가 아니라 **InnerTube 계약 변경**이다.
`transcriber.py` 는 이 PR **무접촉** = 선재 결함. 별건 위임 타당.
※ 실운영 영향: 전량 Whisper 폴백 = 고비용이나 **결과 정확도는 유지**(폴백이 fail-closed 로 동작).

### F4. health 엔드포인트 불일치 — **재현됨**
ANU 직접 호출: `/health` → **404**, `/v1/health` → **200**. `config.py` 는 `/health` 지정(base 에도 동일=선재).
`whisper_ok` 가 아무것도 게이팅하지 않음 확인 → **기능 영향 0, 상시 오경보만**. 1줄 수정, 별건 묶음 권고.

### F5. CI 선재 실패 — **재현됨. 별건 위임 승인**
base·head 모두 **91 errors**, 3파일 내역 완전 동일(§1). 본 PR 무관 **실증**.
`mergeable_state=unstable` 의 원인이 이것이므로, 머지 시 **선재 적색에 대한 명시적 override** 가 된다.

---

## 7. 명세 정정 (봇 지적 2건 — 전건 타당, 명세를 고친다)

1. **`where('docType','==','wiki')` 는 거짓.** 실제 wiki 목록 조회 조건은
   `page.tsx:124` 의 `where('visibility','==','public')` 이며 wiki 탭은 `docType` 을 검사하지 않는다.
   → **ANU 명세 오류.** 명세대로 구현했으면 카드가 안 보였다. 봇 반박이 옳다.
   (t2997 `routes.ts` `section` 죽은 필드와 **동일 유형**의 ANU 오류 — 반복 중이므로 박제한다.)
2. **"기존 첨부 6건" → 실측 8개 문서 / 첨부 10건.** 봇 실측 채택.

봇이 `docType='wiki'` 를 **함께** 넣은 판단도 타당하다(비면 `title` 의 날짜패턴으로 `daily` 오추론).

---

## 8. 머지 판정 및 권고 순서

**머지 판정: 승인 가능** — 회귀·봉인·무손상·프로덕션 미오염 전부 ANU 재측정으로 확인.
CI 적색은 선재(F5) 실증. **단, ANU 는 이번 실행에서 머지하지 않았다** — 되돌리기 어려운 조치이고
아래 순서 결정이 선행되어야 실익이 있으므로 **회장 결정을 기다린다.**

**기능 발현까지 필요한 순서 (이 순서를 지키지 않으면 실효 0)**
1. PR #6 **머지**
2. `/home/jay/projects/insuwiki` 에서 **`git pull --ff-only`** (차단② 해소 · 이걸 빼면 머지 무의미)
3. **`run_pipeline.sh` 배포** (차단① 해소 — **별건 태스크 필요**. 현재 어디에도 없음)
4. 타이머 1주기 관측 후 **wiki 카드 실제 생성 확인**
5. 첨부 **양성 경로**(로그인 시 열림) 실환경 재확인 — 로컬 미실증 항목

**별건 위임 권고 4건**: F5(tsc 91) · F3(자막 tier1) · F4(health 1줄) · G3(동어반복 단언) + F2 키 로테이션(위생)

## 9. 미실증 항목 — ANU 도 증명하지 못함
"로그인하면 첨부가 열린다"는 **여전히 미실증**이다. 하네스에 Firebase Admin 자격증명이 없어
`verifyIdToken` 이 실패한다(ANU 확인: 머신 어디에도 admin 키 파일 없음). 차단(401/400/403/404)만 실증됐다.
봇이 이를 "통과"로 포장하지 않은 것은 **정직한 처신**이며, ANU 도 이를 뒤집지 못한다. **배포 후 확인 필요.**
