<!-- task=m-performance-hotspot-optimization/05+04_go_sustained_memory_metric plan=0 tag=GO_MEM -->

# Implementation Plan - GO_MEM

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성까지가 구현의 마지막 단계다. 구현 후 검증 명령을 실행하고 실제 출력, 계획 대비 변경, 주요 설계 결정을 review stub에 채운 뒤 active 파일을 그대로 둔 채 리뷰 요청으로 멈춘다. finalization, log rename, `complete.log`, archive 이동은 code-review 전용이다. 구현 중 사용자만 결정할 수 있는 blocker가 있으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 증거와 재개 조건을 기록하고 멈춘다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

`go-reference` Epic에서 남은 큰 작업은 Go 30분 sustained memory peak가 구현 leak인지 bench artifact인지 분리하는 것이다. 현재 `go/bench/stress.go`의 sustained profile은 30분 동안 모든 latency sample을 `[]float64`에 계속 append한 뒤 sort copy까지 만들기 때문에, `agent-test/runs/20260602-232000-proto-socket-stress-full.md`의 `57.1MB / 143.4MB / 794.1MB` memory 증가는 harness sample 보관 비용일 가능성이 크다. 이 계획은 Go stress harness의 sustained latency 집계를 bounded sampling으로 바꿔 memory metric을 다시 신뢰 가능한 reference guard로 만든다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 그 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다. 구현 에이전트는 직접 사용자에게 묻지 않는다.

## Roadmap Targets

- Milestone: `agent-roadmap/milestones/performance-hotspot-optimization.md`
- Task ids:
  - `go-sustained-memory-metric`: Go 30분 sustained memory peak 증가가 구현 leak인지 bench artifact인지 분리한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/milestones/performance-hotspot-optimization.md`
- `agent-task/archive/2026/06/m-performance-hotspot-optimization/04+03_compare_rule_full_baseline/complete.log`
- `agent-test/local/rules.md`
- `agent-test/local/proto-socket-performance-baseline.md`
- `agent-test/local/go-smoke.md`
- `agent-test/runs/20260602-232000-proto-socket-stress-full.md`
- `agent-test/runs/20260603-035342-proto-socket-stress-quick.md`
- `agent-test/runs/20260605-025842-proto-socket-performance-full.md`
- `agent-test/runs/20260605-115943-proto-socket-performance-full.md`
- `go/go.mod`
- `go/bench/stress.go`

### 테스트 환경 규칙

`test_env=local`이다. `agent-test/local/rules.md`를 읽었고, 성능/memory/sustained 판단이므로 `agent-test/local/proto-socket-performance-baseline.md`를 적용한다. Go 파일 변경이므로 `agent-test/local/go-smoke.md`도 적용한다. 필수 검증은 Go 변경에 대한 `cd go && go test -count=1 ./...`, 성능 관련 코드 변경에 대한 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --quick`, 그리고 좁은 중간 확인용 `run_stress.sh --quick --lang go --transport tcp --profile sustained`다.

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

`go/bench/stress.go`는 `//go:build ignore` benchmark harness라 unit test가 직접 실행하지 않는다. 기존 Go tests는 transport/protocol correctness를 확인하지만 sustained collector의 bounded memory behavior를 직접 검증하지 않는다. 검증은 focused stress row와 performance quick record에서 Go sustained row가 PASS이고 memory가 bounded sampling 이후 과도하게 sample count에 선형 누적되지 않는지 확인하는 방식이다.

### 심볼 참조

renamed/removed symbol 없음. 새 helper를 추가하더라도 `go/bench/stress.go` 내부에서만 사용한다.

### 분할 판단

split decision policy를 먼저 평가했다. 공유 task group은 `agent-task/m-performance-hotspot-optimization`이고 이 subtask는 `05+04_go_sustained_memory_metric`이다. 디렉터리명상 predecessor `04`는 archive `agent-task/archive/2026/06/m-performance-hotspot-optimization/04+03_compare_rule_full_baseline/complete.log`로 충족된다. 이 작업은 Go stress harness 한 파일과 focused 성능 검증에 한정된 단일 구현 단위라 추가 split은 만들지 않는다. API/foundation과 broad rollout 경계가 없고, 수정 파일과 검증 프로필이 하나의 reviewer가 판단 가능한 범위다.

### 범위 결정 근거

수정 범위는 `go/bench/stress.go`의 sustained latency 집계와 필요한 내부 helper로 제한한다. TCP/WS protocol 구현, public API, Go transport code, 다른 언어 harness, `run_performance.sh` regression threshold, milestone Task 정의는 바꾸지 않는다. 기존 roundtrip/parallel/payload는 현재 정확 percentile을 유지해야 하므로 sustained path에만 bounded sampling을 적용한다.

### 빌드 등급

`cloud-G07`. benchmark-style terminal verification과 memory metric 신뢰성 회복이 중심이고, 30분 full record의 해석을 바꾸는 작업이라 evidence trust가 중요하다.

