<!-- task=m-scheduled-market-data-refresh/04+03_refresh_status_cli plan=4 tag=REVIEW_REVIEW_REVIEW_REVIEW_API -->

# Plan - Review Follow-up: Wire Real Scheduler Import Results

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

이 plan은 `code_review_local_G06_3.log`의 FAIL 판정을 닫기 위한 좁은 후속 작업이다. 사용자에게 직접 질문하지 말고, Milestone lock 결정이 필요한 경우에만 `CODE_REVIEW-local-G06.md`의 `사용자 리뷰 요청` 섹션을 채운 뒤 멈춘다. 현재 발견 사항은 repo 안에서 구현과 검증으로 해결 가능한 문제이므로 기본 경로는 자동 follow-up이다.

## Review Route

- route: `local-G06`
- 근거: worker scheduler, jobs runner/import handler, status store, worker/socket unit tests에 한정된 deterministic local Go 작업이다. 외부 provider live call이나 새 public protobuf surface 판단은 필요하지 않다.

## Archive Evidence Snapshot

- archived plan: `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/plan_local_G06_3.log`
- archived review: `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/code_review_local_G06_3.log`
- verdict: FAIL
- Required summary:
  - `RefreshEvidence` and `ItemResult.ImportSummary` were added, but the real runtime path still cannot populate them: scheduler `Executor.Execute()` returns only `error`, `RunTick()` never sets `ImportSummary`, and `RegisterDailyBarImportHandler()` discards `importer.Result`.
  - `RecordTick()` still records `status=success` and updates `last_success` for succeeded items with no import summary, and the worker loop test expects that no-evidence success state.
- resolved from previous loop:
  - `RecordTick()` no longer increments imported bar count from scheduler item count.
  - Direct `RecordTick()` tests can aggregate manually supplied `RefreshEvidence`.
  - Formatting and the previously expanded operator success/stale/error JSONL fixture remain intact.
- affected files:
  - `services/worker/internal/jobs/runner.go`
  - `services/worker/internal/jobs/marketdata_jobs.go`
  - `services/worker/internal/scheduler/runner.go`
  - `services/worker/internal/scheduler/status_store.go`
  - `services/worker/cmd/alt-worker/main.go`
  - `services/worker/cmd/alt-worker/main_test.go`
  - `services/worker/internal/socket/market.go`
  - `services/worker/internal/socket/scheduler_test.go`
  - likely related tests under `services/worker/internal/jobs`, `services/worker/internal/scheduler`, and `services/worker/internal/marketdata/importer`
- verification evidence from failed loop:
  - `gofmt -l services/worker/internal/scheduler/runner.go services/worker/internal/scheduler/status_store.go services/worker/cmd/alt-worker/main.go services/worker/cmd/alt-worker/main_test.go services/worker/internal/socket/scheduler_test.go` PASS, no output.
  - `go test -count=1 ./services/worker/cmd/alt-worker ./services/worker/internal/scheduler ./services/worker/internal/socket -run 'Test(RefreshStatus|RecordTick|SchedulerRefreshStatus|SchedulerHandlers|StartScheduler|SchedulerLoop|HandleSchedulerRefreshStatus|ActualEvidence)'` PASS; `services/worker/internal/scheduler` reported `[no tests to run]` for this regex.
- narrow archive reread allowed if needed:
  - `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/code_review_local_G06_3.log`
  - `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/plan_local_G06_3.log`

## Roadmap Targets

- `agent-roadmap/phase/backtest-loop/milestones/scheduled-market-data-refresh.md`
  - Handoff epic: `[refresh-status] success/stale/error status`

## 범위 결정 근거

이 follow-up은 새 public protobuf contract를 추가하지 않는다. 이미 존재하는 `SchedulerRefreshStatusEntry` 필드가 실제 runtime evidence 의미를 갖도록 내부 jobs/scheduler execution result와 tests만 바로잡는다. 구현 에이전트는 roadmap target을 직접 체크하지 않는다.

## 구현 항목

### [REVIEW_REVIEW_REVIEW_REVIEW_API-1] Real import result must populate scheduler `ImportSummary`

- 문제: `RefreshEvidence` 타입은 있지만 real worker runtime에서 채워지지 않는다. `jobs.RegisterDailyBarImportHandler()`는 `importer.Result`를 버리고, scheduler `RunTick()`은 `Execute()`의 error만 보므로 `ItemResult.ImportSummary`가 nil인 성공 item만 생성한다.
- 해결:
  - generic job runner를 확장하든, scheduler 전용 daily-bar import executor/adapter를 추가하든, 실제 `ImportDailyBars()`의 `importer.Result`가 scheduler tick의 `ItemResult.ImportSummary`까지 도달하게 한다.
  - 최소한 `importer.Result.Bars`는 `RefreshEvidence.ImportedBars`로 운반한다. missing/gap/duplicate/provider_delay는 실제 freshness/backfill check에서 측정한 값만 싣고, 측정하지 않은 0을 "검증된 정상 값"처럼 성공 evidence로 만들지 않는다.
  - runtime `alt-worker`에서 사용하는 실제 scheduler executor도 이 result-returning path를 사용해야 한다. 테스트 전용 fake path만 추가하면 안 된다.
  - 기존 `jobs.Runner.Execute()` 시그니처를 바꾸는 경우 backtest/job call sites를 모두 갱신하고, 호환 helper가 필요하면 이름과 책임을 명확히 분리한다.
