<!-- task=m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke plan=0 tag=API -->

# Code Review Reference - API

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked by a selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`.
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation.
> Finalization is review-agent-only.

## 개요

date=2026-07-04
task=m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke, plan=0, tag=API

## Roadmap Targets

- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-tool-call-boundary-hardening.md`
- Task ids:
  - `client-smoke`: Pi/Cline형 긴 agent prompt와 `tools[]` dev Edge 호출 evidence
  - `contract-docs`: OpenAI-compatible 계약 문서와 dev 운영 문서 정책 설명
- Completion mode: check-on-pass

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 파일과 evidence path에 대조하고, secret 원문이 tracked 파일이나 review output에 남지 않았는지 확인하세요.

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G07.md`와 `PLAN-cloud-G07.md`를 `.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 archive로 이동한다.
4. PASS이고 task group이 `m-openai-compatible-tool-call-boundary-hardening`이면 완료 이벤트 메타데이터를 보고한다. roadmap 수정은 런타임 책임이다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [API-1] Contract Policy Update | [x] |
| [API-2] Dev Smoke Guide And Test Rule | [x] |
| [API-3] Credentialed Dev Runtime Evidence | [x] |

## 구현 체크리스트

- [x] 01번 `agent-task/m-openai-compatible-tool-call-boundary-hardening/01_provider_text_boundary/complete.log`가 존재하고 Roadmap Completion에 `provider-raw-parse`, `unknown-tool-error`, `stream-flush-guard`, `native-preserve`가 포함됐는지 확인한다. (archive `agent-task/archive/2026/07/m-openai-compatible-tool-call-boundary-hardening/01_provider_text_boundary/complete.log`에서 PASS와 4개 task id 확인)
- [x] `agent-contract/outer/openai-compatible-api.md`가 provider route/provider-pool raw text tool-call 후보의 구조화/차단, unknown/malformed 처리, sentinel sanitize 정책을 설명하도록 갱신한다.
- [x] `docs/edge-local-dev-guide.md`와 `agent-test/dev/edge-smoke.md`에 dev-runtime Pi/Cline형 `tools[]` smoke 기준, evidence 보관 위치, secret 마스킹 원칙을 추가한다.
- [x] 원격 dev-runtime source를 01 완료 source와 동기화하고 dev-runtime Edge/Node binaries를 rebuild한 뒤 `config check`와 필요한 refresh/restart를 수행한다. (01 변경은 `apps/edge/`에 국한, `apps/node/` 무변경이라 Edge 바이너리만 rebuild + Edge serve 재시작; 3개 node는 reconnect 정책으로 유지)
- [x] dev Edge OpenAI-compatible endpoint에 긴 agent prompt와 `tools[]` 요청을 호출해 valid raw text는 `tool_calls`, unknown hallucination은 `tool_validation_error` 또는 retry 후 차단으로 끝나며 raw block이 body/SSE에 남지 않는 evidence를 남긴다.
- [x] `go test ./apps/edge/internal/openai -count=1`와 문서/evidence 검증 명령을 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [x] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [x] PASS이면 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke/`로 이동한다.
- [x] PASS이고 task group이 `m-openai-compatible-tool-call-boundary-hardening`이면 런타임 완료 이벤트 메타데이터를 보고하고 roadmap을 직접 수정하지 않는다.
- [x] PASS split 작업이므로 이동 후 빈 active parent `agent-task/m-openai-compatible-tool-call-boundary-hardening/`를 제거했다.
- [x] WARN/FAIL이 아니므로 다음 active plan/review 파일 또는 unresolved verification report를 작성하지 않았다.

## 계획 대비 변경 사항

- 계약 문단(API-1)을 계획의 "provider route/provider-pool" 한정 서술보다 넓게 갱신했다. 실제 post-01 코드에서 `collectChatCompletionOutput`/live·buffered stream은 `len(req.Tools) > 0`이면 route 구분 없이 text tool-call을 synthesize/validate하며, `shouldSynthesizeTextToolCalls`(fallback/cli 게이트)는 정의만 있고 호출되지 않는 dead symbol이다. 따라서 계약을 "provider route + CLI route 모두"로 서술하고, 구 계약의 `openai_text_tool_fallback` 예외 조건 서술은 제거했다. `tools[]`가 없으면 합성/파싱하지 않는다는 제외 조건은 유지했다.
- 원격 rebuild 범위를 Edge 바이너리로 한정했다. `git diff --stat b55d1a2..59e55d4 -- apps/`가 01 boundary 변경이 전부 `apps/edge/`(openai 패키지 + `node/mapper.go`)이고 `apps/node/` 무변경임을 보여, 계획의 "Edge/Node binaries rebuild" 중 Node 재빌드/재배포는 불필요로 판단했다. Edge serve만 새 바이너리로 재시작했고 3개 node는 reconnect 정책으로 유지되어 재-bootstrap 없이 connected를 확인했다.
- unknown-tool 라이브 evidence는 2회 시도했다. 1차(자유 유도)에서는 모델이 `<tool_call>`/`{{}}` 구조 블록이 아닌 산문(`lookup_weather(city="Seoul")`)을 내 content로 반환(leak 없음, finish_reason stop)되어 validation-error 경로를 유도하지 못했다. 2차에서 literal `<tool_call>` unknown-tool 블록을 강제 출력하도록 프롬프트해 실제 `tool_validation_error`(`tool "lookup_weather" is not in request tools`) 차단을 라이브로 확인했다.

## 주요 설계 결정

- `docs/openai-compatible-api-contract.md`는 계약 pointer이므로 원문 정책을 복제하지 않고 그대로 두었다(grep으로 raw/tool_validation 문구 0건 확인). source of truth는 `agent-contract/outer/openai-compatible-api.md`.
- credential은 원격 `edge.yaml`의 `bearer_token`을 원격 셸 변수로만 추출해 `Authorization` 헤더에 인라인 주입했고, 명령/로그/evidence 파일/보고 어디에도 원문을 남기지 않았다(evidence dir 대상 token grep 0건).
- dev evidence는 gitignore된 `build/dev-runtime/logs/boundary-smoke-<ts>/`(원격)에 request/response 원문을 저장하고, tracked 문서에는 마스킹 요약과 경로만 남겼다.
- 결정적 차단 커버리지(unknown/malformed retry·fail, stream block, sentinel drop, every-candidate validation)는 01의 로컬 unit test 22종이 담당하고, 라이브 dev smoke는 실제 provider-pool 경로에서 no-raw-leak + valid 정규화 + unknown 차단을 보완 검증하는 역할로 분리했다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._

- 상태: 없음
- 사유 유형: 없음
- 연결 대상: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- 01 predecessor completion이 실제로 충족됐는지 확인한다.
- 계약 문서가 `docs/`에 중복 원문을 만들지 않고 `agent-contract`를 source of truth로 유지하는지 확인한다.
- dev smoke evidence가 token/secret 원문을 노출하지 않는지 확인한다.
- raw `<tool_call>`, unknown tool hallucination, `<|mask_end|>` leak 차단 evidence가 남았는지 확인한다.

## 검증 결과

_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._

### API-1 중간 검증
```bash
$ rg --sort path -n "provider route에서|raw text|tool_validation_error|mask_end" agent-contract/outer/openai-compatible-api.md docs/openai-compatible-api-contract.md
agent-contract/outer/openai-compatible-api.md:227: ... raw text tool-call 블록을 ... provider route ... CLI route ... 구조화하거나 차단한다.
agent-contract/outer/openai-compatible-api.md:228: ... malformed 블록 ... tool validation 실패로 처리 ... tool_validation_error 로 응답 ... live SSE ... tool_validation_error 이벤트로 스트림을 종료.
agent-contract/outer/openai-compatible-api.md:230: raw <tool_call>/{{...}} 블록과 <|mask_end|> 같은 sentinel ... content/SSE delta에 노출하지 않는다.
# docs/openai-compatible-api-contract.md : 매칭 0건 (pointer 문서, 원문 미복제 확인)
```

### API-2 중간 검증
```bash
$ rg --sort path -n "raw text tool|tool_validation_error|Pi/Cline|mask_end|agent-test/runs" docs/edge-local-dev-guide.md agent-test/dev/edge-smoke.md
docs/edge-local-dev-guide.md:281,295,304-306,309  (Raw text tool-call boundary smoke 절차/판정/evidence 위치)
agent-test/dev/edge-smoke.md:109  (필수 검증: Pi/Cline형 tools[] non-stream/stream smoke)
agent-test/dev/edge-smoke.md:123-124  (판정 기준 + agent-test/runs evidence 위치)
```

### API-3 원격 preflight / sync / rebuild / restart
```bash
# preflight (재빌드 전): 원격 stale 확인
$ ssh toki@toki-labs.com '... git log --oneline -1; ports; cp status'
b55d1a2 fix: document and verify provider priority leveling   # stale (pre-01)
port 18001/18083/18084/19093 LISTEN; cp_status_http=200

