<!-- task=m-stream-evidence-gate-core/05+02_parallel_evaluation_coordinator plan=2 tag=REVIEW_REVIEW_API -->

# Stream Gate Parallel Evaluation Coordinator 2차 FAIL 후속 구현 계획

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

- 아래 구현 체크리스트와 검증 명령을 완료하고 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 변경 내용과 stdout/stderr를 기록한다.
- active PLAN/CODE_REVIEW 파일은 그대로 두고 review-ready만 보고한다. 판정, 로그 rename, `complete.log`, task archive는 code-review 책임이다.
- 선택된 Milestone `구현 잠금 > 결정 필요`가 구현을 막을 때만 review stub의 `사용자 리뷰 요청`에 정확한 연결 근거를 기록하고 중단한다.
- 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하거나 `USER_REVIEW.md`를 만들지 않는다. 환경/secret/서비스, 일반 범위 조정, 후속 에이전트가 닫을 수 있는 evidence 공백은 사용자 리뷰 요청이 아니다.

## 배경

두 번째 리뷰에서도 계획된 coordinator production/test 파일이 모두 없고 구현·검증 기록이 placeholder라 `FAIL` 판정이 유지됐다. 승인된 SDD S12가 요구하는 immutable batch 병렬 평가, all-complete barrier, single-flight와 bounded backpressure를 기존 evaluation contract 위에 실제 구현하고 결정적 race/ordering evidence로 닫는다.

## 사용자 리뷰 요청 흐름

선택된 Milestone `구현 잠금 > 결정 필요`만 active review stub의 `사용자 리뷰 요청`에 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식을 따르며, 구현 에이전트의 직접 사용자 질문은 금지된다. 요청 검증과 실제 `USER_REVIEW.md` 작성은 code-review가 소유한다.

## Archive Evidence Snapshot

- 이전 plan: `agent-task/m-stream-evidence-gate-core/05+02_parallel_evaluation_coordinator/plan_local_G05_1.log`
- 이전 review: `agent-task/m-stream-evidence-gate-core/05+02_parallel_evaluation_coordinator/code_review_local_G05_1.log`
- Verdict: `FAIL`
- 이슈: Required 2, Suggested 0, Nit 0
- 영향 파일: `packages/go/streamgate/parallel_evaluation.go`, `packages/go/streamgate/parallel_evaluation_test.go`, active review stub 구현 소유 섹션
- 실패 evidence: 두 신규 파일과 `GateCoordinator` 계열 심볼이 없고, Go `1.24.12`의 test-list 명령이 테스트 이름 없이 package `ok`만 출력했다. review stub의 구현 체크리스트와 검증 결과도 미작성 상태였다.
- 환경 evidence: `go version go1.24.12 linux/arm64`, 명시적 `GOROOT` 확인은 exit 0이었다. 두 미존재 파일에 대한 `gofmt -d`는 각각 `no such file or directory`와 exit 2를 반환했다.
- 선행 완료: `agent-task/archive/2026/07/m-stream-evidence-gate-core/02+01_evaluation_contract/complete.log`
- Roadmap carryover: S12 / `parallel-evaluation`의 complete outcome set, deferred-vs-event-only, single-flight evidence를 충족해야 한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/stream-evidence-gate-core.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/stream-evidence-gate-core.md)
- Task ids:
  - `parallel-evaluation`: immutable batch 병렬 평가, complete outcome barrier, single-flight와 bounded backpressure
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `packages/go/streamgate/evaluation_contract.go`
- `packages/go/streamgate/evaluation_contract_test.go`
- `packages/go/streamgate/filter_contract.go`
- `packages/go/streamgate/filter_contract_test.go`
- `go.mod`
- `Makefile`
- `.gitignore`
- `agent-ops/rules/project/domain/platform-common/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.md`
- `agent-spec/index.md`
- `agent-contract/index.md`
- `agent-roadmap/current.md`
- `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`
- `agent-task/archive/2026/07/m-stream-evidence-gate-core/02+01_evaluation_contract/complete.log`
- `agent-task/m-stream-evidence-gate-core/05+02_parallel_evaluation_coordinator/plan_local_G05_1.log`
- `agent-task/m-stream-evidence-gate-core/05+02_parallel_evaluation_coordinator/code_review_local_G05_1.log`

