<!-- task=dart_web_e2e/01_browser_harness plan=0 tag=TEST -->

# Dart Web Browser Harness Plan

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것이 필수다. 구현 후 검증을 실행하고 실제 출력, 계획 대비 변경 사항, 주요 설계 결정을 기록한 뒤 active 파일을 그대로 둔 상태로 리뷰 준비를 보고한다. 최종 archive, `complete.log`, 판정 기록은 code-review-skill 전용이다.

## 배경

Dart 웹 import 컴파일 검사는 추가됐지만 [dart/test/browser_ws_import_compile.dart](/config/workspace/proto-socket/dart/test/browser_ws_import_compile.dart:1)는 실제 브라우저 WebSocket 송수신을 검증하지 않는다. [dart/lib/src/ws_protobuf_client_web.dart](/config/workspace/proto-socket/dart/lib/src/ws_protobuf_client_web.dart:29)는 `dart:html` WebSocket을 실제로 열고 binary frame을 처리하므로 브라우저 런타임 E2E가 별도로 필요하다. 현재 활성 Milestone은 없고, 이 작업은 완료된 지속 검증 영역의 유지보수성 테스트 보강이다.

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/project/domain/dart/rules.md`
- `agent-ops/rules/project/domain/go/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/roadmap/current.md`
- `agent-ops/roadmap/ROADMAP.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `dart/test/browser_ws_import_compile.dart`
- `dart/lib/src/ws_protobuf_client_web.dart`
- `dart/crosstest/go_dart_client.dart`
- `go/crosstest/go_dart.go`
- `go/ws_server.go`
- `go/test/ws_test.go`
- `agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh`

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

- 웹 import 컴파일: `dart compile js test/browser_ws_import_compile.dart`로 커버됨.
- Dart VM/IO TCP/WS/WSS: `dart test`로 커버됨.
- Dart Web 브라우저 WS 런타임: 미커버. 브라우저에서 `_open`, `_onMessage`, `_WebSocketTransport.writePacket` 경로를 실제 서버와 붙여 검증해야 한다.
- WSS 브라우저 런타임: 현재 자체 서명 인증서를 브라우저 신뢰 저장소에 주입하는 흐름이 없어 이번 foundation 작업에서는 제외한다.

### 심볼 참조

- 변경 예정 심볼 rename/remove 없음.
- `WsProtobufClient` 참조는 `rg --sort path "WsProtobufClient|ws_protobuf_client"`로 확인했고, IO crosstest와 웹 구현이 분리되어 있다.

### 분할 판단

분할 정책을 먼저 평가했다. 브라우저 harness 구축과 전체 다중언어 매트릭스 반영은 소유 경계와 검증 방식이 달라 split이 필요하다. 이 계획은 `agent-task/dart_web_e2e/01_browser_harness`로 foundation을 맡고, `agent-task/dart_web_e2e/02+01_multilang_matrix`가 이 작업 완료 후 매트릭스 확장을 맡는다.

### 범위 결정 근거

이번 단계는 Go 서버 기준 브라우저 런타임 E2E만 추가한다. Kotlin, Python, TypeScript 서버 확장과 매트릭스 표 출력은 후속 split 계획으로 넘긴다. WSS는 브라우저 인증서 신뢰 자동화가 필요하므로 이번 단계에서 구현하지 않는다.

### 빌드 등급

build=`cloud-G07`, review=`cloud-G07`. 실제 브라우저 실행, subprocess orchestration, 로그 판정이 성공 조건이라 terminal/browser automation 등급이 필요하다.

## 구현 체크리스트

- [ ] [TEST-1] Dart Web 브라우저 WS 런타임 테스트 파일을 추가한다.
- [ ] [TEST-2] Go 서버 기반 Dart Web E2E 오케스트레이터를 추가한다.
- [ ] [TEST-3] foundation 검증 명령을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [TEST-1] Dart Web Browser Runtime Test

#### 문제

