# SDD User Review

## 상태

해결됨

## 검토 대상

- SDD: [SDD.md](SDD.md)
- Milestone: [Milestone 문서](../../../phase/knowledge-tool-optimization-extension/milestones/openai-compatible-output-validation-filters.md)

## 확정된 설계 기준

- Provider 출력의 기본 경로는 전체 응답을 buffer하지 않고 `provider → staged response-start + rolling pending/look-behind → filters → user release` 순서로 처리한다. schema처럼 terminal 전체가 필요한 명시적 경로만 hard bound가 있는 `terminal_gate`를 사용한다.
- pending tail 기본값은 500 Unicode rune의 반복 판정 증거가 쌓이거나 terminal event가 올 때까지다. 그 뒤 filter가 통과시킨 safe prefix만 사용자에게 전달한다. 시간 경과는 미검증 출력 release 사유가 아니다.
- `stream_hold.evidence_runes`는 environment/model/provider policy로 조정할 수 있으며, 한글은 UTF-8 byte가 아니라 Unicode rune 경계로 센다. evidence 미충족 provider idle timeout은 pending tail을 전달하지 않고 terminal error로 끝낸다.
- response-start 포함 normalized event, rolling/terminal/fragment hold, release commit, terminal sequence는 [Stream Evidence Gate Core](../stream-evidence-gate-core/SDD.md)의 공통 책임으로 분리한다. status/header/opening role도 첫 safe release 전 stage한다. 이 Milestone은 반복·schema·provider-error의 의미 판정과 typed `RecoveryIntent`만 소유하며 Core mechanics를 다시 구현하지 않는다.
- request 시작 시 config generation을 고정하고 활성 filter는 attempt의 실제 model/provider에 따라 다시 resolve한다. 동일 immutable batch의 모든 outcome을 모은 뒤 Core Arbiter가 하나의 action을 고르며, filter는 detector/intent만 제공하고 Core가 single-flight backpressure, budget, abort/rebuild와 cycle별 single re-admission을 소유한다.

## 사용자 결정 항목

### [D01] Assistant history anchor 보정과 반복 복구 루프

- 상태: 확정
- 확정: generic request-history preflight에서 반복된 plain reasoning fragment만 sanitize하고, current non-final reasoning의 동일 fragment만 bounded dedupe한다. assistant final `content`, tool call, signed/encrypted/unknown reasoning field는 변경하지 않는다. dev/dev-corp는 observe-only evidence를 먼저 통과한 model/provider policy부터 mutation을 켠다.
- 확정: completed history가 no-progress이고 current tool call이 아직 release되지 않았으며 side effect 가능 구간이 아닐 때만 continuation repair를 수행한다. 복구용 새 요청에는 원래 사용자 요청·message를 넣지 않고, 반복 전까지 생성된 모델의 content와 think/reasoning 출력 원문을 channel별로 구분해 고정 복구 지시문과 함께 넣는다. 반복으로 판정된 구간만 제외한다. 모델 출력의 의미 요약·임의 절단·재작성은 하지 않으며, 두 channel의 출력 원문 전체가 문맥 한도를 넘으면 자동 복구하지 않는다.
- 확정: caller가 `temperature`를 명시하지 않은 요청만 `[0.2, 0.4, 0.6]` 순서의 후보를 각각 한 번씩 적용한다. 후보에서도 반복되면 다음 값으로 넘어가고, 배열을 모두 소진하면 안전하게 종료한다. caller가 명시한 `temperature`는 바꾸지 않는다.
- 확정: 재개 안내문은 D05에서 확정한 고정 영어 문구를 사용한다. 언어 판별이나 번역을 위한 별도 모델 호출은 하지 않는다.
- 영향: raw tunnel request/response mutation 범위, false positive, visible reasoning, continuation repair와 side-effect 안전 경계를 결정한다.
- 적용 위치:
  - SDD: `State Machine`, `Interface Contract`, `S10`, `S11`
  - Milestone: `repeat-guard`, `구현 잠금`

### [D02] Endpoint 적용 범위