# clean-sync → origin/main(01 포함) + Edge 재빌드 + config check
$ ssh toki@toki-labs.com 'zsh -lc "cd /Users/toki/agent-work/iop-dev; git fetch origin main; git reset --hard origin/main; git clean -fd; go build -trimpath -o build/dev-runtime/bin/edge ./apps/edge/cmd/edge; cd build/dev-runtime; ./bin/edge --config edge.yaml config check"'
HEAD is now at 59e55d4 feat: openai-compatible tool call boundary hardening & knowledge tool optimization
edge binary rebuilt: -rwxr-xr-x 22026946 Jul 4 18:44 build/dev-runtime/bin/edge   (go1.26.3)
OK edge.yaml

# Edge serve 재시작 (old pid 97852 → new pid 21555) + reconnect/health
$ ssh toki@toki-labs.com '... kill $(cat edge.pid); nohup ./bin/edge --config edge.yaml serve ...'
healthz_http=200
CP status nodes: mac-codex-node connected=true, gx10-vllm-node connected=true, onexplayer-lemonade-node connected=true (모두 in_flight=0)
/v1/models: {"data":[{"id":"qwen3.6:35b","object":"model","owned_by":"iop"}]}   # (bearer token 인증, 원문 미기록)

# 최종 remote 상태
$ ssh toki@toki-labs.com '... git log --oneline -1; config refresh --help; cp status'
59e55d4 ...; refresh_help_ok; cp_status=200
```

### API-3 credentialed boundary smoke (token redacted; evidence dir gitignored)
```bash
# 원격 실행. token은 edge.yaml에서 셸 변수로만 추출, Authorization 헤더 인라인 주입, 파일/로그/보고 미기록.
# evidence: (원격, gitignored) build/dev-runtime/logs/boundary-smoke-20260704_184616/

