<!-- task=m-command-first-operator-workflow/02+01_import_cli_scenario plan=1 tag=REVIEW_CLI -->

# Plan - Review Follow-up Import CLI Scenario

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

이 plan은 code-review FAIL 후속 구현 지시서다. 범위는 archived `code_review_cloud_G07_0.log`의 Required 두 건으로 제한한다. 구현 완료 전 active `CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 실제 내용과 검증 출력으로 채운다. 최종 판정, archive 이동, `complete.log` 작성은 code-review-skill 전용이다. 사용자-only blocker가 있으면 review stub의 `사용자 리뷰 요청`을 채우고 멈춘다. 후속 에이전트가 재실행으로 해소할 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

첫 리뷰에서 `import_daily_bars` CLI scenario 구현은 기본 경로 테스트를 통과했지만, 두 가지 계약 문제가 남았다. 새 import count 출력 필드가 non-import step output에 0 값으로 새어 나갈 수 있고, CLI dry-run validation이 API/worker가 요구하는 import date range보다 느슨하다. 이 follow-up은 output contract와 dry-run validation contract를 좁게 복구한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operator-surface/milestones/command-first-operator-workflow.md`
- Task ids:
  - `live-import-command`: KIS live daily bars를 worker storage에 import하는 command 또는 YAML scenario가 있다.
- Completion mode: check-on-pass

## 분석 결과

### 근거 파일

- `agent-task/m-command-first-operator-workflow/02+01_import_cli_scenario/code_review_cloud_G07_0.log`
- `apps/cli/internal/operator/runner.go`
- `apps/cli/internal/operator/output.go`
- `apps/cli/internal/operator/scenario.go`
- `apps/cli/internal/operator/scenario_test.go`
- `apps/cli/internal/operator/runner_market_test.go`
- `apps/cli/internal/operator/runner_error_test.go`
- `services/api/internal/socket/market.go`
- `services/worker/internal/socket/market.go`

### 범위 결정 근거

- Include only the two Required findings from review 0.
- Do not change protobuf/API/worker contracts; the CLI must align with the existing API/worker import date contract.
- Do not add live KIS smoke or secret-dependent evidence.
- Do not touch Flutter UI or roadmap files.

### 빌드 등급

- build=`cloud-G07`, review=`cloud-G07`: CLI scenario validation and stdout/JSONL output contract are the central behavior.

## 구현 체크리스트

- [ ] [REVIEW_CLI-1] Prevent import count fields from leaking into non-import step output. 검증: focused output tests prove non-import steps omit `instrument_count`/`bar_count` while import success still emits them.
- [ ] [REVIEW_CLI-2] Require import date range in CLI dry-run validation to match API/worker contract. 검증: scenario validation tests reject missing `from_yyyymmdd` and `to_yyyymmdd`, and the import smoke fixture still validates.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [REVIEW_CLI-1] Import Count Output Leakage

### 문제

`apps/cli/internal/operator/runner.go:196` initializes the base `StepEvent` with `Count: noCount` only. `apps/cli/internal/operator/output.go:111` and `apps/cli/internal/operator/output.go:152` emit `instrument_count` and `bar_count` whenever the values are `>= 0`. Because Go int fields default to `0`, hello/list/backtest/transport events can print import-specific zero counts.

### 해결 방법

Initialize `InstrumentCount` and `BarCount` to `noCount` for every base event path before `WriteStep`, or introduce an equivalent explicit presence signal. Preserve legitimate import success output, including zero counts if the import response really reports zero instruments or bars. Add negative assertions for at least one non-import text output and one JSONL output path so this regression cannot pass by substring-only tests.

### 수정 파일 및 체크리스트

- [ ] `apps/cli/internal/operator/runner.go`: base/transport `StepEvent` import count defaults.
- [ ] `apps/cli/internal/operator/runner_market_test.go` or `apps/cli/internal/operator/runner_error_test.go`: non-import output must not contain import count keys.
- [ ] `apps/cli/internal/operator/output.go`: only if the writer contract needs an explicit presence signal instead of sentinel initialization.

### 테스트 작성

Add assertions that hello/list/backtest output omits `instrument_count` and `bar_count`; keep the import success test asserting `instrument_count=2` and `bar_count=100`.

### 중간 검증

```bash
go test ./apps/cli/internal/operator -run 'RunScenario.*(API|Market|Backtest|Import)|JSONL' -count=1
```

Expected: focused runner/output tests pass and prove non-import output has no import count keys.

## [REVIEW_CLI-2] Import Date Dry-run Validation

### 문제

`apps/cli/internal/operator/scenario.go:386` validates `from_yyyymmdd` and `to_yyyymmdd` only when present. The API import handler calls `validateImportDateRange` and the worker `parseImportDateRange` requires both endpoints, so `alt operator scenario validate` currently accepts an import scenario that the runtime later rejects as `invalid_request`.

### 해결 방법

Make `import_daily_bars` dry-run validation require both `request.from_yyyymmdd` and `request.to_yyyymmdd`, then keep the existing `YYYYMMDD` shape and ascending range checks. Add table-driven cases for missing `from_yyyymmdd` and missing `to_yyyymmdd`. Keep `kis_daily_import_smoke.yaml` valid.

### 수정 파일 및 체크리스트

- [ ] `apps/cli/internal/operator/scenario.go`: require both import date endpoints before shape/range validation.
- [ ] `apps/cli/internal/operator/scenario_test.go`: add missing-from and missing-to invalid cases.

### 테스트 작성

Extend the existing invalid scenario validation table. The missing-date tests should fail during `validate`, before any socket call.

### 중간 검증

```bash
go test ./apps/cli/internal/operator -run 'Scenario|ImportDailyBars' -count=1
go run ./apps/cli/cmd/alt operator scenario validate --file apps/cli/testdata/operator/kis_daily_import_smoke.yaml
```

Expected: scenario tests pass and the import smoke fixture remains valid.

## 최종 검증

```bash
go test ./apps/cli/internal/operator -run 'RunScenario|ImportDailyBars|Scenario|Handoff|JSONL' -count=1
go test ./apps/cli/... -count=1
go run ./apps/cli/cmd/alt operator scenario validate --file apps/cli/testdata/operator/kis_daily_import_smoke.yaml
git diff --check
```

Expected: all commands exit 0. Record actual stdout/stderr in `CODE_REVIEW-cloud-G07.md`.
