<!-- task=m-inbound-queue-ordering/02+01_receive_thread_model plan=0 tag=REFACTOR -->

# 언어별 수신 Coordinator와 Thread Model 계획

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

이 계획은 구현 에이전트가 읽고 실행한다. 구현 후 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 변경 내용, 검증 명령, stdout/stderr를 채운 뒤 active 파일을 그대로 두고 리뷰를 요청한다. 사용자 결정, 사용자 소유 환경, scope 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션을 채우고 중단한다. `complete.log` 작성, archive 이동, 최종 판정은 code-review 전용이다.

## 배경

현재 지원 언어들은 송신 write queue는 갖고 있지만 수신 request handler를 별도 task/goroutine으로 분기하거나, read loop에서 곧장 dispatch한다. 이 때문에 느린 request handler가 뒤 요청보다 늦게 응답할 수 있고, pending map과 listener dispatch의 상태 소유 경계가 언어마다 다르다.

## 사용자 리뷰 요청 흐름

구현 중 user-only blocker가 있으면 active `CODE_REVIEW-cloud-G08.md`의 `사용자 리뷰 요청` 섹션에 결정 필요 항목과 실행한 명령 출력을 기록한다. code-review가 정당성을 검토하고 실제 `USER_REVIEW.md` 작성 여부를 판단한다.

## Roadmap Targets

- Milestone: `agent-roadmap/milestones/inbound-queue-ordering.md`
- Task ids:
  - `thread-model`: handlers, request handlers, pending response map 접근의 thread-safety 기준을 언어별 구현 메모에 반영한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/milestones/inbound-queue-ordering.md`
- `agent-test/local/rules.md`
- `agent-test/local/proto-socket-full-matrix.md`
- `PORTING_GUIDE.md`
- `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/test/communicator_test.dart`
- `dart/test/socket_test.dart`
- `go/communicator.go`
- `go/tcp_client.go`
- `go/ws_client.go`
- `go/test/communicator_test.go`
- `go/test/tcp_test.go`
- `go/test/ws_test.go`
- `kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt`
- `kotlin/src/main/kotlin/com/tokilabs/proto_socket/TcpClient.kt`
- `kotlin/src/main/kotlin/com/tokilabs/proto_socket/WsClient.kt`
- `kotlin/src/test/kotlin/com/tokilabs/proto_socket/CommunicatorTest.kt`
- `kotlin/src/test/kotlin/com/tokilabs/proto_socket/TcpTest.kt`
- `kotlin/src/test/kotlin/com/tokilabs/proto_socket/WsTest.kt`
- `python/proto_socket/communicator.py`
- `python/proto_socket/tcp_client.py`
- `python/proto_socket/ws_client.py`
- `python/test/test_communicator.py`
- `python/test/test_tcp.py`
- `python/test/test_ws.py`
- `typescript/src/communicator.ts`
- `typescript/src/tcp_client.ts`
- `typescript/src/node_ws_client.ts`
- `typescript/test/communicator.test.ts`
- `typescript/test/tcp.test.ts`
- `typescript/test/ws.test.ts`
- package manifests: `dart/pubspec.yaml`, `go/go.mod`, `kotlin/build.gradle.kts`, `python/pyproject.toml`, `typescript/package.json`

### 테스트 환경 규칙

- test_env: `local`
- 적용 규칙: `agent-test/local/rules.md`, `agent-test/local/proto-socket-full-matrix.md`
- protocol/API 영향과 다섯 언어 concurrency 변경이 있으므로 최종 검증은 full matrix다.
- 보조 검증으로 언어별 단위 테스트를 중간 검증에 둔다. 최종 PASS 판단은 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all`이다.

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

- 일반 수신 순서: Dart TCP에는 `여러 메시지를 순서대로 수신한다`가 있으나 WS와 나머지 언어에는 동일한 느린 handler/order 테스트가 없다.
- request handler 순서: 모든 언어의 기존 concurrent request 테스트는 값 일치만 확인하고, 느린 앞 요청의 자동 응답이 뒤 요청보다 먼저 enqueue되는지 확인하지 않는다.
- inbound queue 포화/close: 송신 queue 테스트는 TypeScript에 있으나 inbound queue capacity, drain/cancel 테스트는 없다.

### 심볼 참조

- rename/remove 없음.
- 수신 진입점 참조:
  - Dart: `onReceivedData` 호출은 `protobuf_client.dart`, `ws_protobuf_client_io.dart`, `ws_protobuf_client_web.dart`.
  - Go: `OnReceivedData` 호출은 `tcp_client.go`, `ws_client.go`.
  - Kotlin: `onReceivedData` 호출은 `TcpClient.kt`, `WsClient.kt`.
  - Python: `on_received_data` 호출은 `tcp_client.py`, `ws_client.py`.
  - TypeScript: `onReceivedData` 호출은 `tcp_client.ts`, `node_ws_client.ts`, `browser_ws_client.ts`.

### 분할 판단

- 이 subtask는 `01_contract_docs`의 완료에 의존한다.
- 언어별 구현을 더 쪼갤 수 있지만 `thread-model` roadmap Task의 완료 조건은 다섯 언어의 공유 상태 소유와 검증을 한 번에 맞추는 것이다. 언어별 체크리스트를 내부 단위로 나누고, review는 cross-language consistency를 한 번에 확인한다.
- `03+02_gateway_contract`는 이 subtask의 완료 뒤 gateway가 receive coordinator 경계를 침범하지 않도록 문서/테스트 anchor를 추가한다.

### 범위 결정 근거

