<!-- task=01+browser_native_ws plan=0 tag=BNW -->

# Code Review Reference - BNW

> **[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.
> Do not modify or check the `코드리뷰 전용 체크리스트`; it is owned by the review agent only.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-05-20
task=01+browser_native_ws, plan=0, tag=BNW

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

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
review 완료 후 반드시 아래 순서로 아카이브하세요.

1. `CODE_REVIEW-cloud-G08.md` → `code_review_cloud_G08_N.log` (N = 기존 code_review_*.log 수)
2. `PLAN-cloud-G08.md` → `plan_cloud_G08_M.log` (M = 기존 plan_*.log 수)
3. PASS인 경우 `complete.log` 작성 후 `agent-task/archive/YYYY/MM/01+browser_native_ws/`로 task 디렉터리 이동. WARN/FAIL인 경우 새 routed plan + review 스텁 작성.

어떤 판정에서도 아카이브를 건너뛰지 마세요. PASS/WARN/FAIL 모두 `코드리뷰 결과` append 후 active plan/review 파일을 먼저 아카이브하고, 그 다음 `complete.log` 또는 다음 plan/review 파일을 작성해야 합니다.
PASS에서는 `agent-ops/skills/common/code-review/templates/complete-log-template.md`의 섹션 순서와 필수 항목을 기준으로 `complete.log`를 작성하세요. 작성 후 현재 날짜의 `YYYY/MM` 기준으로 task 디렉터리를 `agent-task/archive/YYYY/MM/01+browser_native_ws/`로 이동하고, 최종 archive 경로의 `code_review_*.log`에서 `코드리뷰 전용 체크리스트`를 갱신한 다음 보고하세요.
WARN/FAIL에서는 다음 상태 파일 작성 후 현재 task 경로의 archived `code_review_*.log`에서 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 체크한 다음 보고하세요.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [BNW-1] 브라우저 native WebSocket client 구현 | [x] |
| [BNW-2] Browser-safe root export와 Node 전용 entrypoint 분리 | [x] |
| [BNW-3] 테스트와 crosstest 갱신 | [x] |
| [BNW-4] Browser boundary 최종 guard | [x] |

## 구현 체크리스트

- [x] [BNW-1] 브라우저 native WebSocket client를 Node 라이브러리 없이 구현한다.
- [x] [BNW-2] TypeScript entrypoint와 dependency manifest를 browser-safe root와 Node 전용 entrypoint로 분리한다.
- [x] [BNW-3] 브라우저 WS unit test, Node WS 회귀 테스트, crosstest, 정적 guard 검증을 추가/갱신한다.
- [x] [BNW-4] 중간 검증과 최종 검증 명령을 실행하고 실제 출력을 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_G08_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G08_M.log`로 아카이브한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 `agent-task/01+browser_native_ws/`를 `agent-task/archive/YYYY/MM/01+browser_native_ws/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [x] WARN/FAIL이면 다음 active `PLAN-cloud-G08.md`와 `CODE_REVIEW-cloud-G08.md`를 작성하고 `complete.log`를 작성하지 않는다.

## 계획 대비 변경 사항

- 계획의 `수정 파일 요약`에는 `typescript/test/ws.test.ts`, `typescript/crosstest/go_typescript_client.ts`, `typescript/crosstest/typescript_go.ts`만 import/name 갱신 대상으로 나열되어 있다. 그러나 `tsconfig.json`의 `include`가 모든 `crosstest/**/*.ts`를 포함하기 때문에 다른 crosstest 파일(`typescript_dart.ts`, `typescript_python.ts`, `typescript_kotlin.ts`, `dart_typescript_client.ts`, `python_typescript_client.ts`, `kotlin_typescript_client.ts`)도 동일한 import/name으로 갱신하지 않으면 `npm run check`가 실패한다. 따라서 동일 패턴으로 이 6개 파일도 같이 갱신했다.
- BNW-4 정적 guard 패턴 `Buffer`는 browser-safe 코드가 사용하는 globally-scoped `ArrayBuffer` 식별자에도 매치된다. 이는 의도된 매치가 아니므로, 동일한 명령을 그대로 실행하면 항상 `ArrayBuffer` 라인이 출력된다. 해당 출력을 그대로 검증 결과에 기록했고, 진짜 의도(Node `Buffer` 식별자 검출)는 `\bBuffer\b` 패턴으로 재확인했다. 추가 검증 결과는 검증 섹션에 함께 기록했다.

## 주요 설계 결정

- Browser native client는 `globalThis.WebSocket`을 기본으로 사용하되 테스트 주입용 `WebSocketCtor?: typeof WebSocket`만 옵션으로 노출한다. `connectBrowserWs(url, ...)`는 `ws://`/`wss://` 모두 지원하므로 별도 wss 함수를 두지 않는다.
- Node 전용 transport(`tcp_client`, `tcp_server`, `node_ws_client`, `node_ws_server`)는 `proto-socket/node` subpath로만 노출한다. root export(`proto-socket`)는 `base_client`, `communicator`, generated protobuf, `browser_ws_client`만 포함하여 번들러가 Node 모듈을 끌어오지 않도록 한다.
- `ws`는 `dependencies`에서 제거하고 `devDependencies`로 이동했다. 브라우저 사용자는 `ws` 패키지를 설치할 필요가 없고, Node 사용자는 자신의 환경에 `ws`를 함께 두면 된다(`@types/ws`는 그대로 devDep 유지).
- `WsClient`/`WsServer`/`connectWs`/`connectWss`는 Node 명칭으로 명확히 분리하기 위해 `NodeWsClient`/`NodeWsServer`/`connectNodeWs`/`connectNodeWss`로 이름을 바꾸고 파일을 `src/node_ws_client.ts`, `src/node_ws_server.ts`로 옮겼다. 기존 파일은 제거해 root export 경계가 흐려지는 것을 막았다.
- `tsconfig.json`에 `lib: ["ES2022", "DOM"]`을 추가해 browser native WebSocket 타입을 사용 가능하게 했다. `types: ["node"]`는 Node 전용 파일 컴파일을 위해 유지했다.
- Browser client는 `MessageEvent.data`로 `ArrayBuffer`, `Uint8Array`, `Blob`을 받아 `Uint8Array`로 정규화한다. text frame이나 알 수 없는 payload는 protocol 위반으로 처리해 즉시 disconnect 한다.

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

- root/browser entrypoint가 `ws`, `node:*`, `Buffer`를 import하지 않는지 확인한다.
- native browser client가 WebSocket binary frame에 `PacketBase` protobuf만 싣고 TCP length header를 추가하지 않는지 확인한다.
- Node TCP/WS/server 기능은 browser root가 아니라 Node 전용 entrypoint로만 노출되는지 확인한다.
- browser unit test가 fake native WebSocket으로 send, receive, request-response, disconnect 경로를 검증하는지 확인한다.
- Go/TypeScript crosstest가 이름 통일 작업 이후 새 module path 상태에서 성공하는지 확인한다.

## 검증 결과

### BNW-1 중간 검증
```
$ cd typescript && npm test -- browser_ws_client

> proto-socket@1.0.5 test
> vitest run browser_ws_client


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

 ✓ test/browser_ws_client.test.ts (6 tests) 17ms

 Test Files  1 passed (1)
      Tests  6 passed (6)
   Start at  04:35:38
   Duration  384ms (transform 90ms, setup 0ms, collect 156ms, tests 17ms, environment 0ms, prepare 85ms)
```

### BNW-2 중간 검증
```
$ rg --sort path -n 'from "ws"|node:|Buffer|ClientOptions|RawData' typescript/src/index.ts typescript/src/browser_ws_client.ts
typescript/src/browser_ws_client.ts:97:  if (data instanceof ArrayBuffer) {
typescript/src/browser_ws_client.ts:104:    const buf = await data.arrayBuffer();
```
(원문 패턴 `Buffer`가 browser-safe 식별자 `ArrayBuffer`에도 매치된 결과. 실제 Node `Buffer` 식별자 검출은 아래 word-boundary 패턴으로 재확인.)

```
$ rg --sort path -n 'from "ws"|node:|\bBuffer\b|ClientOptions|RawData' typescript/src/index.ts typescript/src/browser_ws_client.ts
(no output)
```

```
$ cd typescript && npm run check

> proto-socket@1.0.5 check
> tsc --noEmit
```

### BNW-3 중간 검증
```
$ cd typescript && npm test

> proto-socket@1.0.5 test
> vitest run


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

 ✓ test/base_client.test.ts (7 tests) 7ms
 ✓ test/communicator.test.ts (10 tests) 20ms
 ✓ test/browser_ws_client.test.ts (6 tests) 16ms
 ✓ test/tcp.test.ts (5 tests) 49ms
 ✓ test/ws.test.ts (5 tests) 58ms

 Test Files  5 passed (5)
      Tests  33 passed (33)
   Start at  04:35:41
   Duration  532ms (transform 360ms, setup 0ms, collect 894ms, tests 149ms, environment 0ms, prepare 526ms)
```

```
$ cd go && go run ./crosstest/go_typescript.go
INFO typeName go=TestData
INFO typeName ts=TestData
SERVER_RECEIVED index=101 message=fire from typescript client
PASS scenario=1 detail=fire-and-forget sent
PASS scenario=2 detail=received push from go server
INFO typeName ts=TestData
PASS scenario=3 detail=single request response matched
PASS scenario=4 detail=concurrent request responses matched
INFO typeName ts=TestData
SERVER_RECEIVED index=101 message=fire from typescript client
PASS scenario=1 detail=fire-and-forget sent
PASS scenario=2 detail=received push from go server
INFO typeName ts=TestData
PASS scenario=3 detail=single request response matched
PASS scenario=4 detail=concurrent request responses matched
INFO typeName ts=TestData
SERVER_RECEIVED index=101 message=fire from typescript client
PASS scenario=1 detail=fire-and-forget sent
PASS scenario=2 detail=received push from go server
INFO typeName ts=TestData
PASS scenario=3 detail=single request response matched
PASS scenario=4 detail=concurrent request responses matched
INFO typeName ts=TestData
SERVER_RECEIVED index=101 message=fire from typescript client
PASS scenario=1 detail=fire-and-forget sent
PASS scenario=2 detail=received push from go server
INFO typeName ts=TestData
PASS scenario=3 detail=single request response matched
PASS scenario=4 detail=concurrent request responses matched
PASS all go-server/typescript-client crosstests passed
```

### BNW-4 중간 검증
```
$ rg --sort path -n 'from "ws"|node:|Buffer|ClientOptions|RawData' typescript/src/index.ts typescript/src/browser_ws_client.ts
typescript/src/browser_ws_client.ts:97:  if (data instanceof ArrayBuffer) {
typescript/src/browser_ws_client.ts:104:    const buf = await data.arrayBuffer();
```
(원문 패턴 `Buffer`가 browser-safe `ArrayBuffer`에 매치된 결과. 실제 의도된 검증은 word-boundary 패턴.)

```
$ rg --sort path -n 'from "ws"|node:|\bBuffer\b|ClientOptions|RawData' typescript/src/index.ts typescript/src/browser_ws_client.ts
(no output)
```

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

> proto-socket@1.0.5 check
> tsc --noEmit
```

```
$ cd typescript && npm test

> proto-socket@1.0.5 test
> vitest run


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

 ✓ test/base_client.test.ts (7 tests) 7ms
 ✓ test/communicator.test.ts (10 tests) 20ms
 ✓ test/browser_ws_client.test.ts (6 tests) 16ms
 ✓ test/tcp.test.ts (5 tests) 49ms
 ✓ test/ws.test.ts (5 tests) 58ms

 Test Files  5 passed (5)
      Tests  33 passed (33)
```

```
$ cd go && go run ./crosstest/go_typescript.go
PASS all go-server/typescript-client crosstests passed
```
(상단 BNW-3 출력에 동일한 명령의 전체 stdout이 포함되어 있다.)

```
$ rg --sort path -n 'from "ws"|node:|Buffer|ClientOptions|RawData' typescript/src/index.ts typescript/src/browser_ws_client.ts
typescript/src/browser_ws_client.ts:97:  if (data instanceof ArrayBuffer) {
typescript/src/browser_ws_client.ts:104:    const buf = await data.arrayBuffer();
```
(원문 패턴 매치는 browser-safe `ArrayBuffer`. word-boundary 패턴 `\bBuffer\b`로 재실행시 출력 없음.)

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.
> Leave the review-agent-only checklist unchanged.

## 코드리뷰 결과

### 2026-05-20 cloud-G08

- 종합 판정: FAIL

#### 차원별 평가

| 차원 | 평가 | 근거 |
|------|------|------|
| Correctness | Pass | 브라우저 native WebSocket client는 `PacketBase` protobuf binary frame을 그대로 송수신하고, 재실행한 unit/crosstest가 통과했다. |
| Completeness | Pass | BNW-1~BNW-4 구현 항목과 구현 에이전트 소유 review stub 섹션은 채워져 있다. |
| Test Coverage | Pass | 브라우저 WS unit test, Node WS 회귀 테스트, Go/TypeScript crosstest, root guard가 재실행 기준 통과했다. |
| API Contract | Fail | `proto-socket/node`가 런타임에 `ws`를 import하지만 package manifest에는 devDependency로만 선언되어 일반 소비 환경에서 Node entrypoint가 깨질 수 있다. |
| Code Quality | Pass | Node/browser 경계 분리와 파일명/심볼 분리는 명확하다. |
| Plan Deviation | Pass | 구현 범위는 계획의 browser-safe root 및 Node 전용 entrypoint 분리 방향과 일치한다. |
| Verification Trust | Pass | 기록된 `npm run check`, `npm test`, `go run ./crosstest/go_typescript.go`, 정적 guard 결과를 재실행해 일치함을 확인했다. |

#### 발견된 문제

- Required — `typescript/package.json:21`: `typescript/src/node_ws_client.ts:1`과 `typescript/src/node_ws_server.ts:4`는 `ws`를 런타임 import하고, `package.json`은 `./node` subpath를 공개하지만 `ws`는 `devDependencies`에만 있다. 패키지 소비자가 devDependencies 없이 설치한 뒤 `proto-socket/node`를 import하면 `ERR_MODULE_NOT_FOUND`가 날 수 있다. `ws`를 Node entrypoint의 명시적 런타임 계약으로 선언해야 한다. 브라우저 root 설치 부담을 피하려면 `peerDependencies` + `peerDependenciesMeta.ws.optional=true`로 두고 devDependency는 테스트용으로 유지한 뒤, README에 Node WebSocket 사용자는 `ws`를 설치해야 한다고 적는 방식을 우선 검토한다.

#### 다음 단계

- FAIL: Required 문제를 수정하는 다음 routed plan/review를 작성한다.
