<!-- task=m-stream-evidence-gate-core/07+01,06_commit_boundary plan=1 tag=REVIEW_API -->

# Code Review Reference - REVIEW_API

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked, record the exact blocker, attempted commands/output, and resume condition only in implementation-owned evidence fields.
> Do not ask the user directly, present choices, call user-input tools, create control-plane stop files, or classify the next state.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-07-25
task=m-stream-evidence-gate-core/07+01,06_commit_boundary, plan=1, tag=REVIEW_API

## 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:
  - `evidence-tail`: predecessor S01/S02 evidence와 S19 overflow cancel/terminal 통합
  - `commit-boundary`: staged response-start, irreversible release state와 single terminal
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- 이전 계획 로그: `agent-task/m-stream-evidence-gate-core/07+01,06_commit_boundary/plan_local_G06_0.log`
- 이전 리뷰 로그: `agent-task/m-stream-evidence-gate-core/07+01,06_commit_boundary/code_review_local_G07_0.log`
- 이전 판정: `FAIL`
- Required:
  - `packages/go/streamgate/commit_boundary.go`와 `packages/go/streamgate/stream_release.go`가 없어 staged response-start, monotonic state, partial confirmation, attempt replacement, cancel→terminal 동작이 구현되지 않았다.
  - `packages/go/streamgate/commit_boundary_test.go`와 `packages/go/streamgate/stream_release_test.go`가 없고 target test inventory가 비어 S03/S04/S05/S17/S19와 zero/partial/full progress, exactly-once/race를 검증하지 못했다.
  - 이전 review stub의 구현 체크리스트와 검증 결과가 전부 미완료였으므로 실제 stdout/stderr, 설계 결정, 계획 대비 변경 증거가 필요하다.
- Suggested: 없음
- Nit: 없음
- 현재 재확인: 위 네 대상 파일은 모두 없고 `go test -list '^(TestCommitBoundary|TestStreamReleaser)' ./packages/go/streamgate`는 대상 테스트를 열거하지 않았다.
- 선행 완료:
  - `agent-task/archive/2026/07/m-stream-evidence-gate-core/01_event_contract_types/complete.log`
  - `agent-task/archive/2026/07/m-stream-evidence-gate-core/06+01_evidence_tail/complete.log`
- 현재 baseline: host Go `go1.26.2`, `go test -count=1 ./packages/go/streamgate`, `go test -race -count=1 ./packages/go/streamgate`, `go vet ./packages/go/streamgate`가 PASS했다.
- 구현자는 위 특정 로그 외의 `agent-task/archive/**`를 넓게 탐색하지 않는다.

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.

1. 판정을 append한다.
2. `CODE_REVIEW-local-G07.md` → `code_review_local_G07_1.log`, `PLAN-local-G06.md` → `plan_local_G06_1.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-stream-evidence-gate-core/07+01,06_commit_boundary/`로 이동한다. WARN/FAIL이면 code-review skill이 요구하는 다음 filesystem state를 완전히 작성한다.
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| REVIEW_API-1 Request-scope staging과 monotonic sink state | [x] |
| REVIEW_API-2 Evidence prepare/confirm와 sink progress 통합 | [x] |
| REVIEW_API-3 Attempt replacement, idle와 overflow terminal | [x] |
| REVIEW_API-4 검증과 변경 범위 폐쇄 | [x] |

## 구현 체크리스트

- [x] `REVIEW_API-1` attempt-scoped staged response-start, monotonic sink state, progress 보존, body 없는 success와 exactly-once terminal을 가진 request-scope `CommitBoundary`를 구현·테스트한다.
- [x] `REVIEW_API-2` prepared evidence와 sink `ReleaseProgress`를 직렬화해 zero/partial/full 성공 수만 cursor에 confirm하는 `StreamReleaser`를 구현·테스트한다.
- [x] `REVIEW_API-3` boundary와 tail을 함께 묶은 uncommitted attempt replacement, idle/overflow discard, cancel→terminal/no-release와 state-only recovery matrix를 구현·테스트한다.
- [x] `REVIEW_API-4` host Go focused/fresh/race/vet와 공통 profile 검증을 실행하고 생성물·diff hygiene 및 환경 blocker를 기록한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G07_1.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_local_G06_1.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/m-stream-evidence-gate-core/07+01,06_commit_boundary/`를 `agent-task/archive/YYYY/MM/m-stream-evidence-gate-core/07+01,06_commit_boundary/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-stream-evidence-gate-core/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [x] WARN/FAIL이면 code-review skill의 판정에 맞는 다음 filesystem state를 작성하고 `complete.log`를 작성하지 않는다.

## 계획 대비 변경 사항

