<!-- task=m-inbound-queue-ordering/08_typescript_gateway plan=0 tag=API -->

# Code Review Reference - API

> **[IMPLEMENTING AGENT — READ FIRST]** 구현 후 이 파일을 채우고 active 파일을 그대로 둔 채 리뷰를 요청한다. 최종화는 code-review 전용이다.

## 개요

date=2026-06-02
task=m-inbound-queue-ordering/08_typescript_gateway, plan=0, tag=API

## Roadmap Targets

- Milestone: `agent-roadmap/milestones/inbound-queue-ordering.md`
- Task ids:
  - `typescript-gateway`: Node/browser worker gateway 후보를 적용한다.
- Completion mode: check-on-pass

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [API-1] TypeScript worker gateway | [x] |

## 구현 체크리스트

- [x] TypeScript gateway input/output에 내부 `seq`를 포함하고 Node worker_threads 후보를 추가한다.
- [x] browser target은 Web Worker 또는 safe fallback을 사용하며 unsupported 환경에서 기존 coordinator path를 유지한다.
- [x] transferable `ArrayBuffer` 사용 여부를 검토하고 복사 비용을 줄인다.
- [x] worker result를 `seq` 기준으로 reorder한 뒤 기존 `enqueueInbound`/coordinator path로 전달한다.
- [x] TypeScript tests에 Node gateway on, fallback, out-of-order completion 검증을 추가한다.
- [x] 중간 검증으로 `cd typescript && npm run check && npm test -- communicator`를 실행한다.
- [x] 최종 검증으로 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all`을 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

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

- [x] verdict append, log archive, PASS complete.log/archive move를 수행한다. FAIL 판정이므로 PASS complete.log/archive move 대신 후속 active plan/review를 작성했다.

## 계획 대비 변경 사항

- gateway 계약(`InboundFrame`/`DecodedFrame`/`DecodedEnvelope`/`InboundGateway`/`FrameReorderBuffer`)과 worker-pool gateway를 신규 파일 `typescript/src/inbound_gateway.ts`(browser-safe)로 분리했다. Go의 `inbound_gateway.go`, Kotlin의 동일 계약과 심볼/주석을 맞췄다.
- Node `worker_threads` 후보는 browser bundle을 깨지 않도록 별도 파일 `typescript/src/node_inbound_gateway.ts` + worker entry `typescript/src/node_inbound_gateway_worker.ts`에 두고, `node:` import가 browser-safe `index.ts`로 새지 않게 `src/node.ts`에서만 re-export 했다. plan의 "gateway 전용 파일(필요 시)" 항목을 사용한 것이며, browser-safe 표면(`index.ts`)에는 worker_threads import가 들어가지 않는다.
- plan 수정 파일 요약대로 transport(`tcp_client.ts`/`node_ws_client.ts`)는 건드리지 않았다. gateway는 opt-in이며 `Communicator.onReceivedFrame`/`enableInboundGateway`/`attachInboundGateway`로만 활성화된다(Go/Kotlin의 opt-in 패턴과 동일). 기존 transport 경로는 inline `enqueueInbound`를 유지한다.
- `npm run check` 통과를 위해 worker의 transfer 대상 buffer를 `ArrayBuffer` instance로 narrowing 했다(`ArrayBufferLike`에 `SharedArrayBuffer` 포함).

## 주요 설계 결정

- **단일 reorder/serialized-sink 코어**: `WorkerGateway`는 최대 `workers`개 async slot이 동시 decode하고, `FrameReorderBuffer`가 `seq`로 재정렬, 단일 `sinkChain` Promise로 sink 호출을 직렬화한다. 그 결과 worker가 out-of-order로 완료해도 coordinator(`enqueueInbound`)에는 입력 순서로만 들어가며 stateful dispatch 소유권이 coordinator에 남는다.
- **safe fallback = inline decode**: `WorkerGateway`의 기본 `decode`는 호출 컨텍스트의 inline `decodePacketBaseEnvelope`이다. gateway 미설정 시 `onReceivedFrame`도 inline decode 후 coordinator로 전달하므로, worker 미지원(browser 포함) 환경에서 기존 coordinator path가 그대로 유지된다. browser는 Web Worker 기반 `decode`를, Node는 `worker_threads` 기반 `decode`를 주입하는 구조다.
- **Node worker entry 로딩**: 빌드 산출물(.js)에서는 worker `.js`를 직접 로드한다. worker `execArgv`가 `--import`를 허용하지 않아(Node 제약) 빌드 전(.ts, vitest/tsx) 환경에서는 worker 안에서 `tsx/esm/api`의 `register()`로 loader를 등록한 뒤 `.ts` worker를 동적 import하는 부트스트랩을 `eval`로 실행한다. tsx는 devDependency이며 이 경로는 빌드 전 환경 전용이다.
- **transferable ArrayBuffer**: worker는 decode 결과 payload(`env.data`, protobuf decode가 입력 frame buffer를 view로 재사용)를 owned일 때 transferList로 main thread에 넘겨 결과 path의 clone 복사를 없앤다. 입력 raw frame은 source view가 buffer 전체를 소유할 때만 transfer하고, pool/공유 view(Node socket Buffer 등)이면 owned buffer로 복사해 detach 부작용을 피한다. inline fallback 경로는 `Uint8Array`를 참조 전달하므로 zero-copy다.
- **frame error 경계**: gateway decode 오류는 sink가 아니라 `seq` 순서로 `setFrameErrorHandler`에 보고되어 한 프레임의 실패가 reorder window를 막지 않는다. transport가 inline path와 동일한 parse-error disconnect 정책을 적용할 수 있게 하되, gateway 자체는 transport를 종료하지 않는다(Go/Kotlin `onError` 계약과 동일).

## 사용자 리뷰 요청

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

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

- Node/browser worker 경계가 package/runtime import를 깨지 않는지 확인한다.
- fallback 모드가 기존 coordinator path를 유지하는지 확인한다.

## 검증 결과

### API-1 중간 검증
```
$ cd typescript && npm run check && npm test -- communicator
> proto-socket@1.0.5 check
> tsc --noEmit
(에러 없음)

