<!-- task=cli_command_responses/01_node_response_contract plan=1 tag=REVIEW_REFACTOR -->

# Node Command Response Contract Follow-up Plan

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

구현 마지막 단계에서 반드시 `CODE_REVIEW-cloud-G06.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 검증 명령을 실행하고 실제 stdout/stderr를 붙인 뒤 active 파일은 그대로 두고 리뷰 준비 상태만 보고한다. 최종 판정, 로그 아카이브, `complete.log` 작성, task archive 이동은 code-review-skill 전용이다.

## 배경

이 후속 계획은 `code_review_cloud_G06_0.log`의 FAIL 판정에서 나온 Required 두 건만 처리한다. 기존 node envelope 보강과 usage nil/raw-only 기본 경로는 유지하되, `SESSION_LIST` machine-readable 필드가 delimiter에 안전하도록 만들고, metadata-only usage status를 raw-only로 오분류하지 않도록 한다.

## 구현 체크리스트

- [ ] [REVIEW_REFACTOR-1] `SESSION_LIST`의 `session.N.*` 키를 typed snapshot에서 생성한다.
- [ ] [REVIEW_REFACTOR-2] metadata-only usage status를 raw-only로 오분류하지 않도록 fallback 판정을 고정한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [REVIEW_REFACTOR-1] `SESSION_LIST`의 `session.N.*` 키를 typed snapshot에서 생성한다

### 문제

`apps/node/internal/adapters/cli/cli.go:296`은 `mode:target/session_id` 문자열을 만든 뒤 다시 파싱해 `session.N.target`과 `session.N.session_id`를 채운다. `session_id`에는 `/` 제한이 없으므로 `team/a` 같은 값이 들어오면 machine-readable payload가 잘못 분리된다.

### 해결 방법

`sessionListEntry{mode,target,sessionID}` 같은 작은 내부 구조체를 만들고, 각 session map에서 이 구조체를 append한다. 정렬은 기존 `sessions` 문자열 순서와 호환되도록 legacy 문자열 기준으로 유지한다. 기존 `count`와 `sessions` 문자열은 같은 구조체에서 생성하고, `session.N.*` 키는 구조체 필드를 직접 사용한다.

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

- [ ] `apps/node/internal/adapters/cli/cli.go`: string re-parse를 제거하고 typed snapshot 기반으로 result map을 생성한다.
- [ ] `apps/node/internal/adapters/cli/cli_internal_test.go`: `/`가 포함된 session_id에서도 `session.N.session_id`가 원문 그대로 보존되는 테스트를 추가한다.

### 중간 검증

```bash
go test -count=1 ./apps/node/internal/adapters/cli
```

## [REVIEW_REFACTOR-2] metadata-only usage status를 raw-only로 오분류하지 않도록 fallback 판정을 고정한다

### 문제

`apps/node/internal/adapters/cli/cli.go:258`의 `parse_status=raw_only` 조건은 daily/weekly limit 필드만 확인한다. 하지만 status parser는 Claude status나 Antigravity model usage처럼 limit 없이 `Metadata`만 채운 구조화 결과도 반환한다. 이 경우 raw-only가 아니므로 fallback metadata를 붙이면 운영 표면이 오해할 수 있다.

### 해결 방법

`handleUsageStatus`에 작은 helper를 두어 structured usage status 여부를 판단한다. daily/weekly 필드뿐 아니라 metadata가 있으면 구조화 결과로 본다. `RawOutput == ""` 또는 raw-only fallback은 structured 정보가 전혀 없을 때만 붙인다.

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

- [ ] `apps/node/internal/adapters/cli/cli.go`: raw-only/empty fallback 조건을 helper로 분리하고 metadata-only 상태를 structured로 취급한다.
- [ ] `apps/node/internal/adapters/cli/lifecycle_blackbox_test.go`: metadata-only `UsageStatus`가 `parse_status=raw_only`를 받지 않는 테스트를 추가한다.
- [ ] `apps/node/internal/adapters/cli/lifecycle_blackbox_test.go`: empty output fallback이 `parse_status=empty`를 유지하는 테스트를 추가한다.

### 중간 검증

```bash
go test -count=1 ./apps/node/internal/adapters/cli
```

## 최종 검증

```bash
go test -count=1 ./apps/node/internal/node ./apps/node/internal/adapters/cli
```

Fresh execution required; cached output is not acceptable.
