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

# Plan - Import CLI Scenario

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

이 plan은 구현 지시서이며, 구현 완료 전 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 반드시 실제 내용과 검증 출력으로 채운다. 이 task는 `01_import_contract_worker_api`의 `complete.log`가 생긴 뒤 구현한다. 최종 판정, archive 이동, `complete.log` 작성은 code-review-skill 전용이다. 사용자-only blocker가 있으면 review stub의 `사용자 리뷰 요청`을 채우고 멈춘다. 후속 에이전트가 재실행으로 해소할 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

Foundation 작업이 import request/response를 열어도 운영자는 아직 YAML scenario로 import를 실행할 수 없다. 이 작업은 `alt operator scenario validate/run`에 `import_daily_bars` action을 추가하고, import result를 JSONL evidence key로 출력한다. PASS 시 현재 마일스톤의 `live-import-command` 기능 Task를 완료 대상으로 삼는다.

## 사용자 리뷰 요청 흐름

구현 중 사용자-only blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 이를 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## 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-test/local/rules.md`
- `agent-test/local/contracts-smoke.md`
- `agent-test/local/api-smoke.md`
- `agent-test/local/worker-smoke.md`
- `agent-test/local/operations-smoke.md`
- `agent-ops/rules/project/domain/contracts/rules.md`
- `agent-ops/rules/project/domain/api/rules.md`
- `agent-ops/rules/project/domain/worker/rules.md`
- `agent-ops/rules/project/domain/operations/rules.md`
- `agent-roadmap/current.md`
- `agent-roadmap/phase/operator-surface/PHASE.md`
- `agent-roadmap/phase/operator-surface/milestones/command-first-operator-workflow.md`
- `apps/cli/internal/operator/scenario.go`
- `apps/cli/internal/operator/scenario_test.go`
- `apps/cli/internal/operator/runner.go`
- `apps/cli/internal/operator/runner_market_test.go`
- `apps/cli/internal/operator/runner_error_test.go`
- `apps/cli/internal/operator/client.go`
- `apps/cli/internal/operator/client_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/headless_validation.md`
- `apps/cli/testdata/operator/market_data_status_query.yaml`
- `apps/cli/testdata/operator/expected/market_data_status_query.jsonl`
- `services/api/internal/socket/market.go`
- `services/api/internal/socket/market_test.go`
- `services/api/internal/workerclient/client.go`
- `services/worker/internal/socket/market.go`
- `services/worker/internal/marketdata/importer/importer.go`
- `docs/kis-live-secret-handoff.md`
- `docs/kis-live-secret-guide.md`

### 테스트 환경 규칙

- test_env: `local`.
- Matched profiles: `operations-smoke` for CLI and fixtures, plus `api-smoke`/`worker-smoke`/`contracts-smoke` inherited from predecessor surface.
- Applied commands: `go test ./apps/cli/...`, focused API/worker tests if interface signatures change, and `git diff --check`.
- External real KIS smoke is not required for this CLI action because prior milestone already proved paper/real provider access, and this plan verifies the command rail with fake socket responses.

### 테스트 커버리지 공백

- CLI scenario validation currently covers list/backtest actions, not `import_daily_bars`; add dry-run tests.
- `APIClient` has no import method until predecessor completes; add round-trip fake API test.
- Runner output has count/run/result fields but no `provider`, `instrument_count`, or `bar_count`; add output tests.
- Live import against a running API/worker remains an integration follow-up and should use sanitized SOPS evidence only when available.

### 심볼 참조

- Renamed/removed symbols: none. Additive `ActionImportDailyBars` and request fields only.

### 분할 판단

- Shared task group: `agent-task/m-command-first-operator-workflow/`.
- This subtask directory is `02+01_import_cli_scenario`; predecessor index `01` is `01_import_contract_worker_api`.
- Predecessor completion status at plan creation: missing active/archive `complete.log`; implementation must wait until `agent-task/m-command-first-operator-workflow/01_import_contract_worker_api/complete.log` or matching archive path exists.

### 범위 결정 근거

- Excludes protobuf/API/worker foundation implementation; it is in predecessor `01`.
- Excludes imported data status scenario beyond import result; it is in `03+02_market_status_scenario`.
- Excludes real secret handling and raw KIS payload capture; only sanitized command evidence is allowed.
- Excludes Flutter UI.

