<!-- task=m-korea-daily-data-foundation/02+01_import_storage_pipeline plan=0 tag=KIS_IMPORT -->

# Plan - Daily Bar Import Storage Pipeline

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

이 subtask는 `agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log`가 생긴 뒤 시작한다. 구현 완료 전 active `CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 채운다. blocker가 있으면 review stub의 `사용자 리뷰 요청`에 근거를 기록하고 멈춘다.

## 배경

Foundation provider가 KIS mock rows를 `market.Instrument`와 `market.Bar`로 만들면, worker는 이를 idempotent하게 저장하는 import pipeline이 필요하다. 현재 storage port는 instrument/bar upsert를 제공하지만 import orchestration, selector payload, idempotent 검증이 없다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 검증과 종료 처리를 소유한다.

## 분석 결과

### 읽은 파일

- `services/worker/internal/storage/ports.go`
- `services/worker/internal/storage/postgres/store.go`
- `services/worker/internal/storage/postgres/mapping.go`
- `services/worker/internal/storage/postgres/queries/queries.sql`
- `services/worker/internal/jobs/job.go`
- `services/worker/internal/jobs/builtin.go`
- `services/worker/internal/jobs/runner.go`
- `services/worker/internal/jobs/runner_test.go`
- `services/worker/testdata/providers/kis/*.json`
- `agent-task/m-korea-daily-data-foundation/01_provider_foundation/PLAN-cloud-G07.md`

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

- Import orchestration: 기존 테스트 없음. in-memory store와 mock provider test 필요.
- Idempotent re-import: storage upsert는 있으나 import-level 중복/갱신 정책 테스트 없음.
- Worker job payload: built-in handler는 placeholder라 payload decode test 없음.

### 심볼 참조

none. 기존 job kind는 유지하고 handler wiring을 보강한다.

### 분할 판단

`02+01_import_storage_pipeline`은 directory name상 `01_provider_foundation`에 의존한다. Provider API와 normalization이 완료되어야 import service가 안정적으로 컴파일된다.

### 범위 결정 근거

PostgreSQL schema 변경은 우선 제외한다. 기존 `instruments`와 `bars` 테이블, `UpsertInstrument`, `UpsertBar`로 idempotent 저장을 검증한다. 실제 KIS HTTP client와 credential wiring도 제외한다.

### 빌드 등급

build=`cloud-G07`, review=`cloud-G07`. worker orchestration과 storage idempotency를 다루므로 storage/behavior review가 필요하다.

## 의존 관계 및 구현 순서

1. `agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log` 확인
2. import service 구현
3. job payload/handler wiring 구현
4. worker tests 실행

## 구현 체크리스트

- [ ] `01_provider_foundation`의 `complete.log`를 확인하고 provider API를 그대로 사용한다.
- [ ] worker 내부에 daily bar import service를 추가해 selector -> instruments/bars -> store upsert 흐름을 구현한다.
- [ ] 같은 fixture를 두 번 import해도 instrument/bar 결과가 중복 없이 갱신되는 test를 작성한다.
- [ ] `KindImportDailyBars` payload decode와 handler registration을 실제 import service에 연결할 수 있게 분리한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [KIS_IMPORT-1] Import Service

문제: [ports.go](/config/workspace/alt/services/worker/internal/storage/ports.go:11)는 store interface만 있고 provider output을 저장하는 orchestration이 없다.

해결 방법: `services/worker/internal/marketdata/importer` 또는 동등한 worker-owned package를 추가한다. `Importer.ImportDailyBars(ctx, selector)`는 provider에서 instruments/bars를 받고 `InstrumentStore.UpsertInstrument`, `BarStore.UpsertBar`를 호출한다.

수정 파일 및 체크리스트:

- [ ] `services/worker/internal/marketdata/importer/importer.go` 추가
- [ ] `services/worker/internal/marketdata/importer/importer_test.go` 추가

테스트 작성: fake provider와 in-memory stores로 instrument 1개, bar 2개가 저장되는지 검증한다.

중간 검증:

```bash
cd services/worker && go test -count=1 ./internal/marketdata/importer
```

### [KIS_IMPORT-2] Idempotent Re-import

문제: [queries.sql](/config/workspace/alt/services/worker/internal/storage/postgres/queries/queries.sql:21)의 `UpsertBar`는 DB upsert를 제공하지만 import service가 같은 fixture를 다시 처리할 때 정책이 유지되는지 테스트가 없다.

해결 방법: importer test에서 같은 provider result를 두 번 import한다. in-memory store는 primary key `(instrument_id,timeframe,timestamp)`로 replace하도록 구현하고 최종 bar 수가 2개인지 확인한다.

수정 파일 및 체크리스트:

- [ ] `services/worker/internal/marketdata/importer/importer_test.go`에 재실행 test 추가

테스트 작성: `TestImporterIsIdempotentForSameDailyBars`.

중간 검증:

```bash
cd services/worker && go test -count=1 ./internal/marketdata/importer
```

### [KIS_IMPORT-3] Worker Job Boundary

문제: [builtin.go](/config/workspace/alt/services/worker/internal/jobs/builtin.go:11)는 `import_daily_bars`를 log-only placeholder로 처리한다.

해결 방법: 기존 `RegisterBuiltins`는 placeholder count test를 유지하되, 실제 import handler를 등록할 수 있는 별도 function 또는 option을 추가한다. payload는 provider, selector kind, symbols/date range를 포함하는 struct로 decode한다.

수정 파일 및 체크리스트:

- [ ] `services/worker/internal/jobs/builtin.go` 또는 새 `marketdata_jobs.go`에 import handler registration 추가
- [ ] `services/worker/internal/jobs/runner_test.go` 또는 새 test에 payload decode/dispatch 검증 추가

테스트 작성: `TestRegisterDailyBarImportHandlerDispatchesImporter`.

중간 검증:

```bash
cd services/worker && go test -count=1 ./internal/jobs ./internal/marketdata/importer
```

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/worker/internal/marketdata/importer/importer.go` | KIS_IMPORT-1, KIS_IMPORT-2 |
| `services/worker/internal/marketdata/importer/importer_test.go` | KIS_IMPORT-1, KIS_IMPORT-2 |
| `services/worker/internal/jobs/builtin.go` 또는 새 job file | KIS_IMPORT-3 |
| `services/worker/internal/jobs/*_test.go` | KIS_IMPORT-3 |

## 최종 검증

```bash
test -f agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log
cd services/worker && go test -count=1 ./internal/jobs ./internal/marketdata/importer ./internal/storage/...
```

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