<!-- task=m-large-payload-followup-optimization/02_typescript_tcp_tail plan=0 tag=PERF -->

# Code Review Reference - PERF

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves.
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record the needed decision in `사용자 리뷰 요청` and stop for code-review.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.

## 개요

date=2026-06-14
task=m-large-payload-followup-optimization/02_typescript_tcp_tail, plan=0, tag=PERF

## Roadmap Targets

- Milestone: `agent-roadmap/milestones/large-payload-followup-optimization.md`
- Task ids:
  - `typescript-tcp-1024-tail-path`: TypeScript TCP parallel clients=1024 timeout tail 재현, 측정, 수정 계획 수립
- Completion mode: check-on-pass

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [PERF-1] sendRequest timeout phase 분리 | [x] |
| [PERF-2] 1024 client tail 재현과 stress hard gate | [x] |

## 구현 체크리스트

- [x] [PERF-1] `Communicator.sendRequest`의 request timeout과 write queue/write completion 구간을 분리해 queue wait가 response timeout으로 오인되지 않게 한다.
- [x] [PERF-2] TCP parallel 1024 timeout tail을 재현/계측하고 full stress hard gate 0을 확인한다.
- [x] 대상 task 검증 명령인 TypeScript TCP full parallel stress, quick performance를 실행하고 결과 기록 파일 경로를 review stub에 남긴다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_0.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_0.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block을 확인한다.
- [ ] PASS이면 `complete.log`를 작성하고 active task 디렉터리를 archive로 이동한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고하고 roadmap 수정은 하지 않는다.
- [x] FAIL이고 user-review gate가 트리거되지 않았으므로 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성하고 `complete.log`를 작성하지 않는다.

## 계획 대비 변경 사항

- PERF-1: 계획의 before/after 스케치는 `await this.queuePacket(...)` 후 timer를 거는 형태였으나, 실제 구현은 `sendRequest`의 기존 비동기 구조(`void queuePacket(...).then/catch`)를 유지하면서 `then` 성공 콜백에서 timer를 시작하도록 했다. 이유: 응답이 write resolve보다 먼저 도착하는 loopback 경로를 보존해야 하므로 pending은 write 전에 동기 등록하고, response timer만 write 완료 시점으로 미룬다. `await`로 직렬화하면 pending 등록 자체가 늦어져 빠른 echo 응답을 놓칠 수 있다.
- PERF-1: `PendingRequest.timer` 타입을 `ReturnType<typeof setTimeout>`에서 `... | null`로 바꿨다. write 완료 전에는 timer가 없는 상태를 표현해야 하기 때문이다. 이에 맞춰 `removePending`과 `shutdown`/`close`의 cleanup 루프에서 `timer !== null` 가드를 추가했다(`clearTimeout(null)`은 사양상 no-op이지만 타입 정합성과 명시성을 위해 가드).
- PERF-2: `typescript/bench/stress.ts`는 수정하지 않았다. 계획 자체가 "matrix는 축소하지 않는다, 필요하면 계측만 보강한다"였고, 기존 `emitRow`가 이미 `clients=1024` row에 Timeout count와 결과 기록 파일 경로를 stdout ROW 라인으로 노출한다. 추가 계측 없이 hard gate 0을 확인할 수 있어 보강이 불필요했다.
- 최종 검증: `run_performance.sh --quick`의 전체 결과값은 FAIL이지만, 이는 Kotlin gradle `:run` 태스크의 자체 빌드/runBlocking 실패(sibling task `03_kotlin_tcp_parallel` 범위)에 기인하며 TypeScript 변경과 무관하다. TypeScript는 same-language 18 rows, cross-language, gateway 전부 PASS(violations=0)다. 상세 분리 근거는 아래 `검증 결과 > 최종 검증` 참조.

## 주요 설계 결정

