---
title: "핸드오프 — 코치노트 AI v1.0-rc2 · spec_01 기반 골격 (→ spec_02 인계)"
category: "handoff"
document_type: "핸드오프"
source_status: "original"
knowledge_group: "03_history"
priority: "High"
purpose: "코치노트 AI v1.0-rc2 6분할 구현 중 spec_01(기반 골격) 완료 후 spec_02(데이터 주입 엔진)로 넘긴 인계 핸드오프. 다음 스레드가 즉시 이어받게 현재 완료분·구현 구조·변경 파일·결정사항·후속 연결·절대 변경 금지·사용자 의도를 보존. RC2 전체 종료 핸드오프(handoff_rc2_complete)의 하위 사료."
read_when: ["코치노트AI","최신상태복구"]
updated: "2026-06-06"
work_timestamp: "20260606_181434"
context: "달록본레포CC (D:\\dallog\\dallog_git)"
source_of_truth: "https://dallog-tools.hansbridge.co.kr/knowledge/"
---

# 핸드오프 — 코치노트 AI v1.0-rc2 · spec_01 기반 골격 (→ spec_02 인계)

> 코치노트 AI v1.0-rc2는 6개 명세(spec01~06)로 분할되어 1스레드=1명세로 순차 구현되었다. 본 문서는 **spec_01(기반 골격) 완료 → spec_02(데이터 주입 엔진) 인계** 핸드오프다. RC2 전체 종료 인계는 `handoff_rc2_complete.md`가 상위 문서이며, 본 문서는 spec_01 작업분의 사료(史料)로 보존한다.

> 용어 병기 — handoff(핸드오프) / migration(DB 구조 변경 SQL) / RLS(행 수준 보안) / RPC(원격 호출 함수) /
> price_version(단가 버전) / chat-proxy(채팅 중계 워커) / system prompt(시스템 지시문) /
> injection block(주입 블록·운동기록 정리문) / implicit caching(암묵적 캐싱) / transcript(대화 원문).

