<!-- task=m-stream-evidence-gate-core/16+15_ingress_snapshot_allocation plan=3 tag=REVIEW_INGRESS_SNAPSHOT_CONCURRENT_OVERFLOW_EVIDENCE -->

# Plan - Ingress snapshot concurrent overflow evidence follow-up

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

구현과 테스트, 실제 검증 evidence만 수행한다. 모든 변경 뒤 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 내용과 stdout/stderr를 채우고 active pair를 그대로 둔 채 리뷰 준비 완료를 보고한다. 막히면 정확한 blocker, 시도한 명령/출력, 재개 조건만 구현 소유 evidence에 기록한다. 사용자에게 질문하거나 user-input 도구를 호출하거나 control-plane stop 파일을 만들거나 다음 상태를 분류하지 않는다. 로그 archive, `complete.log`, 최종 판정은 code-review 스킬 소유다.

## 배경

terminal active-guard fan-out 구현과 deterministic 회귀는 통과했지만, concurrent race fixture의 500,000-byte commit은 1 MiB 한도에서 overflow가 아니어서 `snap.Close()`만으로 PASS했다. committed sibling을 먼저 확보하고 실제 oversized commit·reserved sibling cancel·parent close를 경쟁시켜 winner별 stable state와 외부 rebuilt snapshot closure를 검증해야 SDD S22의 no-dispatch/release evidence가 닫힌다.

## Archive Evidence Snapshot

- 이전 task: `agent-task/m-stream-evidence-gate-core/16+15_ingress_snapshot_allocation`
- 이전 plan: `agent-task/m-stream-evidence-gate-core/16+15_ingress_snapshot_allocation/plan_local_G06_2.log`
- 이전 review: `agent-task/m-stream-evidence-gate-core/16+15_ingress_snapshot_allocation/code_review_cloud_G06_2.log`
- 판정: FAIL
- Required 1건: `TestIngressSnapshot_RebuildConcurrentOverflowCloseCancelRace`가 1 MiB 한도에서 500,000-byte commit을 사용하고 두 Commit 결과와 외부 rebuilt pointer를 버려 실제 overflow/winner별 stable error/committed sibling close를 검증하지 않는다.
- Suggested/Nit: 없음.
- 실제 검증: host Go/proto no-drift, deterministic focused, 기존 focused race, package unit/race, symbol count, gofmt, `git diff --check`는 PASS했다. reviewer source audit에서 500,000-byte commit이 retained/reservation과 합쳐도 1 MiB보다 작고 동시 `snap.Close()`가 false positive terminal state를 만드는 것을 확인했다.
- 영향 파일: `packages/go/streamgate/ingress_snapshot_test.go`.
- Roadmap carryover: 승인된 SDD S22의 rebuild overflow no-dispatch/request-owned canonical·temporary release를 유지한다. 이 subtask PASS만으로 Milestone Task를 직접 체크하지 않는다.

## 분석 결과

### 읽은 파일

- 소스: `packages/go/streamgate/ingress_snapshot.go` 전체.
- 테스트: `packages/go/streamgate/ingress_snapshot_test.go` 전체.
- 규칙/환경: `agent-ops/rules/project/domain/platform-common/rules.md`, `agent-test/local/rules.md`, `agent-test/local/platform-common-smoke.md`, `go.mod` dependency manifest.
- 설계: `agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md`, `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/stream-evidence-gate-core.md`, `agent-roadmap/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md`.
- 현재 loop: `agent-task/m-stream-evidence-gate-core/16+15_ingress_snapshot_allocation/plan_local_G06_2.log`, `agent-task/m-stream-evidence-gate-core/16+15_ingress_snapshot_allocation/code_review_cloud_G06_2.log`.
- 선행 근거: `agent-task/archive/2026/07/m-stream-evidence-gate-core/15+12_ingress_snapshot_ledger/complete.log`.

