<!-- task=m-backtest-multi-timeframe-coverage/02+01_minute_import_rejection plan=2 tag=REVIEW_REVIEW_API -->

# Plan - Review Follow-up: Minute Import Test Closure

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

구현 마지막 단계는 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 검증 출력으로 채우는 것이다. 구현 후 active 파일을 그대로 두고 리뷰 준비를 보고한다. 종결, archive 이동, `complete.log` 작성은 code-review 전용이다.

선택된 SDD 결정 또는 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 막을 때만 review stub의 `사용자 리뷰 요청` 섹션을 채우고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 환경/secret/서비스 차단, 일반 범위 조정, 검증 공백은 사용자 리뷰 요청이 아니라 `검증 결과` 또는 `계획 대비 변경 사항`에 기록한다.

## 배경

이 follow-up은 `code_review_local_G06_1.log`의 FAIL Required 항목만 닫는다. 현재 구현 동작과 SDD S03 fixture evidence는 대체로 충족됐지만, 지난 FAIL에서 요구한 직접 회귀 테스트 두 곳이 아직 간접 검증에 머물러 있다.

## 사용자 리뷰 요청 흐름

사용자 리뷰 요청은 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에만 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 내용을 따른다. 직접 사용자 프롬프트는 금지하며, code-review가 요청 타당성을 검증하고 실제 `USER_REVIEW.md` 작성을 소유한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/backtest-loop/milestones/backtest-multi-timeframe-coverage.md`
- Task ids:
  - `minute-ingest`: 분봉 provider import 경계를 추가하거나, provider 미지원 조합을 typed error로 거부한다.
- Completion mode: check-on-pass

## Spec Targets

- SDD: `agent-roadmap/sdd/backtest-loop/backtest-multi-timeframe-coverage/SDD.md`
- Acceptance scenarios:
  - `S03`: task=`minute-ingest`; evidence=`minute import success 또는 typed reject fixture`
- Completion mode: spec-check-on-pass

## Archive Evidence Snapshot

- Current archived plan: `agent-task/m-backtest-multi-timeframe-coverage/02+01_minute_import_rejection/plan_local_G06_1.log`
- Current archived review: `agent-task/m-backtest-multi-timeframe-coverage/02+01_minute_import_rejection/code_review_local_G06_1.log`
- Verdict: FAIL
- Required summary:
  - `TestRunScenarioImportDailyBarsMinuteTimeframeRejected` checks output text but does not assert the fake API received `ImportDailyBarsRequest.Timeframe`, so runner propagation could regress unnoticed.
  - Worker job handler tests check `"minute"` only; the required domain/proto baseline values `1m`, `5m`, `minute_1`, `minute_5` are not directly covered with importer not-called assertions.
- Direct review cleanup already applied before archive:
  - `apps/cli/internal/operator/scenario.go`: stale comment corrected.
  - `services/worker/internal/jobs/marketdata_jobs.go`: unused `importTimeframeAliases` map removed.
- Affected files:
  - `apps/cli/internal/operator/runner_market_test.go`
  - `services/worker/internal/jobs/marketdata_jobs_test.go`
- Verification evidence from failed loop:
  - `go test ./apps/cli/internal/operator ./services/worker/internal/socket ./services/worker/internal/jobs -count=1`: pass
  - `bin/contracts-check`: pass
  - `go test ./services/api/...`: pass
  - `go test ./services/worker/...`: pass
  - `git diff --check`: pass
- Roadmap/spec carryover:
  - Roadmap target remains `minute-ingest`.
  - Spec target remains SDD `S03`.
- Narrow archive reread allowed:
  - `agent-task/m-backtest-multi-timeframe-coverage/02+01_minute_import_rejection/code_review_local_G06_1.log`
  - `agent-task/m-backtest-multi-timeframe-coverage/02+01_minute_import_rejection/plan_local_G06_1.log`

## 범위 결정 근거

- Source behavior should not need changes unless a test exposes a real bug.
- SDD/roadmap documents are not updated by this follow-up.
- Existing minute import fixture files remain in scope only if tests must reference them; otherwise do not churn fixture content.

## 구현 체크리스트

- [ ] `apps/cli/internal/operator/runner_market_test.go`에서 minute import rejected scenario test가 `api.lastImportDailyBarsReq().GetTimeframe()`을 `TIMEFRAME_MINUTE_1`/`TIMEFRAME_MINUTE_5`로 직접 assert하도록 보강한다.
- [ ] `services/worker/internal/jobs/marketdata_jobs_test.go`에서 `1m`, `5m`, `minute_1`, `minute_5` payload rejection과 importer not-called를 table-driven handler test로 직접 검증한다.
- [ ] `go test ./apps/cli/internal/operator ./services/worker/internal/jobs -count=1`, `go test ./apps/cli/internal/operator ./services/worker/internal/socket ./services/worker/internal/jobs -count=1`, `bin/contracts-check`, `go test ./services/api/...`, `go test ./services/worker/...`, `git diff --check`를 실행한다.
- [ ] CODE_REVIEW-*-G06.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_API-1] CLI Request Timeframe Assertion

#### 문제

`TestRunScenarioImportDailyBarsMinuteTimeframeRejected`는 fake response message에 `tf`를 직접 넣고 output 문자열만 확인한다. runner가 request field를 누락해도 fake response가 그대로 반환되면 테스트가 통과할 수 있다.

#### 해결 방법

각 `tf` case에서 `api.lastImportDailyBarsReq()`를 읽고 nil이 아닌지 확인한 뒤, `minute_1`은 `TIMEFRAME_MINUTE_1`, `minute_5`는 `TIMEFRAME_MINUTE_5`로 매핑됐는지 assert한다.

#### 중간 검증

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

### [REVIEW_REVIEW_API-2] Worker Job Baseline Timeframe Rejection Tests

#### 문제

job payload handler tests는 `"minute"`만 확인한다. follow-up plan과 SDD baseline에서 중요한 값은 domain/proto vocabulary인 `1m`, `5m`, `minute_1`, `minute_5`다.

#### 해결 방법

`TestRegisterDailyBarImportHandlerRejectsNonDailyTimeframe`를 table-driven으로 바꾸거나 새 test를 추가해 `1m`, `5m`, `minute_1`, `minute_5`가 모두 unsupported timeframe error를 반환하고 importer를 호출하지 않는지 검증한다. decode-only test도 같은 값 중 최소 하나 이상을 직접 포함하면 좋다.

#### 중간 검증

```bash
go test ./services/worker/internal/jobs -count=1
```

## 최종 검증

```bash
go test ./apps/cli/internal/operator ./services/worker/internal/jobs -count=1
go test ./apps/cli/internal/operator ./services/worker/internal/socket ./services/worker/internal/jobs -count=1
bin/contracts-check
go test ./services/api/...
go test ./services/worker/...
git diff --check
```

기대 결과: 모든 명령 exit code 0. `bin/contracts-check`와 `git diff --check`는 출력이 없으면 성공으로 기록한다.
