<!-- task=inflight-accounting-recovery/01_node_terminal_events plan=1 tag=REVIEW_G06 -->

# Plan - REVIEW_G06: Terminal Event Review Follow-up

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

이 계획은 직전 code-review FAIL을 닫기 위한 좁은 follow-up이다. 사용자에게 직접 질문하지 말고, 새 결정이 필요해 보이면 `CODE_REVIEW-local-G06.md`의 `사용자 리뷰 요청` 섹션에만 증거를 기록한 뒤 멈춘다.

## Roadmap Targets

- 없음. 원격 field 검증에서 발견된 독립 bug fix task다.

## Archive Evidence Snapshot

- Archived plan: `agent-task/inflight-accounting-recovery/01_node_terminal_events/plan_local_G06_0.log`
- Archived review: `agent-task/inflight-accounting-recovery/01_node_terminal_events/code_review_local_G06_0.log`
- Verdict: FAIL
- Required summary:
  - `agent-task/inflight-accounting-recovery/01_node_terminal_events/CODE_REVIEW-local-G06.md:43`: 구현 evidence가 Go 테스트 중심이며, `scripts/dev/edge.sh` + `scripts/dev/node.sh` 기반 repo 내부/full-cycle user-flow completion evidence가 없다. Reviewer가 `./scripts/e2e-smoke.sh`를 실행해 통과를 확인했지만 해당 스크립트 자체가 보조 smoke라고 보고했다.
  - `apps/node/internal/node/node.go:323`: `runtime.ErrRunCancelled` 합성 분기가 Edge-visible `RunEvent{type:"cancelled"}`로 도착하는 regression test가 없다.
- Reviewer verification evidence:
  - `go test -count=1 ./apps/node/internal/node ./apps/node/internal/adapters/openai_compat`: PASS.
  - `go test -count=1 ./apps/node/...`: 첫 실행에서 `TestCodexAppServerProcCwd`가 일시 실패, 단일 재실행 PASS, 전체 재실행 PASS.
  - `./scripts/e2e-smoke.sh`: PASS, 단 completion에는 dev edge/node user-flow verification이 별도로 필요하다고 출력.
- Reviewer safe repairs already applied:
  - `apps/node/internal/node/sink_test.go`: `gofmt`.
  - `apps/node/internal/node/node.go`: `synthAndEmitTerminal` 주석을 실제 flush 위치와 맞춤.
- Narrow archive reread allowed if needed:
  - `agent-task/inflight-accounting-recovery/01_node_terminal_events/code_review_local_G06_0.log`
  - `agent-task/inflight-accounting-recovery/01_node_terminal_events/plan_local_G06_0.log`

## 범위 결정 근거

production terminal synthesis 구조는 유지한다. follow-up 범위는 누락된 cancellation regression test와 프로젝트 testing 규칙을 만족하는 검증 evidence 수집으로 제한한다. 외부 CLI profile 자체는 변경하지 않았으므로 deterministic mock CLI profile로 repo 내부 edge-node user-flow를 확인한다.

## 구현 체크리스트

