<!-- task=01_go_crosstest_process_cleanup plan=0 tag=TEST -->

# Go Crosstest Process Cleanup

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

**구현 완료의 마지막 단계는 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 모두 채우는 것이다.**
아래 구현 체크리스트와 리뷰 stub의 동일한 체크리스트를 끝까지 수행하고, 중간/최종 검증을 실행한 실제 출력으로 `CODE_REVIEW-cloud-G07.md`를 채운다. 리뷰 파일의 `이 파일을 읽는 리뷰 에이전트에게` 아카이브 지시는 실행하지 말고, `코드리뷰 전용 체크리스트`도 수정하지 않는다.

## 배경

Go 서버 크로스테스트 중 Dart/Python/Kotlin 클라이언트 subprocess를 띄우는 러너가 timeout 이후 pipe scanner goroutine을 무제한 대기한다. TypeScript 방향 러너에는 이미 `WaitDelay`와 pipe scan timeout이 들어가 있으므로 같은 실패 모드를 Go의 나머지 클라이언트 러너에 확산한다.

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/project/domain/go/rules.md`
- `go/crosstest/go_dart.go`
- `go/crosstest/go_python.go`
- `go/crosstest/go_kotlin.go`
- `go/crosstest/go_typescript.go`
- `go/go.mod`

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

- subprocess timeout 후 pipe drain 종료: 별도 unit test 없음. 실제 `go run ./crosstest/go_*.go` 명령이 회귀 검증이다.
- 정상 PASS scenario 수집: 기존 crosstest가 `validateResultLines`로 검증한다.

### 심볼 참조

- renamed/removed symbol: none.
- 관련 참조 검색: `rg --sort path -n "cmd\\.Wait\\(|wg\\.Wait\\(|cmd\\.WaitDelay|pipe scan timed out" go/crosstest`.

### 분할 판단

분할 정책을 먼저 평가했다. 이 작업 묶음은 런타임별 process API와 검증 명령이 달라 분할한다.

- `01_go_crosstest_process_cleanup`: Go server runner가 Go `exec.CommandContext`와 pipe scanner를 다룬다. 의존성 없음.
- `02_dart_crosstest_process_cleanup`: Dart server runner가 Dart `Process` stream을 다룬다. 의존성 없음.
- `03_typescript_crosstest_process_cleanup`: TypeScript server runner가 Node `ChildProcess`를 다룬다. 의존성 없음.

### 범위 결정 근거

이 계획은 `go/crosstest/go_dart.go`, `go/crosstest/go_python.go`, `go/crosstest/go_kotlin.go`만 수정한다. `go/crosstest/go_typescript.go`는 reference로만 사용하고, `agent-task/archive/**`와 client helper binaries는 건드리지 않는다.

### 빌드 등급

build=`cloud-G07`, review=`cloud-G07`. subprocess timeout, stdout/stderr parsing, exit-status 계약을 다루는 terminal-agent 작업이다.

## 구현 체크리스트

- [ ] `go_dart.go`, `go_python.go`, `go_kotlin.go`의 client subprocess 실행에 `WaitDelay`와 bounded pipe scanner wait를 적용한다.
- [ ] timeout/pipe scan 실패가 명확한 error message로 반환되는지 확인한다.
- [ ] 중간 검증과 최종 검증 명령을 실행하고 실제 출력을 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [TEST-1] Harden Go subprocess runner cleanup

#### 문제

- [go_dart.go](/config/workspace/proto-socket/go/crosstest/go_dart.go:405)는 `cmd.Wait()` 뒤 [go_dart.go](/config/workspace/proto-socket/go/crosstest/go_dart.go:406)의 `wg.Wait()`를 무제한 대기한다.
- [go_python.go](/config/workspace/proto-socket/go/crosstest/go_python.go:401)와 [go_kotlin.go](/config/workspace/proto-socket/go/crosstest/go_kotlin.go:396)도 같은 구조다.

Before:

```go
// go/crosstest/go_dart.go:405
waitErr := cmd.Wait()
wg.Wait()
if ctx.Err() == context.DeadlineExceeded {
	return fmt.Errorf("dart client %s/%s timed out", mode, phase)
}
```

#### 해결 방법

`go_typescript.go`의 안정화 패턴을 세 파일에 맞춰 적용한다. 각 `cmd`에 `WaitDelay = 5 * time.Second`를 설정하고, `wg.Wait()`는 channel + `time.After(5 * time.Second)`로 bounded wait한다.

After:

```go
cmd := exec.CommandContext(ctx, "dart", args...)
cmd.Dir = dartDir
cmd.WaitDelay = 5 * time.Second

waitErr := cmd.Wait()
done := make(chan struct{})
go func() { wg.Wait(); close(done) }()
select {
case <-done:
case <-time.After(5 * time.Second):
	return fmt.Errorf("dart client %s/%s pipe scan timed out", mode, phase)
}
if ctx.Err() == context.DeadlineExceeded {
	return fmt.Errorf("dart client %s/%s timed out", mode, phase)
}
```

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

- [ ] `go/crosstest/go_dart.go`: `runDartClient`에 `WaitDelay`와 pipe scan timeout 추가.
- [ ] `go/crosstest/go_python.go`: `runPythonClient`에 같은 패턴 추가.
- [ ] `go/crosstest/go_kotlin.go`: `runKotlinClient`에 같은 패턴 추가.

#### 테스트 작성

별도 unit test는 작성하지 않는다. 이 파일들은 `//go:build ignore` crosstest runner이고 subprocess hang을 단위 테스트로 고립하기 어렵다. 실제 runner 명령을 회귀 검증으로 사용한다.

#### 중간 검증

```bash
cd go && go test ./...
```

기대 결과: exit code 0. Go test cache 출력은 허용한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `go/crosstest/go_dart.go` | TEST-1 |
| `go/crosstest/go_python.go` | TEST-1 |
| `go/crosstest/go_kotlin.go` | TEST-1 |

## 최종 검증

```bash
cd go && go test ./...
cd go && go run ./crosstest/go_dart.go
cd go && go run ./crosstest/go_python.go
cd go && go run ./crosstest/go_kotlin.go
rg --sort path -n "cmd\\.WaitDelay|pipe scan timed out|wg\\.Wait\\(\\)" go/crosstest/go_dart.go go/crosstest/go_python.go go/crosstest/go_kotlin.go go/crosstest/go_typescript.go
```

기대 결과: 모든 실행 명령 exit code 0. `rg` 결과에서 수정 대상 3개 파일은 `cmd.WaitDelay`와 `pipe scan timed out`을 포함하고, 무제한 `wg.Wait()`만 남지 않는다.

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