<!-- task=m-stream-evidence-gate-core/06+01_evidence_tail plan=4 tag=REVIEW_REVIEW_REVIEW_REVIEW_API -->

# Stream Gate suffix confirm과 channel-scoped fragment invalidation 계획

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

구현과 검증을 완료한 항목만 체크하고, 마지막에 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 명령 출력으로 채운다. active pair는 그대로 두고 리뷰 준비 완료만 보고한다. 최종 판정, log rename, `complete.log`, task archive와 다음 상태 분류는 코드리뷰 에이전트 책임이다. 구현이 막히면 정확한 blocker, 실행한 명령/출력, 재개 조건만 구현 소유 evidence에 기록하며 사용자 질문, user-input tool, control-plane stop 파일, archive 작업은 수행하지 않는다.

## 배경

ready epoch는 partial confirm 뒤 원 snapshot의 suffix를 정확히 재준비하지만 두 번째 confirm에서 원래 sequence start를 다시 검사해 정상 release를 거부한다. Fragment completion도 같은 channel의 superseded token만 무효화해야 하나 모든 channel epoch를 지워 독립 channel의 release capability를 깨뜨린다. 기존 green 회귀가 두 경로와 2/200/500-rune Korean exact suffix를 검출하지 못하므로 state 전이와 공개 API evidence를 함께 폐쇄한다.

## Archive Evidence Snapshot

- 이전 plan: `agent-task/m-stream-evidence-gate-core/06+01_evidence_tail/plan_local_G06_3.log`
- 이전 review: `agent-task/m-stream-evidence-gate-core/06+01_evidence_tail/code_review_local_G07_3.log`
- 판정: `FAIL`
- Required:
  - partial confirm 뒤 epoch의 unconfirmed start가 전진하지 않아 `[b,c]` reprepare 뒤 두 번째 confirm이 `prepared snapshot prefix mismatch`로 실패한다.
  - fragment completion이 모든 channel epoch를 invalidated로 만들어 unrelated rolling channel의 outstanding token confirm을 거부한다.
  - reprepare 두 번째 confirm, fragment partial/full confirm과 cross-channel 격리, 2/200/500-rune oversized Korean exact suffix, 필수 public API error-checking 회귀가 빠져 있다.
- Suggested: 없음
- Nit: 없음
- 영향 파일: `packages/go/streamgate/evidence_tail.go`, `packages/go/streamgate/evidence_tail_test.go`, active review stub.
- 현재 검증 evidence:
  - fresh focused/full/race/vet/gofmt/diff는 PASS했다.
  - 공개 API 격리 재현은 `[a,b,c]` 중 1건 confirm 후 `[d,e]`를 append하고 같은 epoch를 재준비했을 때 `[b,c]` snapshot은 얻지만 두 번째 full confirm이 `streamgate: prepared snapshot prefix mismatch`로 실패함을 확인했다.
  - 별도 rolling `text` token 준비 후 `tool` channel fragment completion을 실행하면 unrelated token confirm이 `streamgate: confirm release token not found`로 실패함을 확인했다.
  - local transport unit은 host GOROOT의 `crypto/internal/fips140cache` 누락으로 setup failure다.
- Roadmap carryover: 이 subtask는 S01/S02 evidence-tail state와 S19 tail-side fail-closed signal만 준비한다. attempt cancel→terminal/no-release 통합과 `evidence-tail` Roadmap Task 완료는 dependent `07+01,06_commit_boundary`가 소유하므로 `Roadmap Targets`를 두지 않는다.
- 구현자는 위 두 task log와 선행 완료 근거 `agent-task/archive/2026/07/m-stream-evidence-gate-core/01_event_contract_types/complete.log` 외의 `agent-task/archive/**`를 탐색하지 않는다.

## 분석 결과

### 읽은 파일

