 <!-- task=m-performance-hotspot-optimization/13_kotlin_ws_diagnostics plan=4 tag=REVIEW_REVIEW_REVIEW_REVIEW_TEST -->

# Plan - REVIEW_REVIEW_REVIEW_REVIEW_TEST

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

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수입니다. 구현 후 active 파일은 그대로 두고 리뷰 준비를 보고하세요. 사용자만 결정할 수 있는 범위 변경, 외부 환경, secret, 사용자 소유 장비가 막으면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 기록하고 멈추세요. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 마세요. `USER_REVIEW.md`, 로그 아카이브, `complete.log` 작성은 code-review 전용입니다. 명령 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아닙니다.

## 배경

G10 구현 기록은 clean WSS 단독 test PASS를 기록했지만, review 재실행에서 같은 명령이 실패했다.

```text
cd kotlin && ./gradlew cleanTest test --tests "com.tokilabs.proto_socket.TlsWsTest"
TlsWsTest > testWssRequestResponse() FAILED
java.io.IOException: websocket open timed out
2 tests completed, 1 failed
BUILD FAILED in 1m 25s
```

실패 XML은 `testWssSendReceive()`가 21.361초로 통과한 뒤 `testWssRequestResponse()`가 61.959초 후 [TlsWsTest.kt](/config/workspace/proto-socket/kotlin/src/test/kotlin/com/tokilabs/proto_socket/TlsWsTest.kt:89)의 `dialWssWithRetry`에서 실패했음을 보여준다. 따라서 full-suite 전역 문제뿐 아니라 WSS class 내부 첫 test cleanup/두 번째 test readiness도 직접 검증해야 한다.

G10 변경은 [TlsWsTest.kt](/config/workspace/proto-socket/kotlin/src/test/kotlin/com/tokilabs/proto_socket/TlsWsTest.kt:98)의 cleanup에서 기존 `server.clients().isEmpty()` wait를 제거하고 `server.stop()` 뒤 고정 `delay(1000)`에 의존한다. 이 후속은 WSS timeout을 숨기는 sleep이 아니라, close/server stop/open callback evidence로 실제 원인을 닫아야 한다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active review stub의 `사용자 리뷰 요청` 섹션에 기록합니다. 직접 사용자 프롬프트는 금지이며, code-review가 요청 타당성을 검증하고 실제 `USER_REVIEW.md` 작성을 소유합니다.

## 분석 결과

### 읽은 파일

- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-task/m-performance-hotspot-optimization/13_kotlin_ws_diagnostics/plan_local_G10_3.log`
- `agent-task/m-performance-hotspot-optimization/13_kotlin_ws_diagnostics/code_review_local_G10_3.log`
- `agent-test/local/rules.md`
- `agent-test/local/kotlin-smoke.md`
- `agent-test/local/proto-socket-performance-baseline.md`
- `agent-ops/rules/project/domain/kotlin/rules.md`
- `kotlin/build.gradle.kts`
- `kotlin/src/main/kotlin/com/tokilabs/proto_socket/WsClient.kt`
- `kotlin/src/main/kotlin/com/tokilabs/proto_socket/WsServer.kt`
- `kotlin/src/test/kotlin/com/tokilabs/proto_socket/TlsWsTest.kt`
- `kotlin/src/test/kotlin/com/tokilabs/proto_socket/HeartbeatTest.kt`
- `kotlin/src/test/kotlin/com/tokilabs/proto_socket/TestHelpers.kt`
- `kotlin/build/test-results/test/TEST-com.tokilabs.proto_socket.TlsWsTest.xml`

### 테스트 환경 규칙

`test_env=local`을 사용한다. Kotlin 변경이므로 `agent-test/local/kotlin-smoke.md`의 `cd kotlin && ./gradlew test`가 필수다. 이번 후속은 verification trust 회복이 핵심이므로, 실패 재현과 수정 후 fresh rerun의 실제 stdout/stderr를 모두 기록한다.

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

기존 `TlsWsTest.testWssSendReceive`와 `testWssRequestResponse`가 WSS open/handshake/traffic regression test다. 새 테스트는 cleanup/readiness를 작고 결정적으로 증명할 때만 추가한다. 단순 `delay` 증가나 retry 횟수 증가만으로는 완료 처리하지 않는다.

### 심볼 참조

`getMetricsDiagnostics`는 `internal` 상태를 유지해야 한다. `compileCrosstestKotlin`이 이 internal call을 합법적으로 컴파일하는지 최종 검증에 포함한다.

### 범위 결정 근거

수정 범위는 WSS test ordering failure, WSS open timeout cleanup, server readiness/stop lifecycle, 그리고 그 원인으로 입증된 test client cleanup에 한정한다. Kotlin WS 성능 최적화, TCP behavior, protocol schema, roadmap 갱신은 제외한다. G10의 `HeartbeatTest`와 `freePort` 변경은 실제 실패 원인과 연결되는 경우만 유지하고, 불필요하면 되돌리거나 축소한다.

### 빌드 등급

`cloud-G07`: local-G10에서 동일 WSS gate와 verification trust failure가 재발했다. 실패는 로컬 명령으로 재현 가능하지만, 반복된 PASS 기록 불일치와 test lifecycle 상호작용 진단이 필요하므로 cloud lane으로 올린다. 이번 review-number는 4였고 loop-limit `USER_REVIEW` 기준은 아직 충족하지 않았다.

## 구현 체크리스트

- [ ] `cleanTest` 기반 WSS class 내부 실패를 재현하고, 실패 XML/순서/소요 시간까지 실제 stdout/stderr로 기록한다.
- [ ] WSS 첫 test cleanup과 두 번째 test startup/open readiness를 deterministic evidence로 분리하고, 고정 sleep이나 단순 retry 증가 없이 repo-owned 원인을 수정한다.
- [ ] `WsClient` timeout/failure/close cleanup, `WsServer` stop/onClose lifecycle, `TlsWsTest` cleanup/readiness, 관련 test helper 변경을 최소 범위로 정리한다.
- [ ] fresh rerun으로 WSS 단독, Heartbeat+Tls 조합, clean full Gradle test, 일반 Gradle test, crosstest compile, focused stress, `getMetricsDiagnostics` internal boundary를 모두 재확인한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_REVIEW_REVIEW_TEST-1] Fix WSS Class Ordering Timeout

문제: G10 review 재실행에서 clean WSS 단독 test가 실패했다. 같은 class 안에서 첫 test는 통과하고 두 번째 `testWssRequestResponse`가 세 번의 20초 open timeout 후 실패하므로, 이전 WSS server/client cleanup 또는 다음 server readiness가 결정적으로 닫히지 않았다.

해결 방법:

- `cd kotlin && ./gradlew cleanTest test --tests "com.tokilabs.proto_socket.TlsWsTest"`로 실패를 재현하고, XML에서 두 test 이름, 각 소요 시간, stack trace를 기록한다.
- `TlsWsTest.closeClientAndServer`는 고정 `delay(1000)`만으로 종료하지 않는다. client close, server client removal, server stop completion을 deterministic하게 기다리거나 실패를 명시적으로 드러내도록 정리한다.
- `WsClient`의 OkHttp cleanup은 timeout 전 callback 미발생, open failure, 정상 close, remote close 각각에서 idempotent해야 한다. dispatcher shutdown이 close frame delivery를 방해하지 않는지 검증한다.
- `WsServer.stop()`과 `onClose`가 server-side `WsClient`를 정리하는 순서를 확인하고, stop 중 disconnect listener가 누락되지 않게 한다.
- `HeartbeatTest` cleanup 보강 또는 `freePort` 변경처럼 WSS class failure와 간접 연결된 변경은 근거를 남긴다. 근거가 없거나 범위를 넓힌다면 되돌린다.
- 임시 계측을 추가했다면 최종 제출 전에 제거하되, 검증 결과에는 재현/수정 근거를 충분히 남긴다.

수정 파일 및 체크리스트:

- [ ] `kotlin/src/test/kotlin/com/tokilabs/proto_socket/TlsWsTest.kt`: WSS test cleanup/readiness를 deterministic하게 보장한다.
- [ ] `kotlin/src/main/kotlin/com/tokilabs/proto_socket/WsClient.kt`: timeout/failure/close cleanup을 callback 미발생 케이스까지 보장한다.
- [ ] `kotlin/src/main/kotlin/com/tokilabs/proto_socket/WsServer.kt`: stop/onClose lifecycle 문제가 확인되면 최소 수정한다.
- [ ] `kotlin/src/test/kotlin/com/tokilabs/proto_socket/HeartbeatTest.kt`: WSS failure와 연결되는 cleanup 변경만 유지한다.
- [ ] `kotlin/src/test/kotlin/com/tokilabs/proto_socket/TestHelpers.kt`: WSS/TLS 안정화와 무관한 helper 변경은 되돌리거나 이유를 기록한다.
- [ ] `kotlin/build.gradle.kts`: test JVM property가 실제로 필요할 때만 유지하고, 유지 이유를 review file에 기록한다.

테스트 작성: 기존 WSS tests를 기본 regression으로 사용한다. cleanup/readiness helper test는 필요할 때만 추가한다.

중간 검증:

```bash
cd kotlin && ./gradlew cleanTest test --tests "com.tokilabs.proto_socket.TlsWsTest"
```

```bash
cd kotlin && ./gradlew cleanTest test --tests "com.tokilabs.proto_socket.HeartbeatTest" --tests "com.tokilabs.proto_socket.TlsWsTest"
```

기대 결과: 두 명령 모두 `BUILD SUCCESSFUL`.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `kotlin/src/test/kotlin/com/tokilabs/proto_socket/TlsWsTest.kt` | REVIEW_REVIEW_REVIEW_REVIEW_TEST-1 |
| `kotlin/src/main/kotlin/com/tokilabs/proto_socket/WsClient.kt` | REVIEW_REVIEW_REVIEW_REVIEW_TEST-1 |
| `kotlin/src/main/kotlin/com/tokilabs/proto_socket/WsServer.kt` | REVIEW_REVIEW_REVIEW_REVIEW_TEST-1 if needed |
| `kotlin/src/test/kotlin/com/tokilabs/proto_socket/HeartbeatTest.kt` | REVIEW_REVIEW_REVIEW_REVIEW_TEST-1 if needed |
| `kotlin/src/test/kotlin/com/tokilabs/proto_socket/TestHelpers.kt` | REVIEW_REVIEW_REVIEW_REVIEW_TEST-1 if needed |
| `kotlin/build.gradle.kts` | REVIEW_REVIEW_REVIEW_REVIEW_TEST-1 if needed |

## 최종 검증

```bash
rg --sort path "fun getMetricsDiagnostics|getMetricsDiagnostics" kotlin
```

```bash
cd kotlin && ./gradlew cleanTest test --tests "com.tokilabs.proto_socket.TlsWsTest"
```

```bash
cd kotlin && ./gradlew cleanTest test --tests "com.tokilabs.proto_socket.HeartbeatTest" --tests "com.tokilabs.proto_socket.TlsWsTest"
```

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

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

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

```bash
bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh --quick --lang kotlin --transport ws --profile roundtrip,payload
```

기대 결과: `getMetricsDiagnostics`가 public API로 노출되지 않고, clean WSS 단독/Heartbeat+Tls 조합/clean full Gradle/일반 Gradle/crosstest compile은 `BUILD SUCCESSFUL`, focused stress는 Kotlin WS roundtrip/payload rows를 PASS로 기록한다.

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