> proto-socket@1.0.5 test
> vitest run communicator

 RUN  v3.2.4 /config/workspace/proto-socket/typescript
 ✓ test/communicator.test.ts (21 tests) 363ms
   ✓ Communicator > worker gateway: out-of-order seq를 reorder한 뒤 입력 순서로 dispatch한다
   ✓ FrameReorderBuffer > out-of-order 결과를 contiguous prefix가 채워질 때만 seq 순서로 release한다
   ✓ Communicator inbound gateway > gateway on: worker가 out-of-order로 완료해도 입력 seq 순서로 dispatch한다
   ✓ Communicator inbound gateway > gateway 미설정 fallback: onReceivedFrame이 inline decode로 coordinator path를 유지한다
   ✓ Communicator inbound gateway > gateway decode 오류는 frame error handler로 보고되고 이후 프레임을 막지 않는다
   ✓ Communicator inbound gateway > Node worker_threads gateway: off-thread decode가 입력 순서를 보존한다

 Test Files  1 passed (1)
      Tests  21 passed (21)
```

### 최종 검증
```
$ bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all
(종료 코드 0)

**Proto 동기화**: schema sync PASS
**동일언어**: Dart / Go / Kotlin / Python / TypeScript 모두 PASS
**언어 PASS 매트릭스**: 5 서버 × (Dart.io/Dart.web/Dart.web(WSS)/Go/Kotlin/Python/TypeScript) 전부 PASS
**크로스테스트 상세**: 일반 언어간 20방향 16/16, Dart.web/Dart.web(WSS) 10방향 2/2, FAIL lines 0