- `AGENTS.md`
- `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-ops/skills/common/router.md`
- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/skills/common/finalize-task-routing/SKILL.md`
- `agent-ops/skills/common/plan/templates/review-stub-template.md`
- `agent-test/local/rules.md`
- `agent-test/local/platform-common-smoke.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-task/m-stream-evidence-gate-core/06+01_evidence_tail/plan_local_G06_3.log`
- `agent-task/m-stream-evidence-gate-core/06+01_evidence_tail/code_review_local_G07_3.log`
- `agent-task/archive/2026/07/m-stream-evidence-gate-core/01_event_contract_types/complete.log`
- `packages/go/streamgate/evidence_tail.go`
- `packages/go/streamgate/evidence_tail_test.go`
- `packages/go/streamgate/event.go`의 normalized/release event 구간
- `packages/go/streamgate/filter_contract.go`의 `cloneNormalizedEvent` 구간

`agent-spec/index.md`와 `agent-contract/index.md`에는 내부 evidence-tail release lifecycle에 매칭되는 별도 living spec/contract 문서가 없다. 코드, 승인 SDD와 공개 API 테스트를 직접 기준으로 삼는다.

### SDD 기준

- 문서: `agent-roadmap/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md`
- 상태: 승인됨, SDD 잠금 해제, 사용자 결정 항목 없음.
- S01 / `evidence-tail`: prepare→downstream write→confirm 순서, bounded pending/look-behind와 recovery preservation이 exact release capability를 요구한다.
- S02 / `evidence-tail`: Korean multi-byte input을 Unicode rune boundary로 보존해야 한다.
- S19 / `evidence-tail`: hard overflow는 no partial release로 fail-closed해야 하며 attempt-wide cancel/terminal은 dependent commit-boundary subtask 책임이다.
- Evidence Map의 S01/S02가 reprepare→second-confirm, 2/200/500 exact Korean suffix 회귀를 결정했다. S19는 기존 overflow signal 회귀를 유지하되 sibling의 attempt cancellation을 이 write-set에 끌어오지 않는 경계로 사용한다.

### 테스트 환경 규칙

- `test_env`: `local`
- `agent-test/local/rules.md`: 존재하며 전체를 읽었다. PATH에서 해석되는 host Go를 고정하고 별도 toolchain 우회를 금지한다.
- 매칭 profile: `agent-test/local/platform-common-smoke.md`
- host: `/config/.local/bin/go` → `/config/opt/go/bin/go`, `go1.26.2 linux/arm64`, `GOROOT=/config/opt/go`.
- 적용 검증: fresh focused/full package test, race, vet, gofmt, diff hygiene, proto generated tree status와 transport unit 실행.
- proto/config/wire source는 바꾸지 않으므로 `make proto`는 실행하지 않고 `git status --short -- proto/gen/iop`로 생성물 비변경만 확인한다.
- transport profile의 실제 package는 `./apps/edge/internal/transport`와 `./apps/node/internal/transport`다. 현재 host GOROOT의 `crypto/internal/fips140cache` 누락으로 setup failure이며, host Go 설치 복구가 재개 조건이다. 이 blocker는 evidence-tail 공개 API의 결정적 package/race 검증을 대체하지도 무효화하지도 않는다.
- cached test output은 허용하지 않으며 모든 Go test에 `-count=1`을 사용한다.

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

- 같은 epoch의 partial confirm 뒤 reprepare snapshot content는 일부 확인하지만 재준비 token의 두 번째 partial/full confirm과 최종 cursor/pending state는 확인하지 않는다.
- `CompleteFragment`의 same-channel supersession은 snapshot 길이만 확인하며 다른 channel outstanding token 보존을 검증하지 않는다.
- fragment completion regression은 새 epoch를 준비하지만 A/B safe prefix의 partial/full confirm 후 다음 suffix release를 끝까지 확인하지 않는다.
- look-behind tests는 작은 ASCII bound 중심이며 2/200/500-rune oversized Korean event의 exact trailing content를 표로 검증하지 않는다.
- Required regression 구간의 여러 constructor, `Append`, `PrepareRelease`, `ConfirmRelease`, `CompleteFragment` 반환 오류가 버려져 green evidence 신뢰를 낮춘다.

### 심볼 참조

- 공개 signature 변경은 없다.
- 관련 공개 API: `Append`, `PrepareRelease`, `ConfirmRelease`, `CompleteFragment`, `EffectiveLookBehind`.
- 내부 변경 후보: `epochRecord.snapStartSeq`, `confirmedCount`, `snapshotSize`, `prepared`, `token`, `invalidated`, `markEpochsInvalidated`, `preparedByChannel`.
- production caller는 아직 없고 현재 직접 call site는 `packages/go/streamgate/evidence_tail_test.go`와 dependent commit-boundary 계획이다.
- `RegisterFragment`, `tokenToChannel`, token 외부 parsing 계약은 새로 만들지 않는다.

### 분할 판단

split decision policy를 다시 평가했다. Sequence start advancement와 channel-scoped invalidation은 같은 `epochRecord`/`preparedByChannel` lifecycle을 수정하고, 두 결함을 폐쇄하는 테스트도 같은 두 파일과 public release flow를 공유한다. 별도 production/test subtask나 API/caller 분리는 동일 write-set 경쟁과 인위적 coordination만 만들므로 현재 `06+01_evidence_tail` 단일 plan을 유지한다. 디렉터리의 `+01` 선행 의존은 `agent-task/archive/2026/07/m-stream-evidence-gate-core/01_event_contract_types/complete.log`로 충족됐다.

### 범위 결정 근거

- write-set은 `packages/go/streamgate/evidence_tail.go`, `packages/go/streamgate/evidence_tail_test.go`, 이 task directory의 active implementation evidence로 제한한다.
- `ReleaseSink`, `CommitBoundary`, registry/policy, transport, proto/config, roadmap/SDD/spec/contract, sibling task, dispatcher-owned `WORK_LOG.md`는 수정하지 않는다.
- token 문자열은 opaque public value로 유지하고 signature/wire schema를 바꾸지 않는다.
- unrelated dirty worktree와 동시에 실행 중인 공식 review 산출물은 읽기/수정 대상에서 제외한다.

### 최종 라우팅

- evaluation mode: `isolated-reassessment`
- 격리 입력: 현재 두 public API 실패 재현, 두 소스/테스트 파일, 승인 SDD의 S01/S02/S19 경계, local 검증과 sibling ownership만 포함하고 이전 route-bearing 정보는 제외했다.
- build closures: scope/context/verification/evidence/ownership/decision 모두 `true`. 두 결함은 동일 state machine과 write-set 안에서 결정적 public regression으로 구현·검증 가능하다.
- build scores: scope coupling `1`, state/concurrency `2`, blast/irreversibility `1`, evidence diagnosis `1`, verification complexity `1`.
- build formatter output: lane `local`, grade `G06`, filename `PLAN-local-G06.md`.
- review closures: scope/context/verification/evidence/ownership/decision 모두 `true`. green suite와 실패 public repro를 함께 판정해야 하지만 현재 파일/명령으로 재현 가능하다.
- review scores: scope coupling `1`, state/concurrency `2`, blast/irreversibility `1`, evidence diagnosis `2`, verification complexity `1`.
- review formatter output: lane `local`, grade `G07`, filename `CODE_REVIEW-local-G07.md`.

## 구현 체크리스트

- [ ] `REVIEW_REVIEW_REVIEW_REVIEW_API-1` partial confirm만큼 epoch의 unconfirmed sequence start를 전진시켜 같은 epoch의 exact suffix를 재준비하고 두 번째 partial/full confirm까지 성공시키며 post-snapshot append는 계속 배제한다.
- [ ] `REVIEW_REVIEW_REVIEW_REVIEW_API-2` fragment safe-prefix completion이 같은 channel의 superseded epoch/token만 무효화하고 다른 channel의 outstanding prepared token과 confirm lifecycle은 보존하게 한다.
- [ ] `REVIEW_REVIEW_REVIEW_REVIEW_API-3` reprepare second confirm, fragment partial/full confirm과 cross-channel isolation, 2/200/500-rune oversized Korean exact suffix를 공개 API content assertion과 전 호출 error-checking으로 고정한다.
- [ ] fresh focused/full/race/vet, gofmt/diff/proto hygiene와 host transport setup 결과를 실제 출력으로 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_REVIEW_REVIEW_REVIEW_API-1] Reprepared suffix의 연속 confirm

**문제**

- `packages/go/streamgate/evidence_tail.go:1593`은 매 confirm마다 최초 `snapStartSeq`를 pending prefix 시작으로 요구한다.
- `packages/go/streamgate/evidence_tail.go:1605`가 confirmed prefix를 제거해도 epoch start가 전진하지 않아 같은 epoch의 재준비 suffix token을 confirm할 수 없다.
- `confirmedCount`는 선언만 되고 lifecycle 검증에 사용되지 않는다.

**해결 방법**

```go
// Before: partial confirm 뒤에도 최초 snapshot start를 재검사한다.
if !cs.pendingPrefixMatchesRange(n, targetEpoch.snapStartSeq) {
	return errPreparedSnapshotMismatch
}
cs.pendingEntries = cs.pendingEntries[n:]
```

```go
// After: 현재 unconfirmed prefix를 검증한 뒤 confirmed 수만큼 start를 전진한다.
if !cs.pendingPrefixMatchesRange(n, targetEpoch.snapStartSeq) {
	return errPreparedSnapshotMismatch
}
cs.pendingEntries = cs.pendingEntries[n:]
targetEpoch.snapStartSeq += n
targetEpoch.confirmedCount += n
```

- zero confirm은 start/cursor/pending을 바꾸지 않는다.
- partial confirm 뒤 `PrepareRelease`는 원 snapshot의 남은 sequence 범위만 반환한다.
- 재준비 token의 partial/full confirm은 성공하고 `snapEndSeq` 밖 post-append event는 남는다.
- consumed 판단, token uniqueness, stale old token rejection과 over-confirm 계약을 유지한다.

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

- [ ] `packages/go/streamgate/evidence_tail.go`: epoch unconfirmed start/confirmed count와 consumed lifecycle 정합화
- [ ] `packages/go/streamgate/evidence_tail_test.go`: `[a,b,c]` partial confirm→`[d,e]` append→`[b,c]` reprepare→second confirm 회귀

**테스트 작성**

- `TestEvidenceTailRepreparesOnlyUnconfirmedSnapshotSuffix`에 exact event content, 새 token, 두 번째 full confirm, cursor와 `[d,e]` pending 보존을 검증한다.
- zero-confirm과 stale 이전 token rejection은 기존 회귀를 유지하고 모든 오류를 검사한다.

**중간 검증**

```bash
/config/.local/bin/go test -count=1 ./packages/go/streamgate -run '^(TestEvidenceTailRepreparesOnlyUnconfirmedSnapshotSuffix|TestEvidenceTailRejectsInvalidReleaseLifecycle)$'
```

기대 결과: 두 번째 confirm 포함 전체 PASS, cached output 없음.

### [REVIEW_REVIEW_REVIEW_REVIEW_API-2] Fragment invalidation의 channel 격리

**문제**

- `packages/go/streamgate/evidence_tail.go:1345`의 계약은 같은 channel superseded token 무효화다.
- `packages/go/streamgate/evidence_tail.go:1388`은 global `markEpochsInvalidated()`와 map 전체 교체를 호출해 다른 channel token까지 제거한다.

**해결 방법**

```go
// Before
t.markEpochsInvalidated()
t.preparedByChannel = make(map[string]string)
```

```go
// After: 해당 channel의 superseded, non-consumed epoch/token만 무효화한다.
t.markChannelEpochsInvalidated(channel)
delete(t.preparedByChannel, channel)
```

- continuation/terminal discard는 기존 global invalidation helper를 계속 사용한다.
- fragment completion은 같은 channel의 오래된 prepared token을 stable error로 거부하되 unrelated channel token은 정상 confirm된다.
- helper는 prepared/token/snapshot state를 함께 지워 stale ownership을 남기지 않는다.

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

- [ ] `packages/go/streamgate/evidence_tail.go`: channel-scoped epoch/token invalidation helper와 `CompleteFragment` 적용
- [ ] `packages/go/streamgate/evidence_tail_test.go`: rolling/text outstanding token + fragment/tool completion 교차-channel 회귀

**테스트 작성**

- `TestEvidenceTailFragmentCompletionCreatesSafePrefixEpoch` 또는 명확한 별도 public test에서 unrelated channel token confirm 성공과 same-channel old token rejection을 함께 확인한다.
- interleaved fragment A/B의 safe-prefix partial/full confirm 뒤 남은 suffix content를 검증한다.

**중간 검증**

```bash
/config/.local/bin/go test -count=1 ./packages/go/streamgate -run '^(TestEvidenceTailFragmentCompletionCreatesSafePrefixEpoch|TestEvidenceTailRecoveryTransitions|TestEvidenceTailRejectsInvalidReleaseLifecycle)$'
```

기대 결과: same-channel supersession과 cross-channel independence 모두 PASS.

### [REVIEW_REVIEW_REVIEW_REVIEW_API-3] Public regression evidence 폐쇄

**문제**

- `packages/go/streamgate/evidence_tail_test.go:2583`은 reprepare 길이만 보고 두 번째 confirm을 실행하지 않는다.
- `packages/go/streamgate/evidence_tail_test.go:2714`는 fragment release를 prepare만 하고 partial/full confirm과 다른 channel 격리를 검증하지 않는다.
- `packages/go/streamgate/evidence_tail_test.go:2933`에는 2/200/500-rune oversized Korean exact suffix 표가 없다.
- Required 구간에 `_`로 버리는 constructor/lifecycle error가 다수 남아 green test가 잘못된 setup을 숨길 수 있다.

**해결 방법**

- 기존 Required regression을 table/subtest로 정리해 모든 constructor, `Append`, `PrepareRelease`, `ConfirmRelease`, `CompleteFragment`, release content accessor 오류를 즉시 `Fatalf` 처리한다.
- suffix test는 length가 아니라 ordered text content, committed cursor와 post-snapshot pending content를 확인한다.
- Korean table은 bound `2`, `200`, `500`마다 bound보다 큰 한글 단일 event를 release/confirm하고 `EffectiveLookBehind`가 정확히 trailing bound runes인지, caller mutation이 내부 state를 바꾸지 않는지 확인한다.
- fragment test는 A/B completion마다 준비된 exact prefix를 partial/full confirm하고 다음 epoch가 이미 confirmed된 event를 다시 release하지 않는지 확인한다.

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

- [ ] `packages/go/streamgate/evidence_tail_test.go`: reprepare/fragment/Korean table public regression과 전 호출 error-checking
- [ ] `packages/go/streamgate/evidence_tail_test.go`: `t.Skip`, 우회 epoch, internal-only assertion 없이 public lifecycle 결과를 고정

**테스트 작성**

- 기존 `TestEvidenceTailRepreparesOnlyUnconfirmedSnapshotSuffix`, `TestEvidenceTailFragmentCompletionCreatesSafePrefixEpoch`, `TestEvidenceTailBoundsAndCopiesEffectiveLookBehind`를 보강한다.
- 새 테스트가 더 명확하면 cross-channel isolation만 별도 `TestEvidenceTailFragmentCompletionPreservesOtherChannels`로 분리할 수 있으나 같은 파일을 유지한다.

**중간 검증**

```bash
/config/.local/bin/go test -count=1 ./packages/go/streamgate -run '^(TestEvidenceTailRepreparesOnlyUnconfirmedSnapshotSuffix|TestEvidenceTailFragmentCompletionCreatesSafePrefixEpoch|TestEvidenceTailFragmentCompletionPreservesOtherChannels|TestEvidenceTailBoundsAndCopiesEffectiveLookBehind)$'
```

기대 결과: 존재하는 테스트 이름 전체 PASS, exact content/cursor/pending assertion 포함.

## 의존 관계 및 구현 순서

1. epoch partial-confirm start advancement를 먼저 고쳐 same-epoch second confirm을 복구한다.
2. recovery global invalidation과 fragment channel-scoped invalidation을 분리한다.
3. public regression을 두 state 전이에 연결하고 Korean exact suffix 표를 보강한다.
4. fresh package/race/vet/hygiene와 transport setup evidence를 기록한다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `packages/go/streamgate/evidence_tail.go` | `REVIEW_REVIEW_REVIEW_REVIEW_API-1`, `REVIEW_REVIEW_REVIEW_REVIEW_API-2` |
| `packages/go/streamgate/evidence_tail_test.go` | `REVIEW_REVIEW_REVIEW_REVIEW_API-1`, `REVIEW_REVIEW_REVIEW_REVIEW_API-2`, `REVIEW_REVIEW_REVIEW_REVIEW_API-3` |
| `agent-task/m-stream-evidence-gate-core/06+01_evidence_tail/CODE_REVIEW-local-G07.md` | 구현 체크, 변경/evidence와 실제 검증 출력 |

## 최종 검증

```bash
command -v go
readlink -f "$(command -v go)"
go version
go env GOROOT
/config/.local/bin/go test -count=1 ./packages/go/streamgate -run '^(TestEvidenceTailBindsOnlyReadyEpochSnapshots|TestEvidenceTailRepreparesOnlyUnconfirmedSnapshotSuffix|TestEvidenceTailRecoveryTransitions|TestEvidenceTailTerminalGateConfiguredTrigger|TestEvidenceTailFragmentCompletionCreatesSafePrefixEpoch|TestEvidenceTailFragmentCompletionPreservesOtherChannels|TestEvidenceTailBoundsAndCopiesEffectiveLookBehind|TestEvidenceTailRejectsInvalidReleaseLifecycle|TestEvidenceTailPreservesExactKoreanSuffixContent)$'
/config/.local/bin/go test -count=1 ./packages/go/streamgate -run '^Test(FilterHoldRequirement|EvidencePlan|EvidenceTail)'
/config/.local/bin/go test -race -count=1 ./packages/go/streamgate
/config/.local/bin/go vet ./packages/go/streamgate
gofmt -d packages/go/streamgate/evidence_tail.go packages/go/streamgate/evidence_tail_test.go
git diff --check -- packages/go/streamgate agent-task/m-stream-evidence-gate-core/06+01_evidence_tail
git status --short -- proto/gen/iop
! rg -n --sort path 't\.Skip|RegisterFragment|tokenToChannel' packages/go/streamgate/evidence_tail.go packages/go/streamgate/evidence_tail_test.go
/config/.local/bin/go test -count=1 ./apps/edge/internal/transport ./apps/node/internal/transport
```

기대 결과: focused/full/race/vet/gofmt/diff/proto/search는 PASS하고 cached test output은 없다. Transport unit은 PASS가 원칙이며, host GOROOT의 `crypto/internal/fips140cache` 누락이 지속되면 실제 setup failure와 host Go 복구 조건을 그대로 기록한다. 다른 toolchain으로 우회하지 않는다.

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