# task-1679.1 완료 보고서

**S**: InsuWiki 협업 검토 시스템 Phase 1~2가 master에 머지 완료되어 상태 머신, 검토 제도, 경량 수정 면제 등 핵심 로직이 운영 중이다.

**C**: Phase 3의 신뢰도 점수 계산, 운영 모니터링, 오류 신고, 버전 관리 백엔드가 미구현 상태로, 문서 품질 자동 평가와 검토자 적체 알림이 불가능하다.

**Q**: Phase 3 백엔드 4대 기능(reliabilityScores, 운영 모니터링, 오류 신고, 버전 관리)을 기존 아키텍처 패턴에 맞춰 안전하게 구현할 수 있는가?

**A**: 순수 로직 모듈 + CF wrapper 패턴으로 4대 기능 구현 완료. 122건 테스트 전부 통과, TypeScript 컴파일 에러 0건. 기존 기능 회귀 없음.

## 구현 내역

### Feature 1: reliabilityScores 계산
- 6가중치 compositeReliabilityScore 계산 (verification 0.30, authority 0.20, source 0.15, review 0.15, freshness 0.10, sourceRef 0.10)
- knockout 조건: 한 차원이 0이면 floorScore(0.1) 적용
- freshness 이산 구간: 30d→1.0, 90d→0.8, 180d→0.5, 365d→0.3, 초과→0.1
- 일일 배치 재계산 Scheduled Function (24시간 주기, 커서 기반 페이지네이션 100건씩)
- 가중치 설정 JSON 외부화 (`config/reliabilityWeights.json`)
- 테스트: 67건 순수 함수 + 8건 배치 CF = 75건 통과

### Feature 2: 운영 기능
- 검토자 적체 모니터링 6시간 주기 Scheduled Function
- 3단계 SLA: WARNING(10건/48h), CRITICAL(20건/96h), ESCALATION(168h)
- FCM 멀티캐스트 알림 (admin 유저 대상)
- FCM 토큰 관리 API (`POST/DELETE /api/admin/fcm-token`)
- 테스트: 16건 SLA 판정 + FCM payload 테스트 통과

### Feature 3: 오류 신고
- `documents/{docId}/reports/{reportId}` 서브컬렉션
- `POST /api/wiki/entries/{id}/report` (verifyMember 인증)
- 선택형 사유 4개 (`incorrect_info`, `outdated_info`, `misleading_content`, `missing_source`) + 선택적 메모 (200자 제한)
- Rate limit: 동일 유저×문서 1회/24h, 전체 10건/일 — 초과 시 429 반환
- 신고 → 자동 상태 변경 없음
- Firestore Rules: 신고자 실명은 reviewer/admin만 열람 가능
- 테스트: 6건 rate limit 테스트 통과

### Feature 4: 버전 관리 (백엔드)
- 규정(regulation/policy_pdf) 문서의 `sourceMeta`/`sourceUrl`/`regulationId`/`effectiveDate` 변경 시 자동 `needs_re_review` 전환 — 경량 수정 면제 불가
- 버전 보존 정책: maxVersions(50) 또는 maxAgeDays(365) — 일일 Scheduled Function
- 기존 `onDocumentUpdate.ts`에 규정 변경 감지 통합 (기존 content 체크 앞에 삽입, 기존 로직 무변경)
- 테스트: 8건 규정 변경 + 8건 버전 보존 + onDocumentUpdate 기존 6건 + 신규 3건 = 25건 통과

## 산출물 파일

### 신규 파일 (20개)
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/types/reliability.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/types/report.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/types/version.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/config/reliabilityWeights.json`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/reliabilityScoring.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/reliabilityBatchRecalc.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/backlogMonitoring.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/backlogScheduler.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/reportRateLimiter.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/regulationChangeDetector.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/versionRetention.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/versionRetentionScheduler.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/nextapp/src/app/api/wiki/entries/[id]/report/route.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/nextapp/src/app/api/admin/fcm-token/route.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/reliabilityScoring.test.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/reliabilityBatchRecalc.test.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/backlogMonitoring.test.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/reportRateLimiter.test.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/regulationChangeDetector.test.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/versionRetention.test.ts`

