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

# Plan - Review Follow-up: Actual Runtime S04 Evidence Propagation

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

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

## Review Route

- route: `local-G06`
- 근거: worker scheduler runtime status aggregation과 관련 unit/socket 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_2.log`
- archived review: `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/code_review_local_G06_2.log`
- verdict: FAIL
- Required summary:
  - `RefreshStatusStore.RecordTick()` still derives `imported_bar_count` from succeeded scheduler item count; `Executor.Execute()` returns only `error`, and `ItemResult` has no fields for actual imported bar count or missing/gap/duplicate/provider_delay.
  - `TestSchedulerLoopRecordsTickAndStatusStore` uses a mock executor that returns no import summary but expects `ImportedBarCount > 0`, cementing the fabricated count behavior instead of testing real evidence propagation.
- resolved from previous loop:
  - skipped/mixed/failed classification is no longer treated as unconditional success.
  - operator scheduler refresh status JSONL fixture now covers success, stale, and error entries with exact expected output.
  - `gofmt` drift in touched Go files was repaired during review.
- affected files:
  - `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.go`
  - `services/worker/internal/socket/scheduler_test.go`
  - likely related implementation paths: `services/worker/internal/jobs`, `services/worker/internal/marketdata/importer`, `services/worker/internal/scheduler/backfill.go`
- verification evidence from failed loop:
  - `go test -count=1 ./services/worker/cmd/alt-worker ./services/worker/internal/scheduler ./services/worker/internal/socket -run 'Test(StoreBackedDeps|StartScheduler|SchedulerLoop|RefreshStatus|MarketHandlers|HandleSchedulerRefreshStatus|WorkerHelloCapabilities|RecordTick)'` PASS; `services/worker/internal/scheduler` reported `[no tests to run]`.
  - `go test -count=1 ./apps/cli/internal/operator -run 'Test(RunScenarioRefreshStatus|HeadlessValidationHandoff|ExpectedOutputFixtures|HandoffExpectedFixtures)'` PASS.
  - `gofmt -l services/worker/internal/scheduler/status_store.go services/worker/cmd/alt-worker/main_test.go services/worker/internal/socket/scheduler_test.go apps/cli/internal/operator/runner_market_test.go apps/cli/internal/operator/client_test.go apps/cli/internal/operator/output.go` PASS, no output after review cleanup.
- narrow archive reread allowed if needed:
  - `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/code_review_local_G06_2.log`
  - `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/plan_local_G06_2.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 의미를 갖도록 내부 scheduler execution result, status aggregation, worker/socket tests를 바로잡는다. 구현 에이전트는 roadmap target을 직접 체크하지 않는다.

## 구현 항목

### [REVIEW_REVIEW_REVIEW_API-1] Runtime refresh evidence must come from actual import results

- 문제: `RecordTick()`이 scheduler item 성공 개수를 imported bar count로 누적한다. `Executor.Execute()`와 `ItemResult`에는 actual imported bar count, missing/gap/duplicate/provider_delay를 운반할 수 있는 계약이 없어서 S04 status entry가 실제 refresh 결과가 아니라 scheduler dispatch 결과를 노출한다.
- 해결:
  - scheduler tick result 또는 scheduler 전용 execution path에 actual refresh evidence를 담는 내부 summary를 추가한다. 적어도 imported bar count, missing count, gap count, duplicate count, provider delay days를 구분해서 운반해야 한다.
  - zero imported bars가 실제 결과일 수 있으므로 "0"과 "evidence 없음"을 구분할 수 있는 bool/summary presence를 둔다.
  - `RecordTick()`은 succeeded item 개수를 imported bar count로 추론하지 않는다. 실제 summary가 있는 성공 결과만 success evidence로 기록하고, summary가 없는 success/unknown 상태는 success count evidence를 만들지 않게 방어한다.
  - stale/skipped/error 결과는 기존 last_success를 보존하면서 이번 tick의 last_error 또는 stable reason과 S04 counters를 의미 있게 남긴다.
  - socket response의 `imported_bar_count`, `missing_count`, `gap_count`, `duplicate_count`, `provider_delay_days`는 status store에 기록된 실제 summary에서만 나온다.
- 테스트 결정:
  - scheduler/status aggregation test가 actual summary를 가진 success, actual zero-count success, stale/skipped, failed, mixed, summary 없는 success/unknown을 각각 검증한다.
  - `ImportedBarCount`가 item count와 다른 값(예: symbol 1개, imported 120개)으로 보존되는 케이스를 반드시 넣는다.
- 중간 검증:
  - `go test -count=1 ./services/worker/internal/scheduler ./services/worker/internal/socket -run 'Test(RefreshStatus|RecordTick|SchedulerRefreshStatus|SchedulerHandlers)'`

### [REVIEW_REVIEW_REVIEW_API-2] Worker loop tests must prove evidence propagation end to end

- 문제: current worker loop test는 mock executor가 아무 import summary도 반환하지 않는데도 `ImportedBarCount > 0`을 기대한다. 이 테스트는 실제 S04 evidence propagation을 검증하지 못한다.
- 해결:
  - worker scheduler loop test의 mock executor가 명시적인 refresh evidence summary를 반환하게 한다.
  - tick 후 `RefreshStatusStore.Get(schedule)` 또는 socket handler response를 조회해 status, last_success, last_error, imported_bar_count, missing/gap/duplicate/provider_delay가 mock summary와 정확히 일치하는지 확인한다.
  - summary가 없는 성공 path가 count를 조작하지 않는 regression test를 추가하거나 기존 테스트를 그 의미로 수정한다.
  - `startScheduler()` config wiring/next_run test는 유지하되, actual evidence propagation 검증과 혼동되지 않게 분리한다.
- 테스트 결정:
  - worker cmd tests가 implementation checklist의 "tick 이후 store entry 확인"을 실제 assertion으로 닫는다.
  - socket test가 status store의 actual S04 counters를 response로 보존하는지 확인한다.
- 중간 검증:
  - `go test -count=1 ./services/worker/cmd/alt-worker ./services/worker/internal/socket -run 'Test(StartScheduler|SchedulerLoop|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/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.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_API-1] Runtime refresh status aggregation uses actual import/S04 summary fields and no longer derives counts from scheduler item count.
- [ ] [REVIEW_REVIEW_REVIEW_API-2] Worker loop/socket tests prove actual evidence propagation and reject fabricated count behavior.
- [ ] [TEST-1] Final verification commands are run and actual outputs are recorded in `CODE_REVIEW-local-G06.md`.
- [ ] `CODE_REVIEW-local-G06.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
