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

# Plan - API Codegen Check

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

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

## 배경

이 subtask는 `01_schema_docs`가 고정한 schema를 Go API/worker와 Flutter client가 반복 생성해 소비할 수 있게 만든다. 현재 `.proto`에는 Go `go_package`가 있지만 generated Go module이 없고, Dart generated output과 drift check도 없다. root `bin/test`/`bin/lint`에 contract drift 검증을 묶어 후속 parser map 작업의 기반을 만든다.

## 분석 결과

### 읽은 파일

- `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/contracts/rules.md`
- `agent-ops/rules/project/domain/client/rules.md`
- `agent-ops/rules/project/domain/operations/rules.md`
- `packages/contracts/README.md`
- `packages/contracts/proto/alt/v1/common.proto`
- `packages/contracts/proto/alt/v1/market.proto`
- `packages/contracts/proto/alt/v1/backtest.proto`
- `go.work`
- `bin/test`
- `bin/lint`
- `bin/build`
- `.gitignore`
- `apps/client/pubspec.yaml`
- `apps/client/pubspec.lock`
- `apps/client/README.md`
- `apps/client/test/widget_test.dart`
- `services/api/go.mod`
- `services/worker/go.mod`

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

- Go generated output 생성: 기존 generated module/test 없음. `packages/contracts/gen/go`에 `go test ./...`를 추가해 컴파일 검증한다.
- Dart generated output 생성: 기존 generated Dart/test 없음. Flutter analyze/test가 generated imports를 컴파일하게 한다.
- drift check: 기존 명령 없음. `bin/contracts-check`가 생성 전/후 snapshot diff로 dirty working tree에서도 drift만 감지해야 한다.
- root 검증 연동: `bin/test`, `bin/lint`는 contract check와 generated Go module을 아직 실행하지 않는다.

### 심볼 참조

- renamed/removed symbols: none.

### 분할 판단

분할 정책을 먼저 평가했다. 이 plan은 `02+01_codegen_check`이며 `01_schema_docs`가 `complete.log`를 만든 뒤 시작한다. `03+02_api_parser_map`, `04+02_client_parser_map`은 이 subtask의 generated output과 scripts에 의존한다.

### 범위 결정 근거

이 subtask는 codegen toolchain, generated output, root verification integration까지만 다룬다. API/client parser map helper와 runtime session behavior는 후속 subtask에서 다룬다.

### 빌드 등급

build=`cloud-G07`, review=`cloud-G07`. Shell script orchestration, protoc plugins, generated output, root verification contracts를 다루는 terminal-agent 작업이다.

## 의존 관계 및 구현 순서

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

## 구현 체크리스트

- [ ] [API-1] Go/Dart contract generation 명령과 Dart protoc plugin wrapper를 추가하고 로컬에서 실행한다.
- [ ] [API-2] generated output, generated Go module, root drift/test/lint 연동을 추가한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [API-1] Codegen commands and tool dependencies

#### 문제

`packages/contracts/proto/alt/v1/common.proto:5`, `market.proto:5`, `backtest.proto:5`는 Go output path를 선언하지만 repository에는 `bin/contracts-gen`이나 Dart plugin wrapper가 없다. `apps/client/pubspec.yaml:41`의 dev dependencies에는 `protoc_plugin`이 없어 `protoc --dart_out`을 반복 실행할 수 없다.

Before:

```yaml
# apps/client/pubspec.yaml:41
dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_lints: ^6.0.0
```

해결 후 형태:

```yaml
dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^6.0.0
  protoc_plugin: <resolved by flutter pub add --dev protoc_plugin>
```

```bash
#!/usr/bin/env bash
set -euo pipefail

root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
proto_root="$root/packages/contracts/proto"
protos=(
  "$proto_root/alt/v1/common.proto"
  "$proto_root/alt/v1/market.proto"
  "$proto_root/alt/v1/backtest.proto"
)

protoc -I "$proto_root" --go_out="$root/packages/contracts/gen/go" --go_opt=paths=source_relative "${protos[@]}"
protoc -I "$proto_root" --plugin=protoc-gen-dart="$root/apps/client/tool/protoc-gen-dart" --dart_out="$root/apps/client/lib/src/generated" "${protos[@]}"
```

#### 해결 방법

`bin/contracts-gen`을 만들고 `protoc`, `protoc-gen-go`, Dart wrapper 존재를 명확히 검사한다. `apps/client/tool/protoc-gen-dart`는 `cd apps/client && exec dart run protoc_plugin "$@"` 형태로 두고, `flutter pub add --dev protoc_plugin`으로 `pubspec.yaml`/`pubspec.lock`을 갱신한다.

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

