# InsuWiki — visibility shared 삭제 + ai_suggestions 보안 통합 수정

## 개요
Agent 미팅 합의(shared 삭제, public 통일) + task-1805(ai_suggestions 보안)를 통합하여 한 번에 수정.
task-1805 worktree(task/task-1805-dev3)는 머지하지 않고 폐기. 이 task에서 필요한 부분만 가져와 통합.

## ★★★ 선행 조건 ★★★
Firestore에서 `visibility == "shared"` 문서가 존재하는지 확인:
```bash
# Firebase Admin SDK 또는 Firestore 콘솔에서 확인
# 또는 NextJS API로 확인
cd /home/jay/projects/insuwiki/nextapp
# Firestore 쿼리로 shared 문서 수 확인
```
shared 문서가 0건이면 마이그레이션 불필요. 1건 이상이면 public으로 일괄 변경 필요.

## 수정 내용

### 1. Firestore Rules — ai_suggestions 보안 + shared 삭제
**파일**: `/home/jay/projects/insuwiki/firestore.rules`

(A) ai_suggestions 서브컬렉션 보안 규칙 추가 (task-1805 핵심):
```
match /documents/{docId}/ai_suggestions/{suggestionId} {
  allow read: if isAuthenticated()
    && (resource.data.userId == request.auth.uid
        || get(/databases/$(database)/documents/documents/$(docId)).data.visibility == 'public'
        || get(/databases/$(database)/documents/documents/$(docId)).data.authorId == request.auth.uid);
  allow write: if isAuthenticated();
}
```

(B) isPublic() 함수에서 shared 제거:
```
// 현재
function isPublic() {
  return resource.data.visibility == 'public'
      || resource.data.visibility == 'shared'
      || !('visibility' in resource.data);
}

// 변경
function isPublic() {
  return resource.data.visibility == 'public'
      || !('visibility' in resource.data);  // 레거시 호환 유지
}
```

### 2. 코드 3곳 shared → public 통일
**파일들** (InsuWiki Next.js 앱):

(A) `src/components/BacklinksPanel.tsx`:
- Unlinked Mentions 쿼리: `where('visibility', '==', 'shared')` → `where('visibility', '==', 'public')`

(B) `src/components/FloatingTermDetection.tsx`:
- task-1802에서 이미 authorId 필터 추가됨. 남아있는 shared 쿼리가 있으면 public으로 변경.

(C) `src/components/HubDocuments.tsx` (또는 WikiLinkList.tsx):
- shared 폴백 쿼리 → public으로 변경

### 3. TypeScript visibility 타입 중앙화
**파일**: `src/types/firestore.ts`

현재: `export type Visibility = 'public' | 'private';` (이미 shared 없음 ✅)

추가로 상수화:
```typescript
export const VISIBILITY_VALUES = ['public', 'private'] as const;
export type Visibility = typeof VISIBILITY_VALUES[number];
```

### 4. 코드에서 shared 문자열 리터럴 전수 제거
```bash
grep -rn "'shared'" src/ --include="*.ts" --include="*.tsx" | grep visibility
```
결과가 0건이 되어야 함.

### 5. 머지 + 배포
- 메인 브랜치에 머지 (worktree 사용 시 반드시 머지까지 완료)
- `firebase deploy --only firestore:rules` 실행 (코드 배포 후)

### 6. task-1805 worktree 정리
```bash
cd /home/jay/projects/insuwiki
git worktree remove .worktrees/task-1805-dev3 2>/dev/null || true
git branch -D task/task-1805-dev3 2>/dev/null || true
```

## 검증 시나리오
1. **보안**: 다른 사용자의 private 문서 ai_suggestions 접근 차단 확인
2. **shared 제거**: `grep -rn "shared" firestore.rules` → visibility 관련 shared 0건
3. **shared 제거**: `grep -rn "'shared'" src/ --include="*.ts*" | grep visibility` → 0건
4. **Unlinked Mentions 동작**: BacklinksPanel에서 public 문서의 Unlinked Mentions가 정상 표시
5. **레거시 호환**: visibility 필드 없는 기존 문서가 정상 접근 가능
6. **vitest 통과**: `npx vitest run` 전체 PASS
7. **firebase deploy 성공**: Rules 배포 에러 없음
8. **회귀**: InsuWiki 서비스 전체 기능 정상 동작