<!-- task=m-stream-evidence-gate-core/22+21_filter_observation_runtime plan=3 tag=REVIEW_REVIEW_REVIEW_FILTER_OBSERVATION_RUNTIME -->

# Plan - Arbiter 호출 시 all-complete happens-before 고정

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

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

## 배경

직전 후속은 두 ready evaluator의 동시 진입과 역순 반환을 만들었지만, evaluator의 `done` 신호가 coordinator의 outcome 수신보다 먼저 닫힌다. `done2` 직후의 외부 call-count 검사는 early arbitration goroutine이 아직 스케줄되지 않은 경우를 배제하지 못하므로, production 변경 없이 arbiter 호출 지점 자체에서 all-complete를 검증한다.

## Archive Evidence Snapshot

- 직전 계획 증거: `agent-task/m-stream-evidence-gate-core/22+21_filter_observation_runtime/plan_cloud_G05_2.log`
- 직전 리뷰 증거: `agent-task/m-stream-evidence-gate-core/22+21_filter_observation_runtime/code_review_cloud_G05_2.log`
- 판정: FAIL (`Required=1`, `Suggested=0`, `Nit=0`)
- Required finding: evaluator `done2` close와 coordinator outcome 처리 사이에 happens-before가 없어 직후 `arb.count()==0`이 early arbitration을 결정론적으로 배제하지 못한다.
- 영향 파일: `packages/go/streamgate/filter_observation_test.go`
- fresh 검증: focused tests, coordinator 50회 반복, package unit/race, gofmt, vet, diff check가 모두 통과했고 `evidence_integrity_failure=false`다. 실패 원인은 실행 증거가 아니라 all-complete oracle의 동기화 공백이다.
- 로드맵 계승: 승인·잠금 해제된 `Stream Evidence Gate Core` SDD의 S12 all-complete barrier를 직접 보강하고 S08 exact observation row를 회귀 증거로 유지한다. 특정 Milestone Task 완료를 주장하지 않으므로 `Roadmap Targets`는 두지 않는다.

## 분석 결과

### 읽은 파일

- `agent-task/m-stream-evidence-gate-core/22+21_filter_observation_runtime/plan_cloud_G05_2.log`
- `agent-task/m-stream-evidence-gate-core/22+21_filter_observation_runtime/code_review_cloud_G05_2.log`
- `agent-task/m-stream-evidence-gate-core/22+21_filter_observation_runtime/plan_cloud_G05_1.log`
- `agent-task/m-stream-evidence-gate-core/22+21_filter_observation_runtime/code_review_cloud_G05_1.log`
- `packages/go/streamgate/filter_observation_test.go`
- `packages/go/streamgate/parallel_evaluation.go`
- `packages/go/streamgate/parallel_evaluation_test.go`
- `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/21+14,19_filter_observation_contract/complete.log`
- `agent-ops/rules/project/domain/platform-common/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.md`

### SDD 기준