결과 기록 파일: agent-test/runs/20260602-065016-proto-socket-full-matrix.md
git ref: 84635c3 (working tree, 미커밋 변경 포함)
```

## 코드리뷰 결과

- 종합 판정: FAIL

### 차원별 평가

| 차원 | 평가 | 근거 |
|------|------|------|
| correctness | Fail | `InboundGateway.close()` 계약은 추가 전달 중단인데 `WorkerGateway`가 close 이후 pending decode 결과를 sink/onError로 계속 방출한다. |
| completeness | Fail | gateway close/cancel 경계가 구현 체크리스트의 worker gateway 안정성 범위에서 누락되어 있다. |
| test coverage | Fail | close 이후 pending result가 dispatch되지 않는지 검증하는 테스트가 없다. |
| API contract | Fail | `InboundGateway.close()` 문서 계약과 실제 동작이 불일치한다. |
| code quality | Warn | lifecycle token/closed guard 없이 async chain이 계속 진행되어 reattach/reinitialize 시 stale delivery 위험이 있다. |
| plan deviation | Pass | 계획 범위 자체는 TypeScript gateway 구현과 테스트에 집중되어 있다. |
| verification trust | Pass | `cd typescript && npm run check && npm test -- communicator`는 재실행해 21/21 PASS를 확인했다. 단, 통과 테스트가 아래 lifecycle 결함을 커버하지 못한다. |

### 발견된 문제

- Required: `typescript/src/inbound_gateway.ts:147`의 `close()`는 `closed = true`만 설정하고 queued/in-flight decode, reorder buffer, `sinkChain` 전달을 중단하지 않는다. 실제로 pending decode를 close 이후 resolve하면 `typescript/src/inbound_gateway.ts:172`-`190`의 collect/sink 경로가 계속 실행된다. 이 때문에 `typescript/src/communicator.ts:198`-`208`의 `attachInboundGateway()`/`closeGateway()`가 기존 gateway를 닫고 새 gateway를 붙이거나 `initialize()`가 기존 gateway를 닫은 뒤 상태를 재설정해도, 이전 gateway의 늦은 결과가 현재 communicator 상태로 들어올 수 있다. `close()` 이후 결과와 error 전달을 모두 무시하도록 세대 토큰 또는 closed guard를 collect/sink chain에 적용하고, queued jobs/reorder pending도 정리한 뒤 close-after-pending 테스트를 추가해야 한다.

### 리뷰 검증

```bash
$ cd typescript && npm run check && npm test -- communicator
> proto-socket@1.0.5 check
> tsc --noEmit

> proto-socket@1.0.5 test
> vitest run communicator

 RUN  v3.2.4 /config/workspace/proto-socket/typescript
 ✓ test/communicator.test.ts (21 tests) 395ms
 Test Files  1 passed (1)
      Tests  21 passed (21)
```

```bash
$ cd typescript && node --import tsx --input-type=module <<'EOF'
import { WorkerGateway } from './src/inbound_gateway.ts';
let resolveDecode;
const delivered = [];
const gw = new WorkerGateway({
  workers: 1,
  decode: () => new Promise((resolve) => { resolveDecode = resolve; }),
  sink: (frame) => { delivered.push(frame.seq); },
});
gw.submit({ seq: 1, bytes: new Uint8Array([1]) });
gw.close();
resolveDecode({ typeName: 't', data: new Uint8Array(), incomingNonce: 0, responseNonce: 0 });
await new Promise((r) => setTimeout(r, 0));
console.log(JSON.stringify(delivered));
EOF
[1]
```

### 다음 단계

- WARN/FAIL 후속: active 파일을 archive한 뒤 `close()` 이후 stale sink/onError 전달을 차단하고 테스트를 추가하는 후속 `PLAN-cloud-G08.md` / `CODE_REVIEW-cloud-G08.md`를 작성한다.
