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

# Edge Console Command Smoke Plan

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

구현 시작 전 `agent-task/cli_command_responses/01_node_response_contract/complete.log`가 있는지 확인한다. 구현 마지막 단계에서 반드시 `CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. field smoke는 실제 수행 결과 또는 수행 불가 사유를 그대로 기록한다. 최종 판정, 로그 아카이브, `complete.log` 작성, task archive 이동은 code-review-skill 전용이다.

## 배경

node/cli command response 계약이 보강되면 edge service와 ops console은 그 payload를 운영자가 읽기 쉬운 출력으로 보여줘야 한다. 현재 console은 result map을 단순 key 정렬로 출력한다. 이 계획은 `SESSION_LIST`, `USAGE_STATUS`, `TRANSPORT_STATUS` 표시와 field smoke 검증 절차를 고정한다.

## 분석 결과

### 읽은 파일

- `apps/node/internal/runtime/types.go`
- `apps/node/internal/node/node.go`
- `apps/node/internal/node/node_test.go`
- `apps/node/internal/adapters/cli/cli.go`
- `apps/node/internal/adapters/cli/status/status.go`
- `apps/node/internal/adapters/cli/cli_internal_test.go`
- `apps/node/internal/adapters/cli/lifecycle_blackbox_test.go`
- `apps/edge/internal/service/service.go`
- `apps/edge/internal/service/service_test.go`
- `apps/edge/internal/opsconsole/status.go`
- `apps/edge/internal/opsconsole/console_test.go`
- `apps/edge/internal/opsconsole/events_test.go`
- `apps/node/README.md`
- `apps/edge/README.md`

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

- `FormatNodeCommandView`는 sorted map 출력과 empty result만 테스트한다. structured session fields를 grouped output으로 렌더링하는 테스트가 없다.
- `FormatUsageStatus`는 parsed limits, model usage, raw fallback을 테스트한다. `parse_status` 같은 fallback metadata를 노출할 경우 테스트가 없다.
- service layer는 request build tests는 있으나 `NodeCommandView` result map 보존/copy behavior를 테스트하지 않는다.
- field smoke 기준은 README와 testing rule에 있으나 이번 운영 명령 응답 변경 후 실행 transcript를 남기는 active task artifact가 없다.

### 심볼 참조

- renamed/removed symbol: none.
- 새 dependency 추가 없음. `go.mod` 변경 불필요.

### 분할 판단

split decision policy를 먼저 평가했다. 공유 task group은 `cli_command_responses`다.

- `01_node_response_contract`: 선행 작업. node/cli payload 계약을 완료하고 `complete.log`를 만들어야 한다.
- `02+01_edge_console_smoke`: 이 plan. edge console 표시, docs, package/full/smoke 검증을 수행한다.

디렉터리명 `02+01_edge_console_smoke`의 `+01`이 런타임 의존성의 source of truth다.

### 범위 결정 근거

- node/cli response payload 생성은 선행 task가 소유하므로 여기서 되돌리거나 재설계하지 않는다.
- proto schema 변경은 제외한다.
- Edge HTTP/OpenAI/A2A 입력 표면은 이번 운영 console command 검증 범위 밖이다.
- 실제 외부 CLI별 full verification은 가능한 경우 수행하되, 환경/계정 blocker가 있으면 profile별로 기록한다.

### 빌드 등급

build=`cloud-G07`, review=`cloud-G07`. bin shell 사용자 흐름, field smoke, terminal process orchestration, command output 검증이 성공 조건에 포함되어 mandatory cloud-G07이다.

## 의존 관계 및 구현 순서

- 시작 전 `agent-task/cli_command_responses/01_node_response_contract/complete.log`가 있어야 한다.
- 완료된 01 task의 `complete.log`에서 변경된 `SESSION_LIST`/`USAGE_STATUS` key를 확인한 뒤 edge/console 표시를 구현한다.

## 구현 체크리스트

- [ ] [TEST-1] Edge service command view 경계와 result 보존을 확인한다.
- [ ] [TEST-2] Ops console command 출력과 usage fallback 표시를 보강한다.
- [ ] [TEST-3] README와 field smoke transcript로 운영 명령 검증을 마무리한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [TEST-1] Edge service command view 경계와 result 보존을 확인한다

### 문제

[service.go](/config/workspace/iop/apps/edge/internal/service/service.go:383)의 `sendNodeCommand`는 proto response result를 `NodeCommandView.Result`로 그대로 넘긴다. 이 경계가 새로운 structured session keys를 손상하지 않는지 테스트가 없다.

Before:

```go
// apps/edge/internal/service/service.go:400
return NodeCommandView{
    NodeID:    entry.NodeID,
    NodeLabel: nodeLabel(entry),
    Adapter:   commandReq.GetAdapter(),
    Target:    commandReq.GetTarget(),
    SessionID: commandReq.GetSessionId(),
    Type:      resp.GetType(),
    Result:    resp.GetResult(),
}, nil
```

### 해결 방법

service code는 가능하면 그대로 둔다. 만약 response result map aliasing이 문제가 되면 small helper로 copy를 만든다. `service_test.go`에는 `BuildNodeCommandRequest_NewTypes`를 보강해 target/session/timeout/type이 `SESSION_LIST`, `TRANSPORT_STATUS`에서 유지되는지 확인한다. helper를 추가한 경우 helper 테스트로 result key 보존을 검증한다.

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

- [ ] `apps/edge/internal/service/service.go`: result map copy가 필요할 때만 최소 변경.
- [ ] `apps/edge/internal/service/service_test.go`: command request target/session/timeout 보존 test 보강.
- [ ] `apps/edge/internal/service/service_test.go`: helper 추가 시 structured result key 보존 test 추가.

### 테스트 작성

작성한다. service 코드를 바꾸지 않는 경우도 request build coverage를 보강한다.

### 중간 검증

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

기대 결과: package PASS.

## [TEST-2] Ops console command 출력과 usage fallback 표시를 보강한다

### 문제

[status.go](/config/workspace/iop/apps/edge/internal/opsconsole/status.go:88)의 `FormatNodeCommandView`는 map key를 단순 정렬 출력한다. 선행 task가 `session.N.mode`, `session.N.target`, `session.N.session_id`를 추가하면 출력이 기계적인 key 나열로 보일 수 있다. [status.go](/config/workspace/iop/apps/edge/internal/opsconsole/status.go:104)의 `FormatUsageStatus`는 raw fallback은 있지만 fallback metadata를 보여주지 않는다.

Before:

```go
// apps/edge/internal/opsconsole/status.go:94
keys := make([]string, 0, len(view.Result))
for k := range view.Result {
    keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
    fmt.Fprintf(out, "  %s = %s\n", k, view.Result[k])
}
```

### 해결 방법

`label == "sessions"`이고 `session.N.*` keys가 있으면 사람이 읽기 좋은 grouped output을 먼저 출력한다. 기존 `count`와 `sessions` key는 호환성을 위해 계속 출력하거나, grouped output 아래에 raw summary로 남긴다. `FormatUsageStatus`는 metadata `parse_status`가 있으면 fallback reason을 출력한다. `TRANSPORT_STATUS`는 existing sorted output으로 충분하되 test에서 핵심 라벨을 고정한다.

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

- [ ] `apps/edge/internal/opsconsole/status.go`: structured session result formatter 추가.
- [ ] `apps/edge/internal/opsconsole/status.go`: usage status fallback metadata 출력 추가.
- [ ] `apps/edge/internal/opsconsole/console_test.go`: session list grouped output test 추가.
- [ ] `apps/edge/internal/opsconsole/console_test.go`: transport output 핵심 key 표시 test 추가.
- [ ] `apps/edge/internal/opsconsole/events_test.go`: usage fallback metadata 출력 test 추가.

### 테스트 작성

작성한다.

- `TestFormatNodeCommandView_RendersStructuredSessions`: `session.0.*` keys가 grouped line으로 보이는지 검증.
- `TestFormatNodeCommandView_RendersTransportStatusKeys`: connected/node_id/session_id가 출력되는지 검증.
- `TestFormatUsageStatus_PrintsFallbackMetadata`: `parse_status=raw_only` 또는 `parse_status=empty`가 표시되는지 검증.

### 중간 검증

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

기대 결과: package PASS.

## [TEST-3] README와 field smoke transcript로 운영 명령 검증을 마무리한다

### 문제

[apps/edge/README.md](/config/workspace/iop/apps/edge/README.md:67)는 `/capabilities`, `/transport`, `/sessions` 기준 출력만 기록한다. 이번 task는 `/status`와 structured session output까지 검증해야 하며, testing rule은 bin shell 사용자 흐름을 보조 smoke로 대체하지 말라고 요구한다.

### 해결 방법

응답 형식이 바뀌면 `apps/edge/README.md`와 필요 시 `apps/node/README.md`의 Node Commands 설명을 갱신한다. package test, full Go test, 보조 e2e, bin shell manual smoke를 실행하고 `CODE_REVIEW-cloud-G07.md`에 실제 출력을 기록한다. manual smoke transcript에는 `/nodes`, `/capabilities`, `/transport`, `/sessions`, `/status`, persistent profile이면 `/terminate-session` 결과를 포함한다.

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

- [ ] `apps/edge/README.md`: console command 출력 예시 갱신.
- [ ] `apps/node/README.md`: Node Commands payload 설명 갱신이 필요한 경우만 수정.
- [ ] `CODE_REVIEW-cloud-G07.md`: field smoke transcript 또는 blocker 기록.

### 테스트 작성

문서 변경 자체 테스트는 없다. 대신 package/full/smoke/manual verification을 수행한다.

### 중간 검증

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

기대 결과: package PASS.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/edge/internal/service/service.go` | TEST-1 |
| `apps/edge/internal/service/service_test.go` | TEST-1 |
| `apps/edge/internal/opsconsole/status.go` | TEST-2 |
| `apps/edge/internal/opsconsole/console_test.go` | TEST-2 |
| `apps/edge/internal/opsconsole/events_test.go` | TEST-2 |
| `apps/edge/README.md` | TEST-3 |
| `apps/node/README.md` | TEST-3 |
| `CODE_REVIEW-cloud-G07.md` | TEST-3 |

## 최종 검증

```bash
test -f agent-task/cli_command_responses/01_node_response_contract/complete.log
go test -count=1 ./apps/edge/internal/service ./apps/edge/internal/opsconsole
go test -count=1 ./...
make test-e2e
```

Manual field smoke: start `./bin/edge.sh` and `./bin/node.sh` in separate processes, then record edge console transcript for `/nodes`, `/capabilities`, `/transport`, `/sessions`, `/status`, and persistent profile `/terminate-session` when applicable. Fresh execution required; cached Go test output is not acceptable.

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