<!-- task=m-protocol-evolution-compatibility/06+04,05_nonce_kotlin_python_ts plan=0 tag=API -->

# Plan - API

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

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션 작성까지가 구현의 마지막 단계다. 검증을 실행하고 실제 출력/결과를 기록한 뒤 active 파일을 유지한 채 리뷰를 요청한다. 구현 중 사용자 결정, 사용자 소유 외부 환경, 범위 충돌이 생기면 직접 질문하지 말고 리뷰 stub의 `사용자 리뷰 요청` 섹션에 근거와 재개 조건을 남긴다. 후속 에이전트가 재실행으로 해소할 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

04번 계약과 05번 Dart/Go 기준 구현이 완료되면 Kotlin, Python, TypeScript도 같은 pending nonce skip 정책으로 정렬해야 한다. 이 작업은 남은 세 언어의 boundary regression test를 추가하고 full matrix로 cross-language 호환성을 확인한다. PASS 시 현재 마일스톤의 `nonce-boundary` Task 완료 근거가 된다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 에이전트는 채팅으로 사용자에게 직접 질문하지 않으며, code-review가 해당 요청의 타당성을 판단하고 필요할 때 `USER_REVIEW.md`를 작성한다.

## Roadmap Targets

- Milestone: `agent-roadmap/milestones/protocol-evolution-compatibility.md`
- Task ids:
  - `nonce-boundary`: nonce wrap 시 pending request nonce와 충돌하지 않도록 active pending nonce skip 정책과 boundary 테스트를 보강한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/ROADMAP.md`
- `agent-roadmap/milestones/protocol-evolution-compatibility.md`
- `agent-ops/rules/project/domain/protocol/rules.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-test/local/rules.md`
- `agent-test/local/proto-socket-full-matrix.md`
- `kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt`
- `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt`
- `python/proto_socket/communicator.py`
- `python/test/test_communicator.py`
- `typescript/src/communicator.ts`
- `typescript/test/communicator.test.ts`
- `PROTOCOL.md`
- `VERSIONING.md`
- `PORTING_GUIDE.md`

### 테스트 환경 규칙

- test_env: `local`
- 적용 규칙: `agent-test/local/rules.md`를 읽었다.
- 매칭 프로필: `agent-test/local/proto-socket-full-matrix.md`
- 직접 검증: Kotlin/Python/TypeScript focused tests.
- 최종 검증: `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all`

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

- Kotlin: 기존 `testNonceWrapsAfterIntMaxWithoutEmittingZero`, `testSendRequestNonceWrapsAtInt32Max`는 active pending nonce skip을 검증하지 않는다.
- Python: 기존 `test_nonce_wraps_after_int32_max_without_emitting_zero`, `test_send_request_nonce_wraps_at_int32_max`는 active pending nonce skip을 검증하지 않는다.
- TypeScript: 기존 `nonce wraps after int32 max without emitting zero`, `sendRequest nonce wraps at int32 max`는 active pending nonce skip을 검증하지 않는다.
- full matrix는 cross-language smoke를 보장하지만 wrap boundary 자체는 focused unit tests가 담당한다.

### 심볼 참조

- renamed/removed symbol: none
- Kotlin `nextNonce()` call sites: [Communicator.kt](/config/workspace/proto-socket/kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt:304), [Communicator.kt](/config/workspace/proto-socket/kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt:316), [Communicator.kt](/config/workspace/proto-socket/kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt:683)
- Python `next_nonce()` call sites: [communicator.py](/config/workspace/proto-socket/python/proto_socket/communicator.py:306), [communicator.py](/config/workspace/proto-socket/python/proto_socket/communicator.py:319), [communicator.py](/config/workspace/proto-socket/python/proto_socket/communicator.py:549)
- TypeScript `nextNonce()` call sites: [communicator.ts](/config/workspace/proto-socket/typescript/src/communicator.ts:404), [communicator.ts](/config/workspace/proto-socket/typescript/src/communicator.ts:419), [communicator.ts](/config/workspace/proto-socket/typescript/src/communicator.ts:610)

### 분할 판단

- split decision policy를 먼저 평가했다.
- 공유 task group: `agent-task/m-protocol-evolution-compatibility/`
- `06+04,05_nonce_kotlin_python_ts`: 이 plan. predecessor `04`, `05`가 모두 필요하며 계획 작성 시점에는 active plan만 있고 `complete.log`는 없다.
- 06번은 최종 rollout과 full matrix를 포함하므로 `Roadmap Targets`를 가진다.

### 범위 결정 근거

- 이 plan은 Kotlin/Python/TypeScript communicator와 focused tests를 수정한다.
- Dart/Go는 05번 완료 결과를 전제로 하며 이 plan에서 다시 수정하지 않는다.
- proto schema와 crosstest expected count는 변경하지 않는다.

### 빌드 등급

- `cloud-G07`: 세 async runtime에 같은 protocol/concurrency 정책을 적용하고 full matrix evidence까지 수집한다.

## 구현 체크리스트

- [ ] 04번과 05번 predecessor `complete.log`가 모두 존재하는지 확인하고, 없으면 구현을 시작하지 않고 리뷰 stub에 차단 근거를 남긴다.
- [ ] Kotlin `Communicator.nextNonce()`가 active `pendingRequests` 키와 충돌하는 후보 nonce를 건너뛰도록 수정하고 regression test를 추가한다.
- [ ] Python `Communicator.next_nonce()`가 active `_pending_requests` 키와 충돌하는 후보 nonce를 건너뛰도록 수정하고 regression test를 추가한다.
- [ ] TypeScript `Communicator.nextNonce()`가 active `pendingRequests` 키와 충돌하는 후보 nonce를 건너뛰도록 수정하고 regression 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`과 `git diff --check`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