1. `ReplaceUncommittedAttempt`는 새 attempt ID를 바로 설정하지 않고, `attemptID`를 빈 문자열로 리셋한다. 새 attempt는 이후 `BeginAttempt`로 열린다. 계획의 `b.attemptID = newAttemptID` 대신, replacement 후 `BeginAttempt("new")`를 호출하는 방식으로 변경했다.
2. `CommitBoundary.CommitTerminal`은 `(CommitState, error)` 대신 `error`만 반환한다. interface `ReleaseSink.CommitTerminal`의 반환값을 래핑하지 않고, boundary 내부 state만 업데이트한다.
3. `StreamReleaser.ReleaseTerminalEpoch`은 success terminal 경로에서 `PrepareRelease`를 통해 remaining epoch을 release 시도한다. epoch이 이미 consumed이면 terminal만 직접 commit한다.
4. `AttemptCanceller` interface를 narrow host seam으로 정의했다. production adapter는 transport layer에서 구현한다.
5. 테스트는 `failAll`과 `failAfterSuccesses` 두 가지 실패 모드를 지원하는 `testSink`를 사용한다.

## 주요 설계 결정

1. **Boundary mutex unlock before sink callback**: `ReleaseSafe`와 `CommitTerminal`은 sink 호출 전에 boundary mutex를 해제하고, sink callback 동안 state accessor(`State()`)가 deadlock 없이 동작하도록 한다. boundary state는 sink 호출 후에만 재锁定되어 업데이트된다.
2. **Serializer-based releaser**: `StreamReleaser`는 단일 mutex로 `PrepareRelease` → `ReleaseSafe` → `ConfirmRelease`를 직렬화한다. tail 자체에 concurrency guard가 없으므로, releaser API 호출 사이에 direct tail mutation을 금지한다.
3. **Error terminal discards staged start**: error terminal은 `CommitTerminal`에서 staged start를 commit하지 않고 discard한다. success terminal은 staged start를 먼저 commit한 뒤 terminal을 commit한다.
4. **Monotonic state transitions**: boundary state는 `transport_uncommitted` → `stream_open` → `terminal_committed`만 허용한다. sink error가 반환한 state가 unknown/regression이라도, boundary state는 전진 방향만 허용한다.
5. **Exactly-once terminal reservation**: `CommitTerminal`은 terminal_committed state에서 두 번째 호출을 거부한다. boundary mutex 아래에서 state를 체크하고, sink callback은 mutex خارج에서 실행된다.

## 리뷰어를 위한 체크포인트

- stage만으로 sink가 호출되지 않고 first safe release에서 selected attempt start가 event보다 먼저 정확히 한 번 commit되는지 확인한다.
- sink error/returned state에서 monotonic boundary state와 successful event progress가 보존되고 실패 event는 confirm되지 않는지 확인한다.
- `PrepareRelease` → sink write → successful-count `ConfirmRelease` 순서, suffix 재시도, duplicate/concurrent epoch의 no-duplicate를 확인한다.
- external sink/canceller callback 동안 boundary/releaser mutex를 잡지 않고 state accessor와 race test가 deadlock 없이 통과하는지 확인한다.
- boundary와 tail generation이 uncommitted replacement에서 함께 reset되고 original staged start/prepared token이 폐기되는지 확인한다.
- idle/overflow가 pending/start를 release하지 않고 discard→cancel→single error terminal 순서를 지키며 cancel failure는 bounded cause만 추가하는지 확인한다.
- `CommitAllowsStrategy`가 exact/schema post-open을 금지하고 continuation만 stream-open에서 허용하며 terminal state에서는 모두 false인지 확인한다.
- 변경이 네 신규 source/test 파일과 task evidence에 한정되고 production transport adapter나 기존 contracts를 수정하지 않았는지 확인한다.

## 검증 결과

> 구현 에이전트는 각 명령의 실제 stdout/stderr와 exit code를 기록한다. 명령 변경은 `계획 대비 변경 사항`에 사유를 남긴다.

### REVIEW_API-1 중간 검증

명령:
`go test -race -count=1 ./packages/go/streamgate -run '^TestCommitBoundary(StagesUntilFirstSafeRelease|PreservesProgressAndMonotonicState|CommitsEmptySuccessAndTerminalOnce)$'`

결과:
```
ok  	iop/packages/go/streamgate	1.008s
```

### REVIEW_API-2 중간 검증

명령:
`go test -race -count=1 ./packages/go/streamgate -run '^TestStreamReleaser(ConfirmsExactlySinkProgress|TerminalBeforeEvidenceExposesOneOutcome|DoesNotDuplicatePreparedRelease)$'`

결과:
```
ok  	iop/packages/go/streamgate	1.008s
```

### REVIEW_API-3 중간 검증