### SDD 기준

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md`; 상태 `[승인됨]`, 잠금 `해제`.
- 대상: Acceptance Scenario S22 / Milestone Task `request-rebuilder`.
- Evidence Map S22는 rebuild pre/post-allocation peak overflow에서 no dispatch, request-owned canonical/temporary release, no raw retention을 요구한다.
- 이번 follow-up은 Milestone Task 완료를 주장하지 않고, S22의 concurrent terminal release 증거 공백만 corrected race fixture와 fresh unit/race 출력으로 닫는다.

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md`와 `agent-test/local/platform-common-smoke.md`를 읽었다.
- PATH의 host Go 경로/version/GOROOT를 기록하고 같은 toolchain으로 `-count=1` package unit/race와 `-race -count=20` focused interleaving을 실행한다.
- proto/config/wire를 바꾸지 않지만 local quick-check 기준으로 `make proto && git diff --exit-code -- proto/gen`을 유지한다. Edge/Node consumer/full-cycle은 wire/config/user execution 변경이 없어 생략한다.
- 외부 서비스, Docker, credential, port는 필요 없고 `<확인 필요>` 값도 없다.

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

- deterministic committed/reserved sibling invalidation, repeat overflow error, parent close fan-out은 기존 named tests가 덮고 fresh 실행도 PASS했다.
- `TestIngressSnapshot_RebuildConcurrentOverflowCloseCancelRace`는 guard A를 사전 commit하지 않고 guard B actual output도 overflow 한도보다 작으며 결과를 버려 계획된 concurrent overflow evidence를 덮지 못한다.
- 수정 후에는 guard A의 외부 rebuilt pointer, guard B overflow/closed winner, guard C cancelled/released winner, parent zero retained/reserved state를 함께 검증해야 한다.

### 심볼 참조

- rename/remove/public API 변경은 없다.
- 새 dependency와 `go.mod` 변경은 없다. 대상은 기존 test 함수 한 개와 기존 `IngressSnapshot`/guard API 호출뿐이다.

### 분할 판단

- split policy를 먼저 재평가했다. 한 test 함수 안의 fixture 크기·ordering·result assertion을 같은 state machine 불변조건으로 고치는 test-only 단위다.
- production/API/도메인 경계, 독립 검증 slice, 별도 선행 산출물이 없어 추가 분할은 인위적이다. 기존 단일 subtask `16+15_ingress_snapshot_allocation`을 유지한다.
- predecessor `15`는 `agent-task/archive/2026/07/m-stream-evidence-gate-core/15+12_ingress_snapshot_ledger/complete.log`로 충족됐다.

### 범위 결정 근거

- 포함: `packages/go/streamgate/ingress_snapshot_test.go`의 concurrent overflow fixture와 winner별 assertions.
- 제외: `ingress_snapshot.go` production lifecycle, public API, config/proto/contract, host wiring. corrected fixture가 production failure를 드러내면 범위를 조용히 확장하지 않고 실제 실패와 재개 조건을 review evidence에 기록한다.

### 최종 라우팅

- `evaluation_mode=isolated-reassessment`; `finalizer=finalize-task-policy.sh`; `finalizer_mode=pair`.
- build closures: scope/context/verification/evidence/ownership/decision 모두 true. 한 test 함수, 이미 확인된 false-positive arithmetic, exact focused/full 명령, platform-common 단일 ownership, 사용자 결정 없음으로 닫혔다.
- build route basis: `local-fit`; grade scores: scope=0, state=2, blast=0, evidence=1, verification=1; 합계 G04; capability_gap=`none`; none floor=`none`; lane/file=`local`, `PLAN-local-G04.md`.
- loop risk: ordered transitions state_count=5(reserved/committed/cancelled/overflow/released), adverse paths=overflow/parent-close/cancel/repeat-use. concurrent actors=3(overflow commit, sibling cancel, parent close), constraints=parent-before-guard lock order/exactly-once/race-free. boundary components=2, consumers=0. structured mechanisms=없음. variant axes=3(sibling state, terminal winner, interleaving), combination 검증 필요=true. matched signatures=`temporal_state`, `concurrent_consistency`, `variant_product`; triggered=true.
- review closures: scope/context/verification/evidence/ownership/decision 모두 true. corrected fixture의 실제 overflow 크기, collected winner result, external rebuilt pointer와 fresh race output을 독립 대조할 수 있다.
- review route basis: `official-review`; grade scores: scope=0, state=2, blast=0, evidence=1, verification=1; 합계 G04; capability_gap=`none`; none floor=`none`; lane/file=`cloud`, `CODE_REVIEW-cloud-G04.md`; target=`gpt-5.6-sol` xhigh.

## 구현 체크리스트

