PRD.md §4 엣지케이스가 "안전 관련이라 v1 최소 버전 필요"로 명시한 항목(P0)인데
관련 로직이 0건이었던 마지막 콘텐츠 갭(roadmap.md §2.7-C, deploy-checklist.md
N4-C3a/b)을 채운다. Track C(콘텐츠 갭) A/B/C 전체 완료.
- core-backend/flood_detect.go(신규): floodMessageThreshold=5건 /
floodWindow=2분 상수 + floodDetected()/floodReason(). 안전을 위해 반드시
있어야 하는 기술적 최소값이라 명시적으로 placeholder로 남기고 구현했다 —
roadmap.md §3의 "PoC 결과가 있어야 정할 수 있는 것"(자율성 기본값 등 UX
기본값)과는 성격이 다름. 카운트는 대화방 내 sender_id != 소유자인 메시지만
집계(트윈 자동발송·소유자 본인 발송은 소유자 ID로 남으므로 자동 제외).
- core-backend/main.go: POST /conversations/:id/messages 하드게이트에
peer-veto → 그룹 대화 차단 다음, 에스컬레이션 체크 이전 지점으로 추가 —
트윈 자동발송 시도가 전부 지나가는 동일한 우회 불가 지점. 이미
TwinDisabledByFlood가 켜져 있으면 재계산 없이 즉시 차단(중복 로그/쿼리
방지). 새로 임계치를 넘기면 대화방을 영구 차단하고 EscalationLog 기록 +
notifyUser 푸시.
- core-backend/models.go: Conversation.TwinDisabledByFlood 필드 추가.
TwinDisabledByPeer(거부권, 사람의 일방적 선택 — v1엔 되돌리기 API 없음)와
달리 이건 시스템이 자동으로 취하는 조치라서 AGENTS.md "every automatic
action needs post-hoc notification + one-tap undo"가 그대로 적용됨 —
POST /conversations/:id/flood-reset로 되돌릴 수 있게 별도 필드로 분리.
- core-backend/a1_a2_routes.go: GET /conversations 응답에
twin_disabled_by_flood 추가(twin_disabled_by_peer와 동일한 자리).
- core-backend/flood_detect_test.go(신규): 임계치 경계값(정확히 N건은
통과, N+1건은 차단), 사람 발송은 게이트 안 걸림, 윈도우 밖 과거 메시지는
집계 제외, flood-reset으로 재개, 존재하지 않는 대화방 404 — 5개 테스트.
- mobile/lib/models/models.dart, services/api_client.dart: 모델·API
클라이언트에 twinDisabledByFlood/resetFlood() 추가.
- mobile/lib/screens/conversation_list_screen.dart: 대화 목록에 도배 차단
배지(거부권과 같은 자리, 다른 아이콘/문구).
- mobile/lib/screens/chat_screen.dart: 채팅방을 열면(목록에서 넘어온 초기
상태) 또는 발송 시도가 다시 차단되면 배너에 "자동응대 재개" 버튼을 보여줌
— one-tap undo. 새 알림 메커니즘을 만들지 않고 기존 EscalationLog/
InboxScreen을 그대로 재사용.
- mobile/test/models_test.dart: twin_disabled_by_flood 파싱 테스트 추가.
- docs/roadmap.md §2.7-C, docs/deploy-checklist.md N4-C3a/b·Track C 요약·
"바로 다음 5개"를 완료로 갱신.
테스트: go test ./... 51/51, python3 -m pytest tests/ -q 47/47,
flutter analyze 클린 + flutter test 5/5 모두 통과.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014YSB5PqF38raTxP5ABgr9m
- Resolve visual-direction conflicts (app_theme, main.dart, signup/onboarding
screens, index.html) in favor of the soft-gradient + glassmorphism design;
drop the competing Twin Shadow palette and TwinTokens usage.
- Port non-visual additions from the parallel branch: CORS middleware,
FlutterError/PlatformDispatcher crash handlers + boot timeout/fallback
screen in main.dart, and the shared demo-invite feature
(core-backend/demo.go, demo_test.go, mobile/lib/config.dart), reskinning
the demo panel to match the glass UI.
- Rename the shared demo invite code DEMO-BUNSIN -> DEMO-YKAVU on both
client and server so the tester-facing feature keeps working.
- Purge remaining "분신"/"bunsin" identifiers app-wide: Dart package name
(bunsin_mobile -> ykavu_mobile), Android applicationId/namespace
(com.bunsin.bunsin_mobile -> com.ykavu.ykavu_mobile, incl. Kotlin source
dir move), on-device DB filename, keystore alias/docs, PoC draft-generator
system prompt, and the static web boot placeholder div.