## 1. 현재 완료 spec
**spec_rc2_01_foundation 완료** (총 6개 중 1/6). 작업 영역: DB 운영로그·단가표 스키마 · 모델 연동(chat-proxy) · 프롬프트 캐싱 구조. SQL 적용 완료·코드 커밋(PR#36). chat-proxy CF 배포는 운영 잔여.

## 2. 원본 기준 재확인 (필수)
본 핸드오프는 **보조자료**다. 최종 기준은 본레포 `docs/go_work/` 원본 RC2 문서다. 후속 스레드는 `spec_rc2_00_master_index`(준수 원칙 A절)·`spec_rc2_02_data_injection`(다음 담당)·`decision_log_rc2`(#8 주입구조/#9 확장조회)·`IMPL_KICKOFF_새스레드용`을 직접 재열람한다. 핸드오프와 원본이 충돌하면 **원본 우선**.

## 3. 완료 구현 내용 (무엇을·어떤 구조로·어떤 의도로)

### 3-1. DB 스키마 (마이그레이션)
- 파일: `migrations/2026-06-06_coachchat_foundation.sql`(+ `_GUIDE.html` 실행 가이드).
- `coach_price_table` — 모델 단가표. 단가 변경 시 기존 row 수정 금지·새 `price_version` row 추가(과거 로그 역추적 보존). 활성단가는 provider+model 당 1개. 시드 3행은 **placeholder 추정 단가**(배포 전 공식 단가 갱신 필요).
- `coach_request_log` — 요청별 운영 로그. §2.2 표 **13필드 전부 + 보정필드(provider, pricing_snapshot_id)**. append-only. `estimated_cost`/`price_version` 누락 방지 = NOT NULL + CHECK. RLS 본인만(마스터 우회 없음).
- `get_active_coach_price(provider, model)` RPC — 활성 단가 1행 반환. **클라이언트 원가 계산의 단일 출처**.
- 집계 뷰 4종(일/월내주간/보름/월) + 감사 뷰(`coach_request_log_cost_audit_view`). 전부 `security_invoker=on`(뷰가 RLS 우회 안 함).

> **의도** — 서브파이프라인 달록에서 "돈이 새는 구간"을 일·주·보름·월 다기준으로 감시하려는 사장님 의도(decision_log #14). price_version 동반 저장은 단가가 바뀐 뒤에도 당시 원가를 추적하기 위함. 사장님 보정 지침으로 "계산은 클라이언트, DB는 누락방지·감사 담당" 구조.

### 3-2. 채팅 백엔드 (chat-proxy)
- 신규 워커 `workers/chat-proxy/`. **brief-proxy(단발 브리프) 확장 아님, 신규 분리**(작업지시 §4).
- provider/model **하드코딩 금지** — `wrangler.toml [vars]`로 교체. 기본 = Gemini 2.5 Flash-Lite.
- Gemini/OpenAI/Anthropic 경로. Gemini 키 미설정 시 fallback(openai) 자동 전환 → 키 없이 동작 검증(decision_log #2).
- non-stream + SSE stream. **usage(토큰) 항상 반환** → 클라이언트 원가 로깅.

### 3-3. 캐싱 구조 + 클라이언트 라이브러리 (`src/lib/coachChat/`)
- `systemPrompt.ts` `assembleCoachChat()` — 고정 system 선두(캐싱 대상) / 가변부 messages 앞단 분리(캐싱 제외). `FIXED_SYSTEM_PROMPT`는 spec_01 시점 자리표시자(전문은 spec_03가 채움).
- `pricing.ts` `calculateEstimatedCost()` — **유일한 계산 지점** + `fetchActiveCoachPrice()`(RPC).
- `requestLog.ts` `logCoachRequest()` — 활성단가 조회→단일헬퍼→insert. 로그 실패해도 대화 안 막음.
- `chatApi.ts` `callCoachChat()`/`streamCoachChat()` — 모델 강제 안 함(Worker env 결정).
- `types.ts`(PlanType·QuestionType 10종·ChatProvider·CoachPrice) + `supabase.ts`(CoachRequestLogRow).
- 검증: `npm run build`(tsc + vite) 통과.

## 4. 변경 파일

| 파일 | 구분 | 역할 |
|---|---|---|
| `migrations/2026-06-06_coachchat_foundation.sql` | 신규 | 운영로그·단가표·RPC·집계/감사 뷰·RLS |
| `migrations/2026-06-06_coachchat_foundation_GUIDE.html` | 신규 | SQL 실행 가이드 |
| `workers/chat-proxy/{index.js,wrangler.toml}` | 신규 | 멀티턴 채팅 백엔드(교체가능·Gemini·stream) |
| `src/lib/coachChat/{systemPrompt,types,pricing,requestLog,chatApi}.ts` | 신규 | 캐싱 조립·타입·원가 단일헬퍼·로그·호출 |
| `src/lib/supabase.ts` | 수정 | CoachRequestLogRow 타입 추가(타입만) |

## 5. 이번 스레드 결정사항 (내용·이유·탈락안·재검토)
1. **로그 = 클라이언트 계산 + DB 누락방지/감사**(사장님 지정) — service_role 워커 주입 회피·RLS 본인만 유지, 단일 헬퍼로 분산 방지. 탈락: DB 트리거/워커 service_role. 재검토: 실과금 안정 수신 시 `actual_cost` 확장.
2. **로그 필드 = 표 13필드 진본 + 보정** — §6 "12개"는 단순 기재 불일치. "필드 축소 금지".
3. **chat-proxy 신규 분리 / Gemini 키 없이 착수**(작업지시 §4) — env 교체, 키 전 fallback 검증.
4. **고정 프롬프트 전문은 spec_03로 이연** — spec_01은 "선두 고정 자리"만(캐싱 구조 ↔ 가드레일 내용 분리).

## 6. 후속 spec 연결 정보
**다음 담당: spec_rc2_02_data_injection** (14일 스냅샷 + 여정 요약 + 반자동 확장조회 + 반복패턴 압축).
- spec_02의 "주입 블록(문자열)"은 `systemPrompt.ts`의 `CoachChatVariableContext.injectionBlock`으로 연결. **고정 system에 넣지 말 것**(캐시 깨짐).
- 주입 엔진은 기존 기록 테이블(body/running/strength)에서 생성 — chat-proxy/로그 골격과 독립(키·백엔드 결정에 안 막힘).
- 확장조회 판정 **기본 원칙 = 애매하면 사용자 확인**(decision_log #9). 임의 확대·축소 금지.
- 14일만/원자료 전체 둘 다 배제. "14일+여정요약+반자동확장+압축" 균형 유지.

## 7. 절대 변경 금지 / 주의
- **모델 하드코딩 금지**(env 교체) · **chat-proxy 구조 유지**(brief-proxy 훼손 금지) · **고정 system에 가변 데이터 금지**(캐시 깨짐).
- **로그 필드 축소 금지 / estimated_cost·price_version 누락 금지**(DB가 거부) · **단가표 row 수정 금지**(새 price_version 추가).
- **대화 원문 저장은 spec_04**(활성화 게이트와 함께) — spec_02/03에서 원문 저장 테이블 임의 생성 금지.
- **마이그레이션 자동 적용 금지**(사장님 수동).

## 8. 인계 결과 (사후 확인)
spec_02가 본 핸드오프 §6(주입 슬롯·캐싱 경계)을 그대로 이어받아 구현 완료했고, spec_02→…→spec_06까지 순차 인계가 종료되었다(`handoff_rc2_complete`). §7 절대 금지(모델 하드코딩·캐싱 경계·로그 필드·원문저장 경계)는 후속 전 스레드가 보존했다. 본 핸드오프는 **임무 종료(consumed)** 상태로, spec_01 작업분의 사료로 보존한다.