- [x] [REVIEW_G06-1] Edge-visible cancelled terminal regression test를 추가한다.
- [x] [REVIEW_G06-2] repo 내부 edge-node user-flow/full-cycle 검증 evidence를 수집하고 기록한다.
- [x] `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

### [REVIEW_G06-1] Edge-visible Cancelled Terminal Regression

#### 문제

`synthAndEmitTerminal()`은 `runtime.ErrRunCancelled`를 `EventTypeCancelled`로 합성하지만, 현재 추가된 integration test는 `complete`, `error`, resolve failure만 Edge에서 관측한다. 원래 accounting leak는 요청 중단/terminal 누락과 맞닿아 있으므로 `cancelled` terminal event도 mock Edge가 실제로 받는지 확인해야 한다.

#### 해결 방법

`apps/node/internal/node/node_concurrency_integration_test.go`에 cancellation 전용 regression test를 추가한다. adapter가 terminal event 없이 `runtime.ErrRunCancelled`를 반환하게 하고, mock Edge listener가 같은 run id의 `RunEvent{type:"cancelled"}`를 받는지 검증한다. 가능하면 같은 run id에서 `complete` 또는 `error` terminal이 추가로 도착하지 않는 짧은 settle 검증도 포함한다.

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

- [x] `apps/node/internal/node/node_concurrency_integration_test.go`: `TestOnRunRequest_SynthesizedCancelledObservedByEdge` 추가.
- [x] 새 test double(`cancelAdapter`)은 파일-local로 두고 기존 helper 패턴을 따랐다.
- [x] production code 변경 없음.

#### 중간 검증

```bash
go test -count=1 ./apps/node/internal/node -run 'TestOnRunRequest_Synthesized(Cancelled|Terminal|Error)ObservedByEdge|TestIntegration_ResolveAdapterErrorObservedByEdge' -v
```

### [REVIEW_G06-2] Repo Internal Edge-Node User-flow Evidence

#### 문제

node execution/stream terminal event 경로 변경은 프로젝트 testing 규칙상 Go 테스트만으로 완료 처리하지 않는다. 직전 루프에는 Go 테스트 evidence만 있었고, reviewer가 추가 실행한 `./scripts/e2e-smoke.sh`도 스스로 보조 smoke라고 보고했다.

#### 해결 방법

`agent-ops/skills/project/e2e-smoke/SKILL.md` 기준으로 repo 내부 edge-node user-flow evidence를 수집한다. `scripts/dev/edge.sh`와 `scripts/dev/node.sh`를 임시 config/임시 포트로 실행하고, 같은 session 메시지 2회, `/nodes`, `/capabilities`, `/transport`, `/sessions`, `/terminate-session` 결과를 edge/node 출력에서 확인한다. 자동화된 `./scripts/e2e-smoke.sh`는 보조 evidence로 함께 기록하되 completion 대체로만 쓰지 않는다.

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

- [x] `CODE_REVIEW-local-G06.md`: repo 내부 edge-node 진단 결과를 실제 stdout/stderr로 기록했다.
- [x] `CODE_REVIEW-local-G06.md`: 보조 `./scripts/e2e-smoke.sh` 결과와 repo 내부 edge-node 진단 결과를 구분해서 기록했다.
- [x] 기본 `configs/*.yaml`을 수정하지 않고 `mktemp -d` 아래 임시 config와 mock CLI를 사용했다.
- [x] `apps/node/internal/bootstrap/iop.db` 같은 repo-local 부산물은 검증 evidence로 사용하지 않았다. 새로 생긴 불필요한 부산물은 없음.

#### 중간 검증

```bash
./scripts/e2e-smoke.sh
```

추가로 `scripts/dev/edge.sh`와 `scripts/dev/node.sh`를 각각 실행한 repo 내부 edge-node user-flow 결과를 `CODE_REVIEW-local-G06.md`에 기록한다. 임시 config는 `scripts/e2e-smoke.sh`의 mock `fake-cli` config 패턴을 재사용하되, 결과 보고에서는 보조 smoke와 별도 user-flow evidence로 구분한다.

## 최종 검증

```bash
$ gofmt -l apps/node/internal/node/node.go apps/node/internal/node/sink_test.go apps/node/internal/node/node_test.go apps/node/internal/node/node_concurrency_integration_test.go
(output: empty - all files formatted)
```

```bash
$ go test -count=1 ./apps/node/internal/node ./apps/node/internal/adapters/openai_compat
ok  	iop/apps/node/internal/node	0.747s
ok  	iop/apps/node/internal/adapters/openai_compat	0.066s
```

```bash
$ go test -count=1 ./apps/node/...
ok  	iop/apps/node/cmd/node	0.072s
ok  	iop/apps/node/internal/adapters	0.090s
ok  	iop/apps/node/internal/adapters/cli	46.676s
ok  	iop/apps/node/internal/adapters/cli/status	39.944s
ok  	iop/apps/node/internal/adapters/ollama	0.063s
ok  	iop/apps/node/internal/adapters/openai_compat	0.066s
ok  	iop/apps/node/internal/adapters/vllm	0.073s
ok  	iop/apps/node/internal/bootstrap	0.466s
ok  	iop/apps/node/internal/node	0.747s
ok  	iop/apps/node/internal/router	0.511s
ok  	iop/apps/node/internal/store	0.084s
ok  	iop/apps/node/internal/terminal	0.558s
ok  	iop/apps/node/internal/transport	5.361s
```

```bash
$ ./scripts/e2e-smoke.sh
[e2e] Auxiliary smoke test PASSED.
[e2e] Completion still requires scripts/dev/edge.sh + scripts/dev/node.sh user-flow verification.
```

그리고 `scripts/dev/edge.sh` + `scripts/dev/node.sh` repo 내부 edge-node user-flow evidence를 `CODE_REVIEW-local-G06.md`에 기록했다.