- 상태: 확정
- 확정: `/v1/chat/completions`와 `/v1/responses`는 모두 현재 Milestone의 정식 적용 범위다. 두 endpoint는 공통 filter core와 Stream Evidence Gate Core의 normalized event/release contract를 함께 소비한다.
- 확정: raw request/stream parser와 repair request serializer는 endpoint별 codec으로 분리한다. Chat Completions의 `messages`/delta/tool-call field를 Responses의 input item/reasoning/function-call item에 그대로 적용하지 않으며, Responses 전용 codec과 Acceptance Scenario를 현재 Milestone에 둔다.
- 확정: 반복 감지, 안전 중단, D01 원문 보존·온도 단계 복구, D03 관측, D04 provider 오류 재시도, D05 재개 안내문 정책은 endpoint 이름이 아닌 common normalized history/event와 consumer filter에 적용한다.
- 영향: Milestone 범위, parser/state machine 규모, 계약 문서와 검증 matrix를 결정한다.
- 적용 위치:
  - SDD: `문제 / 비목표`, `State Machine`, `Interface Contract`, `S01`, `S14`, `S18`
  - Milestone: `범위`, `contract-doc`, `filter-pipeline`, `stream-gate-adoption`, `responses-codec`, `구현 잠금`

### [D03] Assembled output 로그 보존

- 상태: 확정
- 확정: assembled output/reasoning 원문 기록은 설정 기본값 `on`으로 시작한다. 설정을 `off`로 바꾸면 이후 요청부터 원문 기록을 중단한다.
- 확정: `off` 상태에서도 반복 감지·재시도·중단 결과, 길이, role/channel, model/provider 등 비원문 운영 정보는 기록한다. 원문 일부를 런타임이 선택적으로 가리는 기능은 만들지 않는다.
- 확정: raw user prompt, tool args/result, 인증 정보는 원문 기록 설정과 무관하게 기록하지 않는다. `off` 전환은 이미 남아 있는 로그를 삭제하지 않는다.
- 영향: 운영 관측성, 개인정보/민감정보 보존, 기존 observability/log-safety 테스트와 로그 호환성에 영향을 준다.
- 적용 위치:
  - SDD: `Interface Contract`, `S07`
  - Milestone: `ops-evidence`, `구현 잠금`

### [D04] Provider 오류 재시도 정책

- 상태: 확정
- 확정: eligible error filter는 `filters[]` 배열이며 각 원소는 `code`와 `message`만 가진다. `code` exact-match와 `message` 포함-match를 모두 만족하면 같은 retry 처리를 한다. 현재 원소는 `{ code: 500, message: "Failed to parse input at pos" }`이고, 유사 오류는 같은 두 필드를 가진 원소를 추가한다. position 숫자와 생성 출력 suffix는 넣지 않는다.
- 확정: provider error filter는 별도 retry loop/counter를 만들지 않고 `exact_replay` RecoveryIntent만 반환한다. [Stream Evidence Gate Core](../stream-evidence-gate-core/SDD.md)의 공통 RecoveryPlan Coordinator가 완료된 [Tool Call Runtime 검증 재시도 MVP](../../../archive/phase/knowledge-tool-optimization-extension/milestones/tool-call-runtime-validation-retry.md)의 exact-replay 실행을 흡수해 provider error와 tool validation의 request-local attempt counter를 공유하고, continuation repair의 별도 strategy budget도 같은 Coordinator가 관리한다.
- 확정: provider error와 Tool Call Runtime validation이 공유하는 exact replay는 최초 실행을 제외하고 request당 최대 3회다. 같은 evaluation epoch의 여러 retryable intent는 하나의 RecoveryPlan으로 합쳐 1회만 소비하며, 새 `transport_uncommitted` attempt에서 다시 위반되면 남은 횟수 안에서만 다음 cycle을 허용한다.
- 확정: 위 exact strategy cap과 별도로 Core는 exact/continuation/schema recovery dispatch를 모두 합산하는 최초 실행 제외 기본값이자 절대 상한 3회의 request 전체 `max_recovery_attempts_total`을 적용한다. policy는 0~3만 허용하고 4 이상은 config validation에서 거부한다. request 시작 시 값을 고정하고 전체 cap이 소진되면 남은 strategy budget이 있어도 다른 filter/strategy로 우회하지 않는다.
- 확정: 재시도 가능 여부는 pending tail 유무가 아니라 Core `CommitState`로 판단한다. provider response-start/status/header/body가 staged 상태면 `transport_uncommitted`지만 status/header/role/body 중 하나가 user-visible하게 commit되면 `stream_open`이다. stream-open 뒤 provider error는 exact replay하지 않는다. 이는 이미 보낸 safe prefix를 보존하는 repetition `continuation_repair`와 별도 전략이다.
- 확정: 재시도는 기존 provider-pool admission에 다시 들어간다. filter와 Coordinator는 동일 provider 고정이나 다른 provider 강제를 하지 않으며, 실제 provider 선택은 기존 pool 정책을 따른다.
- 영향: provider-pool admission, 오류 응답 시점, 재시도 비용/지연, caller가 보는 terminal error, 운영 관측성에 영향을 준다.
- 적용 위치:
  - SDD: `State Machine`, `Interface Contract`, `S15`~`S17`
  - Milestone: `provider-error-retry`, `ops-evidence`, `구현 잠금`