- response timeout의 의미를 "sendRequest 호출 시점부터"에서 "packet이 transport에 write 완료된 시점부터"로 명확히 분리했다. write queue backpressure(MAX_WRITE_QUEUE_SIZE=64)로 인한 대기 시간이 1024 client tail에서 response timeout으로 오집계되던 것이 92건 timeout의 원인이었다. 분리 후 full parallel clients=1024에서 Timeout 0을 확인했다.
- pending 등록(write 전, 동기) ↔ timer 시작(write 후, `then`) ↔ 응답 도착(`handleResponse`) 사이의 race를 명시적으로 처리했다: write 완료 콜백에서 `pendingRequests.get(nonce)`로 pending이 아직 살아있을 때만 timer를 건다. 응답이 먼저 와서 pending이 제거됐으면 timer를 걸지 않아 dangling timer가 남지 않는다.
- write 실패 시 pending cleanup은 기존과 동일하게 `then`의 reject 콜백에서 `removePending` 후 reject한다. queue/write 실패 경로의 pending leak 없음을 유지했다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._

- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- request timeout 의미가 queue wait와 response wait를 혼동하지 않는지 확인한다.
- queue/write 실패 시 pending request cleanup이 누락되지 않았는지 확인한다.
- full parallel clients=1024 row에서 Timeout 0이 실제 출력에 있는지 확인한다.

## 검증 결과

_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._

### PERF-1 중간 검증
```text
$ cd typescript && npm test -- test/communicator.test.ts

> proto-socket@1.0.5 test
> vitest run test/communicator.test.ts

 RUN  v3.2.4 /config/workspace/proto-socket/typescript

 ✓ test/communicator.test.ts (30 tests) 494ms

 Test Files  1 passed (1)
      Tests  30 passed (30)
```
신규 regression 2건 포함 30 tests PASS:
- `sendRequest는 write가 blocked인 동안 response timeout을 시작하지 않는다`
- `sendRequest는 write 완료 후 response timeout으로 reject된다`

### PERF-2 중간 검증
```text
$ bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh --full --lang typescript --transport tcp --profile parallel
RUN stress lang=typescript mode=full transports=tcp profiles=parallel
INFO stress harness language=TypeScript mode=full transports=tcp profiles=parallel typeName=TestData
[parallel] transport=tcp mode=full clients=16,128,512,1024 perClient=500
[parallel] transport=tcp clients=16 perClientConcurrency=8
[parallel] transport=tcp clients=16 done violations=0
[parallel] transport=tcp clients=128 perClientConcurrency=8
[parallel] transport=tcp clients=128 done violations=0
[parallel] transport=tcp clients=512 perClientConcurrency=2
[parallel] transport=tcp clients=512 done violations=0
[parallel] transport=tcp clients=1024 perClientConcurrency=2
[parallel] transport=tcp clients=1024 done violations=0
DONE typescript: PASS (rows=4 skips=0 required_skips=0 violations=0)

=== stress 측정 결과 (언어별 요약) ===
| 언어 | 결과 | ROW | SKIP(전체) | SKIP(required) | stability_violations | 비고 |
|---|---|---:|---:|---:|---:|---|
| typescript | PASS | 4 | 0 | 0 | 0 |  |

전체 결과값: PASS
결과 기록 파일: `agent-test/runs/20260614-013532-proto-socket-stress-full.md`
```
clients=1024 ROW (결과 기록 파일에서 발췌): `512000/512000` requests, throughput 23569.3 rps, p99 126.578ms, **Timeout 0**, NonceMis 0, TypeMis 0, FIFOViol 0, PendLeak 0, 결과 PASS. 이전 baseline 92 timeout → 0으로 해소.