`06+04,05_nonce_kotlin_python_ts`는 디렉터리명 기준 predecessors `04`, `05`를 요구한다. 구현 전 다음 중 하나로 각각의 완료를 확인한다.

- `agent-task/m-protocol-evolution-compatibility/04+03_nonce_contract/complete.log` 또는 matching archive complete log
- `agent-task/m-protocol-evolution-compatibility/05+04_nonce_dart_go/complete.log` 또는 matching archive complete log

### [API-1] Kotlin pending nonce skip

#### 문제

[Communicator.kt](/config/workspace/proto-socket/kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt:234)는 atomic counter만 보고 후보 nonce를 반환한다.

```kotlin
234 internal fun nextNonce(): Int {
235     while (true) {
236         val current = nonce.get()
237         val next = if (current >= MAX_NONCE) 1 else current + 1
238         if (nonce.compareAndSet(current, next)) return next
239     }
240 }
```

#### 해결 방법

후보를 CAS로 확보한 뒤 `rwLock.read { pendingRequests.containsKey(candidate) }`를 확인하고, pending이면 다음 후보를 찾는다. 모든 positive int32 nonce가 pending인 경우 `IllegalStateException`을 던진다.

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

- [ ] `kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt`: pending skip 구현
- [ ] `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt`: skip regression 추가

#### 테스트 작성

`testNextNonceSkipsActivePendingAfterWrap`를 추가한다. nonce를 `Int.MAX_VALUE`, 첫 request로 pending `1`을 만든 뒤 다음 send가 nonce `2`를 발행하는지 확인한다.

#### 중간 검증

```bash
cd kotlin && ./gradlew test
```

기대 결과: BUILD SUCCESSFUL.

### [API-2] Python pending nonce skip

#### 문제

[communicator.py](/config/workspace/proto-socket/python/proto_socket/communicator.py:186)는 wrap 후 `1`을 반환하지만 `_pending_requests`를 확인하지 않는다.

```python
186 def next_nonce(self) -> int:
187     if self._nonce >= MAX_NONCE:
188         self._nonce = 0
189     self._nonce += 1
190     return self._nonce
```

#### 해결 방법

`next_nonce()`에서 bounded loop를 사용해 candidate가 `_pending_requests`에 있으면 skip한다. asyncio 단일 event loop 내 상태 변경이므로 별도 lock은 추가하지 않는다. 후보가 없으면 `RuntimeError` 또는 `ValueError`로 실패한다.

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

- [ ] `python/proto_socket/communicator.py`: pending skip 구현
- [ ] `python/test/test_communicator.py`: skip regression 추가

#### 테스트 작성

`test_next_nonce_skips_active_pending_after_wrap`를 추가한다. `_nonce = MAX_NONCE`, 첫 `send_request`로 pending `1`을 만들고, 응답 전 `send()`를 호출해 두 번째 packet nonce가 `2`인지 확인한다.

#### 중간 검증

```bash
cd python && python3 -m pytest -q
```

기대 결과: pytest PASS.

### [API-3] TypeScript pending nonce skip

#### 문제

[communicator.ts](/config/workspace/proto-socket/typescript/src/communicator.ts:284)는 wrap 후 `1`을 반환하지만 `pendingRequests`를 확인하지 않는다.

```ts
284 private nextNonce(): number {
285   if (this.nonce >= Communicator.MAX_NONCE) {
286     this.nonce = 0;
287   }
288   this.nonce += 1;
289   return this.nonce;
290 }
```

#### 해결 방법

`nextNonce()`에서 bounded loop를 사용해 candidate가 `pendingRequests.has(candidate)`이면 skip한다. JavaScript event loop 상태라 별도 mutex는 추가하지 않는다. 후보가 없으면 `Error`를 던지고 send/sendRequest promise가 reject되게 한다.

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

- [ ] `typescript/src/communicator.ts`: pending skip 구현
- [ ] `typescript/test/communicator.test.ts`: skip regression 추가

#### 테스트 작성

`nextNonce skips active pending request nonce after wrap`를 추가한다. private state cast로 `nonce = MAX_NONCE`, 첫 `sendRequest`로 pending `1`을 만들고 두 번째 send가 nonce `2`를 발행하는지 확인한다.

#### 중간 검증

```bash
cd typescript && npm run check && npm test
```

기대 결과: typecheck/test PASS.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt` | API-1 |
| `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt` | API-1 |
| `python/proto_socket/communicator.py` | API-2 |
| `python/test/test_communicator.py` | API-2 |
| `typescript/src/communicator.ts` | API-3 |
| `typescript/test/communicator.test.ts` | API-3 |

## 최종 검증

```bash
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
git diff --check
```

기대 결과: Kotlin/Python/TypeScript focused tests PASS, full matrix PASS, whitespace error 없음.

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