# 1) known tool, non-stream  →  POST /v1/chat/completions (tools=[shell], tool_choice=auto)
finish_reason=tool_calls  has_tool_calls=True  names=['shell']  first_args={"commands":["ls -la /tmp"]}
raw_leak_tool_call_tag=False  raw_leak_mask_end=False  content_preview=(empty)

# 2) known tool, stream  →  "stream":true
sse_has_tool_calls_delta=2  sse_finish_tool_calls=1  sse_done=1
sse_raw_tool_call_tag=0  sse_raw_mask_end=0  sse_validation_error=0

# 3) unknown tool, 자유 유도, non-stream  →  모델이 산문 출력 (구조 블록 아님)
finish_reason=stop  has_tool_calls=False  raw_leak_tool_call_tag=False  raw_leak_mask_end=False
content_preview=lookup_weather(city="Seoul")   # {{}}/<tool_call> 블록 아님 → content로 반환, leak 없음

# 4) unknown tool, literal <tool_call> 강제, non-stream  →  차단 확인
error_type=tool_validation_error  error_msg=tool "lookup_weather" is not in request tools
has_tool_calls=False  finish_reason=None  raw_leak_tool_call_tag=False  raw_leak_mask_end=False  content_preview=(empty)

# evidence 파일 token 누출 검사: grep "$TOKEN" logs/boundary-smoke-20260704_184616/ → 0건
```

### 최종 검증
```bash
$ go test ./apps/edge/internal/openai -count=1
ok  	iop/apps/edge/internal/openai	1.567s

# 결정적 차단 경로 커버리지 (targeted, 22 tests)
$ go test ./apps/edge/internal/openai -run 'TestChatCompletions.*(Unknown|Malformed|TextToolCall|Synthes)|TestValidateToolCallResponse|TestChatCompletionsProviderStreamBlocksMalformedTextToolCall' -count=1 -v
--- PASS x22 (Synthesizes*, Retries*/Fails* Malformed·Unknown, ProviderStreamBlocks Unknown·Malformed, LeavesUnknown*AsContent, ValidatesEveryToolCall, SentinelDrop ...)
ok  	iop/apps/edge/internal/openai	0.011s

$ rg --sort path -n "tool_validation_error|raw text tool|mask_end|Pi/Cline" agent-contract/outer/openai-compatible-api.md docs/edge-local-dev-guide.md agent-test/dev/edge-smoke.md
# 3개 위치 모두 매칭 (계약/dev 가이드/dev test-rule)

# credentialed dev-runtime evidence: 위 "API-3 credentialed boundary smoke" 참조
# saved output path (원격, gitignored): build/dev-runtime/logs/boundary-smoke-20260704_184616/
```

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.

## 코드리뷰 결과

- 종합 판정: PASS
- 차원별 평가:
  - correctness: Pass
  - completeness: Pass
  - test coverage: Pass
  - API contract: Pass
  - code quality: Pass
  - implementation deviation: Pass
  - verification trust: Pass
  - spec conformance: Pass
- 발견된 문제:
  - Nit (fixed): `agent-task/m-openai-compatible-tool-call-boundary-hardening/02+01_contract_dev_smoke/PLAN-cloud-G07.md:1`의 stray `현재` 접두어를 제거했다.
  - Nit (fixed): `docs/edge-local-dev-guide.md:286`의 secret placeholder export 예시를 제거하고, `docs/edge-local-dev-guide.md:294`의 stream smoke 및 `docs/edge-local-dev-guide.md:300`의 deterministic unknown raw block fixture를 명시했다.
- 다음 단계: PASS 종결. active PLAN/REVIEW를 log로 아카이브하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
