<!-- task=cli_runtime_closeout/02+01_edge_console_smoke plan=0 tag=TEST -->

# Plan - Edge Console And Smoke Contract

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

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증 명령을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤, active 파일은 그대로 두고 review ready로 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용이다.

## 배경

`NodeCommandRequest` 응답이 운영자가 읽기 쉬운 형태인지 확인하려면 edge console 출력과 field smoke가 같은 계약을 검증해야 한다. 현재 보조 smoke는 `/capabilities`, `/transport`, `/sessions`의 존재를 보지만 transport 세부 echo field와 readable state까지 고정하지 않는다. 이 작업은 `01_node_command_contract` 완료 후 edge 쪽 기대값과 smoke 기준을 맞춘다.

## 의존 관계 및 구현 순서

이 subtask 디렉터리명은 `02+01_edge_console_smoke`이므로 같은 task group의 `01_node_command_contract`가 `complete.log`를 생성한 뒤 시작한다. 추가 숨은 dependency는 없다.

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/domain/edge/rules.md`
- `agent-ops/rules/project/domain/testing/rules.md`
- `agent-ops/skills/project/e2e-smoke/SKILL.md`
- `apps/edge/internal/service/service.go`
- `apps/edge/internal/service/service_test.go`
- `apps/edge/internal/opsconsole/status.go`
- `apps/edge/internal/opsconsole/console.go`
- `apps/edge/internal/opsconsole/console_test.go`
- `apps/edge/internal/opsconsole/events.go`
- `apps/edge/internal/opsconsole/events_test.go`
- `scripts/e2e-smoke.sh`
- `apps/edge/README.md`
- `Makefile`

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

- Console renderer: `TestFormatNodeCommandView_RendersTransportStatusKeys` checks `connected/node_id/session_id`, but not the new `state` field from subtask 01.
- Auxiliary smoke: `/transport` checks only header and `connected = true`; it should also check `state = connected`, `adapter = cli`, `node_id = test-node`, `target`, and `session_id`.
- Field user-flow: README documents manual verification, but the implementation pass must record that `make test-e2e` is auxiliary and bin shell flow remains mandatory.

### 심볼 참조

- renamed/removed symbols: none.

### 분할 판단

분할 정책을 먼저 평가했다. 이 작업은 `01_node_command_contract`의 payload를 소비하는 edge/testing subtask라 별도 plan이 필요하다. `03_history_event_boundary`와는 docs ownership이 다르고, `04_milestone_closeout`은 evidence dependency가 있어 분리했다.

### 범위 결정 근거

이 subtask는 edge console renderer 테스트와 `scripts/e2e-smoke.sh` 검증 기준, edge README의 command 예시만 다룬다. `apps/edge/internal/service` request/response transport 구현은 현재 DTO가 result map을 그대로 전달하므로 변경하지 않는다. 기본 `configs/*.yaml`은 오염시키지 않는다.

### 빌드 등급

build=`cloud-G07`, review=`cloud-G07`. Shell/bin smoke, stdout matching, user-flow verification contract가 포함된 terminal-agent work다.

## 구현 체크리스트

- [ ] [TEST-1] edge console transport output test를 node payload의 `state` field까지 고정한다.
- [ ] [TEST-2] `scripts/e2e-smoke.sh`의 command 검증을 `/capabilities`, `/transport`, `/sessions` 세부 field까지 강화한다.
- [ ] [TEST-3] edge README의 command 출력 예시와 검증 문구를 강화된 smoke 기준과 맞춘다.
- [ ] 중간 검증과 최종 검증 명령, bin shell 사용자 흐름 수행 결과를 `CODE_REVIEW-cloud-G07.md`에 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [TEST-1] Console Transport Output Test

#### 문제

[apps/edge/internal/opsconsole/console_test.go](/config/workspace/iop/apps/edge/internal/opsconsole/console_test.go:237)는 transport output의 existing keys만 확인한다. `01_node_command_contract`에서 추가되는 `state`가 console output에 보이는지 고정해야 한다.

#### 해결 방법

Before [apps/edge/internal/opsconsole/console_test.go](/config/workspace/iop/apps/edge/internal/opsconsole/console_test.go:242):

```go
Result: map[string]string{
	"connected":  "true",
	"node_id":    "test-node",
	"session_id": "default",
	"adapter":    "cli",
	"target":     "claude",
},
```

After:

```go
Result: map[string]string{
	"connected":  "true",
	"state":      "connected",
	"node_id":    "test-node",
	"session_id": "default",
	"adapter":    "cli",
	"target":     "claude",
},
```

Also add `"state = connected"` to the expected substrings.

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

- [ ] `apps/edge/internal/opsconsole/console_test.go`: update transport test fixture and expected output.

#### 테스트 작성

Update `TestFormatNodeCommandView_RendersTransportStatusKeys`. No new production code is expected because `FormatNodeCommandView` already sorts and renders arbitrary result keys.

#### 중간 검증

```bash
go test -count=1 ./apps/edge/internal/opsconsole
```

Expected: package passes freshly, no cache-only result required.

### [TEST-2] Auxiliary Smoke Command Assertions

#### 문제

[scripts/e2e-smoke.sh](/config/workspace/iop/scripts/e2e-smoke.sh:292) waits for `/transport` header and `connected = true`, then final checks at [scripts/e2e-smoke.sh](/config/workspace/iop/scripts/e2e-smoke.sh:474) repeat the same narrow assertions. The smoke should catch missing operator-readable fields before manual field verification.

#### 해결 방법

Before [scripts/e2e-smoke.sh](/config/workspace/iop/scripts/e2e-smoke.sh:292):

```bash
send_cmd "/transport"
wait_for_edge_text_since "[node0-transport]" "$LAST_CMD_START_LINE" "/transport output"
wait_for_edge_text_since "connected = true" "$LAST_CMD_START_LINE" "/transport connected status"
```

After:

```bash
send_cmd "/transport"
wait_for_edge_text_since "[node0-transport]" "$LAST_CMD_START_LINE" "/transport output"
wait_for_edge_text_since "adapter = cli" "$LAST_CMD_START_LINE" "/transport adapter"
wait_for_edge_text_since "connected = true" "$LAST_CMD_START_LINE" "/transport connected status"
wait_for_edge_text_since "state = connected" "$LAST_CMD_START_LINE" "/transport state"
wait_for_edge_text_since "node_id = test-node" "$LAST_CMD_START_LINE" "/transport node id"
wait_for_edge_text_since "session_id = default" "$LAST_CMD_START_LINE" "/transport session id"
wait_for_edge_text_since "target = ${TARGET}" "$LAST_CMD_START_LINE" "/transport target"
```

Also mirror these assertions in final `check_grep` block near [scripts/e2e-smoke.sh](/config/workspace/iop/scripts/e2e-smoke.sh:474). For `/sessions`, add a final `check_grep "sessions:" "$EDGE_OUT"` so grouped output remains visible.

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

- [ ] `scripts/e2e-smoke.sh`: strengthen immediate waits after `/transport`.
- [ ] `scripts/e2e-smoke.sh`: strengthen final `check_grep` assertions for transport fields and sessions grouped output.

#### 테스트 작성

No separate unit test for shell script parsing. The test is the script execution itself.

#### 중간 검증

```bash
IOP_E2E_COMMAND_SETTLE_SECONDS=0.2 bash scripts/e2e-smoke.sh
```

Expected: auxiliary smoke passes with mock profile. This remains auxiliary and does not replace bin shell user-flow verification.

### [TEST-3] Edge README Command Example

#### 문제

[apps/edge/README.md](/config/workspace/iop/apps/edge/README.md:85) shows `/transport` output without the new readable `state` field. The manual verification section should also make clear that `/capabilities`, `/transport`, and `/sessions` are command response checks, not just incidental console output.

#### 해결 방법

Before [apps/edge/README.md](/config/workspace/iop/apps/edge/README.md:85):

```text
edge> /transport
[node0-transport] target=fake-cli session=default
  adapter = cli
  connected = true
  node_id = test-node
  session_id = default
  target = fake-cli
```

After:

```text
edge> /transport
[node0-transport] target=fake-cli session=default
  adapter = cli
  connected = true
  node_id = test-node
  session_id = default
  state = connected
  target = fake-cli
```

Keep the prefix style consistent with current code (`[node0-...]`).

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

- [ ] `apps/edge/README.md`: add `state = connected` to transport example.
- [ ] `apps/edge/README.md`: keep the auxiliary-vs-user-flow warning intact.

#### 테스트 작성

Documentation-only change; no unit test. Covered by `rg` final verification and smoke output.

#### 중간 검증

```bash
rg --sort path -n 'state = connected|Completion still requires|auxiliary smoke' apps/edge/README.md scripts/e2e-smoke.sh
```

Expected: README and smoke script include the strengthened wording/field.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `apps/edge/internal/opsconsole/console_test.go` | TEST-1 |
| `scripts/e2e-smoke.sh` | TEST-2 |
| `apps/edge/README.md` | TEST-3 |

## 최종 검증

```bash
go test -count=1 ./apps/edge/internal/opsconsole ./apps/edge/internal/service
```

```bash
IOP_E2E_COMMAND_SETTLE_SECONDS=0.2 bash scripts/e2e-smoke.sh
```

```bash
make test-e2e
```

Bin shell 사용자 흐름도 `agent-ops/skills/project/e2e-smoke/SKILL.md` 기준으로 별도 수행하고 결과를 기록한다. 최소 확인 항목은 `bin/edge.sh`와 `bin/node.sh` 별도 프로세스 실행, node registration, `/nodes`, 같은 session 메시지 2회, node local `[node-message]` payload와 edge `[node*-msg]` 비교, `/capabilities`, `/transport`, `/sessions`, persistent profile의 `/terminate-session`이다.

Expected: Go tests pass freshly, auxiliary smoke passes, `make test-e2e` passes or external fake Ollama blocker를 실제 출력과 함께 기록한다. Bin shell 사용자 흐름은 PASS/FAIL/BLOCKED로 별도 기록한다.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
