<!-- task=m-operator-headless-workflow-validation/02+01_api_market plan=1 tag=REVIEW_OPS -->

# Plan - REVIEW_OPS API Market Review Follow-Up

## 이 파일을 읽는 구현 에이전트에게

이 plan은 `code_review_cloud_G07_0.log`의 FAIL 판정을 해소하는 좁은 follow-up이다. 코드 변경 후 `CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 검증 출력으로 채우고 active 파일을 그대로 둔 채 리뷰를 요청한다. 검증 증거 공백은 사용자 리뷰 요청이 아니며, 원격 실행이 불가능하면 시도한 명령과 실제 stdout/stderr를 남긴다.

## 배경

첫 리뷰에서 API/market scenario runner 구현은 대체로 계획한 surface를 만들었지만, 필수 원격 검증이 실행되지 않았고 test contract 일부가 비어 있었다. 특히 scenario expectation typo가 validation을 통과하고, request field round-trip과 `status=mismatch` 출력 경로가 테스트로 고정되지 않았다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. code-review가 해당 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md`
- Task ids:
  - `terminal-run`: terminal에서 scenario를 실행하거나 대표 조회를 수행할 수 있다.
  - `market-check`: market data status/import 관련 결과를 화면 없이 확인할 수 있다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/private/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-roadmap/current.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/rules/project/domain/operations/rules.md`
- `agent-ops/rules/project/domain/api/rules.md`
- `agent-ops/rules/project/domain/worker/rules.md`
- `agent-ops/rules/project/domain/contracts/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/operations-smoke.md`
- `agent-test/local/api-smoke.md`
- `agent-test/local/worker-smoke.md`
- `agent-test/local/contracts-smoke.md`
- `agent-task/m-operator-headless-workflow-validation/02+01_api_market/plan_cloud_G07_0.log`
- `agent-task/m-operator-headless-workflow-validation/02+01_api_market/code_review_cloud_G07_0.log`
- `apps/cli/internal/operator/scenario.go`
- `apps/cli/internal/operator/scenario_test.go`
- `apps/cli/internal/operator/client.go`
- `apps/cli/internal/operator/client_test.go`
- `apps/cli/internal/operator/runner.go`
- `apps/cli/internal/operator/runner_market_test.go`
- `apps/cli/internal/operator/output.go`
- `apps/cli/internal/operator/parser_map.go`
- `apps/cli/internal/operator/parser_map_test.go`
- `apps/cli/internal/cli/cli.go`
- `apps/cli/internal/cli/cli_test.go`
- `apps/cli/testdata/operator/api_connection_smoke.yaml`
- `apps/cli/testdata/operator/invalid_request_matrix.yaml`
- `apps/cli/testdata/operator/market_data_status_query.yaml`
- `services/api/internal/contracts/parser_map.go`
- `services/worker/internal/contracts/parser_map.go`
- `services/api/internal/socket/handlers.go`
- `services/api/internal/socket/market.go`
- `services/api/internal/workerclient/client.go`
- `../proto-socket/go/ws_client.go`
- `../proto-socket/go/communicator.go`

### 테스트 환경 규칙

- `test_env=local`.
- `agent-test/local/rules.md` 기준 현재 shell에서 테스트/검증 직접 실행은 금지된다.
- 검증은 `ssh toki@toki-labs.com` 접속 후 원격 ALT checkout root에서 실행한다.
- 변경 범위는 `apps/cli/**`이므로 primary profile은 `operations-smoke`; API/worker/contracts runtime source는 수정하지 않지만 parser/transport surface를 호출하므로 최종 `bin/test`를 유지한다.

### 리뷰 이슈 요약

- `expect.status` validation이 없어 typo가 headless dry-run에서 잡히지 않는다.
- `status=mismatch` 출력 경로가 테스트로 고정되지 않았다.
- fake API server가 request fields를 검증하지 않아 client request round-trip 테스트가 약하다.
- 원격 `(cd apps/cli && go test -count=1 ./...)`와 `bin/test` 실제 stdout/stderr가 없다.

### 범위 결정 근거

- API/worker/protobuf schema는 수정하지 않는다. CLI scenario validation과 tests, 필요한 경우 runner test helper만 수정한다.
- 기존 output format 선택(`text` default, `jsonl` option)은 이번 follow-up에서 바꾸지 않는다. 리뷰의 Required 이슈가 아니며 변경하면 terminal contract 범위가 커진다.
- user-review gate는 트리거하지 않는다. 검증 증거는 원격 명령 재실행으로 회수 가능한 follow-up 작업이다.

### 빌드 등급

- build lane: `cloud-G07`, review lane: `cloud-G07`.
- 이유: terminal command, stdout/stderr evidence, exit-status contract, verification-trust recovery가 포함된 CLI workflow follow-up이다.

## 구현 체크리스트

- [ ] `expect.status`와 `expect.error_code` validation을 추가하고 invalid expectation fixture/test를 작성한다.
- [ ] API client fake server가 `ListInstruments`와 `ListBars` request fields를 검증하도록 테스트를 보강한다.
- [ ] runner 출력 테스트에 expectation mismatch 경로를 추가해 exit code 1과 `status=mismatch`를 고정한다.
- [ ] 원격 ALT checkout root에서 focused CLI test와 `bin/test`를 실행하고 실제 stdout/stderr를 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

- 이 directory 이름의 runtime dependency는 `02+01_api_market`이고 predecessor `01_scenario_core`는 `agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/complete.log`로 충족되어 있다.
- 추가 hidden dependency를 만들지 않는다.

### [REVIEW_OPS-1] Scenario Expectation Validation

#### 문제

`Expect.Status`는 `"ok"` 또는 `"error"`만 의미가 있지만 `Scenario.Validate`가 값을 확인하지 않는다. 잘못 쓴 `expect.status`가 parse error가 아니라 ok 기대값처럼 흘러가면 terminal scenario가 실패 조건을 잘못 표현할 수 있다.

#### 해결 방법

- `apps/cli/internal/operator/scenario.go`에 expectation validation을 추가한다.
- 허용 상태는 empty(default ok), `"ok"`, `"error"`로 제한한다.
- `expect.error_code`는 `expect.status: error`일 때만 허용한다.
- `apps/cli/internal/operator/scenario_test.go`에 invalid expectation status와 `error_code` misuse 테스트를 추가한다.

#### 중간 검증

원격 ALT checkout root 기준:

```bash
(cd apps/cli && go test -count=1 ./internal/operator -run 'Test(ParseScenario|LoadScenario)')
```

기대 결과: exit code 0.

### [REVIEW_OPS-2] Request Field And Mismatch Tests

#### 문제

`TestAPIClientListInstruments`는 request field를 보내지만 fake server가 값을 검증하지 않는다. `ListBars` client request field 테스트도 없다. 또한 `status=mismatch` 출력 경로가 테스트로 고정되지 않아 expectation mismatch가 typed ErrorInfo/transport와 구분된다는 계획 항목이 완료되지 않았다.

#### 해결 방법

- `apps/cli/internal/operator/client_test.go`의 fake API helper가 optional assertion callback 또는 captured request fields를 지원하게 한다.
- `TestAPIClientListInstruments`에서 `Market_MARKET_KR` 수신을 검증한다.
- `TestAPIClientListBars`를 추가해 `InstrumentId`, `Timeframe`, `FromUnixMs`, `ToUnixMs`를 검증한다.
- `apps/cli/internal/operator/runner_market_test.go`에 `MinCount` 부족 또는 wrong expected error code scenario를 추가하고 exit code `codeAppError`, `status=mismatch` 출력을 확인한다.

#### 중간 검증

원격 ALT checkout root 기준:

```bash
(cd apps/cli && go test -count=1 ./internal/operator -run 'Test(APIClient|RunScenario)')
```

기대 결과: exit code 0.

### [REVIEW_OPS-3] Verification Evidence Recovery

#### 문제

첫 구현은 로컬 compile gate만 기록했고 계획의 원격 focused CLI test와 operations smoke를 실행하지 않았다. 로컬 테스트 금지 규칙 때문에 pass로 인정할 수 없고, 실제 원격 stdout/stderr가 필요하다.

#### 해결 방법

- 원격 ALT checkout root에서 아래 최종 검증을 실행한다.
- 성공하면 실제 stdout/stderr를 review stub에 붙인다.
- 원격 host/check-out/toolchain 접근이 불가능하면 pass로 표시하지 말고, 시도한 명령과 실제 실패 출력, 미실행 명령을 review stub에 기록한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/cli/internal/operator/scenario.go` | REVIEW_OPS-1 |
| `apps/cli/internal/operator/scenario_test.go` | REVIEW_OPS-1 |
| `apps/cli/internal/operator/client_test.go` | REVIEW_OPS-2 |
| `apps/cli/internal/operator/runner_market_test.go` | REVIEW_OPS-2 |
| `agent-task/m-operator-headless-workflow-validation/02+01_api_market/CODE_REVIEW-cloud-G07.md` | REVIEW_OPS-3 |

## 최종 검증

원격 ALT checkout root 기준으로 실행한다. CLI output/exit code tests must be fresh; use `-count=1`.

```bash
(cd apps/cli && go test -count=1 ./...)
bin/test
```

기대 결과: 모든 명령 exit code 0. 접근 불가나 toolchain 문제는 pass가 아니며 실제 실패 출력과 함께 차단으로 기록한다.
