<!-- task=m-paper-trading-command-workflow/02+01_risk_command plan=0 tag=PAPER_RISK -->

# Plan - PAPER_RISK

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

`CODE_REVIEW-cloud-G08.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 선행 `01_loop_smoke`의 `complete.log`가 없으면 구현을 시작하지 말고 리뷰 준비가 아닌 blocked 상태로 보고한다. 사용자 입력이 필요하면 review stub의 `사용자 리뷰 요청`에 기록하고 직접 질문하지 않는다.

## 배경

현재 status output은 `risk=not_evaluated`로만 안정화되어 있다. 마일스톤은 risk guard 결과를 command workflow에서 typed status/error key로 구분해야 한다. 기존 engine은 rejected orders를 `Snapshot.Rejected`로 보유하지만 service state와 protobuf state에는 노출하지 않는다.

## 사용자 리뷰 요청 흐름

구현 중 범위 충돌이나 사용자 결정이 필요하면 active review stub `사용자 리뷰 요청`에 근거를 기록하고 중단한다. code-review가 사용자 리뷰 전환을 판단한다.

## Roadmap Targets

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

## 분석 결과

### 읽은 파일

- `agent-roadmap/phase/trading-expansion/milestones/paper-trading-command-workflow.md`
- `agent-test/local/rules.md`
- `agent-test/local/contracts-smoke.md`
- `agent-test/local/api-smoke.md`
- `agent-test/local/worker-smoke.md`
- `agent-test/local/operations-smoke.md`
- `packages/contracts/proto/alt/v1/paper_trading.proto`
- `packages/domain/backtest/types.go`
- `packages/domain/backtest/types_test.go`
- `services/worker/internal/papertrading/engine.go`
- `services/worker/internal/papertrading/service.go`
- `services/worker/internal/socket/paper.go`
- `services/worker/internal/socket/paper_mapping.go`
- `services/worker/internal/socket/paper_test.go`
- `services/api/internal/socket/paper.go`
- `services/api/internal/socket/paper_test.go`
- `apps/cli/internal/operator/runner.go`
- `apps/cli/internal/operator/output.go`
- `apps/cli/internal/operator/runner_paper_test.go`

### 테스트 환경 규칙

`test_env=local`. contracts/API/worker/operations smoke 문서를 읽었다. Proto schema가 바뀌면 `bin/contracts-gen`, `bin/contracts-check`, `go test ./packages/contracts/gen/go/...`가 필요하다. API는 `go test ./services/api/...`, worker는 `go test ./services/worker/...`, CLI는 `go test ./apps/cli/internal/operator`, 전체는 `bin/test`.

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

- Domain `CheckRisk` unit은 있다.
- Engine rejected orders test가 있지만 service/proto/API/CLI로 rejected/risk summary가 노출되지 않는다.
- risk command/status key 검증 fixture가 없다.

### 심볼 참조

rename/remove 없음. `rg --sort path "RejectedOrder|RiskDecision|PaperTradingState"` 결과상 worker engine/service/socket, contracts, API/CLI parser and runner가 영향권이다.

### 분할 판단

이 subtask는 `02+01_risk_command`이고 `01_loop_smoke` 완료가 선행이다. 선행 `complete.log`는 아직 없다. 구현 시작 전 `agent-task/m-paper-trading-command-workflow/01_loop_smoke/complete.log` 또는 archive 후보를 확인해야 한다.

### 범위 결정 근거

이 작업은 risk summary/read surface까지만 다룬다. explicit order submit/cancel/fill lifecycle은 `03+02_order_lifecycle`에서 다룬다. 고급 risk engine과 실거래 주문 제출은 범위 밖이다.

### 빌드 등급

`cloud-G08`: protobuf additive schema, worker state mapping, API/CLI output이 함께 바뀌며 terminal evidence와 protocol compatibility가 중요하다.

## 구현 체크리스트

- [x] [PAPER_RISK-1] `Snapshot.Rejected`를 service state와 protobuf state로 additive하게 노출한다.
- [x] [PAPER_RISK-2] worker/API/CLI mapping이 risk summary를 stable key로 출력하게 한다.
- [x] [PAPER_RISK-3] risk allow/block fixture 또는 fake API test가 typed status/error key를 검증하게 한다. 검증: `bin/contracts-check`, `go test ./services/worker/...`, `go test ./services/api/...`, `go test ./apps/cli/internal/operator` 통과.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## PAPER_RISK-1

### 문제

`services/worker/internal/papertrading/engine.go:55`의 `Snapshot.Rejected`가 `service.go:30` State로 복사되지 않고 `paper_trading.proto:36` state에도 risk/rejected field가 없다.

### 해결 방법

`PaperTradingState`에 additive repeated risk/rejected summary field를 추가한다. worker state에 rejected slice를 보존하고 mapping을 추가한다.

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

- [x] `packages/contracts/proto/alt/v1/paper_trading.proto`
- [x] generated Go/Dart contract outputs via `bin/contracts-gen`
- [x] `services/worker/internal/papertrading/service.go`
- [x] `services/worker/internal/socket/paper_mapping.go`

### 테스트 작성

worker paper mapping/service tests에 rejected summary assertion 추가.

### 중간 검증

`bin/contracts-check`

## PAPER_RISK-2

### 문제

`apps/cli/internal/operator/runner.go:641`은 risk를 `not_evaluated`로 고정한다.

### 해결 방법

proto risk/rejected summary를 읽어 `risk=clear|blocked`, `risk_rejected_count`, `risk_reason` 등 stable key로 출력한다. key 이름은 fixture와 tests에 고정한다.

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

- [x] `apps/cli/internal/operator/output.go`
- [x] `apps/cli/internal/operator/runner.go`
- [x] `apps/cli/internal/operator/runner_paper_test.go`

### 테스트 작성

fake API response에 blocked risk case를 추가해 text/JSONL output을 검증한다.

### 중간 검증

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

## PAPER_RISK-3

### 문제

마일스톤 검증은 허용/차단 case가 typed status/error key로 구분되어야 한다.

### 해결 방법

paper scenario fixture 또는 focused fake API test에 clear and blocked cases를 둔다. blocked는 typed error가 아니라 paper state risk summary로 표현하고, 실제 invalid request는 기존 typed error 경로를 유지한다.

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

- [x] `apps/cli/testdata/operator/*`
- [x] `apps/cli/internal/operator/runner_paper_test.go`

### 테스트 작성

`TestRunGetPaperTradingStateOutputsRiskFields` 추가.

### 중간 검증

`go test ./services/worker/... && go test ./services/api/... && go test ./apps/cli/internal/operator`

## 의존 관계 및 구현 순서

이 subtask directory는 `02+01_risk_command`이므로 `01_loop_smoke`의 `complete.log`가 필요하다. directory name 외의 추가 dependency는 없다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `packages/contracts/proto/alt/v1/paper_trading.proto` | PAPER_RISK-1 |
| `packages/contracts/gen/go/alt/v1/paper_trading.pb.go` | PAPER_RISK-1 |
| `apps/client/lib/src/generated/alt/v1/paper_trading*.dart` | PAPER_RISK-1 |
| `services/worker/internal/papertrading/service.go` | PAPER_RISK-1 |
| `services/worker/internal/socket/paper_mapping.go` | PAPER_RISK-1 |
| `services/worker/internal/socket/paper_test.go` | PAPER_RISK-1 |
| `apps/cli/internal/operator/output.go` | PAPER_RISK-2 |
| `apps/cli/internal/operator/runner.go` | PAPER_RISK-2 |
| `apps/cli/internal/operator/runner_paper_test.go` | PAPER_RISK-2, PAPER_RISK-3 |

## 최종 검증

```bash
bin/contracts-check
go test ./services/worker/...
go test ./services/api/...
go test ./apps/cli/internal/operator
bin/test
```

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