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

# Plan - Review Follow-up: Minute Import Typed Rejection

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

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

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

## 배경

이 follow-up은 `code_review_local_G06_0.log`의 FAIL Required 항목만 닫는다. 기존 구현은 CLI/worker/job 단위 테스트를 통과했지만, SDD S03의 stable text/JSONL evidence가 빠졌고 worker socket/job boundary의 timeframe rejection 처리가 edge case와 vocabulary 측면에서 계획과 어긋났다.

## 사용자 리뷰 요청 흐름

사용자 리뷰 요청은 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_0.log`
- Current archived review: `agent-task/m-backtest-multi-timeframe-coverage/02+01_minute_import_rejection/code_review_local_G06_0.log`
- Verdict: FAIL
- Required summary:
  - API-4 fixture was marked complete but skipped; SDD S03 still lacks stable minute import typed rejection JSONL evidence.
  - `services/worker/internal/socket/market.go` can panic on unknown `ImportDailyBarsRequest.timeframe` enum values because it slices `tf.String()` without validating the enum name shape.
  - `services/worker/internal/jobs/marketdata_jobs.go` accepts `"minute"` but does not cover the domain/proto baseline values for `1m`/`5m` or `minute_1`/`minute_5`.
- Affected files:
  - `apps/cli/testdata/operator/headless_validation.md`
  - `apps/cli/testdata/operator/*.yaml`
  - `apps/cli/testdata/operator/expected/*.jsonl`
  - `apps/cli/internal/operator/handoff_test.go`
  - `apps/cli/internal/operator/runner_market_test.go`
  - `services/worker/internal/socket/market.go`
  - `services/worker/internal/socket/market_test.go`
  - `services/worker/internal/jobs/marketdata_jobs.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_0.log`
  - `agent-task/m-backtest-multi-timeframe-coverage/02+01_minute_import_rejection/plan_local_G06_0.log`

## 범위 결정 근거

- 실제 provider minute import 구현은 계속 제외한다. SDD S03은 typed reject fixture로 닫는다.
- 월봉 aggregation 작업(`03_monthly_aggregation`)과 신규 aggregation package 변경은 이 follow-up 범위가 아니다.
- Roadmap/SDD 문서는 이 follow-up에서 수정하지 않는다. PASS 후 runtime이 completion metadata를 소비한다.

## 구현 체크리스트

- [ ] `services/worker/internal/socket/market.go`의 import timeframe normalization을 switch/error-return 기반으로 바꾸고, unknown enum 값이 panic 대신 typed `invalid_request`로 반환되는 test를 추가한다.
- [ ] `services/worker/internal/jobs/marketdata_jobs.go`의 payload timeframe vocabulary를 domain/proto minute baseline과 정렬하고, `1m`/`5m` 또는 `minute_1`/`minute_5` rejection 및 importer not-called tests를 추가한다.
- [ ] `apps/cli/testdata/operator`에 minute import rejection scenario YAML과 expected JSONL fixture를 추가하고, `headless_validation.md` 및 handoff fixture tests에 연결한다.
- [ ] CLI runner test가 실제 scenario execution/fake API request path에서 import `timeframe` propagation과 expected `invalid_request` handling을 검증하도록 보강한다.
- [ ] `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_API-1] Worker Socket Unknown Enum Rejection

#### 문제

`services/worker/internal/socket/market.go:80`의 `importTimeframeNormalise`는 `tf.String()` 결과가 항상 `TIMEFRAME_` prefix를 가진다고 가정한다. 알 수 없는 enum 값은 protobuf runtime에서 숫자 문자열로 표시될 수 있어 slice panic이 날 수 있다.

#### 해결 방법

helper를 `(string, error)` 또는 domain timeframe 결과를 반환하도록 바꾸고, known enum만 switch로 처리한다. unknown enum은 `handleImportDailyBars`에서 `marketInvalidRequest("unsupported timeframe ...")` 형태의 typed response로 반환한다.

#### 체크리스트

- [ ] unknown enum 입력 test 추가: `altv1.Timeframe(99)`가 panic 없이 `invalid_request`가 된다.
- [ ] daily/unspecified/minute_1/minute_5/monthly 기존 rejection semantics 유지.

#### 중간 검증

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

### [REVIEW_API-2] Worker Job Timeframe Vocabulary Alignment

#### 문제

`services/worker/internal/jobs/marketdata_jobs.go:85`는 `"minute"`을 허용하지만, ALT domain timeframe은 `1m`/`5m`이고 CLI/proto baseline은 `minute_1`/`minute_5`다. 이 상태에서는 job path에서 SDD의 minute_1/minute_5 baseline을 안정적으로 검증할 수 없다.

#### 해결 방법

payload vocabulary를 domain values(`1d`, `1m`, `5m`, `1mo`) 또는 명시 매핑(`daily`, `minute_1`, `minute_5`, `monthly`) 중 기존 worker job 관례와 가장 맞는 하나로 정렬한다. 선택한 표준을 tests와 error message에 반영하고, non-daily 값은 decode 통과 후 importer 전에 unsupported timeframe error로 거부한다.

#### 체크리스트

- [ ] `1m`/`5m` 또는 `minute_1`/`minute_5` payload rejection tests 추가.
- [ ] `"minute"` 같은 비표준 값은 허용하지 않거나 호환 alias로 명확히 문서화한다.
- [ ] importer not-called assertion 유지.

#### 중간 검증

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

### [REVIEW_API-3] Stable Minute Reject Fixture

#### 문제

`agent-roadmap/sdd/backtest-loop/backtest-multi-timeframe-coverage/SDD.md:83`과 `:96`은 stable text/JSONL evidence를 요구한다. 기존 구현은 unit tests만 추가하고 `apps/cli/testdata/operator` fixture를 만들지 않았다.

#### 해결 방법

기존 `apps/cli/testdata/operator` naming과 handoff matrix 형식을 따른다. 예: `minute_import_rejected.yaml`과 `expected/minute_import_rejected.jsonl`을 만들고, `import_daily_bars` step에 `timeframe: minute_1` 또는 `minute_5`, `expect.status: error`, `expect.error_code: invalid_request`를 둔다. expected JSONL은 typed error가 기대대로 처리되어 summary `status=ok`/`exit_code=0`으로 남는 형태여야 한다.

#### 체크리스트

- [ ] scenario YAML 추가.
- [ ] expected JSONL fixture 추가.
- [ ] `headless_validation.md` row와 expected output keys 추가.
- [ ] `handoff_test.go` required scenarios 또는 matrix-driven fixture existence tests가 새 scenario를 검증하게 한다.

#### 중간 검증

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

## 최종 검증

```bash
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`는 출력이 없으면 성공으로 기록한다.