### [D05] 고정 영어 재개 안내문

- 상태: 확정
- 확정: 반복 복구 지시문은 다음 영어 문구로 고정한다: `The previous model output was stopped after a repetition loop was detected. Continue from the provided content and reasoning output without repeating already generated text.`
- 확정: 출력 언어 판별, 안내문 번역, 로컬 모델 호출, 번역용 설정과 별도 준비 재시도 단계는 구현하지 않는다. 이전에 논의한 모델 별칭, 20초 제한시간, `target_language` 구조화 반환, 번역 실패 오류와 기본 오류 언어 결정은 모두 폐기한다.
- 확정: Core Arbiter가 continuation plan 하나를 선택하고 current provider transport ownership을 종료한 뒤, endpoint별 복구 요청 조립기가 반복 구간을 제외한 모델의 content와 think/reasoning 원문을 channel별로 구분해 고정 영어 지시문과 직접 조립한다. 사용자 요청·message는 넣지 않는다.
- 확정: 모델 출력은 의미 요약·임의 절단·재작성하지 않는다. 두 channel의 반복 전 원문 전체가 문맥 한도를 넘으면 자동 복구하지 않는다.
- 확정: 별도 준비 호출이 없으므로 복구 budget은 실제 outbound recovery dispatch 때만 소비한다.
- 결정 필요 범위: 없음.
- 영향: 복구 경로에서 번역 모델의 지연·실패·설정 의존성을 제거하고, 모든 출력 언어에 같은 영어 지시문을 사용한다.
- 적용 위치:
  - SDD: `State Machine`, `Interface Contract`, `S11`, `S20`
  - Milestone: `resume-notice-builder`, `repeat-guard`, `구현 잠금`

### [D06] 별도 범용 오류 분석·수정 플랫폼 경계

- 상태: 확정
- 확정: 오류 수집 이후의 cross-request 사건 관리와 LLM 기반 소스 분석·수정 실행은 IOP 출력 필터나 Stream Evidence Gate Core 내부 루프가 아니라, 여러 프로젝트에 연결할 수 있는 별도 플랫폼으로 프로젝트화한다. IOP는 첫 적용 대상이자 선택 가능한 LLM/agent 실행 경로가 될 수 있지만, 동기 serving 요청은 플랫폼 분석 완료를 기다리지 않는다.
- 확정: 기본 생명주기는 `오류 발생 → 수집·민감정보 제거 → 안정 지문 기반 사건 생성/중복 집계 → 실행 버전·커밋·연결 저장소 분석 → 비동기 원인 분석과 재현 근거 → 중립 수정 제안 → 프로젝트별 작업 문서 → 사용자 알림·승인 → 격리 수정·테스트·독립 검토 → 변경 요청 → 병합·배포 → 재발 관찰 → 해결/다시 열기`다.
- 확정: 같은 오류 지문의 후속 발생은 새 마일스톤이나 수정 작업을 만들지 않고 canonical 사건의 count, first/last seen, 영향 버전만 갱신한다. 해결 뒤 재발하거나 지문 의미가 달라지면 기존 사건을 다시 열거나 별도 variant로 분리한다.
- 확정: 플랫폼 내부 source of truth는 특정 프로젝트의 마일스톤 형식이 아닌 중립 수정 제안으로 두고, 프로젝트 adapter가 IOP 마일스톤, 이슈, 티켓 등 해당 저장소의 작업 문서로 변환한다.
- 확정: 현재 Milestone과 Stream Evidence Gate Core는 bounded raw-free terminal code/cause/observation event를 제공하는 경계까지만 소유한다. 사건 저장·중복 count·소스 분석·승인·변경 요청·병합·배포는 현재 구현 범위에 포함하지 않는다.
- 별도 프로젝트 결정으로 이관: 사건 저장 책임과 보존 기간, 오류 지문 schema/version, 분석 LLM 실행·fallback 경계, 소스/자격 증명 권한, 사용자 승인 단계, 자동 병합·배포 허용 수준, 배포 후 관찰 기간과 rollback 정책, 플랫폼 자체 오류의 격리·차단기 정책.
- 영향: 오류/관측 event 계약의 향후 소비자, 다중 프로젝트 source binding, 장기 사건 저장, 코드 변경 권한, 사용자 승인, 저장소·배포 provider 연동 범위를 결정한다.
- 적용 위치:
  - SDD: `문제 / 비목표`, `Source of Truth`, `사용자 리뷰 이력`, `작업 컨텍스트`
  - Milestone: `구현 잠금`, `범위 제외`, `작업 컨텍스트`

