<!-- task=07_cli_profile_proto_message plan=0 tag=API -->

# Code Review Reference - API

## 개요

date=2026-05-04
task=07_cli_profile_proto_message, plan=0, tag=API

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

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료 후 반드시 아래 순서로 아카이브하세요.

1. `CODE_REVIEW-cloud-G10.md` → `code_review_cloud_G10_N.log` (N = 기존 code_review_*.log 수)
2. `PLAN-cloud-G09.md` → `plan_cloud_G09_M.log` (M = 기존 plan_*.log 수)
3. PASS인 경우 `complete.log` 작성 후 종료. WARN/FAIL인 경우 새 routed plan + review 스텁 작성.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [API-1] proto 메시지 정의 및 생성물 갱신 | [x] |
| [API-2] edge buildConfigPayload typed 메시지 갱신 | [x] |
| [API-3] node BuildFromPayload typed 메시지 갱신 | [x] |

## 계획 대비 변경 사항

- **`CLIProfileConfig`에 `completion_marker` / `mode` / `resume_args` 필드 추가**
  - 계획서 §[API-1]의 메시지 스펙에는 이 세 필드가 빠져 있었으나, 실제 `config.CLIProfileConf` (`packages/config/config.go:106-118`)와 기존 edge 직렬화 (`completion_marker`)에 이미 존재하던 필드입니다. 그대로 따르면 edge→node 전송에서 정보가 손실되어 기존 `TestBuildNodePayload_CLICompletionMarker`/`TestCLIConfFromStruct_ProfileCompletionMarker`가 깨지므로, 동일 의미의 typed 필드를 proto에 추가했습니다.
  - `CLICompletionMarker` 메시지(line/regex)를 별도 정의했습니다.
- **`buildConfigPayload`의 mock 어댑터는 빈 `structpb.Struct`를 그대로 사용**합니다. 계획서 §[API-2]가 "mock 분기는 그대로 둔다"고 명시한 것에 따라 oneof 경로로 옮기지 않았습니다.
- **헬퍼 정리**: 계획서대로 `cliConfFromStruct`/`ollamaConfFromStruct`/`vllmConfFromStruct`/`boolFromAny`/`intFromAny` 모두 제거했고, edge의 `stringsToAny`도 제거했습니다. `addAdapter` 클로저는 mock 한 곳에서만 쓰여 인라인으로 정리했습니다.

## 주요 설계 결정

- `AdapterConfig.settings` 필드는 plan 지침대로 보존했습니다 (mock 어댑터 호환성). cli/ollama/vllm은 `settings`를 채우지 않고 `oneof config`만 사용합니다.
- `cliConfFromProto(nil)`은 `Enabled=true`, 빈 profiles 맵을 반환합니다. 호출 지점이 `ac.GetType() == "cli"` && `ac.GetEnabled() == true` 분기 안이라 typed config가 nil이어도 cli 어댑터를 등록하는 것이 자연스럽습니다.
- proto 필드 번호: 계획서 스펙 그대로(1–8) + 추가 필드는 9–11에 배치했습니다.

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

- `proto/iop/runtime.proto`에 `oneof config`가 추가되고 기존 `settings` 필드가 backward-compat을 위해 유지되었는지
  → `proto/iop/runtime.proto:131-173` 참조. `settings = 3` 유지, `oneof config`는 4-6번.
- `proto/gen/iop/runtime.pb.go`가 사람이 직접 편집되지 않고 `make proto`로 생성된 변경인지
  → `make proto` 실행만으로 생성. 헤더에 `protoc-gen-go v1.36.7 / protoc v5.29.3` 그대로.
- edge `buildConfigPayload`가 cli/ollama/vllm 모두 oneof로 채우는지, 더이상 `stringsToAny` 등 우회 헬퍼가 남아있지 않은지
  → `apps/edge/internal/transport/server.go:160-235` 참조. `stringsToAny` 제거 확인.
- node `BuildFromPayload`에서 `cliConfFromStruct`/`boolFromAny`/`intFromAny`가 모두 제거되었는지
  → `apps/node/internal/adapters/factory.go` 전체가 `cliConfFromProto`/`ollamaConfFromProto`/`vllmConfFromProto`로 교체됨. structpb import 제거.
- Persistent/Terminal/Idle timeout 정수 필드가 손실 없이 전달되는 단위 테스트가 추가되었는지
  → edge 측: `TestBuildConfigPayload_CLIOneof` (`apps/edge/internal/transport/server_test.go`). node 측: `TestCLIConfFromProto_PersistentAndIdleTimeouts` (`apps/node/internal/adapters/factory_internal_test.go`).

