<!-- task=m-contract-codegen-baseline/03+02_api_parser_map plan=0 tag=API -->

# Plan - API Parser Map

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 반드시 채운다. 검증 명령을 실행하고 실제 stdout/stderr를 기록한 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 최종화는 code-review 스킬 전용이다.

## 배경

Go API는 proto-socket session boundary를 맡지만 현재 ALT application payload parser map을 등록하지 않는다. `02+01_codegen_check`가 generated Go contracts를 만든 뒤, API 내부에 표준 parser map 위치를 두면 다음 `Socket Session Loop` Milestone에서 handshake/request-response를 바로 얹을 수 있다.

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/foundation-alignment/PHASE.md`
- `agent-roadmap/phase/foundation-alignment/milestones/contract-codegen-baseline.md`
- `agent-ops/rules/project/domain/api/rules.md`
- `agent-ops/rules/project/domain/contracts/rules.md`
- `services/api/go.mod`
- `services/api/internal/socket/server.go`
- `services/api/internal/config/config.go`
- `services/api/cmd/alt-api/main.go`
- `packages/contracts/proto/alt/v1/common.proto`
- `packages/contracts/proto/alt/v1/market.proto`
- `packages/contracts/proto/alt/v1/backtest.proto`
- `../proto-socket/go/communicator.go`
- `bin/test`
- `bin/lint`

### 테스트 커버리지 공백

- API parser map helper: 기존 테스트 없음. 새 unit test가 모든 ALT request/response/result message key와 parse success를 검증해야 한다.
- `socket.NewServer` parser map 주입: 기존 테스트 없음. 최소한 helper가 non-empty이고 server construction이 컴파일되는 것을 `go test ./...`로 검증한다.

### 심볼 참조

- renamed/removed symbols: none.

### 분할 판단

분할 정책을 먼저 평가했다. 이 plan은 `03+02_api_parser_map`이며 `02+01_codegen_check`가 `complete.log`를 만든 뒤 시작한다. Client Dart parser map은 별도 `04+02_client_parser_map`에서 병렬 처리한다.

### 범위 결정 근거

이 subtask는 `services/api/**`와 API module dependency만 다룬다. Flutter client parser map, UI, actual request handlers, worker execution은 범위 밖이다.

### 빌드 등급

build=`cloud-G06`, review=`cloud-G06`. Protocol parser registration이 API runtime boundary에 들어가고 generated module dependency를 추가하므로 cloud review가 필요하다.

## 의존 관계 및 구현 순서

`03+02_api_parser_map`은 같은 task group의 `02+01_codegen_check`가 `complete.log`를 만든 뒤 시작한다. 이 의존성은 directory name의 `+02`가 source of truth다.

## 구현 체크리스트

- [ ] [API-1] API 내부 contract parser map helper와 unit test를 추가한다.
- [ ] [API-2] socket server construction이 API parser map을 사용하도록 연결하고 module dependency를 정리한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [API-1] API contract parser map helper

#### 문제

`../proto-socket/go/communicator.go:20`의 `ParserMap`은 `map[string]func([]byte) (proto.Message, error)`이고, parser가 없으면 `../proto-socket/go/communicator.go:304`에서 error를 반환한다. ALT API에는 generated `alt.v1` messages를 parser map으로 묶는 표준 위치가 없다.

Before:

```go
// ../proto-socket/go/communicator.go:304
func (c *Communicator) parse(typeName string, data []byte) (proto.Message, error) {
	c.mu.RLock()
	parser := c.parserMap[typeName]
	c.mu.RUnlock()
	if parser == nil {
		return nil, fmt.Errorf("protobuf parser is not registered for type %s", typeName)
	}
	return parser(data)
}
```

해결 후 형태:

```go
package contracts

func ParserMap() protoSocket.ParserMap {
	return protoSocket.ParserMap{
		protoSocket.TypeNameOf(&altv1.HelloRequest{}): parserFor(func() proto.Message { return &altv1.HelloRequest{} }),
	}
}
```

#### 해결 방법

`services/api/internal/contracts/parser_map.go`를 만들고 generated `altv1` message 전체를 등록한다. request/response/result payload를 모두 포함하고 helper는 map을 새로 반환해 호출자가 mutate해도 package 전역 상태가 생기지 않게 한다.

#### 수정 파일 및 체크리스트

- [ ] `services/api/internal/contracts/parser_map.go`: `ParserMap()`과 shared `parserFor` helper 추가.
- [ ] `services/api/internal/contracts/parser_map_test.go`: map keys와 parse success 검증.
- [ ] `services/api/go.mod`: generated Go module require 추가.

#### 테스트 작성

작성한다. `TestParserMapIncludesAltMessages`는 `HelloRequest`, `HelloResponse`, `ListInstrumentsRequest`, `ListInstrumentsResponse`, `ListBarsRequest`, `ListBarsResponse`, `StartBacktestRequest`, `StartBacktestResponse`, `GetBacktestRunRequest`, `GetBacktestRunResponse`, `GetBacktestResultRequest`, `GetBacktestResultResponse`, `BacktestResult`를 등록/parse한다.

#### 중간 검증

```bash
cd services/api && go test ./internal/contracts
```

기대 결과: exit 0.

### [API-2] Wire parser map into socket server

#### 문제

`services/api/internal/socket/server.go:12`는 `protoSocket.ParserMap{}`를 전달해 ALT payload가 들어와도 parse할 수 없다.

Before:

```go
// services/api/internal/socket/server.go:10
func NewServer(cfg config.Config) *protoSocket.WsServer {
	return protoSocket.NewWsServer(cfg.Host, cfg.Port, cfg.SocketPath, func(conn *websocket.Conn) *protoSocket.WsClient {
		return protoSocket.NewWsClient(conn, cfg.HeartbeatIntervalSec, cfg.HeartbeatWaitSec, protoSocket.ParserMap{})
	})
}
```

해결 후 형태:

```go
import (
	apiContracts "git.toki-labs.com/toki/alt/services/api/internal/contracts"
)

return protoSocket.NewWsClient(conn, cfg.HeartbeatIntervalSec, cfg.HeartbeatWaitSec, apiContracts.ParserMap())
```

#### 해결 방법

`server.go`에서 API contracts helper를 import하고 `NewWsClient`에 `apiContracts.ParserMap()`을 전달한다. `go mod tidy`로 generated contract module dependency를 정리한다.

#### 수정 파일 및 체크리스트

- [ ] `services/api/internal/socket/server.go`: empty parser map을 API parser map으로 교체.
- [ ] `services/api/go.mod`: generated contract module dependency 유지.
- [ ] `services/api/go.sum`: 필요한 checksum 변경 반영.

#### 테스트 작성

별도 socket server unit test는 작성하지 않는다. 이 subtask는 parser map helper unit test와 `go test ./...` 컴파일 검증으로 충분하다. 실제 handshake/request-response behavior는 후속 `Socket Session Loop` Milestone 범위다.

#### 중간 검증

```bash
cd services/api && go test ./...
```

기대 결과: exit 0.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/api/internal/contracts/parser_map.go` | API-1 |
| `services/api/internal/contracts/parser_map_test.go` | API-1 |
| `services/api/internal/socket/server.go` | API-2 |
| `services/api/go.mod` | API-1, API-2 |
| `services/api/go.sum` | API-2 |

## 최종 검증

```bash
cd services/api && go test ./...
bin/test
bin/lint
```

기대 결과: 모든 명령 exit 0. Go test cache output은 허용한다.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
