Closes the biggest content gap found against PRD.md §3.1 P0: group chat catch-up was one of only two v1 MVP scenarios and had zero implementation. Backend (core-backend): - ConversationParticipant.LastReadMessageID read marker. - POST /conversations/:id/read advances the caller's marker (never backward). - GET /conversations/:id/summary builds context from messages since that marker and calls ai-service's new POST /summarize; returns unread_count and needs_reply. - GET /conversations now reports unread_count per room. - Safety: group conversations now unconditionally block twin-authored sends (POST /conversations/:id/messages), regardless of the sender's global autonomy level. PRD.md §2.3-③ requires this scenario stay L0-fixed with no auto-send; autonomy level is a per-user global setting today, so this closes the only path a global L2 whitelist match could otherwise auto-send into a group. AI service (ai-service): new summarize.py module (same Gemini-call shape as generation.py's draft_reply, no escalation/identity gating since nothing generated here is ever sent) + POST /summarize. Mobile: "새 대화" dialog now supports adding/removing multiple peer fields (2+ peers -> is_group:true automatically); unread badge on conversation rows; ChatScreen takes isGroup and renders its L1 panel as L0-locked for groups; new "안 본 동안 요약" AppBar action opens a dialog with the summary and, when a reply looks needed, a button that feeds straight into the existing draft-request flow. Verified with a real Flutter build against a live core-backend + ai-service instance (Playwright driving 3 demo accounts through group creation, unread badges, and the summary dialog) — this caught a real ordering bug: marking the read marker on chat *open* meant the summary was always empty by the time you could tap it, since opening the room already advanced the marker past everything you'd come to catch up on. Fixed by marking read on screen *exit* (dispose) instead, so the marker reflects what was unread that whole visit and updates once you leave. go test / pytest / flutter analyze+test all pass. |
||
|---|---|---|
| .. | ||
| android | ||
| lib | ||
| test | ||
| web | ||
| .gitignore | ||
| .metadata | ||
| Dockerfile | ||
| Dockerfile.prebuilt | ||
| README.md | ||
| analysis_options.yaml | ||
| pubspec.lock | ||
| pubspec.yaml | ||
README.md
와카뷰 mobile (Flutter)
Phase 1 클라이언트 (docs/roadmap.md §2.3 / A3). v1은 Android 빌드만 대상으로 한다
(docs/tech-design.md §8).
지금 있는 것
- 초대 코드 가입 (
POST /auth/signup) + Bearer 세션 - 말투 샘플 온보딩(기기 로컬 저장; 최종 카피는 PoC 이후)
- 대화방 목록/생성 (
GET/POST /conversations) — 수동 ID 입력 제거 - 연락처 CRUD + 대화 시작 (
/users/:id/contacts) - 채팅 히스토리 로드 + WebSocket 수신
- 와카뷰 초안 / L1 승인(수정·버리기·재초안·승인 발송)
- 와카뷰 뱃지 · 거부권 · 되돌리기
- 사후 알림 함 (
GET /users/:id/escalation-logs) - 자율성 L0~L2 + 화이트리스트 CRUD UI
실행
# core-backend + ai-service 가 떠 있어야 실제 호출이 된다
cd mobile
flutter pub get
# Android 에뮬레이터 → 호스트의 core-backend(기본 8080)
flutter run --dart-define=CORE_API_BASE=http://10.0.2.2:8080
# 실기기/로컬 네트워크
flutter run --dart-define=CORE_API_BASE=http://<your-lan-ip>:8080
E2E
API 레벨 (클라우드/CI에서 실행 가능)
# core-backend :8080 + ai-service :8001 기동 후
export ADMIN_API_TOKEN=dev-admin-token
python3 scripts/e2e_a3.py
A3 체크리스트(가입·연락처·대화·히스토리·draft/L1·에스컬레이션·되돌리기·거부권·화이트리스트)를 HTTP로 검증한다.
에뮬레이터/실기기 UI 탭 (Android SDK 필요)
- 초대 코드로 가입 → 말투 샘플 저장(또는 나중에)
- 연락처에 상대 사용자 ID 등록 → 대화 시작
- 메시지 전송 · 새로고침 후 히스토리 유지
- 초안 요청 → L1 패널에서 수정/버리기/승인 발송 → 와카뷰 뱃지 확인
- 민감 문장으로 에스컬레이션 → 사후 알림 함 진입
- 와카뷰 메시지 되돌리기 · 거부권
- 자율성 L0~L2 + 화이트리스트 추가/삭제
로컬 DB (drift + SQLCipher)
말투 샘플·온보딩 플래그는 lib/db/ 암호화 SQLite에 저장한다. 패스프레이즈는
flutter_secure_storage. Linux 개발 호스트에 libsqlcipher.so가 없으면 메모리 폴백으로
기동한다(Android 릴리즈 경로에서는 SQLCipher 사용).
# Linux에서 drift 테스트/암호화 DB를 쓰려면
sudo apt-get install -y libsqlite3-dev libsqlcipher1
푸시 (FCM)
- 코드:
lib/services/push_token_service.dart— Firebase 가능 시 실 토큰, 아니면install: - Master 설정:
docs/fcm-setup.md(google-services.json+FCM_SERVER_KEY)
아직 없는 것
- Master Firebase 시크릿 주입 후 실기기 푸시 스모크 (N4-1/3/4)
- 오프라인 메시지 큐 / 멀티디바이스 실시간 설정 동기화 고도화
- 온보딩 말투 UX 디테일 (PoC #1 결과는 맨 마지막에 반영)
- iOS 빌드 (v1 범위 밖)