## 의존 관계 및 구현 순서

`05+04_go_sustained_memory_metric`는 `04+03_compare_rule_full_baseline` 완료 뒤 실행한다. 선행 완료 근거는 `agent-task/archive/2026/06/m-performance-hotspot-optimization/04+03_compare_rule_full_baseline/complete.log`이며, 이 로그는 `compare-rule` PASS와 full baseline comparison record를 남겼다.

## 구현 체크리스트

- [ ] `go/bench/stress.go`에 deterministic bounded latency collector를 추가해 sustained profile이 모든 latency를 무제한 보관하지 않게 한다.
- [ ] sustained row의 request count, throughput, p50/p95/p99, stability counters, peak memory 출력 계약을 유지한다.
- [ ] Go focused sustained stress와 Go tests, performance quick을 실행하고 새 결과 record의 Go sustained rows를 확인한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [GO_MEM-1] Go sustained latency collector bounded화

### 문제

[go/bench/stress.go](/config/workspace/proto-socket/go/bench/stress.go:525)는 sustained duration 동안 `latencies := make([]float64, 0, 4096)`를 만든 뒤 [go/bench/stress.go](/config/workspace/proto-socket/go/bench/stress.go:545)에서 모든 request latency를 append한다. [go/bench/stress.go](/config/workspace/proto-socket/go/bench/stress.go:557)는 전체 slice를 `summarize`로 넘기고, [go/bench/stress.go](/config/workspace/proto-socket/go/bench/stress.go:344)는 다시 sort copy를 만든다. 30분 full row는 수천만 request를 만들 수 있으므로 memory peak가 구현 leak이 아니라 harness sample 보관 비용으로 커진다.

### 해결 방법

`go/bench/stress.go` 내부에 bounded collector를 추가한다. collector는 전체 request count와 최대 sample cap을 분리해서 추적하고, sustained profile만 bounded sample로 p50/p95/p99를 계산한다. deterministic reservoir sampling 또는 고정 stride 기반 sampling 중 하나를 사용하되, 결과가 재현 가능하고 sample cap이 코드 상수로 명확해야 한다.

Before:

```go
// go/bench/stress.go:524
var mu sync.Mutex
latencies := make([]float64, 0, 4096)
...
mu.Lock()
latencies = append(latencies, elapsed)
mu.Unlock()
...
summarize("sustained", fmt.Sprintf("duration=%dms", durationMs), transport, latencies, float64(durationMs), 1, s, peak)
```

After direction:

```go
// go/bench/stress.go
collector := newBoundedLatencyCollector(maxSustainedLatencySamples)
...
collector.Add(elapsed)
...
snapshot := collector.Snapshot()
summarizeWithCount("sustained", axis, transport, snapshot.samples, snapshot.count, float64(durationMs), 1, s, peak)
```

`requests`와 throughput은 `snapshot.count`를 사용하고, p50/p95/p99는 bounded sample을 sort해서 계산한다. quick/full sustained row schema는 바꾸지 않는다.

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

- [ ] `go/bench/stress.go`: bounded latency collector type/helper 추가.
- [ ] `go/bench/stress.go`: sustained profile에서 무제한 `latencies` append 제거.
- [ ] `go/bench/stress.go`: request count와 percentile sample count를 분리하는 summarize helper 추가 또는 기존 `summarize`를 안전하게 확장.
- [ ] `go/bench/stress.go`: comments/log에서 sustained memory metric이 sample cap 영향을 받는다는 점을 짧게 명시.

### 테스트 작성

별도 unit test는 작성하지 않는다. `go/bench/stress.go`는 `//go:build ignore` command harness이며 기존 Go unit test package가 직접 import하지 않는다. 대신 focused stress command와 performance quick record를 검증 산출물로 사용한다.

### 중간 검증

```bash
cd go && go test -count=1 ./...
```

기대 결과: 모든 Go package test가 PASS.

```bash
bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh --quick --lang go --transport tcp --profile sustained
```

기대 결과: Go tcp sustained quick row가 PASS이고 stability violations가 0이다. 결과 record에 sustained row의 `requests`, throughput, p50/p95/p99, memory 값이 기록된다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `go/bench/stress.go` | GO_MEM-1 |

## 최종 검증

```bash
cd go && go test -count=1 ./...

bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_stress.sh --quick --lang go --transport tcp --profile sustained

bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_performance.sh --quick

rg --sort path -n "sustained \\| duration=.* \\| Go \\| tcp|sustained \\| duration=.* \\| Go \\| ws|regression_result:|warning rows:" agent-test/runs/<new-stress-or-performance-record>.md

git diff --check
```

기대 결과: Go tests PASS, focused Go sustained quick PASS, performance quick이 결과 record를 남기며 Go sustained rows가 stability hard gate 0을 유지한다. `git diff --check` 출력은 없어야 한다.

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