# Code Review Reference - API ## 개요 date=2026-04-26 task=nonce_overflow_policy, plan=0, tag=API ## 이 파일을 읽는 리뷰 에이전트에게 각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. 리뷰 완료 후 반드시 아래 순서로 아카이브하세요. 1. `CODE_REVIEW.md` → `code_review_N.log` (N = 기존 code_review_*.log 수) 2. `PLAN.md` → `plan_M.log` (M = 기존 plan_*.log 수) 3. PASS인 경우 `complete.log` 작성 후 종료. WARN/FAIL인 경우 새 `PLAN.md` + `CODE_REVIEW.md` 스텁 작성. --- ## 구현 항목별 완료 여부 | 항목 | 완료 여부 | |------|---------| | [API-1] 프로토콜 문서에 공통 nonce overflow 정책 정의 | [x] | | [API-2] 모든 구현체의 nonce 발급 로직을 공통 int32 max wrap 정책으로 통일 | [x] | | [API-3] 요청-응답 상관관계에서 wrap boundary 회귀 테스트 보강 | [x] | ## 계획 대비 변경 사항 - Go는 기존 `go/test`가 외부 패키지라 private `nonce` 접근이 불가능하여 계획의 후보대로 `go/communicator_nonce_test.go` 내부 패키지 테스트를 새로 추가했다. - Dart `sendRequest` 테스트는 `MAX_NONCE` 요청 매칭과 다음 요청의 wrap 값 `1` 매칭을 같은 테스트에서 함께 검증했다. - Go/Kotlin 검증은 로컬 환경에 Go/JDK가 없어 실행하지 못했다. 코드 변경과 테스트 파일은 추가했지만 해당 언어의 컴파일은 이 환경에서 확인되지 않았다. ## 주요 설계 결정 - 모든 구현체의 공통 상한은 protobuf `int32` signed max인 `2,147,483,647`로 고정했다. - `PacketBase.nonce`는 `0`을 송신하지 않는다. 내부 카운터가 `MAX_NONCE` 이상이면 먼저 0으로 리셋한 뒤 다음 발급값 `1`을 반환한다. - Dart/Python/TypeScript는 단일 `nextNonce`/`next_nonce` 경로를 통해 발급한다. - Go/Kotlin은 동시 송신에서 중복 nonce가 나오지 않도록 CAS loop로 `current -> next` 갱신을 수행한다. ## 리뷰어를 위한 체크포인트 - 문서가 공통 상한을 `2,147,483,647` signed int32 max로 명시하는지 확인하세요. - `responseNonce == 0` 예약 의미를 깨지 않도록 실제 송신 `PacketBase.nonce`가 0을 내보내지 않는지 확인하세요. - Dart의 모든 `++nonce` 직접 증가 지점이 `nextNonce()`로 대체됐는지 확인하세요. - Go/Kotlin은 동시 송신에서도 중복 nonce가 나오지 않도록 CAS loop를 사용하는지 확인하세요. - Python/TypeScript는 언어 자체의 큰 정수 범위가 아니라 프로토콜 공통 `int32` max를 상한으로 쓰는지 확인하세요. - 각 언어 테스트가 `MAX_NONCE - 1` boundary에서 `MAX_NONCE`, `1` 순서를 검증하는지 확인하세요. - `sendRequest` boundary 테스트가 request nonce 0을 만들지 않고 matching `responseNonce`로 정상 완료되는지 확인하세요. ## 검증 결과 _구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ ### API-1 중간 검증 ``` $ rg -n "nonce Overflow|2,147,483,647|INT32|int32 max|responseNonce == 0|PacketBase.nonce" PROTOCOL.md VERSIONING.md PROTOCOL.md:131:- Maximum nonce is `2,147,483,647` (`int32` max), the lowest common signed integer max across supported protocol fields PROTOCOL.md:132:- After issuing `2,147,483,647`, implementations reset the internal counter to `0`; the next emitted packet nonce is `1` PROTOCOL.md:133:- `0` is reserved and must not be emitted as `PacketBase.nonce`, because `responseNonce == 0` means "not a response" PROTOCOL.md:159:- `responseNonce == 0`: regular message or outgoing request → routed to `addListener` or `addRequestListener` VERSIONING.md:53:## nonce Overflow VERSIONING.md:56:The maximum emitted nonce is `2,147,483,647` (`int32` max). VERSIONING.md:61:- After issuing `2,147,483,647`, implementations reset the internal counter to `0`; the next emitted `PacketBase.nonce` is `1`. VERSIONING.md:62:- `0` is reserved and must not be emitted as `PacketBase.nonce`, because `responseNonce == 0` means "not a response". VERSIONING.md:64:Changing this wrap behavior or the reserved meaning of `responseNonce == 0` changes `nonce` semantics and requires compatibility review and cross-language boundary tests. ``` ### API-2 중간 검증 ``` $ rg -n "\+\+nonce|incrementAndGet|Add\(1\)|nonce \+= 1" dart/lib go kotlin/src/main python/toki_socket typescript/src -g '!**/build/**' typescript/src/communicator.ts:116: this.nonce += 1; dart/lib/src/communicator.dart:31: _nonce += 1; python/toki_socket/communicator.py:67: self._nonce += 1 go/test/tcp_test.go:182: wg.Add(1) go/test/ws_test.go:88: wg.Add(1) go/crosstest/kotlin_go_client/main.go:170: wg.Add(1) go/crosstest/dart_go_client/main.go:217: wg.Add(1) go/crosstest/python_go_client/main.go:168: wg.Add(1) go/crosstest/typescript_go_client/main.go:170: wg.Add(1) 위 `nonce += 1` 결과는 각 언어의 `nextNonce`/`next_nonce` 내부 발급 구현이다. `wg.Add(1)` 결과는 Go wait group 증가 false positive이며 nonce 발급이 아니다. ``` ### API-3 중간 검증 ``` $ dart test test/communicator_test.dart 00:00 +8: All tests passed! $ go test ./... /usr/bin/bash: line 1: go: command not found $ cd kotlin && ./gradlew test --tests '*CommunicatorTest*' ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. $ cd python && python3 -m pytest test/test_communicator.py -q ....... [100%] 7 passed in 0.20s $ cd typescript && npm test -- --run test/communicator.test.ts ✓ test/communicator.test.ts (10 tests) 21ms Test Files 1 passed (1) Tests 10 passed (10) ``` ### 최종 검증 ``` $ tools/check_proto_sync.sh Proto schemas are in sync. $ cd dart && dart test 00:15 +45: All tests passed! $ cd go && go test ./... /usr/bin/bash: line 1: go: command not found $ cd kotlin && ./gradlew test ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. $ cd python && python3 -m pytest test/ -q ............... [100%] 15 passed in 0.51s $ cd typescript && npm test -- --run ✓ test/base_client.test.ts (7 tests) 14ms ✓ test/communicator.test.ts (10 tests) 22ms ✓ test/tcp.test.ts (3 tests) 40ms ✓ test/ws.test.ts (3 tests) 31ms Test Files 4 passed (4) Tests 23 passed (23) ```