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

# Plan - Review Follow-up: Zero Scheduler Status Timestamp Encoding

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

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

## Review Route

- route: `local-G04`
- 근거: worker socket response mapping과 단위 테스트에 한정된 deterministic local Go 작업이다. 외부 provider live call, 새 protobuf schema 판단, remote runtime evidence가 필요하지 않다.

## Archive Evidence Snapshot

- archived plan: `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/plan_local_G06_5.log`
- archived review: `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/code_review_local_G06_5.log`
- verdict: FAIL
- Required summary:
  - `services/worker/internal/socket/market.go:348`와 `services/worker/internal/socket/market.go:350`에서 zero `time.Time`을 그대로 `UnixMilli()`로 변환해, 첫 stale/error 또는 config 없는 상태의 `last_success_unix_ms`/`next_run_unix_ms`가 `0` 대신 음수 epoch로 노출될 수 있다.
- affected files:
  - `services/worker/internal/socket/market.go`
  - `services/worker/internal/socket/scheduler_test.go`
  - CLI refresh-status fixture/tests if output expectations need confirmation
- verification evidence from failed loop:
  - `gofmt -l services/worker/internal/jobs/runner.go services/worker/internal/jobs/runner_test.go services/worker/internal/jobs/marketdata_jobs.go services/worker/internal/jobs/marketdata_jobs_test.go services/worker/internal/scheduler/runner.go services/worker/internal/scheduler/runner_test.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` PASS, no output.
  - `bin/contracts-check` PASS, no output.
  - `go test -count=1 ./services/worker/internal/jobs ./services/worker/internal/scheduler -run 'Test(Runner|ExecuteWithResult|RegisterDailyBarImportHandler|RunTick)'` PASS.
  - `go test -count=1 ./services/worker/internal/jobs ./services/worker/internal/scheduler ./services/worker/cmd/alt-worker -run 'Test(Runner|ExecuteWithResult|RegisterDailyBarImportHandler|RunTick|SchedulerLoop)'` PASS.
  - `go test ./packages/contracts/gen/go/...` PASS.
  - `go test ./services/api/...` PASS.
  - `go test -count=1 ./services/worker/...` PASS.
  - `go test -count=1 ./apps/cli/internal/operator -run 'Test(RunScenarioRefreshStatus|HeadlessValidationHandoff|ExpectedOutputFixtures|HandoffExpectedFixtures)'` PASS.
  - `bin/lint` exited 0; existing Flutter `avoid_print` info remains under `apps/client/lib/src/integrations/mattermost/`.
- narrow archive reread allowed if needed:
  - `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/code_review_local_G06_5.log`
  - `agent-task/m-scheduled-market-data-refresh/04+03_refresh_status_cli/plan_local_G06_5.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를 추가하지 않는다. S04 status evidence의 기존 `*_unix_ms` 필드가 zero 상태를 `0`으로 안정적으로 표현하도록 worker socket mapping과 테스트만 바로잡는다. 구현 에이전트는 roadmap target을 직접 체크하지 않는다.

## 구현 항목

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_API-1] Zero status timestamps must encode as 0

- 문제: `handleSchedulerRefreshStatus()`가 zero `time.Time`에 `UnixMilli()`를 직접 호출한다. Go zero time은 Unix epoch `0`이 아니므로, 아직 성공 이력이 없거나 next run을 계산할 수 없는 상태가 음수 timestamp로 렌더링될 수 있다.
- 해결:
  - `services/worker/internal/socket/market.go`에 zero time이면 `0`, 아니면 `t.UnixMilli()`를 반환하는 작은 helper를 추가한다.
  - `SchedulerRefreshStatusEntry.LastSuccessUnixMs`와 `NextRunUnixMs` 변환에 helper를 적용한다.
  - `services/worker/internal/socket/scheduler_test.go`에 zero `LastSuccess`와 zero `NextRun`이 각각 `0`으로 응답되는 케이스를 추가한다. 가능하면 config 없는 status 또는 첫 stale/error status를 사용해 실제 결함 조건을 직접 재현한다.
  - 기존 success response field test는 유지하여 non-zero timestamp가 그대로 보존되는지도 확인한다.
- 테스트 결정:
  - worker socket 단위 테스트로 zero timestamp contract를 고정한다.
  - S04 CLI fixture는 worker proto output을 소비하므로 기존 refresh-status CLI fixture test를 재실행해 출력 계약이 깨지지 않는지 확인한다.
- 중간 검증:
  - `go test -count=1 ./services/worker/internal/socket -run 'TestSchedulerRefreshStatus'`

## 구현 체크리스트

- [ ] [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_API-1] Zero `LastSuccess`/`NextRun` values in scheduler refresh status responses encode as `0`, while non-zero timestamps remain unchanged.
- [ ] [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_API-1] Worker socket tests cover zero timestamp and existing non-zero response fields.
- [ ] [TEST-1] Final verification commands are run and actual outputs are recorded in `CODE_REVIEW-local-G04.md`.
- [ ] `CODE_REVIEW-local-G04.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 최종 검증

- `gofmt -l services/worker/internal/socket/market.go services/worker/internal/socket/scheduler_test.go`
- `go test -count=1 ./services/worker/internal/socket -run 'TestSchedulerRefreshStatus'`
- `go test -count=1 ./services/worker/...`
- `go test -count=1 ./apps/cli/internal/operator -run 'Test(RunScenarioRefreshStatus|ExpectedOutputFixtures|HandoffExpectedFixtures)'`