### 빌드 등급

- build=`cloud-G07`, review=`cloud-G07`: CLI/YAML/JSONL exit contract and stdout key behavior are central.

## 의존 관계 및 구현 순서

- Wait for predecessor `01_import_contract_worker_api` complete.log. Directory dependency is authoritative: `02+01_import_cli_scenario`.
- After predecessor completion, implement CLI schema/client/runner/output, then fixtures/tests.

## 구현 체크리스트

- [ ] [CLI-1] Add `import_daily_bars` scenario schema fields and dry-run validation.
- [ ] [CLI-2] Add CLI `APIClient.ImportDailyBars` and fake API test support.
- [ ] [CLI-3] Execute import action in runner and emit `provider`, `instrument_count`, and `bar_count` output keys.
- [ ] [CLI-4] Add import smoke YAML and expected JSONL fixture. 검증: fake/provider test and scenario validation confirm import result counts are emitted.
- [ ] [CLI-5] Run CLI-focused verification and `git diff --check`.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [CLI-1] Scenario Schema

### 문제

[apps/cli/internal/operator/scenario.go](/config/workspace/alt/apps/cli/internal/operator/scenario.go:23) enumerates known actions, but `import_daily_bars` is absent. [apps/cli/internal/operator/scenario.go](/config/workspace/alt/apps/cli/internal/operator/scenario.go:145) has request fields for reads/backtests, not provider/selector/date import input.

### 해결 방법

Add `ActionImportDailyBars Action = "import_daily_bars"` to the action set. Extend `Request` with `Provider`, `SelectorKind`, `Venue`, `Name`, `Symbols`, `FromYYYYMMDD`, and `ToYYYYMMDD`. Add closed validation maps for provider (`kis`), selector kind (`watchlist` for now), and venue (`krx`, `unspecified`). Validate non-empty provider, selector kind, symbols, valid market/venue, and optional dates matching `YYYYMMDD`.

Before (`scenario.go:23`):

```go
const (
    ActionHello Action = "hello"
    ActionListInstruments Action = "list_instruments"
    ActionListBars Action = "list_bars"
)
```

After:

```go
const (
    ActionHello Action = "hello"
    ActionImportDailyBars Action = "import_daily_bars"
    ActionListInstruments Action = "list_instruments"
    ActionListBars Action = "list_bars"
)
```

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

- [ ] `apps/cli/internal/operator/scenario.go`: action, request fields, validation helpers.
- [ ] `apps/cli/internal/operator/scenario_test.go`: valid import parse and invalid provider/selector/symbol/date tests.

### 테스트 작성

Add `TestValidateImportDailyBars` and table-driven invalid cases.

### 중간 검증

```bash
go test ./apps/cli/internal/operator -run 'Scenario|ImportDailyBars' -count=1
```

Expected: scenario tests pass.

## [CLI-2] API Client Method

### 문제

[apps/cli/internal/operator/client.go](/config/workspace/alt/apps/cli/internal/operator/client.go:104) starts market read methods but has no import request method. Fake API server in [apps/cli/internal/operator/client_test.go](/config/workspace/alt/apps/cli/internal/operator/client_test.go:91) has no import listener.

### 해결 방법

Add `ImportDailyBars(ctx, req)` one-line `sendTyped` method. Extend `fakeAPI` with `importDailyBarsResp`, recorded request, and a request listener.

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

- [ ] `apps/cli/internal/operator/client.go`: add import method.
- [ ] `apps/cli/internal/operator/client_test.go`: fake response/recording and method round-trip test.
- [ ] `apps/cli/internal/operator/parser_map_test.go`: assert import response parser is available if not already covered by predecessor.

### 테스트 작성

Add `TestAPIClientImportDailyBars` checking provider and symbols round-trip.

### 중간 검증

```bash
go test ./apps/cli/internal/operator -run APIClientImportDailyBars -count=1
```

Expected: fake API receives request and response counts round-trip.

## [CLI-3] Runner And Output

### 문제

