# Task-820.1 완료 보고서
## InsuWiki AI 노드 자동 연결 — Phase 0 + Phase 1-A 구현

---

**S**: InsuWiki에 500개+ 문서가 축적되어 있으나, 문서 간 관련 노드 연결은 사용자의 수동 WikiLink(`[[]]`) 입력에 의존하고 있다.

**C**: 보험 용어 기반 관련 문서를 자동으로 추천하는 AI 링킹 기능이 필요하나, Firestore links 컬렉션에는 스키마 validation이 없어 오염 위험이 있고, 정적 매칭 로직과 추천 UI가 부재하다.

**Q**: Firestore 보안을 강화하고 insurance_terms 기반 정적 매칭으로 관련 문서를 자동 추천하는 Phase 0 + Phase 1-A를 안전하게 구현할 수 있는가?

**A**: Phase 0(보안 기초)과 Phase 1-A(정적 매칭) 모두 구현 완료. Firestore Rules에 links validation 추가, Cloud Function `staticMatching` onWrite 트리거로 4단계 매칭(exact/alias/공백제거/substring), RelatedDocsSidebar UI로 승인/거부 플로우 구현. 총 43개 테스트 통과(FE 21건 + BE 22건), TypeScript 에러 0건(신규 코드 한정).

---

## 생성/수정 파일 목록

### Phase 0: 보안 기초

- **수정** `nextapp/src/types/firestore.ts` — Link 타입에 method/confidence/createdBy/status/explanation/updatedAt 필드 추가, AiSuggestion 인터페이스 추가, LinkMethod/LinkCreatedBy/LinkStatus 타입 추가, COLLECTIONS.AI_SUGGESTIONS 추가
- **수정** `firestore.rules` — isValidLinkData() helper 추가, links 컬렉션 create/update에 validation 적용(기존 형식 하위 호환), documents/{docId}/ai_suggestions 서브컬렉션 규칙 추가
- **신규** `scripts/backfill-links-schema.ts` — 기존 links에 method:"manual", createdBy:"user", status:"active" 백필 (dry-run 지원, 배치 500건)

### Phase 1-A: 정적 매칭

- **신규** `functions/src/staticMatching.ts` — documents onWrite 트리거, 4단계 매칭(exact:100, alias:90, 공백제거:85, substring:70), insurance_terms/normalizeMap 메모리 캐싱(TTL 1h), config/aiLinking 설정 로드, 중복/dismissed 제외
- **수정** `functions/src/index.ts` — staticMatching export 추가
- **신규** `functions/src/__tests__/staticMatching.test.ts` — 22개 단위 테스트
- **신규** `scripts/seed-normalize-map.ts` — config/normalizeMap 초기 시드 (commonAliases 기반 자동 생성)
- **신규** `scripts/seed-ai-linking-config.ts` — config/aiLinking 초기값 시드
- **신규** `nextapp/src/components/RelatedDocsSidebar.tsx` — AI 추천 사이드바 (confidence 바, method 뱃지, 승인/거부 플로우)
- **신규** `nextapp/src/components/__tests__/RelatedDocsSidebar.test.tsx` — 21개 테스트
- **수정** `nextapp/src/app/docs/[id]/DocumentClient.tsx` — RelatedDocsSidebar 통합 (BacklinksPanel 상단)

---

## 테스트 결과

- **staticMatching 단위 테스트**: 22/22 통과 (195ms)
- **RelatedDocsSidebar 테스트**: 21/21 통과 (1.11s)
- **전체 nextapp 테스트**: 592/617 통과
  - ⚠️ 기존 테스트 실패 25건 (본 작업 범위 외): `src/app/api/ai/feedback/__tests__/route.test.ts` — 작업 전 stash 상태에서도 동일 실패 확인, 기존 결함
- **TypeScript 에러**: 0건 (nextapp), 3건 기존 에러(functions/pdfIndexing.ts, 이번 작업 무관)

---

## 발견 이슈 및 해결

### 자체 해결 (2건)

1. **staticMatching createdBy 값 오류** — 정적 매칭은 시스템 Cloud Function이므로 'ai'가 아닌 'system'으로 수정
   - 상세: `functions/src/staticMatching.ts:418` — `'ai'` → `'system'`

2. **substring match 로직 불일치** — 기존 구현에서 term의 부분문자열을 content에서 찾는 방식이 의도와 다름. content 단어가 term의 앞부분인지 확인하는 `termLower.startsWith(word)` 방식으로 수정하여 "뇌졸중"이라는 content 단어가 "뇌졸중진단"이라는 term과 매칭되도록 함

### 범위 외 미해결 (1건)

1. **ai/feedback 테스트 25건 실패** — 범위 외 사유: 이번 작업 이전부터 존재하던 기존 결함(mockAdminReviewQueueAdd 모킹 문제)

---

## 머지 판단

- **머지 필요**: Yes
- **브랜치**: task/task-820.1-dev1
- **워크트리 경로**: /home/jay/projects/insuwiki/.worktrees/task-820.1-dev1
- **머지 의견**: 8개 커밋, 43개 테스트 통과, TS 에러 0건(신규). 기존 WikiLink/BacklinksPanel 기능 미변경. Firestore Rules 하위 호환(기존 method 없는 링크 허용). 기존 테스트 회귀 없음(feedback 실패는 작업 전부터 존재). 충돌 가능성 낮음 — 머지 권장.

---

## 커밋 이력 (8건)

1. `7c3d8e0` [task-820.1] Link 타입에 AI linking 필드 추가
2. `7a828bb` [task-820.1] Firestore Rules: links validation + ai_suggestions 규칙 추가
3. `beecd2c` [task-820.1] links 백필 스크립트 작성
4. `30891b2` [task-820.1] 정적 매칭 단위 테스트 작성
5. `0390c6f` [task-820.1] Cloud Function: 정적 매칭 로직 구현
6. `059eb16` [task-820.1] normalizeMap + aiLinking config 시드 스크립트 작성
7. `571862b` [task-820.1] RelatedDocsSidebar UI 컴포넌트 + DocumentClient 통합
8. `4f18451` [task-820.1] staticMatching createdBy를 'system'으로 수정

---

## QC 자동 검증 결과

- **overall**: PASS (6 PASS, 6 SKIP)
- **재시도**: 2회 (1회차 tdd_check FAIL → check-files에 테스트 파일 추가 후 2회차 PASS)
- file_check: PASS (8/8 파일 존재 확인)
- data_integrity: PASS
- tdd_check: PASS (테스트 2개 + 구현 5개)
- critical_gap: PASS
- spec_compliance: PASS
- duplicate_check: PASS (최대 유사도 5.6%)
- api_health: SKIP (서버 작업 아님)
- test_runner: SKIP (자동 추론 관련 테스트 0개 — 수동 실행으로 43건 통과 확인 완료)
- pyright_check/style_check: SKIP (Python 파일 없음)
- schema_contract: SKIP (workers 디렉토리 없음)