### SDD 기준

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md`
- 상태: `[승인됨]`, SDD 잠금 `해제`, 사용자 리뷰 `없음`
- 대상 Acceptance Scenario: S12 / Milestone Task `parallel-evaluation`
- Evidence Map S12: evaluated/deferred/not-applicable mixed outcomes, barrier, reverse completion, race, backpressure, failure/cancel fixtures가 필요하다.
- 이 mapping에 따라 `REVIEW_REVIEW_API-1`은 mixed complete set과 barrier를, `REVIEW_REVIEW_API-2`는 single-flight/queue/cancel/deadline/failure policy를 구현·검증하고 focused fresh 및 package race를 최종 evidence로 남긴다.

### 테스트 환경 규칙

- `test_env`: local
- `agent-test/local/rules.md`: 존재하며 전체를 읽었다.
- 매칭 profile: `agent-test/local/platform-common-smoke.md`
- 기본 runner `go1.26.2 linux/arm64`, `GOROOT=/config/opt/go`는 이전 검증에서 `crypto/internal/fips140cache` setup failure가 있어 transport 검증에 사용하지 않는다.
- 정상 runner는 `/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64/bin/go`, `GOROOT=/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64`, `GOTOOLCHAIN=local`이며 module 선언 Go `1.24`와 일치한다. 이번 재확인에서 version/env와 package test-list 실행이 성공했다.
- 적용 명령: 명시적 Go `1.24.12`로 test-name 확인, fresh/race package test, vet, Edge/Node transport test를 실행하고 `make proto`, `gofmt -d`, scoped `git diff --check`, proto 생성물 status를 확인한다.
- 외부 runner/secret/service와 `<확인 필요>`는 없다. 이 task는 local fake evaluator/arbiter로 closure한다.

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

- ready filter 동시 시작, reverse completion, stable complete set, Arbiter all-complete barrier가 구현·테스트되지 않았다.
- not-applicable/deferred가 evaluator 호출 없이 complete set에 포함되는 coordinator evidence가 없다.
- epoch single-flight, bounded pending queue, FIFO accepted work와 saturation backpressure가 구현·테스트되지 않았다.
- caller cancel, host shutdown, per-filter deadline, blocking/observe error normalization과 idempotent close evidence가 없다.
- 기존 `EvidenceBatch` 불변성과 `EpochFilter`/`EvaluationSet` validation은 predecessor 테스트가 커버하며 새 coordinator 테스트가 이를 실행 경계에서 재사용한다.

### 심볼 참조

- rename/remove 없음.
- 새 public symbol 후보는 `EpochArbiter`, `GateCoordinatorOptions`, `GateCoordinator`, `NewGateCoordinator`, `Evaluate`, `Close`이며 기존 call site는 없다.
- 새 third-party dependency는 없다. `context`, `errors`, `sync`, `time` 등 Go 표준 라이브러리만 사용한다.
- `agent-spec/index.md`와 `agent-contract/index.md`에 이 package-local coordinator와 매칭되는 living spec/계약 문서는 없다. 외부 API, wire, config schema를 변경하지 않으므로 별도 spec/contract 갱신은 이 task 범위가 아니다.

### 분할 판단

- split decision policy를 plan 파일 선택 전에 적용했다.
- shared task group: `m-stream-evidence-gate-core`
- 현재 subtask: `05+02_parallel_evaluation_coordinator`
- predecessor `02+01_evaluation_contract`는 `agent-task/archive/2026/07/m-stream-evidence-gate-core/02+01_evaluation_contract/complete.log`의 PASS로 충족됐다.
- fan-out/barrier와 worker/backpressure는 같은 coordinator lifecycle과 같은 두 파일을 함께 변경하며 한쪽만으로 유효한 public contract가 되지 않는다. production과 최소 race/ordering 테스트를 분리하면 independently reviewable unit가 되지 않으므로 현재 단일 subtask를 유지한다.

### 범위 결정 근거

- 수정 허용: 새 `packages/go/streamgate/parallel_evaluation.go`, `packages/go/streamgate/parallel_evaluation_test.go`.
- 기존 `evaluation_contract.go`와 `filter_contract.go`는 소비만 하고 계약을 재정의하지 않는다.
- Registry resolution, concrete decision arbitration/recovery action, request mutation, host dispatch, observation sink, Edge/Node adoption, proto/config/API/spec/contract/roadmap 문서는 변경하지 않는다.
- raw evaluator error는 저장·전달하지 않고 stable `filter_evaluation_error` 또는 `filter_evaluation_deadline`만 outcome에 남긴다.

### 최종 라우팅

- evaluation_mode: `isolated-reassessment`
- build closures: scope/context/verification/evidence/ownership/decision 모두 `true`
  - scores: scope coupling 1, state/concurrency 2, blast/irreversibility 0, evidence diagnosis 0, verification complexity 1
  - result: `local`, `G04`, `PLAN-local-G04.md`
- review closures: scope/context/verification/evidence/ownership/decision 모두 `true`
  - scores: scope coupling 1, state/concurrency 2, blast/irreversibility 0, evidence diagnosis 1, verification complexity 1
  - result: `local`, `G05`, `CODE_REVIEW-local-G05.md`

## 구현 체크리스트

- [ ] `REVIEW_REVIEW_API-1` ready filter를 같은 immutable batch에서 동시에 실행하고 non-ready outcome을 함께 모아 stable complete set 이후에만 EpochArbiter를 한 번 호출하도록 구현·테스트한다.
- [ ] `REVIEW_REVIEW_API-2` epoch worker를 single-flight로 유지하고 bounded pending queue, FIFO accepted work, caller cancel, host shutdown, per-filter deadline, blocking-fatal/observe-error 정책을 leak 없이 처리하도록 구현하고 ordering/race 테스트를 fresh 실행한다.
- [ ] 명시적 Go `1.24.12` toolchain으로 계획의 전체 local 검증 명령을 fresh/race 조건에서 실행하고 실제 stdout/stderr와 exit code를 review stub에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_API-1] Parallel fan-out와 all-complete Arbiter barrier

- 문제:
  - `packages/go/streamgate/evaluation_contract.go:14-22`는 synchronous evaluator seam만 제공하고 coordinator 실행은 없다.
  - `packages/go/streamgate/evaluation_contract.go:233-240`은 stable `EvaluationSet`을 정의하지만 completion barrier와 one-shot Arbiter 호출이 없다.
- 해결 방법:
  - `EpochArbiter.Arbitrate(context.Context, EvidenceBatch, EvaluationSet) error` seam과 validated `GateCoordinator`를 새 파일에 둔다.
  - `Evaluate(ctx, batch, filters)`는 batch와 filter ID/timeout/enforcement/evaluator/duplicate를 fan-out 전에 검증한다.
  - ready filter마다 goroutine 하나를 시작하고 filter 수로 bound된 result channel에서 전부 수집한다. non-ready filter는 evaluator를 호출하지 않고 `NormalizeOutcome()`으로 complete set에 포함한다.
  - successful decision은 `NewFilterOutcomeEvaluated`, evaluator error나 invalid decision은 raw 문자열 없이 `filter_evaluation_error`, per-filter timeout은 `filter_evaluation_deadline`으로 정규화한 뒤 `NewEpochFilterOutcome`과 `NewEvaluationSet`을 통과시킨다.
  - caller/host context가 살아 있고 stable complete set이 준비된 경우에만 Arbiter를 정확히 한 번 호출한다.
- Before (`evaluation_contract.go:14-22`):

```go
type FilterEvaluator interface {
	ID() string
	Evaluate(context.Context, EvidenceBatch) (FilterDecision, error)
}
```

- After (`parallel_evaluation.go`):

```go
type EpochArbiter interface {
	Arbitrate(context.Context, EvidenceBatch, EvaluationSet) error
}

