diff --git a/AGENTS.md b/AGENTS.md index 31f938b..8452443 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,8 @@ copying content into prompts or new files. When documents disagree, follow this order: -1. [`docs/decision-log.md`](docs/decision-log.md) — working assumptions for Q1~Q7 +1. [`docs/decision-log.md`](docs/decision-log.md) — working assumptions for Q1~Q7 (확정) + and Q8/Q9 (계정·설정 IA / L2 의미 — 제안 until Master confirms) 2. [`docs/vision.md`](docs/vision.md) / [`docs/PRD.md`](docs/PRD.md) / [`docs/tech-design.md`](docs/tech-design.md) 3. [`docs/roadmap.md`](docs/roadmap.md) / [`docs/risk-log.md`](docs/risk-log.md) 4. [`docs/PLANNING.md`](docs/PLANNING.md) — process guide @@ -20,7 +21,9 @@ Notes: - Q1~Q7 in `decision-log.md` are **확정** (Phase 1 C, 2026-07-30). PoC-dependent sub-questions (default autonomy level, whitelist defaults, final branding) - stay open — do not invent those. To reverse a Q, update `decision-log.md` and + stay open — do not invent those. Q8/Q9 (logout/settings IA, L2 semantics) are + **제안** — implement only after Master marks them 확정; see + `docs/account-settings-ia.md`. To reverse a Q, update `decision-log.md` and derived docs in the same change. - Prefer `decision-log.md` (and the synced summary in `PLANNING.md` §2) for current working answers. diff --git a/docs/PLANNING.md b/docs/PLANNING.md index 0e95bbf..dd5c60e 100644 --- a/docs/PLANNING.md +++ b/docs/PLANNING.md @@ -30,7 +30,9 @@ 기능 명세를 쓰기 전에 아래 표를 채운다. 답이 안 나온 항목은 "보류 사유"를 적어두고 다음 회의 안건으로 남긴다. 현재 답은 [`decision-log.md`](./decision-log.md)에 있으며, **Q1~Q7은 Phase 1 C에서 확정**(2026-07-30). -PoC 의존 하위 질문(자율성 기본값 등)만 열려 있다 — decision-log를 단일 기준으로 따른다. +PoC 의존 하위 질문(자율성 기본값 등)과 **Q8/Q9(계정·설정 IA / L2 의미, 2026-08-03 제안)** 가 +열려 있다 — decision-log를 단일 기준으로 따른다. 계정 UX 상세는 +[`account-settings-ia.md`](./account-settings-ia.md). | # | 질문 | 결정 | 상태 | |---|------|------|------| @@ -105,7 +107,8 @@ PoC 의존 하위 질문(자율성 기본값 등)만 열려 있다 — decision- 3. [`tech-design.md`](./tech-design.md) — 온디바이스/서버 경계, 데이터 흐름, 에스컬레이션 로직 4. [`risk-log.md`](./risk-log.md) — 회의 자료 §2-6, §oslayer §4의 리스크를 완화 상태와 함께 추적 5. [`roadmap.md`](./roadmap.md) — L0~L4 단계, 자체 앱→OS 레이어 확장 시점 반영 -6. [`decision-log.md`](./decision-log.md) — Q1~Q7 결정과 근거, 계속 누적 기록 +6. [`decision-log.md`](./decision-log.md) — Q1~Q7 확정 + Q8/Q9 제안, 계속 누적 기록 +6a. [`account-settings-ia.md`](./account-settings-ia.md) — 초대 가입·로그아웃·설정 IA·L2 갭 명세 7. [`poc-plan.md`](./poc-plan.md) — PoC #1(말투 학습)·#3(사칭/신뢰 수용성) 실행 계획과 Go/No-Go 기준 8. [`poc-materials.md`](./poc-materials.md) — 모집 문구, 동의 안내, 역할극 스크립트, 인터뷰 질문지 초안 9. [`user-interview-guide.md`](./user-interview-guide.md) — Q3 자율성 수용성 인터뷰 (와카뷰 사용자 관점) diff --git a/docs/PRD.md b/docs/PRD.md index e7f5260..abe3238 100644 --- a/docs/PRD.md +++ b/docs/PRD.md @@ -12,6 +12,17 @@ ## 2. 유저 플로우 +### 2.0 계정 · 입장 (클로즈드 베타) + +Phase 1은 **이메일/비밀번호 계정이 아니라 초대 코드 입장**이다 +(`decision-log.md` Q8 제안, `invite-ops.md`, `account-settings-ia.md`). + +1. **새로 가입** — 발급된 초대 코드 + 표시 이름 → 세션 토큰 발급 → 말투 온보딩 +2. **이미 가입** — 같은 초대 코드로 로그인(토큰 재발급) → 메인 +3. **로그아웃** — 설정에서 명시적 로그아웃 → 서버 세션 종료 + 기기 토큰 삭제 → 입장 화면 + (2026-08-03 기준 앱 UI 미완 — P0 갭) +4. **탈퇴** — Phase 1 앱 UI 비범위 (운영 수동) + ### 2.1 온보딩 (말투 학습) 1. 가입 시 기존 대화 일부 임포트 또는 짧은 질문 응답으로 말투 초기 세팅 (목표: 5분 이내) 2. 관계별 페르소나 초기값 설정 — 최소 "가까운 사이 / 공식적인 사이" 2종만 v1에서 지원 @@ -20,12 +31,18 @@ ### 2.2 읽씹 종결 시나리오 (핵심) 1. 사용자가 "방해금지"(수면 중 등) 상태를 켜거나, 앱이 비활성 상태 감지 시 자동 제안 2. 상대가 메시지를 보냄 → 와카뷰가 맥락(상태, 최근 활동 패턴)을 보고 응답 초안 생성 -3. **L1**: 사용자에게 "지금 자동 응답 보낼까요?" 알림 → 승인 시 발송 (기본값) -4. **L2**: 사용자가 화이트리스트에 등록한 상대·주제(예: "가벼운 안부, 시간 문의")에 한해 +3. **L1**: 사용자에게 "지금 자동 응답 보낼까요?" 알림 → 승인 시 발송 +4. **L2 (목표)**: 사용자가 화이트리스트에 등록한 상대·주제(예: "가벼운 안부, 시간 문의")에 한해 즉시 자동 발송, 사후 알림 5. 상대에게는 와카뷰 뱃지가 붙은 말풍선으로 표시됨 (3.1 참고) 6. 사용자가 복귀하면 미응답/보류 항목 요약 제공, 필요 시 후속 메시지 작성 +> **구현 갭 (Q9 제안):** 2026-08 현재 클라이언트는 L2를 “수신 즉시 자동응대”가 아니라 +> **와카뷰 발송 시 승인 생략 게이트트**로만 구현한다. 수신 트리거 자동응대는 +> `account-settings-ia.md` §3.4 / `roadmap.md` 후속 항목으로 분리한다. 그전까지 제품 카피는 +> 게이트 현실에 맞게 쓴다. + + ### 2.3 단톡 따라잡기 시나리오 1. 사용자가 오랜만에 단톡방 진입 또는 "안 본 동안 요약" 요청 2. 와카뷰가 **나에게 멘션된 것 / 결정된 사항** 위주로 3~5줄 요약 @@ -38,6 +55,9 @@ | 기능 | 설명 | |---|---| +| 초대 코드 가입·재로그인 | 클로즈드 베타 입장. 이메일/비번 없음 (`account-settings-ia.md`) | +| 로그아웃 | 설정에서 세션 종료 + 로컬 토큰 클리어 (P0 갭 — UI 미완) | +| 설정 IA | 말투·자율성·세션·데이터흐름·로그아웃을 한 진입점에서 발견 가능 | | 와카뷰 뱃지 | 와카뷰가 작성한 말풍선은 사람 말풍선과 시각적으로 구분(점선 테두리 + 뱃지 라벨) | | 자율성 설정(L0~L2) | 전역 기본값 + 상대별 예외 설정 | | 응답 승인 UI | L1 초안을 한 탭으로 검토·수정·발송 | diff --git a/docs/account-settings-ia.md b/docs/account-settings-ia.md new file mode 100644 index 0000000..242fbba --- /dev/null +++ b/docs/account-settings-ia.md @@ -0,0 +1,111 @@ +# 계정 · 인증 · 설정 IA (Phase 1 갭 명세) + +권위: [`decision-log.md`](./decision-log.md) Q8·Q9 (제안) · [`PRD.md`](./PRD.md) · [`invite-ops.md`](./invite-ops.md). +이 문서는 **구현 전에** 제품 경계를 고정하기 위한 명세다. Q8/Q9가 `확정`되기 전에는 +코드를 크게 바꾸지 않는다. + +## 1. 현재 상태 (as-is) + +| 영역 | 있음 | 없음 / 불완전 | +|------|------|----------------| +| 가입 | 초대 코드 + 표시 이름 (`SignupScreen`, `POST /auth/signup`) | 이메일·비밀번호·소셜 | +| 로그인 | 서버 `POST /auth/login` (초대 코드) | 앱 UI에 「이미 가입」경로 | +| 세션 | Bearer 토큰 로컬 저장, `SessionsScreen` 목록/종료 | 종료 후 로컬 토큰 클리어·가입 화면 복귀 | +| 로그아웃 | — | 메인 메뉴 로그아웃 **없음** | +| 설정 | ⋮ → 말투 / 자율성(안에 세션·데이터흐름) | 단일 「설정」진입점·로그아웃 묶음 | +| L2 | 서버 발송 게이트 + 화이트리스트 키워드 | PRD §2.2식 **수신 트리거 자동응대** | + +## 2. Phase 1 목표 모델 (to-be, Q8 제안) + +### 2.1 계정 + +- **초대 코드 클로즈드 베타만.** 이메일/비번/OAuth는 Phase 1 비범위 (`decision-log` Q7·Q8a). +- 한 초대 코드 = 한 가입 (`invite-ops.md`). 데모 코드 `DEMO-YKAVU`는 운영 예외(재사용 허용). +- 사용자는 **숫자 사용자 ID**로 페어링한다 (표시 이름만으로는 대화 불가 — 기존 Track A). + +### 2.2 가입 / 로그인 UX + +``` +[스플래시] + → 토큰 유효? → 메인(대화 목록) + → 없음 → [입장 화면] + ├─ 새로 가입 (초대 코드 + 표시 이름) → 말투 온보딩 → 메인 + └─ 이미 가입 (초대 코드로 로그인) → 메인 +``` + +- 「이미 가입」은 같은 초대 코드로 `POST /auth/login`을 호출해 새 세션 토큰을 받는다. +- 비밀번호 없음. 초대 코드 유출 = 계정 탈취 가능 → 베타 한정 리스크로 `invite-ops`에 명시. + +### 2.3 로그아웃 (P0 갭) + +1. 설정(또는 ⋮) → **로그아웃** 확인 +2. `DELETE /users/:id/sessions/:current` (또는 동등 revoke) +3. 로컬: `session_token` / 유저 캐시 삭제, API 클라이언트 토큰 클리어 +4. 네비게이션: 입장(가입/로그인) 화면으로 스택 리셋 +5. 말투 샘플 등 온디바이스 DB는 **기기 잔존**(같은 사용자가 다시 로그인하면 재사용) — + “기기에서 전부 삭제”는 별 옵션(후속) + +### 2.4 설정 IA + +대화 목록에서 발견 가능한 한 진입점(권장: ⋮ 유지 + 라벨 「설정」, 또는 톱니 아이콘). + +| 항목 | 화면 | 비고 | +|------|------|------| +| 말투 샘플 | `OnboardingToneScreen` | 기존 | +| 자율성 · 화이트리스트 · 관계 기본 | `AutonomySettingsScreen` | 기존 | +| 로그인 세션 | `SessionsScreen` | 기존, 로그아웃과 구분 | +| 데이터 흐름 | `DataFlowScreen` | 기존 | +| **로그아웃** | 확인 다이얼로그만 | **신규** | + +채팅방 앱바에는 설정 전체를 넣지 않는다 (거부권·스누즈만 유지). + +### 2.5 계정 삭제 + +- Phase 1 앱 UI **비범위** (Q8e 제안). +- 운영 필요 시 서버/DB 수동. 정식 탈퇴 API는 베타 이후 Q. + +## 3. L2 의미 (Q9 제안) — 카피·구현 정렬 + +### 3.1 PRD가 말하는 것 (`PRD.md` §2.2) + +상대가 메시지를 보냄 → (화이트리스트면) 와카뷰가 **즉시 자동 발송** + 사후 알림. + +### 3.2 지금 코드가 하는 것 + +- 사용자가 ✨으로 초안을 받고, 클라이언트가 `sender_mode=twin`으로 발송할 때 +- 서버가 L2 + 화이트리스트 매칭이면 `approved` 없이도 저장·릴레이 +- **상대 메시지 수신을 감지해 스스로 초안·발송하는 루프는 없음** + +### 3.3 Phase 1에서의 제품 카피 (제안) + +- 자율성 화면 L2 설명: + 「화이트리스트 주제의 **와카뷰 답장**은 승인 없이 보낼 수 있습니다. + 상대 메시지를 받으면 ✨으로 초안을 만든 뒤 보내세요.」 +- “자리를 비우면 알아서 답함”은 **수신 트리거 자동응대** 구현 전까지 쓰지 않음. + +### 3.4 후속 구현 (별 로드맵, Q9 확정 후) + +1. 1:1에서 상대 메시지 WS/수신 이벤트 +2. 에스컬레이션·거부권·도배·그룹 하드게이트 통과 시만 +3. 초안 생성 → L2+화이트리스트면 twin 발송, 아니면 L1 알림 +4. 사후 알림 + 되돌리기 (기존 불변식) + +## 4. 수락 기준 (구현 착수 시) + +**계정/설정 (Q8 확정 후)** + +- [ ] 입장 화면에 가입 / 이미 가입(로그인) 구분 +- [ ] 로그아웃 시 서버 세션 무효 + 로컬 토큰 삭제 + 입장 화면 복귀 +- [ ] 설정 진입에서 로그아웃이 한 번의 탭 경로로 발견 가능 +- [ ] 이메일/비번 UI를 추가하지 않음 + +**L2 정렬 (Q9 확정 후)** + +- [ ] L2 설명이 발송 게이트 현실과 모순되지 않음 +- [ ] 수신 자동응대를 넣을 경우 별도 roadmap 체크리스트 + 안전 불변식 테스트 + +## 5. 비범위 (이 문서에서 다루지 않음) + +- 이메일 인증, OAuth, 비밀번호 재설정 +- OS 레이어 계정 연동 +- L3/L4 diff --git a/docs/decision-log.md b/docs/decision-log.md index 50d76c3..4ec6909 100644 --- a/docs/decision-log.md +++ b/docs/decision-log.md @@ -34,9 +34,35 @@ **순서:** ① 자체 앱 클로즈드 베타로 핵심 가설 검증 → ② 검증되면 OS 레이어 읽기 전용 허브 → ③ 자체 앱은 L3·L4 등 완전한 기능의 최종 목적지로 유지. +## Q8 — 계정·인증·설정 IA (클로즈드 베타 갭) — 제안 + +2026-08-03 Master 피드백: 앱에 **로그아웃/설정 진입이 불명확**하고, 일반 이메일·비밀번호 +계정 시스템처럼 보이지 않는다. 코드부터 넣지 말고 문서에 먼저 못을 박는다. + +| # | 질문 | 제안 결정 | 근거 | 상태 | +|---|------|----------|------|------| +| Q8a | Phase 1 계정 모델? | **초대 코드 클로즈드 베타 유지** (이메일/비번·소셜 로그인 **비범위**) | Q7 자체 앱 베타 + `invite-ops.md`. 인증 표면을 키우면 가설 검증보다 계정 인프라에 시간이 간다 | 제안 | +| Q8b | 재접속(로그인) UX? | **초대 코드 + 표시 이름 재입력**으로 토큰 재발급. 가입 화면에 「이미 가입」경로 명시 | 서버에 `POST /auth/login`(초대 코드)은 있음. 앱 UI가 없음 | 제안 | +| Q8c | 로그아웃? | **필수 P0 갭**. 메뉴에서 로그아웃 → 서버 현재 세션 revoke + 로컬 토큰/유저 상태 클리어 → 가입/로그인 화면 | 지금 `SessionsScreen` 종료는 로컬 클리어가 없어 불완전 | 제안 | +| Q8d | 설정 IA? | 대화 목록 **⋮ 또는 설정 진입점** 하나로 묶음: 말투 · 자율성 · 로그인 세션 · 데이터 흐름 · **로그아웃** | 자율성만 깊숙이 있어 발견성이 떨어짐 | 제안 | +| Q8e | 계정 삭제/탈퇴? | Phase 1에서는 **운영자 수동/후속**. 베타 UX에 「탈퇴」를 넣지 않음(초대 코드 1회용 정책과 맞춤). 필요 시 C 이후 별도 Q | 탈퇴·GDPR급 삭제는 베타 가설 검증보다 큼 | 제안 | + +**Master 승인 시** 위 행 상태를 `확정`으로 바꾸고 `PRD.md` / `tech-design.md` / `roadmap.md`의 +대응 항목을 같이 갱신한 뒤 구현한다. + +상세 명세: [`account-settings-ia.md`](./account-settings-ia.md). + +## Q9 — L2 “자동 응답”의 제품 의미 — 제안 + +| # | 질문 | 제안 결정 | 근거 | 상태 | +|---|------|----------|------|------| +| Q9 | L2는 무엇인가? | **목표(PRD §2.2)**: 상대 메시지 수신 → 화이트리스트면 초안·발송까지 무인. **현재 구현**: 클라이언트가 와카뷰 발송을 시도할 때 서버가 화이트리스트면 `approved` 없이 통과시키는 **발송 게이트**. Phase 1 베타 직전 최소는 게이트를 유지하되, UI/카피로 “상대가 오면 알아서 답한다”고 오해되지 않게 하고, **수신 트리거 자동응대는 별도 로드맵 항목**으로 분리 | 2026-08-03 실사용에서 L2+주제 등록 후에도 ✨ 없이는 응답이 없어 혼란 | 제안 | + ## 아직 열려 있는 하위 질문 (PoC/실사용 의존 — D 이후) - 와카뷰 응답 임계점·화이트리스트 기본 주제 목록 (실사용 데이터) - 자율성 기본 시작 레벨 L1 vs L2 (Q3 인터뷰) - 상표 정식 등록 여부 (Q6은 "와카뷰"로 명칭 확정, 등록 절차는 베타 반응 이후) - 베타 참가자 모집 규모와 방식 +- Q8 / Q9 제안 → Master 확정 + diff --git a/docs/deploy-checklist.md b/docs/deploy-checklist.md index c3990b4..5dd98de 100644 --- a/docs/deploy-checklist.md +++ b/docs/deploy-checklist.md @@ -39,13 +39,11 @@ Phase 1 **A~C** 이후 실행 트랙. 작업 단위를 하나씩 처리한다. - **`https://msn.iykyka.com` 라이브 + N3 완료 + Gemini 실초안 OK + Track A/B 완료** - 진행 중: **N4 FCM 코드 경로** → Master 시크릿 대기 → Android UI QA - UI: **iMessage-inspired light default** + soft charcoal dark 프로덕션 반영 - (`ee8a41d`, 2026-08-03) — 기본 `ThemeMode.light`, 내 버블 `#007AFF`, - 다크 캔버스 `#141418` · web/core/ai 재배포 완료 -- **Track C 콘텐츠 갭 A~F**: **GitHub+Gitea `main` 동기화 · 프로덕션 재배포 완료** - (`95422bb`, 2026-08-03) — web/core/ai 재빌드. C1 단톡 따라잡기 · C2 관계별 페르소나 · - C3 스팸/도배 · C4 자율성 상대별 예외 · C5 관계 메모 프롬프트 · C6 답장 마감(인앱 - 배지·배너 포함; OS 로컬 알림 실기기 발사는 N4 Android QA에서 확인). -- 실 FCM 기기 수신 · Android 실기기 탭(답장 마감 알림 실제 발사 확인 포함) · 사람 PoC 실행은 남음 +- **Track C 콘텐츠 갭 A~F**: 프로덕션 반영 완료 (`95422bb`대) +- **문서 갭 (2026-08-03):** 로그아웃·설정 IA·L2 의미 — + [`decision-log.md`](./decision-log.md) Q8/Q9 **제안**, + [`account-settings-ia.md`](./account-settings-ia.md). **Master 확정 전 구현 착수하지 않음** +- 실 FCM 기기 수신 · Android 실기기 탭 · 사람 PoC 실행은 남음 ### NEXT 순서 diff --git a/docs/roadmap.md b/docs/roadmap.md index 70ca738..253b741 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -333,6 +333,11 @@ Master 합의 착수 순서: **A → B → C → D(맨 마지막)**. E는 Phase - [x] 로그인 세션/토큰 (`Session`, signup/login 시 Bearer 발급) - [x] `/invites`, `/admin/metrics` 접근 제어 (`ADMIN_API_TOKEN`) - [x] 프로덕션 DB 마이그레이션 명령 (`go run . migrate`) +- [ ] **계정·설정 IA 갭** (`decision-log` Q8 제안, [`account-settings-ia.md`](./account-settings-ia.md)) + — Master 확정 후 착수. 이메일/비번 도입 금지(Phase 1) + - [ ] 입장 화면: 새로 가입 / 이미 가입(`POST /auth/login`) 구분 + - [ ] 로그아웃: 세션 revoke + 로컬 토큰 클리어 + 입장 화면 복귀 + - [ ] 설정 진입점에 로그아웃·세션·자율성·말투·데이터흐름 묶기 **A3. Flutter — 메신저답게 다듬기** (A1/A2 이후) - [x] 대화 목록/연락처 UI를 서버 API에 연결 @@ -342,6 +347,11 @@ Master 합의 착수 순서: **A → B → C → D(맨 마지막)**. E는 Phase - [x] E2E QA — `scripts/e2e_a3.py`로 A3 HTTP 플로우 16/16 통과(가입·연락처·대화·히스토리· draft/L1·에스컬레이션·알림 로그·되돌리기·거부권·화이트리스트). `go test`/`pytest`/`flutter test` 동시 통과. Android 에뮬레이터 UI 탭은 이 환경에 SDK가 없어 체크리스트는 `mobile/README.md`에 유지 +- [ ] **L2 카피·의미 정렬** (`decision-log` Q9 제안) — 현재는 발송 게이트; UI가 + “수신 자동응대”로 오해되지 않게 설명 수정. 수신 트리거 자동응대는 별 항목 +- [ ] **L2 수신 트리거 자동응대** (PRD §2.2 목표) — Q9 확정·안전 게이트 테스트 후. + 상대 메시지 수신 → 초안 → 화이트리스트면 twin 발송 + 사후 알림/되돌리기 + ##### B. 그다음 — 베타 품질 - [~] FCM 푸시 연동 — 토큰 등록 + 에스컬레이션 시 `notifyUser` + `POST /admin/push-test`. diff --git a/docs/tech-design.md b/docs/tech-design.md index 5a2180e..adf9fff 100644 --- a/docs/tech-design.md +++ b/docs/tech-design.md @@ -55,6 +55,20 @@ v1에서는 커스텀 모델을 새로 학습하지 않는다. 대신 **검색 - **v2 이후 온디바이스 증류 대비**: 배터리/지연/프라이버시 압박으로 자체 경량 모델이 필요해지면, 이 코퍼스가 그 모델의 기반 학습 데이터가 된다. v1 시점에는 착수하지 않는다 +## 2-2. 인증 · 세션 (클로즈드 베타) + +상세 UX: [`account-settings-ia.md`](./account-settings-ia.md) · 운영: [`invite-ops.md`](./invite-ops.md). + +| 경로 | 역할 | +|------|------| +| `POST /auth/signup` | 미사용 초대 코드 + `display_name` → User + Session(Bearer) | +| `POST /auth/login` | 이미 사용된 초대 코드 → 새 Session (앱 UI 갭) | +| `GET/DELETE /users/:id/sessions...` | 멀티 디바이스 목록·종료 | +| 클라이언트 | `shared_preferences`에 토큰 저장; **로그아웃 시 revoke + 로컬 삭제** (갭) | + +- Phase 1은 **비밀번호·OAuth 없음.** 초대 코드가 비밀에 해당한다. +- 로그아웃은 서버 세션 무효화만으로는 부족하고, 클라이언트가 토큰을 지워야 입장 화면으로 돌아간다. + ## 3. 자율성 엔진 (L0~L2) 1. 수신 메시지 → 에스컬레이션 판정기 먼저 통과 (금전/약속 확정/민감 키워드+의도 분류) @@ -62,9 +76,18 @@ v1에서는 커스텀 모델을 새로 학습하지 않는다. 대신 **검색 3. 아니면 자율성 레벨 확인: - L0: 초안만 생성해 사용자에게 보여줌, 발송 없음 - L1: 초안 생성 + 발송 승인 요청 알림 - - L2: 상대·주제가 화이트리스트에 있으면 즉시 발송, 아니면 L1과 동일하게 강등 + - L2 **(목표)**: 상대·주제가 화이트리스트에 있으면 즉시 발송, 아니면 L1과 동일하게 강등 4. 발송된 모든 자동 응답은 로컬 이벤트 로그에 기록 (사후 알림 + 되돌리기 버튼 노출) +### 3-1. 현재 서버 게이트 vs 목표 오케스트레이션 + +- **현재 (`core-backend` 메시지 POST):** `sender_mode=twin`일 때만 레벨 검사. + L2 + `whitelistMatches(text)`면 `approved` 없이 통과. 수신 이벤트가 이 경로를 + 자동 호출하지는 않는다. +- **목표 (PRD §2.2 / decision-log Q9):** 상대 메시지 수신 → (게이트 통과 시) 초안 → + L2면 twin 발송. 구현은 별도 로드맵 항목으로 분리한다. + + 에스컬레이션 판정기는 v1에서는 규칙 기반(키워드 + 간단한 의도 분류) + 온디바이스 모델의 결합으로 시작하고, 오탐/누락 사례를 베타 로그로 계속 튜닝한다. 100% 정확도를 목표하지 않는다 — 애매하면 항상 에스컬레이션 쪽으로 fail-safe.