- [x] REVIEW_INGRESS_SNAPSHOT_CONCURRENT_OVERFLOW_EVIDENCE-1 guard A를 경쟁 전에 commit해 외부 rebuilt pointer를 확보하고, guard B가 실제 post-peak overflow를 시도하도록 고친 뒤 overflow/parent close/reserved cancel winner별 stable state·error, rebuilt close, zero retention/reservation을 검증하며 fresh focused/unit/race/proto/gofmt/diff 명령을 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_INGRESS_SNAPSHOT_CONCURRENT_OVERFLOW_EVIDENCE-1] Real concurrent overflow fixture

- 문제: `packages/go/streamgate/ingress_snapshot_test.go:2373-2489`은 guard A commit도 경쟁 안에 넣고 guard B에 500,000 bytes만 전달한다. `maxBytes=1_048_576`에서 이는 post-peak overflow가 아니며 두 Commit 결과와 외부 rebuilt pointer도 버려 `snap.Close()` false positive를 구분하지 못한다.
- 해결 방법: guard A를 사전 commit해 `rebuiltA`를 보존한다. guard B에는 retained와 sibling reservation을 포함해도 반드시 overflow인 output을 전달하고, start barrier/result channel로 guard B commit·guard C cancel·parent close를 경쟁시킨다. 종료 뒤 guard B는 winner에 따라 `Overflow` 또는 `Released`와 대응 error를, guard C는 `Cancelled` 또는 `Released`를 허용하되 parent/guards/rebuiltA가 모두 terminal이고 retained/reserved bytes가 0임을 확인한다.

Before (`packages/go/streamgate/ingress_snapshot_test.go:2413-2439`):

```go
go func() {
    guardA.Commit([]byte("rebuilt-output-data-for-guard-a"))
}()
go func() {
    guardB.Commit(make([]byte, 500_000))
}()
go func() {
    guardC.Cancel()
}()
go func() {
    snap.Close()
}()
```

After:

```go
rebuiltA, err := guardA.Commit([]byte("rebuilt-output-data-for-guard-a"))
if err != nil {
    t.Fatalf("guardA Commit: %v", err)
}
start := make(chan struct{})
overflowResult := make(chan error, 1)
cancelResult := make(chan error, 1)
// Each goroutine waits on start; guardB uses make([]byte, maxBytes).
// Collect results after wg.Wait and assert the allowed winner-specific terminal states.
```

- 수정 파일 및 체크리스트:
  - [ ] `packages/go/streamgate/ingress_snapshot_test.go`: 실제 oversized commit, 사전 committed sibling pointer, start/result coordination, winner별 stable assertions를 반영한다.
- 테스트 작성: 기존 `TestIngressSnapshot_RebuildConcurrentOverflowCloseCancelRace`를 수정한다. actual overflow 또는 concurrent parent close winner를 관찰하고 외부 `rebuiltA` closed, guard no-dispatch/no-reference, parent zero retained/reserved를 검증한다.
- 중간 검증: `go test -race -count=20 ./packages/go/streamgate -run '^TestIngressSnapshot_RebuildConcurrentOverflowCloseCancelRace$'`가 PASS한다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `packages/go/streamgate/ingress_snapshot_test.go` | REVIEW_INGRESS_SNAPSHOT_CONCURRENT_OVERFLOW_EVIDENCE-1 |

## 최종 검증

- `command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT`
- `make proto && git diff --exit-code -- proto/gen`
- `go test -count=1 ./packages/go/streamgate -run '^TestIngressSnapshot_(RebuildOverflowInvalidatesCommittedAndReservedSiblings|RebuildOverflowErrorStableOnRepeat|CloseInvalidatesCommittedGuard)$' -v`
- `go test -race -count=20 ./packages/go/streamgate -run '^TestIngressSnapshot_RebuildConcurrentOverflowCloseCancelRace$'`
- `go test -count=1 ./packages/go/streamgate`
- `go test -race -count=1 ./packages/go/streamgate`
- `if rg -n '500_000' packages/go/streamgate/ingress_snapshot_test.go; then exit 1; fi; rg --sort path -n 'TestIngressSnapshot_RebuildConcurrentOverflowCloseCancelRace|guard[ABC]\.(Commit|Cancel)|DispatchableSnapshot' packages/go/streamgate/ingress_snapshot_test.go`
- `gofmt -d packages/go/streamgate/ingress_snapshot_test.go`
- `git diff --check`

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다.