## 검증 결과

### API-1 중간 검증
```
$ make proto
protoc \
	--go_out=. \
	--go_opt=module=iop \
	--proto_path=. \
	proto/iop/runtime.proto \
	proto/iop/node.proto \
	proto/iop/control.proto \
	proto/iop/job.proto
$ go build ./proto/gen/...
(no output)
```

### API-2 중간 검증
```
$ go test ./apps/edge/internal/transport/...
ok  	iop/apps/edge/internal/transport	0.006s
```

### API-3 중간 검증
```
$ go test ./apps/node/internal/adapters/...
ok  	iop/apps/node/internal/adapters	0.004s
ok  	iop/apps/node/internal/adapters/cli	87.604s
?   	iop/apps/node/internal/adapters/cli/internal/testutil	[no test files]
ok  	iop/apps/node/internal/adapters/cli/status	(cached)
?   	iop/apps/node/internal/adapters/mock	[no test files]
?   	iop/apps/node/internal/adapters/ollama	[no test files]
?   	iop/apps/node/internal/adapters/vllm	[no test files]
```

### 최종 검증
```
$ make proto
protoc \
	--go_out=. \
	--go_opt=module=iop \
	--proto_path=. \
	proto/iop/runtime.proto \
	proto/iop/node.proto \
	proto/iop/control.proto \
	proto/iop/job.proto

$ go build ./...
(no output)

$ go test ./...
?   	iop/apps/control-plane/cmd/control-plane	[no test files]
ok  	iop/apps/edge/cmd/edge	(cached)
?   	iop/apps/edge/internal/bootstrap	[no test files]
ok  	iop/apps/edge/internal/node	(cached)
ok  	iop/apps/edge/internal/transport	(cached)
?   	iop/apps/node/cmd/node	[no test files]
ok  	iop/apps/node/internal/adapters	(cached)
ok  	iop/apps/node/internal/adapters/cli	(cached)
?   	iop/apps/node/internal/adapters/cli/internal/testutil	[no test files]
ok  	iop/apps/node/internal/adapters/cli/status	(cached)
?   	iop/apps/node/internal/adapters/mock	[no test files]
?   	iop/apps/node/internal/adapters/ollama	[no test files]
?   	iop/apps/node/internal/adapters/vllm	[no test files]
ok  	iop/apps/node/internal/bootstrap	(cached)
ok  	iop/apps/node/internal/node	(cached)
ok  	iop/apps/node/internal/router	(cached)
?   	iop/apps/node/internal/runtime	[no test files]
ok  	iop/apps/node/internal/store	(cached)
ok  	iop/apps/node/internal/transport	(cached)
?   	iop/apps/worker/cmd/worker	[no test files]
?   	iop/packages/auth	[no test files]
ok  	iop/packages/config	(cached)
?   	iop/packages/jobs	[no test files]
?   	iop/packages/metadata	[no test files]
?   	iop/packages/observability	[no test files]
?   	iop/packages/policy	[no test files]
?   	iop/packages/version	[no test files]
?   	iop/proto/gen/iop	[no test files]
```

## 코드리뷰 결과

- 종합 판정: FAIL

- 차원별 평가
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Fail
  - code quality: Pass
  - plan deviation: Warn
  - verification trust: Pass

- 발견된 문제
  - Required — `apps/node/internal/adapters/factory.go:28-33`, `apps/node/internal/adapters/factory.go:41-63`, `proto/iop/runtime.proto:135`: `AdapterConfig.settings`를 `legacy/compat path`로 유지한다고 선언했지만 node 역직렬화가 `GetCli()/GetOllama()/GetVllm()`만 읽도록 바뀌어 구버전 payload 호환성이 실제로 깨졌습니다. oneof가 비어 있고 `settings`만 채워진 기존 payload를 받으면 `ollama`/`vllm`은 빈 endpoint로 등록되고, `cli`는 빈 profiles 맵으로 등록되어 동작이 망가집니다. `settings` fallback 파서를 유지해 oneof가 nil일 때만 legacy 경로를 복원하거나, 정말 호환을 끊을 계획이라면 proto 주석/plan/review 문서/배포 계약을 모두 breaking change로 명시하고 관련 검증을 바꿔야 합니다. 최소한 legacy payload를 넣는 회귀 테스트를 `apps/node/internal/adapters/factory_test.go` 또는 `factory_internal_test.go`에 추가해야 합니다.

- 다음 단계
  - FAIL: `settings` legacy payload 호환성을 실제 구현과 테스트로 복구하거나, 호환 제거를 명시적 breaking change로 재계획한 뒤 다시 리뷰한다.
