<!-- task=m-protocol-evolution-compatibility/01_proto_fullname_foundation plan=0 tag=FOUNDATION -->

# Plan - FOUNDATION

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

이 plan은 `proto-package-sync`와 `fullname-type`을 함께 끝내는 foundation 작업이다. 구현 후 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 변경 내용, 실행한 검증 출력, 계획 대비 변경 사항을 채워야 하며, finalization은 code-review 스킬 전용이다. 사용자 결정, 사용자 소유 외부 환경, 범위 충돌이 아니면 사용자에게 직접 묻지 말고 review stub의 `사용자 리뷰 요청` 섹션에 근거를 남긴다. 재실행이나 증거 수집으로 해소 가능한 검증 공백은 사용자 리뷰 요청이 아니다.

## 배경

현재 공통 proto에는 package가 없고 일부 구현은 simple name을 전제로 한다. 마일스톤 결정은 canonical package `proto_socket`과 full proto name 기반 송신이므로, proto/package sync와 각 언어의 `typeName` 산출을 한 번에 맞춰야 중간 상태에서 테스트가 깨지지 않는다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 에이전트는 채팅으로 직접 질문하거나 선택지를 제시하지 않는다. code-review가 blocker 정당성을 판단하고 필요할 때만 `USER_REVIEW.md`를 쓴다.

## Roadmap Targets

- Milestone: `agent-roadmap/milestones/protocol-evolution-compatibility.md`
- Task ids:
  - `proto-package-sync`: Canonical proto package 추가와 언어별 proto/generated binding 동기화
  - `fullname-type`: 사용 가능 언어의 `typeName` 또는 message metadata를 full proto name 기준으로 정렬
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/skills/common/update-roadmap/SKILL.md`
- `agent-ops/rules/project/domain/protocol/rules.md`
- `agent-ops/rules/project/domain/tools/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/protocol-smoke.md`
- `agent-test/local/tools-smoke.md`
- `agent-test/local/proto-socket-full-matrix.md`
- `agent-roadmap/current.md`
- `agent-roadmap/milestones/protocol-evolution-compatibility.md`
- `PROTOCOL.md`, `VERSIONING.md`, `PORTING_GUIDE.md`
- `proto/message_common.proto`
- `go/packets/message_common.proto`, `go/packets/message_common.pb.go`, `go/communicator.go`, `go/test/test_helpers_test.go`, `go/test/communicator_test.go`
- `kotlin/src/main/proto/message_common.proto`, `kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt`, `kotlin/src/test/kotlin/com/tokilabs/proto_socket/TestHelpers.kt`, `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt`
- `python/proto_socket/packets/message_common.proto`, `python/proto_socket/packets/message_common_pb2.py`, `python/proto_socket/communicator.py`, `python/test/test_communicator.py`, `python/test/test_tcp.py`
- `typescript/src/packets/message_common_pb.ts`, `typescript/src/communicator.ts`, `typescript/test/communicator.test.ts`, `typescript/test/base_client.test.ts`, `typescript/test/tcp.test.ts`
- `dart/lib/src/communicator.dart`, `dart/lib/src/protobuf_client.dart`, `dart/lib/src/ws_protobuf_client_io.dart`, `dart/lib/src/ws_protobuf_client_web.dart`, `dart/lib/src/heartbeat_mixin.dart`, `dart/test/communicator_test.dart`
- `tools/generate_proto.sh`, `tools/check_proto_sync.sh`, `README.md`

### 테스트 환경 규칙

`test_env=local`로 판단했다. `agent-test/local/rules.md`를 읽었고, protocol smoke는 `tools/check_proto_sync.sh`, tools smoke는 `tools/check_proto_sync.sh`, public protocol/API 영향은 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all`로 확장해야 한다. 구조가 비어 있거나 `<확인 필요>`인 필드는 없었다. `tools/generate_proto.sh`는 proto 변경 후 필수이며, 생성 도구 부재 시 `command -v protoc`, `command -v protoc-gen-dart`, `command -v protoc-gen-go` 결과를 review stub에 기록한다.

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

- Proto package 추가 후 generated full name 변화는 기존 tests 일부가 simple name을 기대해 실패한다.
- `go/test/test_helpers_test.go:20`은 `TypeNameOf(TestData) == "TestData"`를 기대한다.
- `python/test/test_communicator.py:51`은 송신 `typeName == "TestData"`를 기대한다.
- TypeScript static packet metadata는 `typescript/src/packets/message_common_pb.ts:35-70`에서 simple name을 hard-code한다.
- Dart는 package-qualified 수신 테스트가 있으나 canonical package `proto_socket` 전환 후 typed registration 기대값을 재확인해야 한다.

