<!-- task=m-operator-client-api-core-integration/01_contract_mapping plan=0 tag=API -->

# Plan - API Contract Mapping

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성까지가 구현의 마지막 단계다. 구현 후 검증을 실행하고 실제 변경 내용, 설계 결정, stdout/stderr를 채운 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경, 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청`에 근거를 채우고 멈춘다. 후속 에이전트가 재실행으로 해소할 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

Milestone은 Flutter 화면 전에 generated ALT protobuf를 client core에서 안정적으로 쓰는 것을 요구한다. 현재 `altParserMap()`은 parser 등록만 검증하며, 응답 payload의 `ErrorInfo`와 success payload를 repository가 다룰 공통 result 형태가 없다. 후속 socket/market/backtest 계획이 같은 result 모델을 재사용하도록 첫 split에서 기반을 만든다.

## 사용자 리뷰 요청 흐름

구현 중 사용자 전용 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. code-review만 `USER_REVIEW.md` 작성, log archive, `complete.log` 생성을 수행한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-client-api-core-integration.md`
- Task ids:
  - `client-contracts`: generated/mapped contracts를 client core에서 사용할 수 있게 정리한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-test/local/rules.md`
- `agent-test/local/client-smoke.md`
- `agent-ops/rules/project/domain/client/rules.md`
- `apps/client/pubspec.yaml`
- `apps/client/analysis_options.yaml`
- `apps/client/lib/src/contracts/alt_contracts.dart`
- `apps/client/lib/src/integrations/socket/alt_socket_client.dart`
- `apps/client/lib/src/integrations/socket/socket_connection_controller.dart`
- `apps/client/lib/src/integrations/socket/socket_endpoint.dart`
- `apps/client/lib/src/features/dashboard/presentation/dashboard_screen.dart`
- `apps/client/test/contracts/alt_contracts_test.dart`
- `apps/client/test/integrations/socket/alt_socket_client_test.dart`
- `apps/client/test/widget_test.dart`
- `apps/client/test/app/bootstrap_test.dart`
- `packages/contracts/proto/alt/v1/common.proto`
- `packages/contracts/proto/alt/v1/market.proto`
- `packages/contracts/proto/alt/v1/backtest.proto`

### 테스트 환경 규칙

`test_env=local`. `agent-test/local/rules.md`와 matched profile `agent-test/local/client-smoke.md`를 읽었다. client 변경의 기본 검증은 `cd apps/client && flutter test`; generated contracts나 analysis options가 바뀌면 `bin/lint` 가능 여부를 확인한다. 이번 plan은 generated 파일을 손수 수정하지 않으며, fallback 명령은 필요 없다.

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

공통 client result/error mapping은 아직 테스트가 없다. 이 plan에서 `apps/client/test/contracts/alt_api_result_test.dart`를 추가해 success/error/transport failure mapping을 덮는다.

### 심볼 참조

renamed/removed symbol: none.

### 분할 판단

split decision policy를 먼저 평가했다. 공유 result 모델은 socket state, market repository, backtest repository의 선행 API/foundation 변경이므로 `01_contract_mapping`으로 분리한다. sibling 계획은 `01_contract_mapping`, `02+01_socket_state`, `03+01_market_repository`, `04+02,03_backtest_error`이며, 이 plan은 predecessor가 없다.

### 범위 결정 근거

`apps/client/lib/src/generated/**`는 generated 출력이므로 제외한다. Flutter 화면, dashboard layout, API server/worker 구현은 Milestone 범위 밖이므로 제외한다. 이 plan은 client core에서 쓸 result/error mapper와 tests만 다룬다.

### 빌드 등급

build=`cloud-G06`, review=`cloud-G06`. 후속 여러 plan이 의존할 public-ish client core API라 local보다 신중한 review가 필요하다.

## 구현 체크리스트

- [ ] `AltApiResult<T>`, `AltApiError`, `AltTransportState` 등 UI copy 없는 client core result 타입을 추가하고 `ErrorInfo` mapping test를 작성한다. 검증: `cd apps/client && flutter test test/contracts/alt_api_result_test.dart`
- [ ] response별 `ErrorInfo` 추출 helper를 추가해 market/backtest response에서 공통으로 쓸 수 있게 한다. 검증: `cd apps/client && flutter test test/contracts/alt_api_result_test.dart`
- [ ] `cd apps/client && flutter test`를 실행해 기존 parser/socket/widget 회귀를 확인한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [API-1] Client result/error mapping 추가

#### 문제

`apps/client/lib/src/contracts/alt_contracts.dart:7`에는 parser map만 있고, `packages/contracts/proto/alt/v1/common.proto:37`의 `ErrorInfo`를 stable client state로 보존하는 result 타입이 없다.

```dart
// apps/client/lib/src/contracts/alt_contracts.dart:7
Map<String, GeneratedMessage Function(List<int>)> altParserMap() {
```

#### 해결 방법

`apps/client/lib/src/contracts/alt_api_result.dart`를 추가한다. `AltApiResult<T>.success`, `AltApiResult<T>.apiError`, `AltApiResult<T>.transportFailure`와 `AltApiError.fromErrorInfo(ErrorInfo)`를 제공한다. UI 문구 대신 `code`, `message`, `transportKind` 같은 안정 필드만 둔다.

```dart
import 'package:alt_client/src/generated/alt/v1/common.pb.dart';

sealed class AltApiResult<T> {
  const AltApiResult();
}

final class AltApiSuccess<T> extends AltApiResult<T> {
  const AltApiSuccess(this.value);
  final T value;
}
```

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

- [ ] `apps/client/lib/src/contracts/alt_api_result.dart` 추가
- [ ] `apps/client/test/contracts/alt_api_result_test.dart` 추가

#### 테스트 작성

작성한다. `AltApiError.fromErrorInfo`가 `code/message`를 보존하고, transport failure가 `ErrorInfo` 없이도 stable state로 남는지 확인한다.

#### 중간 검증

`cd apps/client && flutter test test/contracts/alt_api_result_test.dart` exit code 0.

### [API-2] Response error extraction helper 추가

#### 문제

market/backtest response는 `error` field를 각자 갖지만, client repository가 이를 통일해서 읽을 helper가 없다. 관련 response field는 `packages/contracts/proto/alt/v1/market.proto:44`, `packages/contracts/proto/alt/v1/backtest.proto:39` 이후에 흩어져 있다.

#### 해결 방법

`alt_api_result.dart`에 `AltResponseErrorReader<T>` 또는 top-level helper를 두고 supported response type별 `hasError()`/`error`를 안전하게 추출한다. unsupported type은 `null`을 반환해 success path를 깨지 않는다.

```dart
ErrorInfo? altResponseError(Object response) {
  return switch (response) {
    ListInstrumentsResponse(:final error) when response.hasError() => error,
    _ => null,
  };
}
```

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

- [ ] `apps/client/lib/src/contracts/alt_api_result.dart`
- [ ] `apps/client/test/contracts/alt_api_result_test.dart`

#### 테스트 작성

작성한다. `ListInstrumentsResponse`, `ListBarsResponse`, `ListBacktestRunsResponse`, `GetBacktestRunDetailResponse`, `GetBacktestResultResponse`, `StartBacktestResponse`의 typed error 추출을 확인한다.

#### 중간 검증

`cd apps/client && flutter test test/contracts/alt_api_result_test.dart` exit code 0.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/client/lib/src/contracts/alt_api_result.dart` | API-1, API-2 |
| `apps/client/test/contracts/alt_api_result_test.dart` | API-1, API-2 |

## 최종 검증

```bash
cd apps/client && flutter test
```

기대 결과: exit code 0. Go/Dart test cache 여부와 무관하게 Flutter test 전체가 통과해야 한다.

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