<!-- task=m-stream-evidence-gate-core/19+18_core_runtime_loop plan=13 tag=REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_CORE_RUNTIME_LOOP -->

# Plan - Non-zero empty replacement range preservation

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

`CODE_REVIEW-cloud-G03.md`의 구현 에이전트 소유 섹션을 채우는 것이 구현의 필수 마지막 단계다. 아래 검증을 실행해 실제 내용과 stdout/stderr를 기록하고 active 파일을 그대로 둔 채 리뷰 준비 완료를 보고한다. finalization, 로그 rename, `complete.log`, archive 이동은 code-review 전용이다. 차단되면 구현 소유 evidence 필드에 정확한 blocker, 시도한 명령/출력, 재개 조건만 기록한다. 사용자에게 질문하거나 user-input 도구/control-plane stop 파일을 만들거나 다음 상태를 분류하거나 로그를 archive하지 않는다.

## 배경

captured range exact-width 검사는 이미 소비된 non-empty epoch의 replacement 재사용을 막지만, pending이 완전히 비었을 때 ready epoch의 시작 sequence가 현재 monotonic 위치가 아니라 `0`으로 돌아간다. 그 결과 release 이력이 있는 정상 terminal trigger가 `[n,n)` empty range로 표현되지 않고 replacement가 거부된다. 이 후속은 empty-state sequence 기준점을 바로잡고 기존 stale non-empty 거부를 유지한다.

## Archive Evidence Snapshot

- 이전 Plan: `agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/plan_local_G03_12.log`
- 이전 Review: `agent-task/m-stream-evidence-gate-core/19+18_core_runtime_loop/code_review_cloud_G03_12.log`
- 판정: `FAIL`
- 문제 수: Required 1, Suggested 0, Nit 0
- 문제 요약: `firstPendingSequence()`가 빈 pending에서 `nextSequence`가 아닌 `0`을 반환해, 이전 prefix를 전부 release한 뒤 terminal이 오면 정상 empty epoch가 `[0,n)`으로 기록되고 exact-width replacement prepare가 거부된다.
- 영향 파일: `packages/go/streamgate/evidence_tail.go`, `packages/go/streamgate/evidence_tail_test.go`.
- 실제 검증 evidence:
  - `/config/.local/bin/go` → `/config/opt/go/bin/go`, Go `1.26.2`, `GOROOT=/config/opt/go`였다.
  - `make proto`, package build, focused/contract/package unit·race, gofmt, addition guard, import boundary, `git diff --check`는 fresh 실행에서 통과했다.
  - prefix event 하나를 full confirm한 뒤 terminal을 append한 임시 package probe는 `terminal empty range = [0,1), want [n,n)`로 실패했다. probe 파일은 제거됐다.
- 필요한 경우 위 두 log만 좁게 읽는다. `agent-task/archive/**`를 탐색하지 않는다.
- Roadmap carryover: 없음. 이 split subtask PASS만으로 Milestone 기능 Task 완료를 주장하지 않으므로 `Roadmap Targets`를 두지 않는다.

## 분석 결과

### 읽은 파일