### 심볼 참조

- 변경 예정: `type_name_of` 반환값(`python/proto_socket/communicator.py:539-543`), TypeScript `MessageType.typeName`/`$typeName` literal, generated protobuf descriptors.
- 제거 예정 symbol 없음.
- call site: parser maps and tests in `go/test/**`, `kotlin/src/test/**`, `python/test/**`, `typescript/test/**`, crosstest parser maps under `*/crosstest/**`.

### 분할 판단

분할 정책을 평가했다. 공통 task group은 `agent-task/m-protocol-evolution-compatibility/`다. `01_proto_fullname_foundation`은 독립 선행 작업이며 predecessor 없음. `02+01_legacy_alias`는 full-name foundation 완료 후 alias registry를 구현한다. `03+01,02_fullname_tests`는 foundation과 alias 둘 다 완료된 뒤 동일/크로스 테스트를 확장한다. `proto-package-sync`와 `fullname-type`은 따로 분리하면 generated descriptor가 바뀐 중간 상태에서 기존 tests가 깨지므로 하나의 plan으로 묶는다.

### 범위 결정 근거

이 plan은 package 선언, generated binding sync, canonical full-name 송신/metadata 정렬까지만 다룬다. legacy simple-name receive alias, alias collision policy, 크로스 언어 legacy 호환 테스트 추가는 후속 split plan의 범위다. evolution 에픽(`hello-cap`, `std-error`, `nonce-boundary`, `ordering-doc`)은 수정하지 않는다.

### 빌드 등급

`cloud-G06`: proto/schema와 다중 언어 generated binding, public protocol identity가 함께 바뀌어 review context가 넓다.

## 구현 체크리스트

- [ ] `proto/message_common.proto`에 `package proto_socket;`을 추가하고 Go/Kotlin/Python proto copy와 generated binding을 동기화한다.
- [ ] Dart/Go/Kotlin/Python/TypeScript의 기본 송신 `typeName`과 static/native metadata 기대값을 `proto_socket.<MessageName>`으로 맞춘다.
- [ ] simple-name을 직접 기대하는 즉시 실패 테스트와 helper를 canonical full name 기준으로 갱신한다.
- [ ] `tools/generate_proto.sh`, `tools/check_proto_sync.sh`, 각 언어 focused unit test, 전체 matrix를 실행하거나 실행 불가 근거를 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [FOUNDATION-1] Proto Package Sync

문제: canonical proto와 언어별 proto copy에 package가 없다.

```protobuf
// proto/message_common.proto:1-3
syntax = "proto3";

message PacketBase {
```

해결 방법: canonical proto는 `syntax` 다음에 `package proto_socket;`을 추가한다. Go/Kotlin copy는 언어별 option 위에 같은 package를 추가하고, Python copy도 동기화한다. 그 뒤 generated bindings를 재생성한다. `tools/check_proto_sync.sh`는 Python proto copy도 normalize/compare하도록 확장한다.

수정 파일 및 체크리스트:

- [ ] `proto/message_common.proto`
- [ ] `go/packets/message_common.proto`
- [ ] `kotlin/src/main/proto/message_common.proto`
- [ ] `python/proto_socket/packets/message_common.proto`
- [ ] `dart/lib/src/packets/message_common.pb*.dart`
- [ ] `go/packets/message_common.pb.go`
- [ ] `python/proto_socket/packets/message_common_pb2.py`, `python/proto_socket/packets/message_common_pb2.pyi` if regenerated
- [ ] `typescript/src/packets/message_common_pb.ts`
- [ ] `tools/check_proto_sync.sh`

테스트 작성: 생성물 정합성은 script로 검증한다. 별도 test 파일보다 `tools/check_proto_sync.sh`에 Python copy mismatch 실패 경로를 포함한다.

중간 검증:

```bash
tools/generate_proto.sh
tools/check_proto_sync.sh
```

### [FOUNDATION-2] Canonical Type Metadata

문제: 일부 언어는 full name이 아니라 simple/static name을 사용한다.

```python
# python/proto_socket/communicator.py:539-543
def type_name_of(message_or_class: Message | type[Message]) -> str:
    descriptor = getattr(message_or_class, "DESCRIPTOR", None)
    if descriptor is None:
        descriptor = message_or_class.__class__.DESCRIPTOR
    return descriptor.name
```