- 테스트 결정:
  - real handler/runner 또는 scheduler adapter test에서 fake importer가 `importer.Result{Bars: 120}`을 반환하면 `RunTick()` 결과의 `ItemResult.ImportSummary.ImportedBars == 120`이 되는지 검증한다.
  - 이 값이 `RefreshStatusStore`와 socket response의 `imported_bar_count`까지 보존되는지 검증한다.
- 중간 검증:
  - `go test -count=1 ./services/worker/internal/jobs ./services/worker/internal/scheduler ./services/worker/internal/socket -run 'Test(ImportDailyBars|RunTick|RefreshStatus|RecordTick|SchedulerRefreshStatus)'`

### [REVIEW_REVIEW_REVIEW_REVIEW_API-2] No-evidence success must not produce success evidence

- 문제: summary 없는 succeeded item이 `status=success`와 `last_success`를 만든다. 이는 active plan의 "summary 없는 success/unknown path가 success/count evidence를 만들지 않는다" 요구를 충족하지 못한다.
- 해결:
  - `RecordTick()`에서 succeeded item이 모두 `ImportSummary == nil`이면 success가 아니라 stale/no-evidence 상태로 남긴다. 기존 last_success는 보존하고 새 last_success를 만들지 않는다.
  - 일부 item에는 evidence가 있고 일부 item은 no-evidence인 mixed case는 success로 조용히 승격하지 않는다. status와 error/reason을 운영자가 success와 구분할 수 있게 한다.
  - `TestSchedulerLoopRecordsTickAndStatusStore`는 no-evidence executor가 success/last_success evidence를 만들지 않는 regression test로 바꾼다.
  - 현재 `TestSchedulerLoopWithActualEvidence`는 loop 테스트가 아니라 direct `RecordTick()` 테스트다. 실제 loop/executor result-returning path를 통과하는 테스트를 추가하거나 이름을 정정하고 별도 end-to-end 테스트를 둔다.
- 테스트 결정:
  - no-evidence success, actual evidence success, mixed evidence/no-evidence, failed, skipped case를 각각 검증한다.
  - actual evidence success만 `last_success`와 imported count를 갱신하는지 확인한다.
- 중간 검증:
  - `go test -count=1 ./services/worker/cmd/alt-worker ./services/worker/internal/scheduler ./services/worker/internal/socket -run 'Test(StartScheduler|SchedulerLoop|RunTick|RecordTick|HandleSchedulerRefreshStatus|SchedulerRefreshStatus)'`

### [TEST-1] Final verification and review evidence

- 문제: FAIL 후속은 실제 stdout/stderr evidence를 다시 남겨야 한다.
- 해결:
  - 아래 최종 검증을 실행하고 `CODE_REVIEW-local-G06.md`에 실제 출력 또는 차단 사유를 붙인다.
  - touched Go files는 저장 전 `gofmt` 상태를 확인한다.
- 최종 검증:
  - `gofmt -l services/worker/internal/jobs/runner.go services/worker/internal/jobs/marketdata_jobs.go services/worker/internal/scheduler/runner.go services/worker/internal/scheduler/status_store.go services/worker/cmd/alt-worker/main.go services/worker/cmd/alt-worker/main_test.go services/worker/internal/socket/market.go services/worker/internal/socket/scheduler_test.go`
  - `bin/contracts-check`
  - `go test ./packages/contracts/gen/go/...`
  - `go test ./services/api/...`
  - `go test ./services/worker/...`
  - `go test -count=1 ./apps/cli/internal/operator -run 'Test(RunScenarioRefreshStatus|HeadlessValidationHandoff|ExpectedOutputFixtures|HandoffExpectedFixtures)'`
  - `bin/lint`

## 구현 체크리스트

- [ ] [REVIEW_REVIEW_REVIEW_REVIEW_API-1] Real import results from the worker runtime path populate scheduler `ItemResult.ImportSummary` and reach the socket refresh status response.
- [ ] [REVIEW_REVIEW_REVIEW_REVIEW_API-2] No-evidence success no longer creates `success`/`last_success` evidence, and tests cover actual loop/executor propagation rather than direct-only `RecordTick()` calls.
- [ ] [TEST-1] Final verification commands are run and actual outputs are recorded in `CODE_REVIEW-local-G06.md`.
- [ ] `CODE_REVIEW-local-G06.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
