<!-- task=review_followup_alignment/01_openai_log_preview_guard plan=0 tag=REVIEW_LOG -->
# PLAN-local-G04: OpenAI 호환 로그 preview 안전장치
date=2026-07-06

## 목적
OpenAI-compatible 입력/출력 로그가 prompt, content, reasoning 일부를 preview 필드로 남기지 않도록 정리한다. 운영 관측은 유지하되, 내용 원문은 redaction/retention 정책이 확정되기 전까지 로그 표면에 올리지 않는다.

## 분석 결과
- 읽은 파일: `agent-ops/rules/project/rules.md`, `agent-ops/rules/private/rules.md`, `agent-ops/rules/common/rules-roadmap.md`, `agent-ops/skills/common/router.md`, `agent-ops/skills/common/plan/SKILL.md`, `agent-test/local/rules.md`, `agent-test/local/edge-smoke.md`, `agent-ops/rules/project/domain/edge/rules.md`, `agent-ops/rules/project/domain/testing/rules.md`, `apps/edge/internal/openai/chat_handler.go`, `apps/edge/internal/openai/responses_handler.go`, `apps/edge/internal/openai/stream.go`.
- SDD 기준: 기존 리뷰 follow-up이며 새 기능 SDD가 필요한 범위가 아니다. 현재 로드맵 active Milestone은 `[스케치]` 및 구현 잠금 상태이므로 이 plan은 로드맵 완료 항목을 선언하지 않는다.
- 테스트 환경 규칙: local 기준. OpenAI-compatible Edge package 단위 Go 테스트와 정적 검색으로 검증한다.
- 테스트 커버리지 공백: 현재 로그에 민감 preview 필드가 남지 않는지 확인하는 전용 테스트가 없다.
- 심볼 참조: `logOpenAICompatStreamOutput`, `openAICompatTraceEnabled`, `previewString`.
- 분할 판단: 리뷰 follow-up은 `review_followup_alignment` task group의 split 작업이다. 이 plan은 OpenAI 로그 표면만 다루며 문서, Client/Nexo, mock 활성화와 독립적으로 완료 가능하다.
- 범위 결정 근거: 로그 필드 제거/대체와 테스트 추가로 닫을 수 있는 Edge-local 변경이다. usage ledger, redaction policy, export policy 설계는 현재 locked Milestone 영역이므로 건드리지 않는다.
- 빌드 등급: `local-G04`. HTTP handler/stream 로그 경로와 테스트가 포함되지만 외부 서비스는 필요 없다.

## 이 파일을 읽는 구현 에이전트에게
구현 후 `CODE_REVIEW-local-G04.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 검증 출력으로 채운다. 구현 중 직접 사용자에게 질문하거나 선택지를 제시하지 않는다. 선택된 Milestone의 `구현 잠금 > 결정 필요` 항목이 실제 구현을 차단할 때만 review stub의 `사용자 리뷰 요청` 섹션에 근거를 기록하고 멈춘다.

## 작업 항목

### REVIEW_LOG-1. OpenAI-compatible 로그 preview 비노출
- 문제:
  - `apps/edge/internal/openai/chat_handler.go:62`와 `apps/edge/internal/openai/chat_handler.go:330` 계열 로그가 입력 prompt와 출력 content preview를 남긴다.
  - `apps/edge/internal/openai/responses_handler.go:87`와 `apps/edge/internal/openai/responses_handler.go:179` 계열 로그가 입력/출력 preview를 남긴다.
  - `apps/edge/internal/openai/stream.go:55`, `apps/edge/internal/openai/stream.go:105`, `apps/edge/internal/openai/stream.go:136`, `apps/edge/internal/openai/stream.go:499`, `apps/edge/internal/openai/stream.go:594` 계열 로그가 stream source, content, reasoning, delta preview를 남긴다.
  - 결과적으로 사용자 prompt, 모델 응답, reasoning 조각이 운영 로그에 섞일 수 있다.
- 해결 방법:
  - 기본 로그에서 `*_preview` 필드를 제거한다.
  - 필요한 관측값은 `*_len`, message count, model, request id, route, finish reason 같은 비내용 메타데이터로 대체한다.
  - trace/debug 경로도 정책 확정 전에는 원문 preview를 남기지 않는다. 명시 trace 여부는 유지하되 내용 대신 길이와 타입만 기록한다.
  - 변경 전 예:
    ```go
    zap.String("content_preview", previewString(content, 160))
    ```
  - 변경 후 예:
    ```go
    zap.Int("content_len", len(content))
    ```
- 수정 파일 및 체크리스트:
  - [ ] `apps/edge/internal/openai/chat_handler.go`: 입력/출력 preview 로그 제거 및 메타데이터 대체.
  - [ ] `apps/edge/internal/openai/responses_handler.go`: 입력/출력 preview 로그 제거 및 메타데이터 대체.
  - [ ] `apps/edge/internal/openai/stream.go`: stream trace/output preview 로그 제거 및 메타데이터 대체.
  - [ ] `apps/edge/internal/openai/log_safety_test.go` 또는 기존 OpenAI 테스트 파일: preview 필드 회귀 테스트 추가.
- 테스트 작성:
  - [ ] secret-like prompt/content/reasoning 문자열을 포함한 로그 케이스에서 `prompt_preview`, `content_preview`, `reasoning_preview`, `source_preview`, `delta_preview` 필드가 기록되지 않음을 검증한다.
  - [ ] 내용 길이/count 같은 비내용 필드는 남아 운영 관측이 완전히 사라지지 않음을 검증한다.
- 중간 검증:
  - [ ] `go test -count=1 ./apps/edge/internal/openai`
  - [ ] `rg --sort path -n 'zap\\.(String|Any)\\("(prompt|content|reasoning|source|delta)_preview"' apps/edge/internal/openai` 결과가 비어 있는지 확인한다.

## 구현 체크리스트
- [ ] REVIEW_LOG-1의 로그 preview 제거, 메타데이터 대체, 회귀 테스트를 구현한다.
- [ ] 중간 검증 명령을 실행하고 실패 시 원인과 수정 내용을 `CODE_REVIEW-local-G04.md`에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 최종 검증
```bash
go test -count=1 ./apps/edge/internal/openai
rg --sort path -n 'zap\.(String|Any)\("(prompt|content|reasoning|source|delta)_preview"' apps/edge/internal/openai
```
예상 결과: Go 테스트가 PASS하고, `rg` 명령은 런타임 로그에 남은 preview zap field를 출력하지 않는다.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