```typescript
// typescript/src/packets/message_common_pb.ts:35-70
export const PacketBaseSchema: MessageType<PacketBase> = {
  typeName: "PacketBase",
...
export const TestDataSchema: MessageType<TestData> = {
  typeName: "TestData",
```

해결 방법: Python은 `descriptor.full_name`을 반환한다. TypeScript는 `$typeName` literal과 `MessageType.typeName`을 `proto_socket.PacketBase`, `proto_socket.HeartBeat`, `proto_socket.TestData`로 바꾼다. Dart typed registration은 generated `qualifiedMessageName`과 일치하는 canonical helper를 사용하도록 조정한다. Go/Kotlin은 package 추가 후 descriptor 기반 값이 full name인지 테스트로 고정한다.

수정 파일 및 체크리스트:

- [ ] `python/proto_socket/communicator.py`
- [ ] `typescript/src/packets/message_common_pb.ts`
- [ ] `dart/lib/src/communicator.dart`
- [ ] `dart/lib/src/protobuf_client.dart`
- [ ] `dart/lib/src/ws_protobuf_client_io.dart`
- [ ] `dart/lib/src/ws_protobuf_client_web.dart`
- [ ] `dart/lib/src/heartbeat_mixin.dart`

테스트 작성: 즉시 기대값 테스트를 각 언어 unit에 반영한다. alias/legacy receive 테스트는 `03+01,02_fullname_tests`에서 확장한다.

중간 검증:

```bash
dart test test/communicator_test.dart
go test ./...
cd kotlin && ./gradlew test
cd python && python3 -m pytest -q
cd typescript && npm run check && npm test
```

### [FOUNDATION-3] Immediate Test Expectations

문제: tests/helper가 simple name을 canonical 값으로 고정하고 있다.

```go
// go/test/test_helpers_test.go:20-23
func TestTypeNameMatchesDartConvention(t *testing.T) {
    if got := toki.TypeNameOf(&packets.TestData{}); got != "TestData" {
        t.Fatalf("type name = %q, want TestData", got)
```

```python
# python/test/test_communicator.py:48-52
await communicator.send(ProtoTestData(index=7, message="hello"))
assert len(transport.packets) == 1
packet = transport.packets[0]
assert packet.typeName == "TestData"
```

해결 방법: 기대값을 `proto_socket.TestData`로 바꾸고, HeartBeat/TestData parser maps가 canonical key로 초기화되는지 확인한다. crosstest의 INFO 출력은 값이 바뀌어도 runner가 특정 simple string을 파싱하지 않는지 확인만 한다.

수정 파일 및 체크리스트:

- [ ] `go/test/test_helpers_test.go`
- [ ] `python/test/test_communicator.py`
- [ ] `typescript/test/communicator.test.ts`
- [ ] `typescript/test/base_client.test.ts`
- [ ] `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt`
- [ ] `dart/test/communicator_test.dart`

테스트 작성: 기존 assertion 갱신이며 새 behavior 추가는 후속 tests plan에서 한다.

중간 검증:

```bash
rg --sort path '"TestData"|typeName == "TestData"|want TestData' dart go kotlin python typescript
```

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `proto/message_common.proto`, `go/packets/message_common.proto`, `kotlin/src/main/proto/message_common.proto`, `python/proto_socket/packets/message_common.proto` | FOUNDATION-1 |
| `dart/lib/src/packets/*`, `go/packets/message_common.pb.go`, `python/proto_socket/packets/message_common_pb2.py`, `typescript/src/packets/message_common_pb.ts` | FOUNDATION-1, FOUNDATION-2 |
| `dart/lib/src/communicator.dart`, `python/proto_socket/communicator.py`, `typescript/src/communicator.ts` | FOUNDATION-2 |
| `tools/check_proto_sync.sh` | FOUNDATION-1 |
| `dart/test/communicator_test.dart`, `go/test/test_helpers_test.go`, `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt`, `python/test/test_communicator.py`, `typescript/test/communicator.test.ts` | FOUNDATION-3 |

## 최종 검증

```bash
tools/generate_proto.sh
tools/check_proto_sync.sh
dart test test/communicator_test.dart
go test ./...
cd kotlin && ./gradlew test
cd python && python3 -m pytest -q
cd typescript && npm run check && npm test
bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all
```

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