# Task: conversation_memory.py P1 보강 + InlineKeyboard + 안정성

## 개요
task-597.1에서 P0 수정 + 미팅 합의 핵심 기능(topic_tag, TopicDetector, 메타데이터 확장, PII 마스킹, XML 태그 분리, /메모리·/기억·/목차 명령어)을 구현 완료.
task-597.2에서는 미팅 합의 중 P1 항목 + UX 개선을 반영한다.

## 참고 문서
- 미팅 기록: `/home/jay/workspace/memory/meetings/2026-03-15-groupchat-memory-intelligence.md`
- task-597.1 보고서: `/home/jay/workspace/memory/reports/task-597.1.md`
- 이전 Phase 산출물: `/home/jay/workspace/services/multimodel-bot/conversation_memory.py`, `main_bot.py`

## 대상 파일
- `/home/jay/workspace/services/multimodel-bot/conversation_memory.py`
- `/home/jay/workspace/services/multimodel-bot/main_bot.py`
- `/home/jay/workspace/services/multimodel-bot/tests/test_conversation_memory.py`

## 작업 내용

### 1. 요약 중복 실행 방지 세마포어 (P1)
`_generate_summary()` 동시 실행 방지:
```python
self._summary_lock: dict[int, bool] = {}  # chat_id별 요약 진행 중 플래그
```
요약 시작 시 lock 확인 → 이미 실행 중이면 스킵. 완료 시 해제.

### 2. 레이트 리밋 + 일일 LLM 호출 예산 (P1)
미팅 합의: 비용 DoS 방지.
- `_llm_call_count: dict[str, int] = {}` (날짜별 호출 횟수)
- 일일 상한: 50회 (설정 가능)
- 상한 초과 시 요약/insight 생성 스킵 + 로그 경고

### 3. /메모리 InlineKeyboard 페이지네이션
현재 `/메모리`는 텍스트 목록만 표시. InlineKeyboardButton으로 각 요약을 클릭하면 전문을 표시:
- callback_data 형식: `mem:{filename}`
- CallbackQueryHandler 등록
- 페이지네이션: 5건씩 + "이전/다음" 버튼

### 4. 날짜 전환 경계 시간순 역전 방지 (P0 from meeting)
메시지 timestamp 기준으로 JSONL 파일 결정:
```python
def _today_jsonl_path(self, ts: datetime | None = None) -> Path | None:
    target_date = (ts or datetime.now()).strftime("%Y-%m-%d")
    ...
```
자정 직후 메시지가 이전 날짜 파일에 쓰이지 않도록 보장.

### 5. _current_topic 확정 로직
task-597.1에서 topic 변경 시 "pending"으로만 설정. 다음 요약 생성 시 LLM이 반환한 topic_tag로 확정:
```python
# _generate_summary 내, 요약 성공 후:
if self._current_topic.get(chat_id) == "pending":
    self._current_topic[chat_id] = topic_slug
```

## 테스트 요구사항
- task-597.1의 87개 테스트 회귀 없음
- 신규 테스트: 세마포어 중복 방지, 레이트 리밋, 날짜 전환 경계, topic 확정
- pytest 전체 통과 + pyright 0 에러

## 절대 규칙
- 기존 동작 하위호환 유지
- bot_settings.json 모델 설정 변경 금지
- 인메모리 deque에는 원본 저장, JSONL에만 PII 마스킹