[apps/cli/internal/operator/runner.go](/config/workspace/alt/apps/cli/internal/operator/runner.go:191) has no case for import. [apps/cli/internal/operator/output.go](/config/workspace/alt/apps/cli/internal/operator/output.go:23) has no fields for import counts, so scripts cannot grep import evidence keys.

### 해결 방법

Map scenario strings to `altv1.ImportDailyBarsRequest`, call `client.ImportDailyBars`, and evaluate typed error the same way market actions do. Add `Provider`, `InstrumentCount`, and `BarCount` to `StepEvent`; render JSONL keys `provider`, `instrument_count`, `bar_count` and text keys `provider=`, `instrument_count=`, `bar_count=`.

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

- [ ] `apps/cli/internal/operator/runner.go`: mapping, runStep case, evaluation helper.
- [ ] `apps/cli/internal/operator/output.go`: output fields and rendering.
- [ ] `apps/cli/internal/operator/runner_market_test.go`: import success and expected typed error tests.
- [ ] `apps/cli/internal/operator/runner_error_test.go`: mismatch/error exit behavior if needed.

### 테스트 작성

Add runner tests for success counts and expected typed error `unavailable`.

### 중간 검증

```bash
go test ./apps/cli/internal/operator -run 'RunScenario.*Import|ExpectedTypedError' -count=1
```

Expected: import output keys appear and expected typed errors exit 0.

## [CLI-4] Import Fixture

### 문제

[apps/cli/testdata/operator/headless_validation.md](/config/workspace/alt/apps/cli/testdata/operator/headless_validation.md:24) has no import scenario row, and there is no import YAML fixture.

### 해결 방법

Add `kis_daily_import_smoke.yaml` with one `import_daily_bars` step for provider `kis`, market `kr`, venue `krx`, selector kind `watchlist`, symbol `005930`, and date range `20240527`-`20240528`. Add expected JSONL fixture with `provider`, `instrument_count`, `bar_count`, and summary. Add it to the handoff matrix as command-first evidence, but leave UI dependency deferred.

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

- [ ] `apps/cli/testdata/operator/kis_daily_import_smoke.yaml`: new fixture.
- [ ] `apps/cli/testdata/operator/expected/kis_daily_import_smoke.jsonl`: new expected fixture.
- [ ] `apps/cli/testdata/operator/headless_validation.md`: add matrix row.
- [ ] `apps/cli/internal/operator/handoff_test.go`: include required scenario.

### 테스트 작성

Extend handoff tests to include the new fixture and JSONL keys. Do not include raw KIS payload or secret values.

### 중간 검증

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

Expected: tests pass and validate prints `scenario=kis_daily_import_smoke status=valid`.

## [CLI-5] Verification

### 문제

CLI output contracts are easy to regress through fixture drift or parse-only tests.

### 해결 방법

Run full CLI tests and diff check. Record optional live run instructions only in docs/handoff if actual running API/worker and SOPS env are available; do not block this fake-socket task on user secrets.

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

- [ ] No extra files.

### 테스트 작성

No additional tests beyond above.

### 중간 검증

```bash
git diff --check
```

Expected: no whitespace warnings.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/cli/internal/operator/scenario.go` | CLI-1 |
| `apps/cli/internal/operator/scenario_test.go` | CLI-1 |
| `apps/cli/internal/operator/client.go` | CLI-2 |
| `apps/cli/internal/operator/client_test.go` | CLI-2 |
| `apps/cli/internal/operator/parser_map_test.go` | CLI-2 |
| `apps/cli/internal/operator/runner.go` | CLI-3 |
| `apps/cli/internal/operator/output.go` | CLI-3 |
| `apps/cli/internal/operator/runner_market_test.go` | CLI-3 |
| `apps/cli/internal/operator/runner_error_test.go` | CLI-3 |
| `apps/cli/testdata/operator/kis_daily_import_smoke.yaml` | CLI-4 |
| `apps/cli/testdata/operator/expected/kis_daily_import_smoke.jsonl` | CLI-4 |
| `apps/cli/testdata/operator/headless_validation.md` | CLI-4 |
| `apps/cli/internal/operator/handoff_test.go` | CLI-4 |

## 최종 검증

```bash
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. Fresh `go test -count=1` is required for CLI behavior.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
