<!-- task=m-performance-hotspot-optimization/10_dart_isolate_gateway_contract plan=0 tag=REFACTOR -->

# Code Review Reference - REFACTOR

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> Fill implementation-owned sections, then stop with active files in place and report ready for review. Do not create `complete.log` or archive files.

## 개요

date=2026-06-06
task=m-performance-hotspot-optimization/10_dart_isolate_gateway_contract, plan=0, tag=REFACTOR

## Roadmap Targets

- Milestone: `agent-roadmap/milestones/performance-hotspot-optimization.md`
- Task ids:
  - `dart-isolate-backpressure`: bounded backpressure와 backlog 계측
  - `dart-isolate-error-ordering`: decode error ordered result와 disconnect semantics
  - `dart-isolate-cleanup`: lifecycle cleanup hard gate
- Completion mode: check-on-pass

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

> **[REVIEW AGENT ONLY]** 종결 절차는 코드리뷰 에이전트 전용이다.

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [REFACTOR-1] Async submit and bounded backlog | [x] |
| [REFACTOR-2] Ordered decode error result | [x] |
| [REFACTOR-3] Cleanup snapshot | [x] |

## 구현 체크리스트

- [x] `InboundGateway.submit`을 bounded acceptance/backpressure가 가능한 async contract로 바꾸고 current call sites를 갱신한다.
- [x] gateway result model이 decoded frame과 ordered decode error를 같은 seq stream으로 표현하게 한다.
- [x] `FrameReorderBuffer`에 pending count/clear 또는 snapshot을 추가해 cleanup/backlog evidence를 만들 수 있게 한다.
- [x] IO/Web gateway가 close 후 pending submit/result stream/reorder state를 남기지 않게 한다.
- [x] backpressure, ordered error, repeated cleanup tests를 `dart/test/communicator_test.dart`에 추가한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

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

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

- [x] 판정을 append한다.
- [x] active 파일을 `.log`로 아카이브하고 PASS이면 `complete.log`를 작성한다.
- [x] PASS이면 런타임 완료 이벤트 메타데이터를 보고한다.

## 계획 대비 변경 사항

- 계획 sketch는 `int get queuedCount; int get inFlightCount;`를 제안했고, 인터페이스에 두 getter를 모두 추가했다. 의미는 `inFlightCount`=worker에 보냈으나 결과가 오지 않은 frame 수(mailbox backlog), `queuedCount`=reorder buffer에 잔류 중인 frame 수(`FrameReorderBuffer.pendingCount`)로 나눠 정의했다. cleanup hard gate가 두 backlog를 따로 0으로 증명할 수 있게 하기 위함이다.
- ordered decode error는 별도 result type을 만들지 않고 기존 `DecodedFrame`에 `error` 필드와 `DecodedFrame.error()` 명명 생성자를 추가했다. 같은 `seq` stream/`FrameReorderBuffer`를 그대로 재사용해 reorder hang을 막기 위함이며, `results` 스트림 타입(`Stream<DecodedFrame>`)을 바꾸지 않아 호출부 변경을 최소화했다.
- isolate worker reply를 5-element에서 6-element(`[seq, typeName, data, nonce, responseNonce, error]`)로 확장했다. error는 isolate 경계에서 항상 직렬화되도록 `Object`가 아니라 문자열(`'decode failed: ...'`)로 전달한다.
- web fallback `IsolateInboundGateway`에 무시되는 `capacity` 선택 인자를 추가해 conditional-export 생성자 시그니처를 IO 버전과 일치시켰다. in-process 동기 디코드에는 bound할 worker mailbox가 없어 동작에는 영향이 없다.
- `onReceivedFrame`은 `gateway.submit(...)`을 `await`하도록 바꿔 backpressure가 transport read loop로 전파되게 했다. 현재 production wiring(attach 호출부)은 없고 `attachInboundGateway`는 테스트 전용 경로뿐이므로 실제 transport 연결은 후속 `11+10`에서 처리한다.

## 주요 설계 결정

- **submit readiness drop 제거(REFACTOR-1)**: `submit`은 `_ready` completer가 미완료면 await한 뒤 hand-off한다. isolate spawn 중 도착한 frame이 `_toWorker == null`로 조용히 drop되던 경로를 제거했다.
- **bounded backpressure(REFACTOR-1)**: `_inFlight >= capacity`이면 `_capacityWaiter`로 대기하고, worker 결과 수신 시 `_inFlight--` 후 slot을 release한다. `defaultCapacity=1024`로 무제한 `SendPort` mailbox를 bound한다.
- **ordered error → connection teardown(REFACTOR-2)**: malformed frame은 worker에서 catch되어 같은 seq의 error result로 돌아오고, `FrameReorderBuffer`를 통과해 후속 seq의 영구 잔류를 막는다. coordinator는 `onGatewayDecodeError`에서 `close()`를 호출해 connection close + pending 정리로 연결한다. `BaseClient.close()` override가 transport close까지 수행한다.
- **cleanup hard gate(REFACTOR-3)**: `close()`는 isolate kill/ReceivePort close에 더해 `_inFlight=0`, `_reorder.clear()`, 대기 중 completer 해제까지 수행한다. 반복 start/submit/close 20회 후 `inFlightCount`/`queuedCount`가 0으로 유지됨을 테스트로 증명한다.
- **isolate 생존(REFACTOR-2)**: worker `_gatewayEntry`의 디코드를 try/catch로 감싸 malformed frame이 isolate를 죽이지 않고 error result만 반환하게 했다.

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다._

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

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

- submit readiness에서 frame drop이 없는지 확인한다.
- malformed frame이 seq gap/reorder hang을 남기지 않는지 확인한다.
- close/repeated cleanup test가 actual counts를 검증하는지 확인한다.

## 검증 결과

### REFACTOR-1 중간 검증
```text
$ cd dart && dart test test/communicator_test.dart -r expanded
00:00 +21: ... IsolateInboundGateway submit waits for ready and preserves all frames under capacity pressure
00:00 +22: ... SyncInboundGateway: malformed frame이 ordered decode error를 emit한다
00:00 +23: ... IsolateInboundGateway: malformed frame이 isolate를 죽이지 않고 connection close + pending cleanup으로 이어진다
00:00 +24: ... FrameReorderBuffer: pendingCount와 clear가 backlog를 노출하고 초기화한다
00:00 +25: ... IsolateInboundGateway: 반복 start/submit/close가 residual backlog를 남기지 않는다
00:00 +26: All tests passed!
```

### 최종 검증
```text
$ cd dart && dart test test/communicator_test.dart -r expanded
00:00 +26: All tests passed!

$ cd dart && dart test test/socket_test.dart -r expanded
00:16 +41: All tests passed!

$ cd dart && dart compile js test/browser_ws_import_compile.dart -o /tmp/proto_socket_browser_ws_import_compile.js
Compiled 10,871,530 input bytes (5,872,758 characters source) to 11,692 characters JavaScript in 0.47 seconds
(/tmp/proto_socket_browser_ws_import_compile.js 산출물 생성됨)

$ cd dart && dart test    # dart-smoke 필수 전체 실행
00:17 +67: All tests passed!
```

## 코드리뷰 결과

- 종합 판정: PASS
- 차원별 평가:
  - correctness: Pass
  - completeness: Pass
  - test coverage: Pass
  - API contract: Pass
  - code quality: Pass
  - plan deviation: Pass
  - verification trust: Pass
- 발견된 문제: 없음
- 다음 단계: PASS이므로 active plan/review를 `.log`로 아카이브하고 `complete.log` 작성 후 task 디렉터리를 archive로 이동한다.