- wire format, proto schema, nonce/responseNonce 의미는 변경하지 않는다.
- worker gateway 병렬 decode 구현은 하지 않는다.
- public API 이름 변경은 하지 않는다.
- 외부 dependency 추가는 하지 않는다. 현재 manifest의 standard/runtime API와 기존 protobuf/websocket dependency만 사용한다.

### 빌드 등급

- build: `cloud-G08`, review: `cloud-G08`
- 이유: 다섯 언어 수신 경로, concurrency/order semantics, close/cancel 동작을 동시에 건드린다.

## 구현 체크리스트

- [ ] Dart에 bounded inbound queue와 단일 receive worker를 추가하고, request handler 자동 응답이 FIFO를 보존하도록 직렬화한다.
- [ ] Go에 inbound queue channel과 receive worker를 추가하고 `go reqHandler(...)` 분기를 coordinator 뒤의 순차 처리로 바꾼다.
- [ ] Kotlin에 inbound `Channel`과 receive coroutine을 추가하고 `scope.launch { reqHandler(...) }` 분기를 FIFO 처리로 바꾼다.
- [ ] Python에 `asyncio.Queue` inbound queue와 receive task를 추가하고 `asyncio.create_task(_run_request_handler(...))`가 request 순서를 추월하지 않게 한다.
- [ ] TypeScript에 inbound queue와 async receive worker를 추가하고 `void runRequestHandler(...)`가 request 순서를 추월하지 않게 한다.
- [ ] 각 언어 테스트에 일반 수신 순서, 느린 request handler 자동 응답 순서, close 시 queued/pending 취소 검증을 추가한다.
- [ ] `PORTING_GUIDE.md`에 언어별 공유 상태 소유 메모를 업데이트한다.
- [ ] 최종 검증으로 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all`을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REFACTOR-1] Receive Coordinator Rollout

#### 문제

- Dart read loop는 frame parse 직후 `onReceivedData`를 호출하고, request handler Future를 await하지 않는다. `dart/lib/src/protobuf_client.dart:95`와 `dart/lib/src/communicator.dart:204`.
- Go는 request handler를 별도 goroutine으로 실행한다. `go/communicator.go:266-272`.
- Kotlin은 request handler를 별도 coroutine으로 실행한다. `kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt:233-236`.
- Python은 request handler를 `asyncio.create_task`로 분기한다. `python/proto_socket/communicator.py:212-218`.
- TypeScript는 request handler Promise를 fire-and-forget으로 실행한다. `typescript/src/communicator.ts:267-275`.

#### 해결 방법

각 connection에 `InboundItem` queue와 receive worker를 둔다. read loop는 `PacketBase` decode 뒤 queue에 넣고 heartbeat reset을 보존한다. worker만 pending response map, listener dispatch, request handler 실행, 자동 response enqueue를 수행한다.

Before:

```go
go/communicator.go:266
if reqHandler != nil {
    msg, err := c.parse(typeName, data)
    if err != nil {
        return
    }
    go reqHandler(msg, incomingNonce)
    return
}
```

After:

```go
// readLoop
c.EnqueueInbound(base)

// receiveLoop
if reqHandler != nil {
    msg, err := c.parse(typeName, data)
    if err == nil {
        reqHandler(msg, incomingNonce)
    }
    return
}
```

같은 구조를 Dart `Future` chain/`StreamController` 또는 async queue, Kotlin `Channel`, Python `asyncio.Queue`, TypeScript array+waiter 또는 small async queue로 구현한다. queue capacity 기본값은 64로 맞추고, 포화 시 read loop가 enqueue를 기다려 transport backpressure가 걸리게 한다.

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

- [ ] `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`
- [ ] `go/communicator.go`, `go/tcp_client.go`, `go/ws_client.go`
- [ ] `kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt`, `TcpClient.kt`, `WsClient.kt`
- [ ] `python/proto_socket/communicator.py`, `tcp_client.py`, `ws_client.py`
- [ ] `typescript/src/communicator.ts`, `tcp_client.ts`, `node_ws_client.ts`, `browser_ws_client.ts`
- [ ] `PORTING_GUIDE.md`

#### 테스트 작성

테스트를 작성한다.

- Dart: `dart/test/communicator_test.dart`와 `dart/test/socket_test.dart`에 inbound queue/order, slow request handler, close cancellation 테스트.
- Go: `go/test/communicator_test.go`, `go/test/tcp_test.go`, `go/test/ws_test.go`에 같은 시나리오.
- Kotlin: `CommunicatorTest.kt`, `TcpTest.kt`, `WsTest.kt`에 같은 시나리오.
- Python: `test_communicator.py`, `test_tcp.py`, `test_ws.py`에 같은 시나리오.
- TypeScript: `communicator.test.ts`, `tcp.test.ts`, `ws.test.ts`에 같은 시나리오.

#### 중간 검증

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

기대 결과: 모든 명령 exit code 0. Go test cache는 허용하지 않으려면 구현 에이전트가 `go test -count=1 ./...`로 대체하고 사유를 `계획 대비 변경 사항`에 기록한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `dart/lib/src/communicator.dart` 외 Dart 수신 client/test | REFACTOR-1 |
| `go/communicator.go` 외 Go 수신 client/test | REFACTOR-1 |
| `kotlin/src/main/kotlin/com/tokilabs/proto_socket/Communicator.kt` 외 Kotlin 수신 client/test | REFACTOR-1 |
| `python/proto_socket/communicator.py` 외 Python 수신 client/test | REFACTOR-1 |
| `typescript/src/communicator.ts` 외 TypeScript 수신 client/test | REFACTOR-1 |
| `PORTING_GUIDE.md` | REFACTOR-1 |

## 최종 검증

```bash
bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all
```

기대 결과: exit code 0, matrix 결과 파일의 최종 결과값 PASS.

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