- `packages/go/streamgate/evidence_tail.go`
- `packages/go/streamgate/evidence_tail_test.go`
- `packages/go/streamgate/stream_release.go`
- `packages/go/streamgate/stream_release_test.go`
- `packages/go/streamgate/runtime.go`
- `packages/go/streamgate/runtime_test.go`
- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/common/rules-agent-spec.md`
- `agent-ops/rules/project/domain/platform-common/rules.md`
- `agent-ops/rules/project/domain/testing/rules.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-spec/index.md`
- `agent-contract/index.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`, 상태 `[승인됨]`, SDD 잠금 `해제`, 사용자 리뷰 없음.
- S01 → Milestone Task `evidence-tail`: pending range와 committed look-behind/cursor는 실제 downstream release 이후의 monotonic 위치를 유지해야 한다. release 뒤 empty terminal epoch도 현재 sequence의 `[n,n)`으로 표현한다.
- S07 → Milestone Task `policy-hook`: channel-bound single buffer의 sequence identity가 empty/non-empty trigger에 동일하게 적용돼야 한다.
- S13 → Milestone Task `decision-arbiter`: legitimate terminal replacement는 stale non-empty rejection과 구분돼 single action으로 실행 가능해야 한다.
- Evidence Map의 S01 range/cursor assertion을 non-zero empty range 회귀에 반영한다. 이 plan은 해당 Milestone Task 전체 완료를 주장하지 않는다.

### 테스트 환경 규칙

- `test_env=local`; `agent-test/local/rules.md`를 읽었다.
- `packages/go/**` 변경에 매칭되는 `agent-test/local/platform-common-smoke.md`를 읽었다. host Go identity, `make proto`, fresh package unit/race와 `git diff --check`를 적용한다.
- wire/proto/config 및 사용자 실행 entrypoint는 바꾸지 않으므로 Edge-Node full-cycle은 적용하지 않는다. 모든 Go test에 `-count=1`을 사용한다.

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

- 기존 `empty_range_terminal_trigger_accepts_replacement`는 새 channel의 `[0,0)`만 검증한다.
- 이전 pending prefix를 full confirm해 `nextSequence > 0`, pending empty가 된 뒤 terminal replacement를 허용하는 `[n,n)` 회귀가 없다.
- stale consumed non-empty rejection, zero/partial/full settlement와 runtime exact output 회귀는 기존 테스트가 유지한다.

### 심볼 참조

- rename/remove 없음.
- `firstPendingSequence()` 호출은 terminal/provider-error trigger epoch, threshold-ready epoch, fragment completion epoch 생성 세 곳이다. threshold와 fragment completion은 pending이 non-empty인 경로이고, empty fallback 변경은 terminal/provider-error trigger의 현재 monotonic boundary를 바로잡는다.

### 분할 판단

- 기존 split `m-stream-evidence-gate-core/19+18_core_runtime_loop`의 후속이며 predecessor `18`은 `agent-task/archive/2026/07/m-stream-evidence-gate-core/18+14,16_runtime_contract/complete.log`의 PASS로 충족됐다.
- 한 package-private helper의 empty-state 반환값과 같은 EvidenceTail regression만 수정한다. 별도 API, caller, ownership 또는 독립 검증 경계가 없어 추가 split은 인위적이다.

### 범위 결정 근거

- `prepareReplacement`/`confirmReplacement`의 exact-width 검사와 `StreamReleaser`, `RequestRuntime`, public 계약은 변경하지 않는다.
- `agent-roadmap`, `agent-spec`, `agent-contract`, `agent-ops`와 dispatcher-owned `WORK_LOG.md`는 수정하지 않는다.
- non-empty stale invalidation 정책을 재설계하지 않고 ready epoch의 empty sequence anchor만 현재 monotonic 위치로 보정한다.

### 최종 라우팅

- evaluation_mode: `isolated-reassessment`
- finalizer: `finalize-task-policy.sh`; finalizer_mode: `pair`
- build closures: `scope_closed=true`, `context_closed=true`, `verification_closed=true`, `evidence_trusted=true`, `ownership_closed=true`, `decision_closed=true`.
  - scope/context: `firstPendingSequence`의 세 call site와 failing non-zero empty probe로 영향 경계가 닫혔다.
  - verification/evidence: focused regression, package unit/race와 exact range assertion으로 성공을 결정적으로 판정할 수 있다.
  - ownership/decision: `packages/go/streamgate` 내부에서 끝나며 사용자 결정이나 외부 환경이 필요 없다.
- build route_basis: `local-fit`; capability_gap: `none`; grade scores: `scope_coupling=0`, `state_concurrency=1`, `blast_irreversibility=0`, `evidence_diagnosis=1`, `verification_complexity=1`; lane/grade/file: `local`, `G03`, `PLAN-local-G03.md`; floor: `none`.
- review closures: `scope_closed=true`, `context_closed=true`, `verification_closed=true`, `evidence_trusted=true`, `ownership_closed=true`, `decision_closed=true`.
- review route_basis: `official-review`; capability_gap: `none`; grade scores: `scope_coupling=0`, `state_concurrency=1`, `blast_irreversibility=0`, `evidence_diagnosis=1`, `verification_complexity=1`; lane/grade/file: `cloud`, `G03`, `CODE_REVIEW-cloud-G03.md`; adapter/model/effort: `codex`, `gpt-5.6-sol`, `xhigh`; floor: `none`.
- build loop-risk observations:
  - ordered_transitions: state_count=3 (`prefix released`, `empty terminal epoch created`, `replacement settled`), adverse_paths=`stale non-empty|fresh empty|non-zero empty`; evidence=`evidence_tail.go:1529-1536,1658-1662,2023-2029`과 failing probe.
  - concurrent_consistency: actor_count=1 (`serializer owner`), constraints=`monotonic sequence and single channel reservation`; evidence=`EvidenceTail` package-private call path.
  - boundary_contract: component_count=1, consumer_count=1, constraints=`exact captured range and monotonic empty boundary`; evidence=`firstPendingSequence` call sites.
  - structured_interpretation: mechanisms=0, hazards=[]; evidence=`none`.
  - variant_product: independent_axis_count=2 (`empty/non-empty range`, `initial/non-zero sequence`), combination_verification_required=true; evidence=`TestEvidenceTailReplacementSettlement`과 failing probe.
  - matched signatures: `temporal_state`, `variant_product`; triggered=true.
- missing_evidence: `[]`; blocked_reason: `null`.

## 구현 체크리스트

- [ ] pending이 비었을 때 ready epoch 시작점이 `nextSequence`를 사용해 release 이력이 있는 terminal/provider-error trigger도 exact `[n,n)` range를 갖게 한다.
- [ ] `TestEvidenceTailReplacementSettlement`에 fully released prefix 뒤 non-zero empty terminal replacement prepare/confirm 성공과 state 보존 회귀를 추가한다.
- [ ] fresh focused/package unit·race, format, guard, import boundary와 diff 검증을 통과시킨다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_CORE_RUNTIME_LOOP-1] Preserve non-zero empty epoch boundary

#### 문제

`packages/go/streamgate/evidence_tail.go:1658-1662`는 pending이 비면 sequence history와 무관하게 `0`을 반환한다.

```go
func (cs *channelState) firstPendingSequence() int {
    if len(cs.pendingEntries) == 0 {
        return 0
    }
    return cs.pendingEntries[0].sequence
}
```

prefix를 전부 confirm한 channel은 `nextSequence > 0`이고 pending이 비어 있다. 이때 terminal trigger record가 `[0,nextSequence)`로 만들어져, 논리적으로 empty인데도 exact-width replacement validation이 stale non-empty range처럼 거부한다.

#### 해결 방법

pending이 비면 다음 pending entry가 가질 monotonic sequence인 `cs.nextSequence`를 반환한다. non-empty일 때는 기존 첫 entry sequence를 유지한다.

```go
func (cs *channelState) firstPendingSequence() int {
    if len(cs.pendingEntries) == 0 {
        return cs.nextSequence
    }
    return cs.pendingEntries[0].sequence
}
```

이 변경은 fresh channel에서 계속 `[0,0)`을 만들고, release 이력이 있는 empty channel에서는 `[n,n)`을 만든다. threshold/fragment call site는 pending non-empty 전제라 기존 결과가 유지된다.

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

- [ ] `packages/go/streamgate/evidence_tail.go`: empty pending의 first sequence를 `nextSequence`로 반환한다.
- [ ] `packages/go/streamgate/evidence_tail_test.go`: rolling prefix를 full confirm한 뒤 terminal epoch range, replacement prepare/confirm, pending/cursor/token state를 검증한다.

#### 테스트 작성

- 확장: `TestEvidenceTailReplacementSettlement/empty_range_after_fully_released_prefix_accepts_replacement`. rolling event 하나를 prepare/full confirm해 `nextSequence > 0`과 empty pending을 만든다. terminal epoch가 `[n,n)`인지, replacement prepare/confirm이 성공하는지, original pending이 다시 소비되지 않고 replacement sink progress만 cursor/look-behind에 반영되는지 assertion한다.
- 유지: `stale_consumed_non_empty_range_rejected`와 기존 fresh `[0,0)` empty case가 함께 통과해 stale/legitimate 구분을 고정한다.

#### 중간 검증

- `gofmt -d packages/go/streamgate/evidence_tail.go packages/go/streamgate/evidence_tail_test.go`: 출력 없음.
- `go test -count=1 -v ./packages/go/streamgate -run '^TestEvidenceTailReplacementSettlement$'`: fresh/non-zero empty와 stale non-empty 회귀 PASS.
- `go test -race -count=1 ./packages/go/streamgate -run '^TestEvidenceTailReplacementSettlement$'`: race 없이 PASS.

## 의존 관계 및 구현 순서

1. predecessor `18`은 `agent-task/archive/2026/07/m-stream-evidence-gate-core/18+14,16_runtime_contract/complete.log`로 충족됐다.
2. empty pending sequence anchor를 수정한다.
3. non-zero empty와 기존 stale non-empty 조합 회귀를 실행한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `packages/go/streamgate/evidence_tail.go` | REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_CORE_RUNTIME_LOOP-1 |
| `packages/go/streamgate/evidence_tail_test.go` | REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_REVIEW_CORE_RUNTIME_LOOP-1 |

## 최종 검증

- `command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT`: host Go path/version/GOROOT 기록.
- `make proto && go build ./packages/go/streamgate`: proto generation 및 package build PASS.
- `gofmt -d packages/go/streamgate/evidence_tail.go packages/go/streamgate/evidence_tail_test.go`: 출력 없음.
- `go test -count=1 -v ./packages/go/streamgate -run '^(TestEvidenceTailReplacementSettlement|TestStreamReleaserReplacementSettlement|TestRequestRuntimeReplacementProposalRelease)$'`: fresh focused PASS.
- `go test -race -count=1 ./packages/go/streamgate -run '^(TestEvidenceTailReplacementSettlement|TestStreamReleaserReplacementSettlement|TestRequestRuntimeReplacementProposalRelease)$'`: focused race PASS.
- `go test -count=1 -v ./packages/go/streamgate -run '^(TestEvidenceTailTerminalGateConfiguredTrigger|TestEvidenceTailFragmentCompletionPreservesOtherChannels|TestStreamReleaserConfirmsExactlySinkProgress|TestRequestRuntimeLifecycleMatrix)$'`: terminal/channel/progress/runtime contract regression PASS.
- `go test -count=1 ./packages/go/streamgate`: fresh package PASS; cache 출력 불허.
- `go test -race -count=1 ./packages/go/streamgate`: fresh package race PASS.
- `! git diff -U0 -- packages/go/streamgate/evidence_tail.go packages/go/streamgate/evidence_tail_test.go | rg '^\+' | rg 'DEBUG|, _ := New|_ = New|desc, _ :=|cause, _ :=|causes, _ :='`: 출력 없음.
- `! rg -n --sort path 'iop/apps/' packages/go/streamgate/evidence_tail.go`: 출력 없음.
- `git diff --check`: 출력 없음.

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