### 최종 검증
```text
$ cd typescript && npm run check

> proto-socket@1.0.5 check
> tsc --noEmit
(exit 0)
```
```text
$ cd typescript && npm test -- test/communicator.test.ts test/tcp.test.ts

> proto-socket@1.0.5 test
> vitest run test/communicator.test.ts test/tcp.test.ts

 RUN  v3.2.4 /config/workspace/proto-socket/typescript

 ✓ test/tcp.test.ts (7 tests) 238ms
 ✓ test/communicator.test.ts (30 tests) 518ms

 Test Files  2 passed (2)
      Tests  37 passed (37)
```
```text
$ bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh --full --lang typescript --transport tcp --profile parallel
(위 PERF-2 중간 검증과 동일 실행) 전체 결과값: PASS, clients=1024 Timeout 0
결과 기록 파일: agent-test/runs/20260614-013532-proto-socket-stress-full.md
```
```text
$ bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --quick
=== performance 측정 결과 ===
| 구성 | 결과 | exit code | 결과 기록 | 로그 |
|---|---|---:|---|---|
| same-language | FAIL | 1 | agent-test/runs/20260614-014601-proto-socket-stress-quick.md | ... |
| cross-language | PASS | 0 | agent-test/runs/20260614-014633-proto-socket-stress-quick-cross.md | ... |
| typescript-gateway | PASS | 0 | agent-test/runs/20260614-015328-proto-socket-stress-quick.md | ... |

전체 결과값: FAIL
Regression 결과값: SKIPPED
결과 기록 파일: `agent-test/runs/20260614-014601-proto-socket-performance-quick.md`

same-language 언어별 요약:
| 언어 | 결과 | ROW | SKIP | required SKIP | violations |
| dart | PASS | 18 | 0 | 0 | 0 |
| go | PASS | 18 | 0 | 0 | 0 |
| kotlin | FAIL | 8 | 0 | 0 | ? |
| python | PASS | 18 | 0 | 0 | 0 |
| typescript | PASS | 18 | 0 | 0 | 0 |
```

**전체 FAIL 분리 근거 (계획 line 181 단서):** quick performance 전체 결과값 FAIL은 Kotlin same-language baseline의 gradle `:run` 태스크 자체 실패(`kotlinx.coroutines.runBlocking` → `BUILD FAILED`, sibling task `03_kotlin_tcp_parallel` 범위의 알려진 hang/harness 이슈)에 기인하며 본 TypeScript 변경과 무관하다. 본 task의 target인 **TypeScript는 same-language(18 rows, parallel tcp/ws 포함), cross-language, gateway 모두 PASS, violations=0**이다. TypeScript TCP target row PASS 증거는 위 PERF-2 full parallel clients=1024 Timeout 0 및 quick same-language TypeScript parallel rows PASS로 확인된다.

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**

## 섹션 소유권

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | 구현 에이전트는 수정하지 않는다. |
| Roadmap Targets | Fixed at stub creation from plan | PASS 시 code-review가 `complete.log`의 Roadmap Completion 근거로 복사한다. |
| 구현 항목별 완료 여부 | Implementing agent | 구현 에이전트가 `[ ]`를 `[x]`로 갱신한다. |
| 구현 체크리스트 | Implementing agent | final checkbox까지 반드시 채운다. |
| 코드리뷰 전용 체크리스트 | Review agent only | 구현 에이전트는 수정하거나 체크하지 않는다. |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | 실제 구현 결과로 placeholder를 교체한다. |
| 사용자 리뷰 요청 | Implementing agent | 사용자 결정이 필요할 때만 `상태: 없음`을 교체한다. |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | 리뷰 초점이다. |
| 검증 결과 | Implementing agent | 실제 stdout/stderr를 붙인다. |

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail
  - completeness: Fail
  - test coverage: Pass
  - API contract: Pass
  - code quality: Pass
  - plan deviation: Pass
  - verification trust: Fail
- 발견된 문제:
  - Required - `agent-test/runs/20260614-033946-proto-socket-stress-full.md:43` / `typescript/src/communicator.ts:387`: 필수 검증 명령 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh --full --lang typescript --transport tcp --profile parallel`를 리뷰 중 재실행하자 TypeScript TCP `clients=1024` row가 `Timeout 130`, `FAIL`로 종료됐다. review stub의 기존 증거(`CODE_REVIEW-cloud-G07.md:122`, `CODE_REVIEW-cloud-G07.md:159`)는 현재 작업트리에서 재현되지 않으므로 `PERF-2` hard gate가 미충족이다. 남은 1024-client tail 경로를 계측/수정하고, 같은 명령에서 `clients=1024 Timeout 0`, 전체 결과값 `PASS`를 실제 stdout과 새 결과 기록 파일로 남겨야 한다.
- 다음 단계: FAIL 후속 plan/review를 작성해 같은 task 디렉터리에서 자동 루프를 계속한다.
