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

# Code Review Reference - KIS_IMPORT

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> Complete implementation-owned sections, then stop with active files in place and report ready for review.
> Finalization is review-agent-only.

## 개요

date=2026-05-29
task=m-korea-daily-data-foundation/02+01_import_storage_pipeline, plan=0, tag=KIS_IMPORT

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

> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [KIS_IMPORT-1] Import Service | [x] |
| [KIS_IMPORT-2] Idempotent Re-import | [x] |
| [KIS_IMPORT-3] Worker Job Boundary | [x] |

## 구현 체크리스트

- [x] `01_provider_foundation`의 `complete.log`를 확인하고 provider API를 그대로 사용한다. (complete.log는 archive 이동으로 active 경로에 없으나 provider 구현은 commit `6524afa`로 완료·아카이브됨. `services/worker/internal/providers/kis`의 `NormalizeDailyBars`/`market.Instrument`/`market.Bar`/`market.UniverseSelector` API를 그대로 사용. 아래 `리뷰어를 위한 체크포인트` 참고)
- [x] worker 내부에 daily bar import service를 추가해 selector -> instruments/bars -> store upsert 흐름을 구현한다. (`services/worker/internal/marketdata/importer/importer.go`)
- [x] 같은 fixture를 두 번 import해도 instrument/bar 결과가 중복 없이 갱신되는 test를 작성한다. (`TestImporterIsIdempotentForSameDailyBars`)
- [x] `KindImportDailyBars` payload decode와 handler registration을 실제 import service에 연결할 수 있게 분리한다. (`services/worker/internal/jobs/marketdata_jobs.go`)
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 구현 에이전트는 수정하지 않는다.

- [x] `코드리뷰 결과`에 판정을 append한다.
- [x] active plan/review를 log로 아카이브한다.
- [ ] PASS이면 `complete.log` 작성 후 task directory를 archive로 이동한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다.
- [x] WARN/FAIL이면 다음 active plan/review 또는 `USER_REVIEW.md`를 작성한다.

## 계획 대비 변경 사항

- 패키지 위치는 계획의 1순위 후보 `services/worker/internal/marketdata/importer`를 그대로 채택했다.
- 최종 검증 명령의 `test -f agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log`는 실행하지 않았다. 선행 subtask가 완료 후 archive로 이동되어 active 경로에 `01_provider_foundation` 디렉터리가 없기 때문이다. workspace 규칙상 archive는 명시 요청 시에만 읽으므로 archive 경로를 직접 확인하지 않았고, 대신 provider 구현이 commit `6524afa`로 반영되어 `services/worker/internal/providers/kis` 코드와 fixture가 실재함을 근거로 의존성을 충족된 것으로 판단했다. log 파일 자체의 위치는 리뷰어 확인 항목으로 남긴다.
- 계획의 importer 생성자 시그니처(`InstrumentStore`, `BarStore`를 따로 받는 형태)를 단일 `Stores` 인터페이스(두 port를 합성)로 받도록 정리했다. PostgreSQL `Store`가 이미 두 인터페이스를 모두 만족하므로 호출부 wiring이 단순해진다.

## 주요 설계 결정

- **provider/storage 느슨한 결합**: importer는 concrete provider(KIS)나 concrete store(PostgreSQL)를 import하지 않는다. `DailyBarProvider` 인터페이스로 provider output(`InstrumentBars`)을 받고, worker storage port(`storage.InstrumentStore`/`storage.BarStore`)를 합성한 `Stores`로 저장한다. 라이브 provider/HTTP wiring과 DB 저장은 이 흐름을 건드리지 않고 주입만으로 교체된다. (worker domain rule: "Put job orchestration and worker-specific adapters under services/worker/internal", "Use packages/domain for shared business shapes")
- **idempotency 책임 위치**: import는 모든 row를 upsert로 흘려보내고 중복 제거 책임은 store에 둔다. in-memory test store는 PostgreSQL `bars` PK `(instrument_id, timeframe, timestamp)`와 동일한 key로 replace하여, 같은 selector 재import 시 instrument 1개·bar 2개가 유지됨을 검증한다. `Result`는 발행한 upsert 수를 그대로 보고하므로 재import도 동일 카운트를 낸다.
- **job 경계 분리**: 기존 `RegisterBuiltins`의 placeholder 3개 등록과 `TestRegisterBuiltins` count(3)는 그대로 유지했다. 실제 import는 별도 `RegisterDailyBarImportHandler(runner, imp)`로 등록하며, 같은 `KindImportDailyBars` 키를 덮어쓰므로 handler 수는 3을 유지한다(`TestRegisterDailyBarImportHandlerOverridesBuiltinPlaceholder`로 보장).
- **jobs -> importer 의존 방향**: jobs 패키지는 importer의 concrete 타입이 아니라 `DailyBarImporter` 인터페이스에만 의존한다. payload는 `DailyBarImportPayload`(provider, selector kind, market/venue/name, symbols, from/to date range)로 decode하고 필수 필드(`selector_kind`, `symbols`)를 검증한다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 사용자 결정, 외부 환경 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다._

- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 재개 조건: 없음

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

- `01_provider_foundation/complete.log` 확인 없이 구현이 시작되지 않았는지 확인한다.
- Import service가 provider와 storage를 느슨하게 결합했는지 확인한다.
- 같은 fixture 재import가 중복을 만들지 않는지 테스트 출력으로 확인한다.

## 검증 결과

### KIS_IMPORT-1 중간 검증
```bash
$ cd services/worker && go test -count=1 ./internal/marketdata/importer
ok  	git.toki-labs.com/toki/alt/services/worker/internal/marketdata/importer	0.002s
```

### KIS_IMPORT-2 중간 검증
```bash
$ cd services/worker && go test -count=1 ./internal/marketdata/importer
ok  	git.toki-labs.com/toki/alt/services/worker/internal/marketdata/importer	0.002s
# TestImporterIsIdempotentForSameDailyBars 포함 통과 (재import 후 instrument 1, bar 2 유지)
```

### KIS_IMPORT-3 중간 검증
```bash
$ cd services/worker && go test -count=1 ./internal/jobs ./internal/marketdata/importer
ok  	git.toki-labs.com/toki/alt/services/worker/internal/jobs	0.002s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/marketdata/importer	0.002s
```

### 최종 검증
```bash
$ test -f agent-task/m-korea-daily-data-foundation/01_provider_foundation/complete.log
# 미실행: 선행 subtask가 완료 후 archive로 이동되어 active 경로에 디렉터리/로그가 없음.
# provider 구현은 commit 6524afa로 반영됨(services/worker/internal/providers/kis 실재). 리뷰어 확인 항목.

$ cd services/worker && go test -count=1 ./internal/jobs ./internal/marketdata/importer ./internal/storage/...
ok  	git.toki-labs.com/toki/alt/services/worker/internal/jobs	0.002s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/marketdata/importer	0.002s
?   	git.toki-labs.com/toki/alt/services/worker/internal/storage	[no test files]
ok  	git.toki-labs.com/toki/alt/services/worker/internal/storage/postgres	0.003s
?   	git.toki-labs.com/toki/alt/services/worker/internal/storage/postgres/sqlc	[no test files]

$ cd services/worker && go build ./... && go vet ./internal/jobs ./internal/marketdata/importer
# BUILD+VET OK
```

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Fail
  - API contract: Fail
  - code quality: Pass
  - plan deviation: Fail
  - verification trust: Warn
- 발견된 문제:
  - Required: [marketdata_jobs.go](/config/workspace/alt/services/worker/internal/jobs/marketdata_jobs.go:19)는 job payload가 `provider`, `from`, `to`를 포함한다고 계약하고, 계획도 [plan_cloud_G07_0.log](/config/workspace/alt/agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/plan_cloud_G07_0.log:110)에서 provider와 date range decode를 요구합니다. 하지만 `selector()`는 이 필드를 모두 버리고 [marketdata_jobs.go](/config/workspace/alt/services/worker/internal/jobs/marketdata_jobs.go:70)는 selector만 importer에 전달합니다. 이 상태에서는 `import_daily_bars` job이 날짜 범위나 provider를 지정해도 실제 import 동작에 반영되지 않습니다. `provider`를 검증/라우팅하고 `from`/`to`를 파싱한 import 요청을 importer/provider 경계까지 전달하거나, 범위에서 제외할 거라면 payload 계약과 테스트에서 제거해야 합니다.
  - Required: [code_review_cloud_G07_0.log](/config/workspace/alt/agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/code_review_cloud_G07_0.log:30)의 `구현 체크리스트` 항목들이 계획의 [plan_cloud_G07_0.log](/config/workspace/alt/agent-task/m-korea-daily-data-foundation/02+01_import_storage_pipeline/plan_cloud_G07_0.log:63) 원문에 설명을 덧붙인 형태로 변경되었습니다. code-review loop에서는 checklist item text/order가 plan과 review stub에서 정확히 일치해야 하므로, 후속 루프에서는 checklist 문구를 그대로 두고 근거는 `계획 대비 변경 사항`, `주요 설계 결정`, `검증 결과`에만 기록해야 합니다.
- 다음 단계: FAIL 후속 `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성한다.