[dart/test/browser_ws_import_compile.dart](/config/workspace/proto-socket/dart/test/browser_ws_import_compile.dart:4)는 타입 참조만 해서 `WsProtobufClient.connect`, binary send, binary receive, request-response를 검증하지 않는다. [dart/lib/src/ws_protobuf_client_web.dart](/config/workspace/proto-socket/dart/lib/src/ws_protobuf_client_web.dart:29)의 실제 브라우저 WebSocket 경로가 빠져 있다.

#### 해결 방법

`dart/test/browser_ws_runtime_test.dart`를 추가한다. `@TestOn('browser')` 테스트로 고정 포트의 Go WS 서버에 연결하고, `TestData` send-push와 `sendRequest`를 검증한다. 서버 주소는 compile-time env 대신 고정 포트로 시작해 기존 crosstest 스타일과 맞춘다.

Before:

```dart
// dart/test/browser_ws_import_compile.dart:1
import 'package:proto_socket/proto_socket.dart';
```

After:

```dart
// dart/test/browser_ws_runtime_test.dart:1
@TestOn('browser')
import 'package:test/test.dart';
import 'package:proto_socket/proto_socket.dart';
```

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

- [ ] `dart/test/browser_ws_runtime_test.dart` 생성
- [ ] browser-only annotation 추가
- [ ] `WsProtobufClient.connect('127.0.0.1', 29198)` 사용
- [ ] `TestData` listener, send, sendRequest 검증
- [ ] PASS/FAIL가 테스트 실패로 이어지도록 assertion 작성

#### 테스트 작성

작성한다. 테스트명은 `Dart Web WsProtobufClient talks to Go WS server`로 두고 browser platform에서만 실행한다.

#### 중간 검증

```bash
cd dart && dart analyze test/browser_ws_runtime_test.dart
```

기대 결과: analyzer issue 없음.

### [TEST-2] Go Server Orchestrator

#### 문제

[go/crosstest/go_dart.go](/config/workspace/proto-socket/go/crosstest/go_dart.go:174)는 Dart VM 클라이언트를 subprocess로 실행한다. 브라우저 테스트는 Origin header가 생기므로 [go/ws_server.go](/config/workspace/proto-socket/go/ws_server.go:47)의 `WsServerOptions`를 써서 `localhost` origin을 허용해야 한다.

#### 해결 방법

`go/crosstest/go_dart_web.go`를 추가한다. Go WS 서버를 `NewWsServerWithOptions`로 시작하고 `OriginPatterns: []string{"localhost", "127.0.0.1"}`를 설정한다. 이후 `(cd dart && dart test -p chrome test/browser_ws_runtime_test.dart)`를 subprocess로 실행하고 결과를 검증한다.

Before:

```go
// go/crosstest/go_dart.go:179
server := toki.NewWsServer(host, goDartWSPort, wsPath, func(conn *websocket.Conn) *toki.WsClient {
```

After:

```go
server := toki.NewWsServerWithOptions(host, goDartWebWSPort, wsPath, toki.WsServerOptions{
    AcceptOptions: &websocket.AcceptOptions{OriginPatterns: []string{"localhost", "127.0.0.1"}},
}, func(conn *websocket.Conn) *toki.WsClient {
```

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

- [ ] `go/crosstest/go_dart_web.go` 생성
- [ ] `//go:build ignore` 유지
- [ ] send-push 서버 listener와 request listener 작성
- [ ] Dart browser test subprocess 실행
- [ ] stdout/stderr 수집, timeout, non-zero exit 처리

#### 테스트 작성

작성한다. `go run ./crosstest/go_dart_web.go`가 통합 테스트 진입점이다.

#### 중간 검증

```bash
cd go && go run ./crosstest/go_dart_web.go
```

기대 결과: `PASS all go-server/dart-web-client crosstests passed`.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `dart/test/browser_ws_runtime_test.dart` | TEST-1 |
| `go/crosstest/go_dart_web.go` | TEST-2 |

## 최종 검증

```bash
cd dart && dart analyze test/browser_ws_runtime_test.dart
cd go && go run ./crosstest/go_dart_web.go
```

Go test cache는 사용하지 않는다. 브라우저 smoke는 매번 실제 `dart test -p chrome`가 실행되어야 한다.

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