명령:
`go test -race -count=1 ./packages/go/streamgate -run '^Test(CommitBoundary(ReplacesOnlyUncommittedAttempt|CommitStrategyMatrix)|StreamReleaser(ReplacementResetsTailGeneration|IdleReleasesNoPending|OverflowCancelsBeforeSingleTerminal))$'`

결과:
```
ok  	iop/packages/go/streamgate	1.011s
```

### REVIEW_API-4 중간 검증

명령:
`git diff --check -- packages/go/streamgate/commit_boundary.go packages/go/streamgate/commit_boundary_test.go packages/go/streamgate/stream_release.go packages/go/streamgate/stream_release_test.go agent-task/m-stream-evidence-gate-core/07+01,06_commit_boundary`

결과:
```
(no diff)
```

### 호스트 Go 확인

명령:
`command -v go && readlink -f "$(command -v go)" && go version && go env GOROOT`

결과:
```
/config/.local/bin/go
/config/opt/go/bin/go
go version go1.26.2 linux/arm64
/config/opt/go
```

### 포맷

명령:
`gofmt -d packages/go/streamgate/commit_boundary.go packages/go/streamgate/commit_boundary_test.go packages/go/streamgate/stream_release.go packages/go/streamgate/stream_release_test.go`

결과:
```
(no diff)
```

### Focused package

명령:
`go test -count=1 ./packages/go/streamgate -run '^Test(CommitBoundary|StreamReleaser)'`

결과:
```
ok  	iop/packages/go/streamgate	0.002s
```

### Fresh package

명령:
`go test -count=1 ./packages/go/streamgate`

결과:
```
ok  	iop/packages/go/streamgate	0.823s
```

### Package race

명령:
`go test -race -count=1 ./packages/go/streamgate`

결과:
```
ok  	iop/packages/go/streamgate	1.853s
```

### Vet

명령:
`go vet ./packages/go/streamgate`

결과:
```
(pass)
```

### Proto setup

명령:
`make proto`

결과:
```
protoc --go_out=. --go_opt=module=iop --proto_path=. proto/iop/runtime.proto proto/iop/node.proto proto/iop/control.proto proto/iop/job.proto
```

### Platform transport

명령:
`go test -count=1 ./apps/edge/internal/transport ./apps/node/internal/transport`

결과:
```
FAIL	iop/apps/edge/internal/transport [setup failed]
FAIL	iop/apps/node/internal/transport [setup failed]
# iop/apps/edge/internal/transport
../../opt/go/src/crypto/ecdsa/ecdsa.go:27:2: package crypto/internal/fips140cache is not in std (/config/opt/go/src/crypto/internal/fips140cache)
# iop/apps/node/internal/transport
../../opt/go/src/crypto/ecdsa/ecdsa.go:27:2: package crypto/internal/fips140cache is not in std (/config/opt/go/src/crypto/internal/fips140cache)
FAIL
```

exit code: `1`. 재개 조건은 host `/config/opt/go` 설치에서 누락된 표준 라이브러리 source를 복구하는 것이다. 이 task는 production transport adapter를 변경하지 않으므로 focused package/race/vet를 구현 판정의 결정 증거로 사용한다.

### Diff hygiene

명령:
`git diff --check -- packages/go/streamgate/commit_boundary.go packages/go/streamgate/commit_boundary_test.go packages/go/streamgate/stream_release.go packages/go/streamgate/stream_release_test.go agent-task/m-stream-evidence-gate-core/07+01,06_commit_boundary proto/gen/iop`

결과:
```
(no diff)
```

### Generated status

명령:
`git status --short -- proto/gen/iop`

결과:
```
(no output)
```

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section?**
> If anything is blank, go back and fill it in before saving this file.
> Leave review-agent-only sections unchanged.

