<!-- task=m-openai-compatible-tool-call-boundary-hardening/03+02_closure_verification plan=2 tag=REVIEW_REVIEW_VERIFY -->

# Code Review Reference - REVIEW_REVIEW_VERIFY

> **[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 evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service setup, generic scope conflicts, loop exhaustion, and evidence gaps that a follow-up agent can close are normal follow-up issues, not user-review blockers by themselves.
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record only the linked Milestone lock decision in `사용자 리뷰 요청` and stop for code-review.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-07-04
task=m-openai-compatible-tool-call-boundary-hardening/03+02_closure_verification, plan=2, tag=REVIEW_REVIEW_VERIFY

## Roadmap Targets

- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/openai-compatible-tool-call-boundary-hardening.md`
- Task ids:
  - `stream-flush-guard`: streaming 경로에서 tool-call 후보를 보류한 뒤 terminal event에서 구조화/차단한다.
  - `native-preserve`: native OpenAI-compatible `tool_calls` pass-through와 schema-normalization 동작을 유지한다.
  - `client-smoke`: Pi/Cline형 긴 agent prompt와 `tools[]` 요청을 직접 Edge에 호출해 raw text leak이 없는 근거를 남긴다.
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Current archived plan: `agent-task/m-openai-compatible-tool-call-boundary-hardening/03+02_closure_verification/plan_cloud_G07_1.log`
- Current archived review: `agent-task/m-openai-compatible-tool-call-boundary-hardening/03+02_closure_verification/code_review_cloud_G07_1.log`
- Verdict: FAIL
- Required summary:
  - dev `18083` `/v1/chat/completions` Pi/Cline형 `tools[]` non-stream/stream smoke가 guarded runtime 기준으로 수집되지 않았다.
  - stored evidence raw/secret marker 검사와 `tool_calls` 또는 `tool_validation_error` positive evidence가 없다.
  - remote token preflight는 `token_missing`이고, running Edge는 guarded binary rebuild 이전 process였다.
- Affected files and evidence:
  - `apps/edge/internal/openai/stream.go:204` uses `FlushNonCandidate()` in the native stream `tool_calls` branch.
  - `apps/edge/internal/openai/stream.go:333` defines `FlushNonCandidate()`.
  - `apps/edge/internal/openai/server_test.go:1703` covers native `tool_calls` plus raw text delta.
  - Review-agent rerun: targeted openai regression PASS, `go test ./apps/edge/... -count=1` PASS, `go test ./... -count=1` PASS.
  - Review-agent remote preflight: `/Users/toki/agent-work/iop-dev` -> HEAD `59e55d4`, dirty guard patch on `stream.go`/`server_test.go`, guard/test symbols present, `token_missing`.
- Required verification still missing:
  - guarded binary로 실제 dev Edge `18083`를 재시작하거나 running process가 guarded binary임을 증명한다.
  - dev `18083` `/v1/chat/completions` Pi/Cline형 `tools[]` non-stream smoke on guarded runtime.
  - dev `18083` `/v1/chat/completions` Pi/Cline형 `tools[]` stream smoke on guarded runtime.
  - evidence path with raw marker check proving `<tool_call>`, `{{`, `<|mask_end|>`, `Authorization`, and `Bearer` do not appear in stored responses.
- Allowed narrow archive reread, if exact prior context is needed:
  - `agent-task/m-openai-compatible-tool-call-boundary-hardening/03+02_closure_verification/plan_cloud_G07_1.log`
  - `agent-task/m-openai-compatible-tool-call-boundary-hardening/03+02_closure_verification/code_review_cloud_G07_1.log`

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

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

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.

1. 판정을 append한다.
2. `CODE_REVIEW-{review_lane}-GNN.md` -> `code_review_{review_lane}_GNN_N.log`, `PLAN-{build_lane}-GNN.md` -> `plan_{build_lane}_GNN_M.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 연결된 Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [REVIEW_REVIEW_VERIFY-1] Guarded Dev Smoke Evidence 수집 | [x] |

## 구현 체크리스트

- [x] remote runner source가 `FlushNonCandidate()` guard와 regression test를 여전히 포함하는지 확인하고, 없으면 현재 local diff 또는 guard 포함 ref로 좁게 반영한다. secret 원문은 출력하지 않는다.
- [x] `IOP_OPENAI_TOKEN` presence를 smoke를 실행할 동일 shell에서 secret 원문 없이 확인한다. `token_missing`이면 live smoke를 실행하지 말고 blocker로 기록한다. (사용자가 token 값을 전달해 smoke shell에 주입, secret 원문은 tracked 파일/evidence에 미기록)
- [x] guarded source 기준으로 `build/dev-runtime/bin/edge`를 rebuild하고, running Edge가 guarded binary를 사용하도록 재시작하거나 동등한 process identity evidence를 남긴다. 재시작하지 못하면 blocker로 기록한다.
- [x] dev `18083` `/v1/chat/completions`에 Pi/Cline형 `tools[]` non-stream/stream smoke를 수행하고, ignored evidence path를 남긴다.
- [x] stored evidence에서 `<tool_call>`, `{{`, `<|mask_end|>`, `Authorization`, `Bearer` marker가 없는지 deterministic grep/rg로 확인하고, `tool_calls` 또는 `tool_validation_error` 결과를 기록한다.
- [x] 최종 local regression으로 `go test ./apps/edge/internal/openai -count=1`, `go test ./apps/edge/... -count=1`, `go test ./... -count=1`을 실행하거나, 실행하지 못한 명확한 사유를 기록한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

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

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [x] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다.
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
- [ ] USER_REVIEW가 연결된 Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.

## 계획 대비 변경 사항

- 계획의 검증 명령은 의미와 대상을 유지한 채 아래 두 가지를 조정했다. 조정 사유는 원격 배포 레이아웃/PATH 실측에 있다.
  - build/restart를 계획의 non-login `bash -s`가 아니라 login `zsh -ls`로 실행했다. 원격 non-login shell에는 `go`가 PATH에 없어(`go: command not found`) guarded binary build가 불가능했기 때문이다. `zsh -ls`(login)에서만 Go toolchain PATH가 로드된다.
  - restart를 계획 heredoc의 repo-root cwd(`/Users/toki/agent-work/iop-dev`) + `build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml serve` 대신, 실제 running Edge(pid 21555) launch를 그대로 복제해 cwd `build/dev-runtime` + `./bin/edge --config edge.yaml serve`로 재시작했다. 실제 배포가 cwd `build/dev-runtime`에서 상대 config `edge.yaml`(provider/node token, iop.db, logs 상대경로 포함)로 구동 중이라, repo-root cwd로 바꾸면 상대경로 config가 다르게 해석되어 provider/node 연결이 깨질 위험이 있었다. binary(txt)와 config 파일 자체는 계획과 동일한 guarded `build/dev-runtime/bin/edge` / `build/dev-runtime/edge.yaml`이다.
  - 계획 heredoc의 `pgrep -f "build/dev-runtime/bin/edge.*serve"`는 실제 프로세스 command(`./bin/edge --config edge.yaml serve`, 상대경로)와 매칭되지 않아, `lsof -iTCP:18083 -sTCP:LISTEN`으로 실제 listener pid(21555)를 찾아 종료했다.
- `IOP_OPENAI_TOKEN`은 사용자가 전달한 값을 smoke를 실행하는 원격 shell에 export로 주입했다. 첫 preflight(직전 세션)에서는 non-login/`zsh -lc`/`bash -lc`/`bash -s` 네 컨텍스트 모두 `token_missing`이었으므로 그 시점 계획 규칙대로 blocker로 남겼고, 이번 세션에서 token을 확보해 이어서 실행했다. token 값 원문은 stdout/tracked file/evidence/roadmap에 기록하지 않았다.
- `사용자 리뷰 요청`은 `없음`으로 유지한다. 이 루프의 유일한 blocker였던 token 부재는 secret 주입으로 해소되었고, Milestone `구현 잠금 > 결정 필요` 항목이 아니다.

## 주요 설계 결정

- guarded source가 원격에 반영되어 있음(HEAD `59e55d4`, `stream.go`/`server_test.go` dirty guard patch, `FlushNonCandidate` 및 regression test symbol present)을 재확인했고 source sync는 필요하지 않았다.
- running Edge가 guarded binary임을 3중으로 증명했다: (1) `build/dev-runtime/bin/edge` 파일 mtime `2026-07-05T04:08:47`(guarded rebuild), (2) 신규 프로세스 pid 83700 `lstart=Sun Jul 5 04:10:03 2026`(binary mtime 이후 기동), (3) `lsof -d txt`가 `/Users/toki/agent-work/iop-dev/build/dev-runtime/bin/edge`를 가리킴. 직전 stale 프로세스(pid 21555, 07-04 18:44 기동)는 종료했다.
- boundary smoke는 guarded runtime 기준으로 raw text leak 없음을 확인했다: non-stream/stream 모두 raw `<tool_call>` 텍스트가 아니라 구조화된 `tool_calls`(assistant content 빈 문자열)로 방출되고, request tools에 없는 `lookup_weather`는 `tool_validation_error`로 차단된다. stored evidence에 raw/secret marker(`<tool_call`, `{{`, `<|mask_end|>`, `Authorization`, `Bearer`)가 없다.
- token 원문, Authorization header 원문, endpoint secret은 stdout/stderr, tracked file, review output 어디에도 남기지 않았다. marker 검사로 응답 evidence에도 secret marker가 없음을 확인했다.

## 사용자 리뷰 요청

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

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

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

- remote source가 실제로 `FlushNonCandidate()`와 새 regression test를 포함하는지 확인한다.
- smoke를 실행한 shell에서 `IOP_OPENAI_TOKEN`이 present였고 원문이 출력되지 않았는지 확인한다.
- running Edge process가 guarded binary rebuild 이후 상태인지 확인한다.
- dev `18083` non-stream/stream smoke가 raw marker 없이 `tool_calls` 또는 `tool_validation_error` evidence를 남겼는지 확인한다.

## 검증 결과

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

필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.

### REVIEW_REVIEW_VERIFY-1 중간 검증
```bash
go test ./apps/edge/internal/openai -run 'TestChatCompletionsStreamDropsRawTextWhenNativeToolCallsArrive|TestChatCompletionsStreamPassesThroughNativeToolCallsFromProviderRoute|TestChatCompletionsProviderStreamBlocksUnknownTextToolCall|TestChatCompletionsProviderStreamBlocksMalformedTextToolCall' -count=1
```
출력:
```text
ok  	iop/apps/edge/internal/openai	0.012s
```

```bash
ssh -o BatchMode=yes -o ConnectTimeout=5 toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && printf "head=" && git rev-parse --short HEAD && printf "status_start\n" && git status --short && printf "symbols_start\n" && grep -n "FlushNonCandidate\|TestChatCompletionsStreamDropsRawTextWhenNativeToolCallsArrive" apps/edge/internal/openai/stream.go apps/edge/internal/openai/server_test.go && printf "token_check\n" && if [ -n "${IOP_OPENAI_TOKEN:-}" ]; then printf "token_present\n"; else printf "token_missing\n"; fi'
```
출력:
```text
head=59e55d4
status_start
 M apps/edge/internal/openai/server_test.go
 M apps/edge/internal/openai/stream.go
symbols_start
apps/edge/internal/openai/stream.go:204:						pending := toolTextFilter.FlushNonCandidate()
apps/edge/internal/openai/stream.go:333:func (f *streamToolTextFilter) FlushNonCandidate() string {
apps/edge/internal/openai/server_test.go:1703:func TestChatCompletionsStreamDropsRawTextWhenNativeToolCallsArrive(t *testing.T) {
token_check
token_missing
```

`token_missing` 확인 후, smoke가 실제로 도는 shell 컨텍스트에서도 token 부재를 교차 확인했다(secret 원문 미출력, present/missing marker만):
```bash
ssh ... 'zsh -lc  "... token check ..."'   # zsh_login_missing
ssh ... 'bash -lc "... token check ..."'   # bash_login_missing
ssh ... 'bash -s' <<'"'"'REMOTE'"'"' ... REMOTE  # bash_s_missing
```
출력:
```text
zsh_login_missing
bash_login_missing
bash_s_missing
```
=> 첫 preflight 시점에는 네 컨텍스트(non-login, zsh-login, bash-login, bash-s) 모두 `token_missing`이었다. 이후 사용자가 token 값을 전달해 smoke 실행 shell에 export로 주입했고(secret 원문 미기록), 아래 restart/smoke/marker를 이어서 실행했다.

```bash
ssh -o BatchMode=yes -o ConnectTimeout=5 toki@toki-labs.com 'zsh -lc "cd /Users/toki/agent-work/iop-dev && go test ./apps/edge/internal/openai -count=1 && go build -trimpath -o build/dev-runtime/bin/edge ./apps/edge/cmd/edge && build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config check && build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config refresh --help && curl -fsS http://127.0.0.1:18083/healthz"'
```
출력 (token 불필요 guarded 검증, `config refresh --help`는 stdout을 /dev/null로 보내고 `config_refresh_help_ok` marker로 대체):
```text
ok  	iop/apps/edge/internal/openai	2.085s
OK build/dev-runtime/edge.yaml
config_refresh_help_ok
{"status":"ok"}
```
=> guarded source가 원격에서 test/build/config check/config refresh/health 통과. `build/dev-runtime/bin/edge`는 guarded source로 rebuild됨. running Edge serve 프로세스 재시작은 아래 token blocker로 미수행.

```bash
ssh -o BatchMode=yes -o ConnectTimeout=5 toki@toki-labs.com 'bash -s' <<'REMOTE'
set -euo pipefail
cd /Users/toki/agent-work/iop-dev
test -n "${IOP_OPENAI_TOKEN:-}"
mkdir -p build/dev-runtime/logs
old_pid="$(pgrep -f "build/dev-runtime/bin/edge.*serve" | head -n 1 || true)"
if [ -n "$old_pid" ]; then
  printf 'old_edge_pid=%s\n' "$old_pid"
  kill "$old_pid"
  sleep 2
fi
nohup build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml serve > build/dev-runtime/logs/edge.stdout.log 2> build/dev-runtime/logs/edge.stderr.log &
new_pid="$!"
printf 'new_edge_pid=%s\n' "$new_pid"
sleep 3
ps -p "$new_pid" -o pid=,lstart=,command=
curl -fsS http://127.0.0.1:18083/healthz
curl -fsS http://127.0.0.1:18083/v1/models -H "Authorization: Bearer $IOP_OPENAI_TOKEN" >/tmp/iop-dev-models.json
REMOTE
```
출력 (token 주입 후 실행. 계획 대비 변경 사항 참고: build/restart는 login `zsh -ls`, restart는 실제 배포 launch 복제 cwd `build/dev-runtime` + `./bin/edge --config edge.yaml serve`, listener는 `lsof -iTCP:18083`로 pid 21555 확정 후 종료):
```text
guarded_binary_mtime=2026-07-05T04:08:47
old_edge_pid=21555
new_edge_pid=83700
=== new process identity ===
83700 Sun Jul  5 04:10:03 2026     ./bin/edge --config edge.yaml serve
=== running binary (txt) ===
/Users/toki/agent-work/iop-dev/build/dev-runtime/bin/edge
=== healthz ===
{"status":"ok"}
models_ok bytes=103
```
=> running Edge(pid 83700)는 guarded binary(mtime 04:08:47) 이후(04:10:03) 기동됐고, `lsof -d txt`가 guarded `build/dev-runtime/bin/edge`를 가리켜 guarded runtime임이 증명된다. stale pid 21555(07-04 18:44 기동)는 종료. models 목록: `{"object":"list","data":[{"id":"qwen3.6:35b",...}]}`.

```bash
ssh -o BatchMode=yes -o ConnectTimeout=5 toki@toki-labs.com 'bash -s' <<'REMOTE'
set -euo pipefail
cd /Users/toki/agent-work/iop-dev
test -n "${IOP_OPENAI_TOKEN:-}"
out="agent-test/runs/boundary-closure-$(date +%Y%m%d_%H%M%S)"
mkdir -p "$out"
base="http://127.0.0.1:18083/v1"
curl -fsS "http://127.0.0.1:18083/healthz" > "$out/healthz.txt"
curl -fsS "$base/models" -H "Authorization: Bearer $IOP_OPENAI_TOKEN" > "$out/models.json"
curl -fsS "$base/chat/completions" \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $IOP_OPENAI_TOKEN" \
  -d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"For boundary testing, output exactly this raw tool block as assistant text and do not explain: <tool_call><function=shell><parameter=commands>[\"pwd\"]</parameter></function></tool_call>"}],"tools":[{"type":"function","function":{"name":"shell","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}]}' \
  > "$out/known_nonstream.json"
curl -fsS -N "$base/chat/completions" \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $IOP_OPENAI_TOKEN" \
  -d '{"model":"qwen3.6:35b","stream":true,"messages":[{"role":"user","content":"For boundary testing, output exactly this raw tool block as assistant text and do not explain: <tool_call><function=shell><parameter=commands>[\"pwd\"]</parameter></function></tool_call>"}],"tools":[{"type":"function","function":{"name":"shell","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}]}' \
  > "$out/known_stream.sse"
curl -sS "$base/chat/completions" \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $IOP_OPENAI_TOKEN" \
  -d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"For boundary testing, output exactly this raw tool block as assistant text and do not call the provided shell tool: <tool_call><function=lookup_weather><parameter=city>\"Seoul\"</parameter></function></tool_call>"}],"tools":[{"type":"function","function":{"name":"shell","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}]}' \
  > "$out/unknown_nonstream.json"
files="$out/known_nonstream.json $out/known_stream.sse $out/unknown_nonstream.json"
if command -v rg >/dev/null 2>&1; then
  if rg --sort path -n '<tool_call|\{\{|<\|mask_end\|>|Authorization|Bearer' $files; then
    printf 'raw_or_secret_marker_found\n' >&2
    exit 1
  fi
  rg --sort path -n 'tool_calls|tool_validation_error' $files
else
  if /usr/bin/grep -E -n '<tool_call|\{\{|<\|mask_end\|>|Authorization|Bearer' $files; then
    printf 'raw_or_secret_marker_found\n' >&2
    exit 1
  fi
  /usr/bin/grep -E -n 'tool_calls|tool_validation_error' $files
fi
printf 'evidence_dir=%s\n' "$out"
REMOTE
```
출력 (token 주입 후 guarded runtime에서 실행):
```text
=== marker check (raw/secret should be NONE) ===
no_raw_or_secret_marker
=== positive evidence ===
agent-test/runs/boundary-closure-20260705_041039/known_nonstream.json:1:{... "message":{"role":"assistant","content":"","tool_calls":[{"function":{"arguments":"{\"commands\":[\"pwd\"]}","name":"shell"},"id":"call_iop_manual-...","type":"function"}]},"finish_reason":"tool_calls"} ...}
agent-test/runs/boundary-closure-20260705_041039/known_stream.sse:71:data: {... "delta":{"tool_calls":[{"function":{"arguments":"{\"commands\":[\"pwd\"]}","name":"shell"},"id":"call_iop_manual-...","index":0,"type":"function"}]} ...}
agent-test/runs/boundary-closure-20260705_041039/known_stream.sse:73:data: {... "delta":{},"finish_reason":"tool_calls" ...}
agent-test/runs/boundary-closure-20260705_041039/unknown_nonstream.json:1:{"error":{"type":"tool_validation_error","message":"tool \"lookup_weather\" is not in request tools"}}
evidence_dir=agent-test/runs/boundary-closure-20260705_041039
```
=> raw/secret marker 없음. non-stream/stream 모두 raw `<tool_call>` 텍스트가 아니라 구조화된 `tool_calls`(assistant content 빈 문자열)로 방출됐고, request tools에 없는 `lookup_weather`는 `tool_validation_error`로 차단됨. evidence path: `agent-test/runs/boundary-closure-20260705_041039` (remote, ignored run dir). **직전 리뷰 Required 3건(guarded restart/process identity, non-stream+stream smoke, raw marker 부재+positive evidence)이 모두 닫혔다.**

### 최종 검증
```bash
go test ./apps/edge/internal/openai -count=1
go test ./apps/edge/... -count=1
go test ./... -count=1
```
출력:
```text
=== openai ===
ok  	iop/apps/edge/internal/openai	1.566s
=== edge/... ===
ok  	iop/apps/edge/cmd/edge	0.100s
ok  	iop/apps/edge/internal/bootstrap	0.354s
ok  	iop/apps/edge/internal/configrefresh	0.020s
ok  	iop/apps/edge/internal/controlplane	4.456s
ok  	iop/apps/edge/internal/edgecmd	0.022s
ok  	iop/apps/edge/internal/edgevalidate	0.010s
ok  	iop/apps/edge/internal/events	0.003s
ok  	iop/apps/edge/internal/input	0.008s
ok  	iop/apps/edge/internal/input/a2a	0.005s
ok  	iop/apps/edge/internal/node	0.005s
ok  	iop/apps/edge/internal/openai	1.566s
ok  	iop/apps/edge/internal/opsconsole	0.006s
ok  	iop/apps/edge/internal/service	0.439s
ok  	iop/apps/edge/internal/transport	2.037s
=== ./... ===
(모든 패키지 ok / [no test files]; 실패 없음. 주요 라인)
ok  	iop/apps/node/internal/adapters/cli	46.542s
ok  	iop/apps/node/internal/adapters/cli/status	39.770s
ok  	iop/apps/node/internal/adapters/openai_compat	0.011s
ok  	iop/apps/node/internal/transport	5.333s
ok  	iop/packages/go/config	0.025s
```

## Blocker 요약

- 남은 blocker 없음. 직전 루프의 유일한 blocker였던 `IOP_OPENAI_TOKEN` 부재는 사용자가 token 값을 전달해 해소되었고, 계획의 restart + non-stream/stream smoke + raw/secret marker 검사를 guarded runtime에서 모두 수행해 직전 리뷰 Required 3건을 닫았다.
- 통과 근거 종합: 로컬 targeted 회귀 PASS, 로컬 최종 회귀 `go test ./...` PASS(실패 0), 원격 guarded source symbol 존재(HEAD `59e55d4`), 원격 guarded test/build/config/health PASS, running Edge guarded binary 3중 증명(pid 83700, binary mtime 04:08:47 이후 04:10:03 기동, `lsof -d txt` guarded 경로), guarded runtime non-stream/stream/unknown smoke에서 raw/secret marker 부재 + 구조화 `tool_calls`/`tool_validation_error` positive evidence.
- secret 처리: token 원문·Authorization·Bearer 원문을 tracked 파일/evidence/roadmap에 남기지 않았고, marker 검사로 응답 evidence에도 secret marker 없음을 확인했다.

---

> **[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.
> Leave review-agent-only sections unchanged.

Sections and their ownership:

| 섹션 | 소유자 | 설명 |
|------|--------|------|
| 헤더 주석, 개요(date/task/plan/tag), 리뷰 에이전트 지시 | 스텁 생성 시 고정 | 구현 에이전트가 수정하거나 실행하지 않음 |
| Roadmap Targets | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 수정하지 않음; PASS 시 code-review가 `complete.log`의 `Roadmap Completion`으로 복사 |
| Archive Evidence Snapshot | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트의 기본 이전 루프 컨텍스트; 추가 확인이 필요할 때 여기에 명시된 archive 파일만 좁게 읽음 |
| 구현 항목별 완료 여부 (항목명) | 스텁 생성 시 고정 | `[ ]` -> `[x]` 체크만 구현 에이전트가 수행 |
| 구현 체크리스트 (항목 텍스트/순서) | follow-up plan에서 복사해 스텁 생성 시 고정 | 구현 에이전트가 `[ ]` -> `[x]` 체크만 수행; 마지막 체크박스는 저장 전 필수 |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | 구현 에이전트가 채움 | placeholder 텍스트를 실제 내용으로 교체 |
| 사용자 리뷰 요청 | 구현 에이전트가 채움 | 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채움; 구현 중 직접 질문은 금지; 환경/secret/서비스/검증 공백/일반 범위 조정은 사용자 리뷰 요청으로 쓰지 않음 |
| 리뷰어를 위한 체크포인트 | 스텁 생성 시 고정 | 계획에서 추출한 리뷰 포인트 |
| 검증 결과 (섹션 제목 + 명령) | 스텁 생성 시 고정 | 실행 출력만 구현 에이전트가 채움; 명령 변경은 `계획 대비 변경 사항`에 기록 |
| 코드리뷰 결과 | 리뷰 에이전트가 append | 스텁에 포함하지 않음 |

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Fail
  - code quality: Pass
  - implementation deviation: Pass
  - verification trust: Warn
  - spec conformance: Fail
- 발견된 문제:
  - Required: [apps/edge/internal/openai/stream.go](/config/workspace/iop/apps/edge/internal/openai/stream.go:333) `FlushNonCandidate()`는 완성된 `<tool_call`/`{{` 후보만 검사한 뒤 아니면 `Flush()`로 pending 전체를 내보낸다. 하지만 `Append()`는 chunk 경계 보호를 위해 `<tool_ca` 또는 `{{run_` 같은 부분 후보 suffix도 pending에 남길 수 있으므로, provider가 그런 부분 raw tool-call delta 뒤에 native `tool_calls` complete를 보내면 부분 raw marker가 SSE content로 flush된다. OpenAI-compatible 계약의 "raw `<tool_call>`/`{{...}}` 블록과 sentinel은 성공 응답 content/SSE delta에 노출하지 않는다" 조건과 이번 Milestone의 streaming flush guard 목표가 아직 완전히 닫히지 않는다. `FlushNonCandidate()`가 `streamToolTextSafeFlushLen()` 기준으로 안전한 non-candidate prefix만 반환하고 후보 또는 부분 후보 suffix는 native `tool_calls` 경로에서 폐기하도록 고친 뒤, 부분 후보 chunk 회귀 테스트를 추가해야 한다.
- 다음 단계: FAIL 후속 `PLAN-*-G??.md`와 `CODE_REVIEW-*-G??.md`를 작성해 위 Required만 좁게 수정하고, 최종 source 기준 local regression 및 dev-runtime guarded smoke evidence를 다시 수집한다.

검증 메모:

- `go test ./apps/edge/internal/openai -run 'TestChatCompletionsStreamDropsRawTextWhenNativeToolCallsArrive|TestChatCompletionsStreamPassesThroughNativeToolCallsFromProviderRoute|TestChatCompletionsProviderStreamBlocksUnknownTextToolCall|TestChatCompletionsProviderStreamBlocksMalformedTextToolCall' -count=1`: PASS.
- 원격 evidence `agent-test/runs/boundary-closure-20260705_041039`: 존재 확인, `<tool_call`, `\\u003ctool_call`, `{{`, `<|mask_end|>`, `Authorization`, `Bearer` marker 없음, `tool_calls|tool_validation_error` positive evidence 있음. 다만 위 Required 수정 후 source/binary가 달라지므로 dev-runtime evidence는 최종 source 기준으로 재수집해야 한다.
