ai-service:
- escalation_filter/retrieve_style/generation/main(FastAPI)의 ad-hoc
TestClient 검증을 ai-service/tests/ 정식 pytest 스위트로 승격(34개).
Gemini 호출은 mock, retrieve_style은 overlap이 recency를 항상
이긴다는 것(과거 버그 재발 방지)까지 포함
- on_event(deprecated) -> lifespan 컨텍스트 매니저로 교체
core-backend: 자율성 플로우(L0->L1->L2) 통합 테스트를 쓰려면 실제 분기
로직이 있어야 해서, roadmap.md §2.2에서 미결로 남아있던 자율성 엔진
오케스트레이션 최소 버전을 이번에 구현:
- PATCH /users/:id/twin-settings -- 자율성 레벨 변경 (기본값 L0)
- 트윈 발송 시 에스컬레이션 통과 후 레벨 확인: L0는 항상 차단, L1은
approved:true 필요, L2는 화이트리스트 매칭 시 즉시 자동발송·매칭
없으면 L1과 동일하게 승인 필요. 에스컬레이션은 레벨/화이트리스트/
승인 여부와 무관하게 항상 우선(테스트로 확인)
- 화이트리스트 매칭은 v1 최소 구현(전역 키워드 매칭, 상대별 예외는
아직) -- 대화방↔연락처 연결이 모델링되지 않아 보류, README에 명시
온보딩·채팅·설정 수동 QA는 Flutter 클라이언트가 없어 이 환경에서는
보류, roadmap.md에 근거 남김
발견한 문제: POST /conversations/:id/messages가 sender_mode=twin을
검증 없이 그대로 저장·브로드캐스트하고 있었음 -- 에스컬레이션 게이트는
초안 생성(/draft) 경로에만 있었고 실제 발송 경로엔 없어서, 클라이언트가
/draft를 거치지 않고 바로 twin 메시지를 보내면 안전선을 완전히
우회할 수 있었다.
- ai-service: /draft와 별개인 POST /escalate/check 하드게이트 엔드포인트 추가
- core-backend: AIServiceClient.checkEscalation 추가, 메시지 저장 직전에
twin 발송이면 무조건 호출하도록 해서 발송이 실제로 일어나는 단
하나의 지점에서 막음. AI 서비스 응답 불가 시 fail-safe로 발송 차단.
에스컬레이션되면 저장/브로드캐스트 없이 escalation_logs에만 기록.
사람이 직접 보내는 메시지는 게이트 대상 아님
- core-backend: DELETE /users/:id 추가 -- 유저가 걸린 모든 행(트윈 설정·
화이트리스트·연락처·대화참여·메시지·에스컬레이션로그·유저 본인)을
트랜잭션으로 삭제 (tech-design.md §5 "사용자가 언제든 초기화 가능")
- 온디바이스 암호화·데이터 흐름 대시보드는 Flutter 클라이언트 책임이라
이 환경에서는 보류, roadmap.md에 근거 남김
ai-service/ wraps generate_draft/escalation_filter/retrieve_style
behind a single POST /draft endpoint that the Go core will call
internally. poc/tone-corpus/ stays untouched for corpus experiments
and blind-eval; this is the promoted copy meant for the real service.
Verified with TestClient: style_examples path, history/retrieval
path (confirms the earlier scoring fix still ranks the on-topic
exemplar first), escalation short-circuit, and 422 validation when
zero or both of style_examples/history are given.
Still missing: the Go core's actual HTTP client calling this service.