## 승인 항목

- [x] D01 보정과 반복 복구 루프를 승인했다.
- [x] D02 endpoint 범위를 승인했다.
- [x] D03 로그 보존 정책을 승인했다.
- [x] D04 provider 오류 재시도 정책을 승인했다.
- [x] D05 고정 영어 재개 안내문 정책을 승인했다.
- [x] D06 별도 범용 오류 분석·수정 플랫폼 책임 경계를 승인했다.
- [x] SDD 잠금 해제를 승인했다.

## 답변 기록

- 2026-07-23: D01을 확정했다. 반복된 plain reasoning만 sanitize/live dedupe하고, no-progress·tool call 미release·side effect 비해당인 경우에만 자동 복구한다. 반복 전 원문은 유지하고 반복 구간만 제외하며, caller가 온도를 지정하지 않은 요청은 `[0.2, 0.4, 0.6]` 순서로 복구를 시도한다. 원문 요약·임의 절단과 caller 지정 온도 변경은 하지 않는다.
- 2026-07-23: 마지막 출력 언어 판별과 재개 안내문 번역에 필요한 로컬 모델 호출 계약은 D05로 분리했다.
- 2026-07-23: D02를 확정했다. Chat Completions와 Responses를 현재 Milestone에 함께 포함하고, 공통 filter/core는 공유하되 raw parser·repair serializer는 endpoint별 codec으로 분리한다.
- 2026-07-23: D03을 확정했다. assembled output/reasoning 원문 기록은 기본 `on`이며 설정 `off` 뒤 요청부터 중단한다. 비원문 운영 정보는 유지하고 raw user prompt/tool args/result/auth는 항상 제외하며 기존 로그 자동 삭제는 하지 않는다.
- 2026-07-23: D04를 확정했다. provider 오류와 Tool Call Runtime validation은 최초 실행을 제외하고 request-local exact replay 최대 3회를 공유한다. 500-rune pending tail이 남아도 safe prefix release 뒤에는 replay하지 않으며, 재시도 provider 선택은 기존 provider-pool admission 정책을 따른다.
- 2026-07-23: 재검토에서 D04의 pre-commit을 `transport_uncommitted`로 구체화해 staged response-start는 replay 가능하지만 user-visible status/header/role/body commit 뒤에는 exact replay하지 않도록 명확히 했다. 반복 continuation은 safe prefix를 보존하는 별도 stream-open 복구다.
- 2026-07-23: D05에서 재개 안내문은 설정이 가리키는 IOP 서빙 로컬 모델 별칭을 사용하기로 부분 확정했다. 모델명은 코드에 고정하지 않고 별칭의 기존 IOP admission/provider-pool은 허용하되, 원 요청 실패 route나 별칭 밖 모델로 fallback하지 않는다. 호출 실패는 반복 감지 뒤 번역 실패를 뜻하는 bounded sanitized 원인 사슬을 남기고 endpoint별 terminal 오류 하나로 끝낸다. 실제 config field, 입력·반환 계약, hard deadline 값과 기본 오류 언어는 다음 결정으로 남긴다.
- 2026-07-23: 안정성 재검토에서 D04 request 전체 recovery를 최초 실행 제외 기본값/절대 상한 3회로 고정하고 policy 0~3만 허용했다. D05 translator는 filter가 아니라 all-complete와 current attempt 종료 뒤 실행되는 optional `RecoveryPlanPreparer`로 분리하고, plan/idempotency key당 최대 1회와 hard deadline을 적용하며 timeout/invalid/error를 재호출이나 provider fallback으로 우회하지 않도록 부분 확정 범위를 보강했다. 실패 시 recovery dispatch/budget 소비 없이 terminal로 끝내며 deadline의 구체적인 값은 계속 D05 결정으로 남긴다.
- 2026-07-23: D05의 언어 판별과 반환 책임을 추가 확정했다. 사용자 명시 출력 언어, 마지막 정상 user-visible 출력, 마지막 사용자 요청, 설정 기본 언어 순으로 대상 언어를 고르고 think/reasoning 언어는 근거에서 제외한다. 번역 모델에는 구분된 untrusted data와 고정 복구 지시문만 주며 `target_language`와 `recovery_instruction`만 반환하게 한다. 전체 재시도 prompt는 번역 모델이 만들지 않고 IOP runtime이 원래 요청·반복 전 원문 출력·번역된 지시문을 조립한다. 입력 hard bound와 언어 코드/반환값의 세부 검증 한도는 계속 D05 결정으로 남긴다.
- 2026-07-23: D05 번역 호출 자체가 실패한 경우의 외부 오류 언어는 설정된 오류 안내 기본 언어를 따르고, 설정이 없으면 영어(`en`)를 사용하는 것으로 확정했다. 실패한 번역 모델을 오류 안내문 번역을 위해 다시 호출하지 않는다.
- 2026-07-23: D05 번역 호출은 provider-pool 대기, 모델 생성, 구조화된 반환 검증을 모두 합산해 최대 20초로 확정했다. 20초를 넘으면 translator 재호출이나 다른 모델 fallback 없이 번역 실패 terminal 경로로 종료한다.
- 2026-07-23: D05 번역 입력의 마지막 사용자 요청은 언어 지시 유실을 막기 위해 기존 16 MiB request snapshot 한도 안의 전체 텍스트를 원문 그대로 전달하고, 마지막 user-visible 출력은 기존 Core의 500 Unicode rune 표본을 사용하도록 확정했다. 번역 모델 context에 들어가지 않으면 임의 절단·요약하지 않고 `recovery_notice_translation_input_too_large`로 실패 처리한다.
- 2026-07-23: D05 번역 입력을 재검토해 사용자 요청은 전달하지 않는 것으로 앞선 결정을 대체했다. 번역 모델은 마지막 정상 user-visible 출력 500 Unicode rune, 설정 기본 언어와 고정 복구 지시문만 받고, 언어 판별도 `user-visible 출력 → 설정 기본 언어`만 사용한다. 원래 사용자 요청은 번역 모델이 아닌 runtime의 재작업 prompt 조립 단계에서만 원문 그대로 유지한다.
- 2026-07-23: D01/D05 복구 입력을 다시 좁혀 원래 사용자 요청·message는 번역 호출과 실제 재작업 요청 모두에서 제외하는 것으로 앞선 결정을 대체했다. 입력은 반복 전 모델 응답 content 출력과 고정 복구 지시문만 사용하고 think/reasoning도 제외한다. 번역용 언어 표본은 모델 응답 content의 마지막 500 Unicode rune이다.
- 2026-07-23: D01/D05의 모델 출력 범위를 다시 확장해 content와 think/reasoning을 channel별로 모두 사용하도록 앞선 결정을 대체했다. 번역 표본은 각 channel의 마지막 500 Unicode rune이고 언어 우선순위는 `content → think/reasoning → 설정 기본 언어`다. 실제 재작업 요청에도 반복 전 두 channel 원문과 복구 지시문만 넣으며 사용자 요청·message는 계속 제외한다.
- 2026-07-23: D06을 확정했다. 오류 사건의 안전한 수집·지문 기반 중복 count·소스/버전 연결·비동기 LLM 분석·중립 수정 제안·프로젝트별 작업 문서·사용자 승인·격리 수정/검토·변경 요청·병합·배포·재발 확인은 별도 범용 플랫폼으로 프로젝트화한다. 현재 출력 필터/Core는 raw-free terminal/observation event 제공까지만 맡고, 플랫폼 상세 결정은 별도 브랜치 대화로 이관한다.
- 2026-07-23: D05의 앞선 번역 관련 결정을 모두 대체했다. 언어 판별·번역·로컬 모델 호출·모델 별칭·20초 제한시간·구조화 반환·번역 실패 오류는 구현하지 않고, 반복 복구에는 고정 영어 지시문을 직접 사용한다. 재작업 요청은 반복 전 content와 think/reasoning 원문을 channel별로 구분해 넣고 사용자 요청·message는 제외한다.
- 2026-07-23: 사용자가 D01~D06이 모두 반영된 SDD의 최종 잠금 해제를 승인했다.

## 해결 조건

- 모든 사용자 결정 항목의 답변이 SDD에 반영되어 있다.
- `USER_REVIEW.md`가 `user_review_N.log`로 이동되어 있다.
- 남은 잠금 항목이 없으면 SDD 상태가 `[승인됨]`이고 `SDD 잠금` 상태가 `해제`다.