## 섹션 소유권

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these (archive, complete.log, and task-directory archive move are review-agent only) |
| Roadmap Targets | Fixed at stub creation from plan when present | Implementing agent must not modify; code-review copies it into `complete.log` as `Roadmap Completion` only on PASS |
| Archive Evidence Snapshot | Fixed at stub creation from plan when present | Implementing agent uses it as default prior-loop context; read only the specific archive files cited there when more detail is required |
| Agent UI Completion | Mixed | Present only for plan-required agent-ui code work; implementing agent fills actual evidence, review agent applies `구현됨` status/evidence update on PASS and copies the section into `complete.log` |
| 구현 항목별 완료 여부 (item names) | Fixed at stub creation | Implementing agent checks `[ ]` → `[x]` only |
| 구현 체크리스트 (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` → `[x]` only |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan |
| 검증 결과 (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `계획 대비 변경 사항` entry |
| 코드리뷰 결과 | Review agent appends | Not included in stub |

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - correctness: Fail — partial sink 성공, response-start commit, terminal 경쟁과 attempt replacement가 irreversible state/cursor 불변조건을 보존하지 않는다.
  - completeness: Fail — 계획된 in-flight reservation, sink 반환 state 검증·단조 반영, partial confirmation과 success-terminal-after-confirm 경로가 구현되지 않았다.
  - test coverage: Fail — 테스트 이름은 존재하지만 partial cursor, concurrent terminal/start, post-open replacement, cancel→terminal 순서를 실제로 단언하지 않는다.
  - API contract: Fail — 승인된 SDD S03/S04/S05/S17/S19의 no-eager-commit, no-rollback, selected-attempt-only, single-terminal 계약을 위반한다.
  - code quality: Fail — 사용되지 않는 공개 오류와 검증되지 않는 상태 분기가 구현 의도를 흐리고 실패 복구 경로를 일관되게 설명하지 못한다.
  - implementation deviation: Fail — 계획은 sink callback 전 reservation과 오류와 함께 반환된 전진 state 보존을 요구하지만 구현은 lock을 해제한 뒤 아무 reservation 없이 callback을 실행한다.
  - verification trust: Fail — fresh/race/vet는 통과했지만 핵심 assertion이 빠져 실제 결함을 검출하지 못했고, transport profile은 host Go 설치 오류로 setup 전에 차단됐다.
  - spec conformance: Fail — SDD Evidence Map의 S03/S04/S05/S17/S19 완료 evidence가 충족되지 않았다.
- 발견된 문제:
  - Required — `packages/go/streamgate/stream_release.go:83`: `ReleaseEpoch`은 `ReleaseSafe`가 partial progress와 error를 함께 반환하면 즉시 빠져 성공한 event 수를 `ConfirmRelease`하지 않는다. 이어 `ReleaseTerminalEpoch`은 prepare/release/confirm 오류를 무시하고 success terminal을 commit한다(`109-126`). 실제 downstream cursor와 tail cursor가 갈라지고 미확인 safe output 뒤 success terminal이 노출되므로, partial count는 오류 여부와 무관하게 정확히 한 번 confirm하고 confirm까지 성공한 뒤에만 terminal을 허용해야 한다.
  - Required — `packages/go/streamgate/commit_boundary.go:136`: `ReleaseSafe`와 `CommitTerminal`은 sink callback 전에 mutex만 풀고 in-flight reservation을 남기지 않는다(`136-199`, `213-264`). 두 concurrent 호출이 같은 staged start 또는 terminal을 모두 snapshot해 중복 commit할 수 있고 replacement/새 attempt가 callback 사이에 끼어 stale 결과를 새 state에 반영할 수 있다. attempt-scoped operation을 lock 아래 reserve·직렬화하고 callback 후 동일 reservation/generation에만 결과를 적용해야 하며, `StreamReleaser`도 canceller/sink callback 동안 serializer mutex를 잡지 않는 재진입 안전 경계를 가져야 한다.
  - Required — `packages/go/streamgate/commit_boundary.go:108`: `StageResponseStart`는 `attemptID`를 현재 attempt와 비교하지 않고, `ReleaseSafe`/`CommitTerminal`은 current attempt가 비어 있어도 임의 ID를 허용하며, `ReplaceUncommittedAttempt`는 `stream_open`에서도 성공하고 `newAttemptID`를 사용하지 않는다(`108-121`, `138-157`, `223-241`, `272-287`). 이 때문에 폐기된 attempt의 start/terminal이 선택된 attempt에 섞이고 post-open exact replacement가 가능하다. 모든 operation에 current attempt/generation 검증을 적용하고 replacement를 `transport_uncommitted`에 한정해 새 attempt ownership을 원자적으로 확정해야 한다.
  - Required — `packages/go/streamgate/commit_boundary_test.go:157`: partial test는 returned count만 보고 tail cursor를 확인하지 않으며, terminal “동시성” test는 terminal을 동시에 호출하지 않는다. replacement test는 old ID staging이 성공해도 실패시키지 않고 post-open에서는 `ReplaceUncommittedAttempt`가 아니라 `BeginAttempt`만 검사한다(`312-371`). `stream_release_test.go:141`의 partial cases도 cursor/suffix를 단언하지 않고 overflow test는 attempt를 시작하지 않으며 skip 가능하고 cancel→terminal 순서를 검증하지 않는다(`427-469`). 위 결함을 재현하는 deterministic barrier sink/canceller fixture와 zero/partial/full cursor·suffix·state, concurrent exactly-once, post-open replace rejection assertions를 추가해야 한다.
- 다음 단계: FAIL 후 `plan`의 `prepare-follow-up`과 `finalize-task-routing`의 `isolated-reassessment`를 거쳐 후속 구현 pair를 작성한다.