func (c *GateCoordinator) Evaluate(
	ctx context.Context,
	batch EvidenceBatch,
	filters []EpochFilter,
) (EvaluationSet, error)
```

- 수정 파일 및 체크리스트:
  - [ ] `packages/go/streamgate/parallel_evaluation.go`: input preflight, ready fan-out, buffered all-complete collection, stable set, one-shot Arbiter call
  - [ ] `packages/go/streamgate/parallel_evaluation_test.go`: mixed outcomes, simultaneous start, reverse completion, complete-set barrier, stable order, evaluator/Arbiter call count, immutable batch
- 테스트 작성:
  - `TestGateCoordinatorWaitsForCompleteOutcomeSet`: ready 두 개를 동시에 시작해 역순 완료시키고 deferred/not-applicable을 포함한 네 outcome 전에는 Arbiter가 호출되지 않음을 검증한다.
  - `TestGateCoordinatorPassesSameImmutableBatch`: evaluator accessor mutation이 다른 evaluator와 원본 batch에 영향을 주지 않고 epoch/capturedAt snapshot이 일치함을 검증한다.
- 중간 검증:
  - `env GOROOT=/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64 GOTOOLCHAIN=local /config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64/bin/go test -count=1 ./packages/go/streamgate -run '^TestGateCoordinator(WaitsForCompleteOutcomeSet|PassesSameImmutableBatch)$'`
  - 기대 결과: PASS

### [REVIEW_REVIEW_API-2] Single-flight, bounded backpressure와 cancel/failure policy

- 문제:
  - `packages/go/streamgate/evaluation_contract.go:126-128`은 per-filter timeout 값만 노출하고 적용 주체가 없다.
  - `packages/go/streamgate/evaluation_contract.go:180-218`은 failure disposition을 정의하지만 evaluator error/deadline을 stable outcome으로 만드는 lifecycle이 없다.
  - 현재 package에는 epoch worker나 bounded ingress queue가 없어 SDD S12의 overlap/unbounded queue 금지를 충족하지 못한다.
- 해결 방법:
  - `NewGateCoordinator(hostCtx, GateCoordinatorOptions{MaxQueuedEpochs: n}, arbiter)`가 positive bound와 non-nil 의존성을 검증하고 host-derived cancel context, 단일 jobs channel/worker, idempotent `Close`를 소유한다.
  - jobs channel capacity는 pending epoch 수만 허용한다. active epoch 하나 외에 configured 수만 queue하고 추가 caller는 내부 적재 없이 send select에서 block되며 caller cancel/host close로 해제된다.
  - worker는 accepted job을 FIFO로 하나씩 실행하고 dequeue 전에 caller가 취소된 job은 evaluator/Arbiter 호출 없이 완료한다.
  - active epoch는 caller cancel/host close를 filter child context에 전파한다. epoch context가 취소되면 partial set과 Arbiter 호출 없이 종료하고, filter timeout만 발생했을 때 stable deadline outcome을 만든다.
  - filter는 context 준수 계약을 따른다. buffered result/reply channel과 worker wait를 사용해 cancel 경로의 producer/submitter leak을 막고 `Close`는 여러 번 호출해도 안전하게 queued waiter를 해제한다.
- Before (`evaluation_contract.go:126-128`):

```go
// EvaluationTimeout returns the positive duration allocated to a single
// Evaluate call.
func (f EpochFilter) EvaluationTimeout() time.Duration { return f.timeout }
```

- After (`parallel_evaluation.go`):

```go
type GateCoordinatorOptions struct {
	MaxQueuedEpochs int
}