- SDD: `agent-roadmap/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md`, 상태 `[승인됨]`, 잠금 `해제`.
- S12 / `parallel-evaluation`: complete outcome set 전에는 Arbiter를 호출하지 않고 ready evaluation의 병렬 진입·역순 완료·race 증거가 있어야 한다.
- S08 / `filter-observation`: request/config/attempt/epoch/filter별 exact observation timeline은 현재 구현된 row oracle을 회귀 증거로 유지한다.
- Evidence Map의 S12 barrier/race 요구를 arbiter callback 내부 completion 검사와 focused/race 검증에 반영하고, S08 multi-consumer timeline은 기존 exact row 단언을 그대로 최종 검증에 포함한다.

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md`와 `agent-test/local/platform-common-smoke.md`를 읽었다.
- PATH에서 해석되는 host Go provenance를 기록하고 focused/package/race를 `-count=1`로 fresh 실행한다. 추가 도구를 repo 안에 생성하지 않는다.
- proto/config/wire 원본을 변경하지 않는 test-only 후속이므로 profile의 `make proto`, Edge-Node transport, full-cycle 항목은 적용 대상이 아니다. 대상 package 검증은 프로젝트 domain rule과 기존 Go test layout을 fallback source로 사용한다.
- 외부 runner, provider, Docker, device가 필요하지 않아 비-local 프리플라이트는 없다.

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

- `controlledGateEvaluator.doneCh`는 evaluator 반환 시 닫히지만 coordinator wrapper가 `FilterOutcome`을 만들고 `outcomeCh`로 보내기 전이므로, `done2` 수신 후 외부 call-count 확인은 coordinator 처리 완료를 뜻하지 않는다.
- 현재 `mockGateArbiter`는 호출 횟수와 batch/set만 저장하고 호출 순간 두 ready evaluator가 모두 완료됐는지 검사하지 않는다.
- `parallel_evaluation_test.go`의 기존 all-complete fixture도 시간 대기와 최종 call count를 사용하므로 이 observation 결합 fixture의 deterministic 대체 evidence가 아니다.

### 심볼 참조

- rename/remove되는 production 또는 test symbol은 없다.
- `mockGateArbiter`와 `controlledGateEvaluator`는 `packages/go/streamgate/filter_observation_test.go` 내부에서만 사용된다.

### 분할 판단

- arbiter invocation-time completion guard와 기존 observation row/suppression 회귀는 한 테스트 fixture와 한 package 검증으로 같은 S12 invariant를 닫으므로 하나의 compact plan으로 유지한다.
- subtask `22+21_filter_observation_runtime`의 predecessor 21은 `agent-task/archive/2026/07/m-stream-evidence-gate-core/21+14,19_filter_observation_contract/complete.log`의 PASS로 충족됐다.

### 범위 결정 근거

- `packages/go/streamgate/*.go` production 코드, `parallel_evaluation_test.go`, runtime lifecycle row 내용, config/proto/contract, Edge sink는 변경하지 않는다.
- 기존 두 evaluator entered handshake, 역순 release, exact four observation rows, non-ready outcome/suppression 단언은 삭제하거나 완화하지 않는다.

### 최종 라우팅

- `evaluation_mode=isolated-reassessment`, `finalizer=finalize-task-policy.sh`, `finalizer_mode=pair`.
- build/review closure: `scope_closed=true`, `context_closed=true`, `verification_closed=true`, `evidence_trusted=true`, `ownership_closed=true`, `decision_closed=true`; capability gap 없음.
- build grade scores: scope=1, state/concurrency=2, blast=0, evidence=1, verification=1 → G05, base `local-fit`.
- review grade scores: scope=1, state/concurrency=2, blast=0, evidence=1, verification=1 → G05.
- `large_indivisible_context=false`; positive loop-risk는 `temporal_state`, `concurrent_consistency`, `boundary_contract` 3개다.
- recovery signals: `review_rework_count=3`, `evidence_integrity_failure=false`; build route는 `recovery-boundary`, cloud G05, `PLAN-cloud-G05.md`다.
- review route는 `official-review`, cloud G05, Codex `gpt-5.6-sol` xhigh, `CODE_REVIEW-cloud-G05.md`다.

## 구현 체크리스트

- [ ] REVIEW_REVIEW_REVIEW_FILTER_OBSERVATION_RUNTIME-1 Arbiter 호출 순간 두 ready evaluator의 completion을 직접 검증해 early arbitration을 결정론적으로 실패시킨다.
- [ ] REVIEW_REVIEW_REVIEW_FILTER_OBSERVATION_RUNTIME-2 focused/unit/race/gofmt/vet/diff 검증을 fresh 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_REVIEW_REVIEW_FILTER_OBSERVATION_RUNTIME-1] invocation-time all-complete guard

- 문제: `packages/go/streamgate/filter_observation_test.go:1526-1528`의 `doneCh`는 evaluator 반환 직전에 닫히고, `packages/go/streamgate/filter_observation_test.go:1624-1635`는 coordinator가 해당 outcome을 처리했다는 신호 없이 외부에서 Arbiter count만 읽는다. 호출이 이 검사 뒤에 스케줄되는 early-arbitration 구현은 최종 count 1로 통과할 수 있다.
- 해결 방법:

Before (`packages/go/streamgate/filter_observation_test.go:1480-1495`):

```go
type mockGateArbiter struct {
	mu        sync.Mutex
	calls     int
	lastBatch EvidenceBatch
	lastSet   EvaluationSet
}

func (m *mockGateArbiter) Arbitrate(_ context.Context, b EvidenceBatch, s EvaluationSet) (ArbitrationResult, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.calls++
	m.lastBatch = b
	m.lastSet = s
```

After:

```go
type mockGateArbiter struct {
	mu                sync.Mutex
	calls             int
	lastBatch         EvidenceBatch
	lastSet           EvaluationSet
	requiredCompleted []<-chan struct{}
}

func (m *mockGateArbiter) Arbitrate(_ context.Context, b EvidenceBatch, s EvaluationSet) (ArbitrationResult, error) {
	for _, done := range m.requiredCompleted {
		select {
		case <-done:
		default:
			return ArbitrationResult{}, errors.New("arbiter invoked before all ready evaluators completed")
		}
	}
	m.mu.Lock()
	defer m.mu.Unlock()
	m.calls++
```

- 수정 파일 및 체크리스트:
  - [ ] `packages/go/streamgate/filter_observation_test.go`: `mockGateArbiter`가 invocation-time에 `done1`/`done2` 폐쇄를 직접 확인하도록 completion guard를 추가한다.
  - [ ] test goroutine 시작 전에 두 completion channel을 arbiter에 연결하고, filter 2 선완료 뒤 filter 1이 열린 상태에서의 early invocation이 반드시 오류로 드러나게 한다.
  - [ ] 두 ready evaluator 동시 진입, filter 2→1 역순 완료, 첫 완료 시 외부 count 0, 최종 count 1, 4-filter complete set, exact observation rows와 non-ready suppression 단언을 유지한다.
- 테스트 작성: `TestGateCoordinatorObservation_ParallelCompletionAndDeltaSuppression`을 강화한다. 새 테스트 파일이나 production seam은 추가하지 않는다.
- 중간 검증: `go test -count=1 ./packages/go/streamgate -run '^TestGateCoordinatorObservation_ParallelCompletionAndDeltaSuppression$'`가 통과해야 한다.

### [REVIEW_REVIEW_REVIEW_FILTER_OBSERVATION_RUNTIME-2] fresh verification evidence

- 문제: 기존 명령은 통과했지만 invocation-time guard가 없었으므로 강화 뒤 같은 checkout에서 S08/S12 회귀와 race를 다시 증명해야 한다.
- 해결 방법: 아래 최종 검증을 순서대로 실행하고 실제 stdout/stderr를 review stub에 기록한다. Go cache 결과는 사용하지 않는다.
- 수정 파일 및 체크리스트:
  - [ ] production 및 unrelated 파일이 변경되지 않았는지 확인한다.
  - [ ] Go provenance를 기록하고 focused/combined/package/race/gofmt/vet/diff 명령을 모두 성공시킨다.
- 테스트 작성: 항목 1에서 기존 fixture를 강화하므로 별도 테스트 파일은 만들지 않는다.
- 중간 검증: `git diff --check`가 clean이어야 한다.

## 의존 관계 및 구현 순서

invocation-time completion guard → focused/combined/package/race 검증 → review stub evidence 작성. Predecessor 21은 위 archived `complete.log`로 충족됐다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `packages/go/streamgate/filter_observation_test.go` | REVIEW_REVIEW_REVIEW_FILTER_OBSERVATION_RUNTIME-1 |

## 최종 검증

- `command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT`
- `go test -count=1 ./packages/go/streamgate -run '^TestGateCoordinatorObservation_ParallelCompletionAndDeltaSuppression$'`
- `go test -count=1 ./packages/go/streamgate -run '^(TestRuntimeObservation_LifecycleOrderAndCorrelation|TestRuntimeObservation_DeltaSuppression|TestGateCoordinatorObservation_ParallelCompletionAndDeltaSuppression)$'`
- `go test -count=1 ./packages/go/streamgate`
- `go test -race -count=1 ./packages/go/streamgate`
- `test -z "$(gofmt -l packages/go/streamgate/filter_observation_test.go)"`
- `go vet ./packages/go/streamgate/`
- `git diff --check`

기대 결과는 host Go provenance가 local rule과 일치하고 모든 검증 exit 0, gofmt 대상 없음, diff check clean이다.

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