- [ ] `bin/contracts-gen`: Go/Dart generated output을 한 번에 갱신하는 executable script 추가.
- [ ] `apps/client/tool/protoc-gen-dart`: protoc plugin wrapper 추가.
- [ ] `apps/client/pubspec.yaml`: `protoc_plugin` dev dependency 추가.
- [ ] `apps/client/pubspec.lock`: dependency resolution 결과 반영.
- [ ] `packages/contracts/README.md`: `bin/contracts-gen` 사용법 추가.

#### 테스트 작성

별도 테스트 파일은 작성하지 않는다. script 실행 자체와 generated output compile이 검증이다.

#### 중간 검증

```bash
command -v protoc
command -v protoc-gen-go
cd apps/client && flutter pub get
bin/contracts-gen
```

기대 결과: 모든 명령 exit 0. 현재 분석 시 `protoc=/config/.local/bin/protoc`, `protoc-gen-go=/config/.local/bin/protoc-gen-go`, `protoc-gen-dart`는 PATH에 없어서 wrapper가 필요하다.

### [API-2] Generated modules and drift checks

#### 문제

`go.work:3`의 workspace module 목록에 generated contract module이 없고, `bin/test:6`/`bin/lint:6`의 module loop도 contracts generated Go package를 검사하지 않는다. drift check 명령이 없어서 source schema와 generated files가 어긋나도 root 검증에서 감지되지 않는다.

Before:

```text
# go.work:3
use (
	../proto-socket/go
	./apps/cli
	./packages/domain
	./services/api
	./services/worker
)
```

```bash
# bin/test:6
for module in \
  "$root/packages/domain" \
  "$root/services/api" \
  "$root/services/worker" \
  "$root/apps/cli"
do
  (cd "$module" && go test ./...)
done
```

해결 후 형태:

```text
use (
	../proto-socket/go
	./apps/cli
	./packages/contracts/gen/go
	./packages/domain
	./services/api
	./services/worker
)
```

```bash
"$root/bin/contracts-check"

for module in \
  "$root/packages/contracts/gen/go" \
  "$root/packages/domain" \
  ...
```

#### 해결 방법

생성 산출물은 repository에 커밋되는 source-derived files로 둔다. `packages/contracts/gen/go`는 독립 Go module로 만들고 `go.work`, `bin/test`, `bin/lint`에 추가한다. `bin/contracts-check`는 generated paths를 temp snapshot으로 복사한 뒤 `bin/contracts-gen`을 실행하고 snapshot과 현재 generated paths를 `diff -ru`로 비교해 drift만 잡는다.

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

- [ ] `packages/contracts/gen/go/go.mod`: module `git.toki-labs.com/toki/alt/packages/contracts/gen/go` 추가.
- [ ] `packages/contracts/gen/go/alt/v1/*.pb.go`: generated Go output 추가.
- [ ] `apps/client/lib/src/generated/alt/v1/*.pb*.dart`: generated Dart output 추가.
- [ ] `go.work`: `./packages/contracts/gen/go` 추가.
- [ ] `bin/contracts-check`: snapshot 기반 drift check script 추가.
- [ ] `bin/test`: `bin/contracts-check`와 generated Go module test 추가.
- [ ] `bin/lint`: generated Go module vet 추가.
- [ ] `packages/contracts/README.md`: `bin/contracts-check` 사용법 추가.

#### 테스트 작성

별도 hand-written 테스트는 후속 parser map subtask에서 작성한다. 이 subtask는 generated output compile과 drift script 검증으로 충분하다.

#### 중간 검증

```bash
bin/contracts-check
cd packages/contracts/gen/go && go test ./...
cd apps/client && flutter analyze --no-fatal-infos
```

기대 결과: 모든 명령 exit 0. `bin/contracts-check`는 working tree에 이미 있는 generated file 변경 자체가 아니라, regeneration이 추가 변경을 만들 때만 실패한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `bin/contracts-gen` | API-1 |
| `apps/client/tool/protoc-gen-dart` | API-1 |
| `apps/client/pubspec.yaml` | API-1 |
| `apps/client/pubspec.lock` | API-1 |
| `packages/contracts/README.md` | API-1, API-2 |
| `packages/contracts/gen/go/go.mod` | API-2 |
| `packages/contracts/gen/go/alt/v1/*.pb.go` | API-2 |
| `apps/client/lib/src/generated/alt/v1/*.pb*.dart` | API-2 |
| `go.work` | API-2 |
| `bin/contracts-check` | API-2 |
| `bin/test` | API-2 |
| `bin/lint` | API-2 |

## 최종 검증

```bash
bin/contracts-gen
bin/contracts-check
bin/test
bin/lint
```

기대 결과: 모든 명령 exit 0. `bin/test`/`bin/lint`의 Go test cache output은 허용하지만 `bin/contracts-check`는 fresh regeneration을 수행해야 한다.

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