<!-- task=m-api-centered-proto-socket-rail/04+02_backtest_rail plan=0 tag=BACKTEST -->
# PLAN-cloud-G08: Backtest API to Worker Proto-Socket Rail

## 이 파일을 읽는 구현 에이전트에게
이 계획은 backtest 관련 client-facing 요청을 API에서 받아 worker로 넘기고, worker가 job/store 결과를 proto response로 돌려주는 레일을 만든다. `02+01_worker_socket_rail` 완료 후 진행한다.

## 배경
contracts에는 `StartBacktestRequest`, run 조회, result 조회, compare 요청이 있다. worker에는 `RunBacktestPayload`와 runner/store가 있고, postgres store에는 run/result/compare 조회 메서드가 있다. 하지만 API가 이 요청들을 handler로 받거나 worker에 전달하는 연결이 아직 없다.

## 사용자 리뷰 요청 흐름
backtest 실행을 API 프로세스 안에서 직접 수행해야 한다는 요구가 나오면 중단한다. 마일스톤 목표는 API 중심 제어와 worker 실행 분리이므로 범위 변경이다.

## 분석 결과
### 읽은 파일
- `packages/contracts/proto/alt/v1/backtest.proto`
- `services/api/internal/socket/server.go`
- `services/api/internal/contracts/parser_map.go`
- `services/worker/internal/jobs/runner.go`
- `services/worker/internal/jobs/backtest_jobs.go`
- `services/worker/internal/storage/ports.go`
- `services/worker/internal/storage/postgres/store.go`
- `services/worker/internal/storage/postgres/mapping.go`
- `services/worker/internal/storage/postgres/queries/queries.sql`
- `services/worker/internal/storage/postgres/store_test.go`
- `services/worker/internal/jobs/backtest_jobs_test.go`

### 테스트 커버리지 공백
- backtest job tests는 worker 내부 실행 단위 중심이다.
- API handler -> worker client -> worker handler integration coverage가 없다.
- store mapping tests는 있지만 proto response mapping coverage는 약하다.

### 심볼 참조
- `StartBacktestRequest`: `packages/contracts/proto/alt/v1/backtest.proto:35`
- `StartBacktestResponse`: `packages/contracts/proto/alt/v1/backtest.proto:39`
- `ListBacktestRunsRequest`: `packages/contracts/proto/alt/v1/backtest.proto:95`
- `GetBacktestRunDetailRequest`: `packages/contracts/proto/alt/v1/backtest.proto:103`
- `CompareBacktestRunsRequest`: `packages/contracts/proto/alt/v1/backtest.proto:112`
- `RunBacktestPayload`: `services/worker/internal/jobs/backtest_jobs.go:20`
- `RegisterRunBacktestHandler`: `services/worker/internal/jobs/backtest_jobs.go:83`
- `ListRuns`: `services/worker/internal/storage/postgres/store.go:142`
- `GetRunDetail`: `services/worker/internal/storage/postgres/store.go:158`
- `CompareResults`: `services/worker/internal/storage/postgres/store.go:174`
- `GetResult`: `services/worker/internal/storage/postgres/store.go:197`

### 분할 판단
이 작업은 backtest domain 전체를 연결하므로 G08이다. market data는 `05+02_market_rail`에서 따로 처리한다.

### 범위 결정 근거
- backtest는 command(start)와 queries(list/detail/result/compare)가 섞여 있어 API/worker 양쪽 handler와 mapping을 함께 설계해야 한다.
- worker storage ownership을 유지해야 하므로 API는 store에 직접 접근하지 않는다.
- existing job runner를 재사용하고 새 execution engine을 만들지 않는다.

### 빌드 등급
- Build lane: `cloud-G08`
- Review lane: `cloud-G08`
- 근거: user-facing backtest behavior, worker execution, persistence read mapping, protocol errors를 모두 건드린다.

## 구현 체크리스트
### [BACKTEST-1] API backtest handlers 추가
문제:
API socket server가 backtest 요청을 받지 않는다.

해결 방법:
`01_contracts_api_registry`의 registry에 backtest handlers를 붙인다. handler는 request validation과 worker client 호출만 수행하고, execution/store logic을 API에 넣지 않는다.

수정 파일 및 체크리스트:
- `services/api/internal/socket/**`
- `services/api/internal/workerclient/**`
- `services/api/internal/contracts/**`
- [ ] start/list/detail/result/compare handler가 등록된다.
- [ ] validation error와 worker unavailable error가 proto-socket response error로 일관되게 변환된다.
- [ ] API가 worker internal/storage package를 import하지 않는다.

테스트 작성:
- fake worker client 기반 API handler unit tests.
- invalid request tests.
- worker failure mapping tests.

중간 검증:
- 원격 검증 환경에서만 `go test ./services/api/...` 실행.

### [BACKTEST-2] worker backtest socket handlers 추가
문제:
worker 내부 job/store 기능은 있지만 proto-socket request와 연결되어 있지 않다.

해결 방법:
worker socket server에 backtest command/query handlers를 추가한다. start는 runner를 통해 실행하고, queries는 worker-owned store port를 사용한다.

수정 파일 및 체크리스트:
- `services/worker/internal/socket/**`
- `services/worker/internal/jobs/**` 필요 시 최소 보강
- `services/worker/internal/storage/**` 필요 시 mapping helper
- [ ] start request가 `RunBacktestPayload`로 안전하게 변환된다.
- [ ] list/detail/result/compare request가 store port로 처리된다.
- [ ] not found/empty result semantics가 계약과 일치한다.

테스트 작성:
- fake runner/fake store 기반 worker handler tests.
- request -> domain payload mapping tests.
- domain/store result -> proto response mapping tests.

중간 검증:
- 원격 검증 환경에서만 `go test ./services/worker/...` 실행.

### [BACKTEST-3] backtest response mapping 정리
문제:
store/domain 모델과 proto response 사이의 mapping 책임이 흩어질 수 있다.

해결 방법:
worker 안에 mapper를 두어 domain/store 모델을 proto message로 변환한다. API는 pass-through에 가깝게 유지한다.

수정 파일 및 체크리스트:
- `services/worker/internal/socket/**` 또는 `services/worker/internal/contracts/**`
- 관련 tests
- [ ] timestamps, IDs, enum/status 값 변환이 테스트된다.
- [ ] nil/empty collection behavior가 명확하다.
- [ ] response message가 기존 client expectations와 맞는다.

테스트 작성:
- mapper table tests.
- edge case tests for missing result/compare pair.

중간 검증:
- 원격 검증 환경에서만 worker/API smoke를 실행한다.

## 수정 파일 요약
- 예상 API 코드: `services/api/internal/socket/**`, `services/api/internal/workerclient/**`
- 예상 worker 코드: `services/worker/internal/socket/**`, `services/worker/internal/jobs/**`, `services/worker/internal/storage/**`
- 예상 tests: API handler tests, worker handler/mapper tests

## 최종 검증
- 원격 검증 환경에서만 `go test ./services/api/...`
- 원격 검증 환경에서만 `go test ./services/worker/...`
- 원격 검증 환경에서만 API/worker smoke
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
