proto-socket/agent-task/nonce_overflow_policy/code_review_0.log
toki 9cc1f1d58f sync: update communicator implementation across all languages
- Align protocol documentation (PROTOCOL.md, README.md, VERSIONING.md)
- Go: add nonce test, update communicator
- Kotlin: update Communicator, TcpClient, TcpServer, add TLS test
- Python: update all modules, add certificate test resources
- TypeScript: update communicator, tcp/ws clients and servers, add tests
- Dart: update communicator, heartbeat mixin, and tests
2026-04-26 05:31:56 +09:00

132 lines
6.4 KiB
Text

<!-- task=nonce_overflow_policy plan=0 tag=API -->
# 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)
```