func NewGateCoordinator(
	hostCtx context.Context,
	options GateCoordinatorOptions,
	arbiter EpochArbiter,
) (*GateCoordinator, error)
```

- 수정 파일 및 체크리스트:
  - [ ] `packages/go/streamgate/parallel_evaluation.go`: single worker, bounded queue, FIFO job lifecycle, caller/host context composition, deadline/error normalization, idempotent close
  - [ ] `packages/go/streamgate/parallel_evaluation_test.go`: max concurrent epoch, queue saturation, FIFO accepted work, caller cancel, host close, deadline/error disposition, repeated close, goroutine/race cleanup
- 테스트 작성:
  - `TestGateCoordinatorSingleFlightAndBoundedBackpressure`: active epoch 1개와 configured pending 수만 수용하고 추가 submit이 backpressure를 받으며 accepted epoch 순서와 max concurrent epoch가 1임을 검증한다.
  - `TestGateCoordinatorCancellationSkipsArbiter`: queued caller cancel, active caller cancel, host close가 evaluator context와 waiters를 해제하고 Arbiter 호출 수를 늘리지 않으며 repeated close가 안전함을 검증한다.
  - `TestGateCoordinatorNormalizesDeadlineAndFailurePolicy`: blocking/observe evaluator error와 per-filter deadline을 stable code/disposition으로 구분하고 raw error가 outcome에 없음을 검증한다.
- 중간 검증:
  - `env GOROOT=/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64 GOTOOLCHAIN=local /config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64/bin/go test -race -count=1 ./packages/go/streamgate -run '^TestGateCoordinator(SingleFlightAndBoundedBackpressure|CancellationSkipsArbiter|NormalizesDeadlineAndFailurePolicy)$'`
  - 기대 결과: PASS, race 없음

## 의존 관계 및 구현 순서

1. predecessor `02+01_evaluation_contract`는 `agent-task/archive/2026/07/m-stream-evidence-gate-core/02+01_evaluation_contract/complete.log`로 충족됐다.
2. `REVIEW_REVIEW_API-1`의 input preflight, fan-out, all-complete set과 Arbiter 경계를 먼저 구현한다.
3. `REVIEW_REVIEW_API-2`의 단일 worker/queue가 `REVIEW_REVIEW_API-1`의 한 epoch 실행 함수를 직렬화한다. directory 이름에 없는 의존은 추가하지 않는다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `packages/go/streamgate/parallel_evaluation.go` | REVIEW_REVIEW_API-1, REVIEW_REVIEW_API-2 |
| `packages/go/streamgate/parallel_evaluation_test.go` | REVIEW_REVIEW_API-1, REVIEW_REVIEW_API-2 |

## 최종 검증

1. `/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64/bin/gofmt -d packages/go/streamgate/parallel_evaluation.go packages/go/streamgate/parallel_evaluation_test.go`
   - 기대 결과: stdout 없음
2. `env GOROOT=/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64 GOTOOLCHAIN=local /config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64/bin/go test ./packages/go/streamgate -list '^TestGateCoordinator(WaitsForCompleteOutcomeSet|PassesSameImmutableBatch|SingleFlightAndBoundedBackpressure|CancellationSkipsArbiter|NormalizesDeadlineAndFailurePolicy)$'`
   - 기대 결과: 다섯 테스트 이름이 모두 출력되고 package가 PASS
3. `env GOROOT=/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64 GOTOOLCHAIN=local /config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64/bin/go test -count=1 ./packages/go/streamgate -run '^TestGateCoordinator(WaitsForCompleteOutcomeSet|PassesSameImmutableBatch|SingleFlightAndBoundedBackpressure|CancellationSkipsArbiter|NormalizesDeadlineAndFailurePolicy)$'`
   - 기대 결과: PASS
4. `env GOROOT=/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64 GOTOOLCHAIN=local /config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64/bin/go test -race -count=1 ./packages/go/streamgate -run '^TestGateCoordinator'`
   - 기대 결과: PASS, race 없음
5. `env GOROOT=/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64 GOTOOLCHAIN=local /config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64/bin/go test -race -count=1 ./packages/go/streamgate`
   - 기대 결과: PASS, race 없음
6. `env GOROOT=/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64 GOTOOLCHAIN=local /config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64/bin/go vet ./packages/go/streamgate`
   - 기대 결과: PASS, stdout 없음
7. `make proto`
   - 기대 결과: 성공
8. `env GOROOT=/config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64 GOTOOLCHAIN=local /config/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.12.linux-arm64/bin/go test -count=1 ./apps/edge/internal/transport ./apps/node/internal/transport`
   - 기대 결과: PASS
9. `git diff --check -- packages/go/streamgate proto/gen/iop`
   - 기대 결과: stdout 없음
10. `git status --short -- proto/gen/iop`
   - 기대 결과: stdout 없음

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