### 수정 파일 (4개)
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/index.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/onDocumentUpdate.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/onDocumentUpdate.test.ts`
- `/home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/firestore.rules`

## 발견 이슈 및 해결

### 자체 해결 (3건)
1. **reliability.ts 미사용 Timestamp import** — 타입 파일에서 FirestoreTimestamp alias 제거. 실제 사용하는 모듈은 `Date` 타입만 사용.
2. **report route.ts 크로스바운더리 import** — Next.js API 라우트에서 functions/src를 상대경로로 import 불가. rate limit 로직과 상수를 route 파일에 인라인으로 이동.
3. **onDocumentUpdate.test.ts 미사용 writeAuditLog import** — mock에서만 사용되므로 직접 import 제거.

## 테스트 결과

- **순수 함수 테스트 4개 파일**: 99건 전부 통과 (271ms)
- **모킹 테스트 3개 파일**: 23건 전부 통과 (255ms)
- **합계**: 122건 전부 통과
- **TypeScript 컴파일**: 에러 0건
- **기존 테스트 회귀**: 없음 (onDocumentUpdate 기존 6건 유지 + 3건 추가)

## 머지 판단
- **머지 필요**: Yes
- **브랜치**: task/task-1679.1-dev1
- **워크트리 경로**: /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1
- **머지 의견**: 122건 테스트 전체 통과, TSC 에러 0건, 기존 기능 회귀 없음. 기존 코드 수정은 `onDocumentUpdate.ts`의 규정 변경 체크 추가뿐이며, 기존 content 체크 로직은 변경 없음. Firestore Rules에 reports 서브컬렉션 규칙 추가로 보안 확보. 머지 권장.

## 마아트 독립 QC 검증

발견 이슈 8건 (CRITICAL 2, HIGH 2, MEDIUM 3, LOW 1) — **전건 수정 완료**

### 자체 해결 (8건)
1. **[CRITICAL] reports collectionGroup 인덱스 누락** — `firestore.indexes.json`에 reports+reviews 복합 인덱스 추가
2. **[CRITICAL] Rate Limit TOCTOU 경쟁 조건** — report 문서 ID를 `{userId}_{날짜}` 고정 패턴으로 변경하여 원자적 보장
3. **[HIGH] applyKnockout 미사용 + 단순 평균 불일치** — 함수 삭제, computeCompositeReliabilityScore 내 인라인 knockout 로직만 유지
4. **[HIGH] reliabilityBatchRecalc N+1 쿼리** — Promise.all 병렬화 + try/catch 개별 에러 격리
5. **[MEDIUM] FCM 토큰 API 권한 과다** — verifyMember → verifyAdmin 교체
6. **[MEDIUM] backlogScheduler timestamp 초기화** — Date.now() → Infinity로 변경
7. **[MEDIUM] firestore.rules helper 미사용** — get() 인라인 → isReviewerOrAbove()/isMemberOrAbove() 교체
8. **[LOW] SLA 경계값 테스트 누락** — 168h/96h/48h/count=19/count=9 경계값 5건 추가

## QC 자동 검증 결과

```json
{"overall": "WARN", "summary": "5 PASS, 7 SKIP, 1 WARN", "trust_summary": {"Tested": true, "Readable": true, "Unified": true, "Secured": true, "Trackable": true}}
```

- WARN 1건: TDD 순서 (병렬 에이전트 실행으로 구현/테스트 동시 생성)

## 테스트 최종 결과

- **7개 테스트 파일, 126건 전부 통과** (464ms)
- TypeScript 컴파일 에러: 0건
- 기존 테스트 회귀: 없음

## 모델 사용 기록
- 불칸-A / reliabilityScores 구현 / sonnet / -
- 불칸-B / 오류 신고 구현 / sonnet / -
- 불칸-C / 운영+버전관리 구현 / sonnet / -
- 불칸 / 마아트 QC 이슈 수정 / sonnet / -
- 마아트 / 독립 QC 검증 / sonnet / -

## 세션 통계
- 총 도구 호출: 54회

### 수정 파일 목록
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/nextapp/src/app/api/wiki/entries/[id]/report/route.ts: 5회 (Edit, Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/reliabilityScoring.test.ts: 4회 (Edit, Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/types/reliability.ts: 4회 (Edit, Write)
- bash_cmd: 4회 (Bash)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/onDocumentUpdate.test.ts: 3회 (Edit)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/reliabilityBatchRecalc.test.ts: 3회 (Edit, Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/reliabilityScoring.ts: 3회 (Edit, Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/nextapp/src/app/api/admin/fcm-token/route.ts: 3회 (Edit, Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/firestore.rules: 2회 (Edit)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/backlogMonitoring.test.ts: 2회 (Edit, Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/backlogScheduler.ts: 2회 (Edit, Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/reliabilityBatchRecalc.ts: 2회 (Edit, Write)
- /home/jay/workspace/memory/reports/task-1679.1.md: 2회 (Edit, Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/firestore.indexes.json: 1회 (Edit)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/regulationChangeDetector.test.ts: 1회 (Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/reportRateLimiter.test.ts: 1회 (Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/__tests__/versionRetention.test.ts: 1회 (Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/backlogMonitoring.ts: 1회 (Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/config/reliabilityWeights.json: 1회 (Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/index.ts: 1회 (Edit)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/onDocumentUpdate.ts: 1회 (Edit)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/regulationChangeDetector.ts: 1회 (Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/reportRateLimiter.ts: 1회 (Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/types/report.ts: 1회 (Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/types/version.ts: 1회 (Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/versionRetention.ts: 1회 (Write)
- /home/jay/projects/insuwiki/.worktrees/task-1679.1-dev1/functions/src/versionRetentionScheduler.ts: 1회 (Write)
- /home/jay/workspace/memory/tasks/task-1679.1.md: 1회 (dispatch)

### 도구 사용 현황
- Edit: 27회
- Write: 22회
- Bash: 4회
- dispatch: 1회

