<!-- task=m-paper-trading-command-workflow/01_loop_smoke plan=0 tag=PAPER_LOOP -->

# Plan - PAPER_LOOP

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

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채워야 구현이 끝난다. 구현 후 active 파일을 유지하고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경, 또는 범위 충돌로 막히면 `사용자 리뷰 요청`에 근거를 기록하고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 최종 archive, `complete.log`, roadmap 반영은 code-review 책임이다.

## 배경

`paper-status-command`는 status key 보강으로 작게 처리되었다. 다음 큰 단위는 paper execution loop smoke가 run id, terminal status, position/equity summary를 안정적으로 출력하게 하는 것이다. CLI/YAML/log evidence가 먼저 고정되어야 이후 risk/order command가 같은 output vocabulary를 재사용할 수 있다.

## 사용자 리뷰 요청 흐름

구현 중 사용자 전용 결정이나 외부 환경 준비가 필요하면 active review stub의 `사용자 리뷰 요청` 섹션을 채우고 중단한다. 직접 사용자 프롬프트는 금지이며, code-review가 `USER_REVIEW.md` 작성 여부를 판단한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/trading-expansion/milestones/paper-trading-command-workflow.md`
- Task ids:
  - `paper-loop-smoke`: paper execution loop smoke
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/trading-expansion/PHASE.md`
- `agent-roadmap/phase/trading-expansion/milestones/paper-trading-command-workflow.md`
- `agent-test/local/rules.md`
- `agent-test/local/operations-smoke.md`
- `apps/cli/internal/operator/output.go`
- `apps/cli/internal/operator/runner.go`
- `apps/cli/internal/operator/scenario.go`
- `apps/cli/internal/operator/client.go`
- `apps/cli/internal/operator/runner_paper_test.go`
- `apps/cli/internal/operator/client_test.go`
- `apps/cli/internal/operator/scenario_test.go`
- `apps/cli/testdata/operator/paper_trading_state.yaml`
- `apps/cli/testdata/operator/expected/paper_trading_state.jsonl`
- `packages/contracts/proto/alt/v1/paper_trading.proto`
- `services/worker/internal/socket/paper_mapping.go`
- `services/worker/internal/socket/paper_test.go`

### 테스트 환경 규칙

`test_env=local`. `agent-test/local/rules.md`와 `agent-test/local/operations-smoke.md`를 읽었다. CLI operator 변경은 operations 도메인에 해당하며 focused unit은 `go test ./apps/cli/internal/operator`, 전체 smoke는 `bin/test`다. Go test cache 출력은 focused unit에서는 허용하고, reviewer가 필요하면 `-count=1`로 신선 실행한다.

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

- `run_id`, `run_status`, `position_count`, `cash`, `risk=not_evaluated`는 기존/직접 수정 테스트로 커버된다.
- `equity summary`는 현재 CLI `StepEvent`에 없다. 이 plan에서 `equity_point_count`와 `ending_equity` 또는 `latest_equity` 출력 테스트를 추가해야 한다.

### 심볼 참조

이 plan은 rename/remove가 없다. `rg --sort path "PaperTradingState|equity"` 결과상 CLI output, worker paper mapping, contract state가 주요 call site다.

### 분할 판단

split policy를 적용했다. 전체 마일스톤 잔여 작업은 CLI loop smoke, risk command, order lifecycle, UI handoff로 경계가 나뉜다. 이 subtask는 `01_loop_smoke`이며 선행 의존성이 없다.

### 범위 결정 근거

이 작업은 CLI output과 scenario/fixture/test 보강만 다룬다. 새 protobuf message, worker runtime risk/order behavior, UI 문서는 제외한다. risk 값은 현재 `not_evaluated`를 유지하고 실제 risk 판정은 `02+01_risk_command`에서 다룬다.

### 빌드 등급

`cloud-G07`: CLI workflow stdout/JSONL contract와 scenario fixture가 핵심이라 terminal evidence 판단이 중요하지만 범위는 apps/cli에 제한된다.

## 구현 체크리스트

- [ ] [PAPER_LOOP-1] `apps/cli/internal/operator/output.go`에 paper loop summary field를 추가하고 text/JSONL에 출력한다.
- [ ] [PAPER_LOOP-2] `apps/cli/internal/operator/runner.go`의 `evaluatePaper`가 equity curve에서 stable summary를 계산하게 한다.
- [ ] [PAPER_LOOP-3] `apps/cli/internal/operator/runner_paper_test.go`와 `apps/cli/testdata/operator/expected/paper_trading_state.jsonl`이 run id, terminal status, position/equity summary를 검증하게 한다. 검증: `go test ./apps/cli/internal/operator` 통과.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## PAPER_LOOP-1

### 문제

`apps/cli/internal/operator/output.go:46`의 paper output fields는 account/cash/position/fill/risk만 있고 equity summary가 없다.

### 해결 방법

`StepEvent`에 `EquityPointCount`와 `LatestEquity`를 추가하고 AccountID가 있을 때만 렌더링한다.

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

- [ ] `apps/cli/internal/operator/output.go` field 추가
- [ ] JSONL key는 `equity_point_count`, `latest_equity`로 둔다.
- [ ] text key는 `equity_point_count=<n> latest_equity=<value>`로 둔다.

### 테스트 작성

`runner_paper_test.go`에서 text output assertion을 추가한다.

### 중간 검증

`go test ./apps/cli/internal/operator`

## PAPER_LOOP-2

### 문제

`apps/cli/internal/operator/runner.go:641`의 `evaluatePaper`가 `state.GetEquityCurve()`를 읽지 않는다.

### 해결 방법

equity curve length를 count로 저장하고 마지막 point의 equity amount를 latest equity로 저장한다. empty curve는 field를 생략한다.

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

- [ ] `apps/cli/internal/operator/runner.go` `evaluatePaper` 수정
- [ ] nil state/nil equity 안전 처리

### 테스트 작성

`paperState()` fixture에 equity curve를 추가하고 start/state 출력 테스트가 이를 검증한다.

### 중간 검증

`go test ./apps/cli/internal/operator`

## PAPER_LOOP-3

### 문제

`apps/cli/testdata/operator/expected/paper_trading_state.jsonl`은 loop smoke evidence에 equity summary를 남기지 않는다.

### 해결 방법

expected JSONL의 start/state line에 equity keys를 추가하고 fixture validation test가 이를 확인하게 한다.

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

- [ ] `apps/cli/testdata/operator/expected/paper_trading_state.jsonl` 갱신
- [ ] `apps/cli/internal/operator/runner_paper_test.go` fixture validation이 equity key 존재를 확인

### 테스트 작성

기존 `TestPaperTradingFixtureIsValid`를 확장한다.

### 중간 검증

`go test ./apps/cli/internal/operator`

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `apps/cli/internal/operator/output.go` | PAPER_LOOP-1 |
| `apps/cli/internal/operator/runner.go` | PAPER_LOOP-2 |
| `apps/cli/internal/operator/runner_paper_test.go` | PAPER_LOOP-2, PAPER_LOOP-3 |
| `apps/cli/testdata/operator/expected/paper_trading_state.jsonl` | PAPER_LOOP-3 |

## 최종 검증

```bash
go test ./apps/cli/internal/operator
bin/test
```

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