<!-- task=opencode_json_stream plan=0 tag=REFACTOR -->

# Code Review Reference - REFACTOR

## 개요

date=2026-05-04
task=opencode_json_stream, plan=0, tag=REFACTOR

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

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

1. `CODE_REVIEW.md` → `code_review_N.log` (N = 기존 code_review_*.log 수)
2. `PLAN.md` → `plan_M.log` (M = 기존 plan_*.log 수)
3. PASS인 경우 `complete.log` 작성 후 종료. WARN/FAIL인 경우 새 `PLAN.md` + `CODE_REVIEW.md` 스텁 작성.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [REFACTOR-1] Opencode 프로필을 JSON 이벤트 출력으로 전환 | [ ] |
| [REFACTOR-2] Opencode JSON 이벤트 스트림 파서를 실제 CLI 출력에 맞게 갱신 | [ ] |
| [REFACTOR-3] Opencode JSON 스트림 회귀 테스트와 수동 smoke test를 정리 | [ ] |

## 계획 대비 변경 사항

_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._

## 주요 설계 결정

_구현 에이전트가 주요 설계 결정 사항을 기록한다._

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

- `configs/edge.yaml`의 `opencode` 프로필이 `--format json`과 `output_format: "opencode-json"`을 함께 사용하고 있는지 확인하세요.
- `emitOpencodeJSON`가 더 이상 `io.ReadAll` 기반 단일 `{"response":"..."}` 파서를 쓰지 않고, line-by-line JSON 이벤트를 읽어 `type:"text"`의 `part.text`만 delta로 내보내는지 확인하세요.
- `emitOpencodeJSON`가 `step_start`/`step_finish` 같은 비텍스트 이벤트를 중복 출력하지 않고, 오류 이벤트만 `RuntimeEventError`로 반영하는지 확인하세요.
- `apps/node/internal/adapters/cli/oneshot/cli_test.go`의 `opencode-json` 회귀 테스트 fixture가 실제 이벤트 stream 형식(`step_start`/`text`/`step_finish`)으로 바뀌었는지 확인하세요.
- 구현 에이전트가 PATH 축소 환경 smoke test 또는 동등한 검증으로 `opencode` 실행 경로와 JSON 파서 동작을 함께 검증했는지 확인하세요.

## 검증 결과

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

### REFACTOR-1 중간 검증
```bash
$ rg -n 'opencode:|--format|output_format' configs/edge.yaml
(output)
```

### REFACTOR-2 중간 검증
```bash
$ go test ./apps/node/internal/adapters/cli/oneshot -run TestCLIExecuteOneShotOpencodeJSON -count=1
(output)
```

### REFACTOR-3 중간 검증
```bash
$ go test ./apps/node/internal/adapters/cli/oneshot -run 'TestCLIExecuteOneShot(OpencodeJSON|StreamJSON|CodexJSON|ClaudeJSON)' -count=1
(output)
```

### 최종 검증
```bash
$ go test ./apps/node/internal/adapters/cli/oneshot -count=1
$ env -i HOME="$HOME" PATH=/config/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin go run ./apps/edge/cmd/edge console --config configs/edge.yaml
$ env -i HOME="$HOME" PATH=/config/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ./bin/node.sh
(output)
```

## 코드리뷰 결과

- 종합 판정: FAIL

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

- 발견된 문제
  - Required: [apps/node/internal/adapters/cli/oneshot.go:361](/config/workspace/go-iop/apps/node/internal/adapters/cli/oneshot.go:361) 에서 `opencode`의 `error` 이벤트를 `string`으로 파싱하고 있어 실제 출력 형식인 `{"type":"error","error":{"name":"UnknownError","data":{"message":"..."}}}`를 해석하지 못합니다. 이 상태에서는 모델 선택 실패 같은 런타임 오류가 `RuntimeEventError` 없이 지나가고, 프로세스 exit code가 `0`이면 상위 로직이 그대로 `complete`를 보내므로 실패를 성공으로 오인할 수 있습니다. `error.data.message` 또는 동등한 중첩 필드를 파싱하도록 구조체와 테스트를 함께 보강해야 합니다.
  - Suggested: [agent-task/opencode_json_stream/CODE_REVIEW.md:25](/config/workspace/go-iop/agent-task/opencode_json_stream/CODE_REVIEW.md:25) 이후 섹션이 구현 결과로 채워지지 않아 체크리스트와 검증 출력이 모두 placeholder 상태입니다. 후속 구현에서는 실제 검증 명령 출력과 설계 변경 사항을 문서에 반영해 리뷰 신뢰도를 복구하는 것이 좋습니다.

- 다음 단계
  - FAIL: Required 이슈를 반영하는 새 `PLAN.md`와 `CODE_REVIEW.md` 스텁을 작성해 다음 구현 루프를 시작한다.
