<!-- task=m-paper-trading-readiness/03+01,02_contract_api_cli_monitoring plan=0 tag=PAPER_MONITOR -->

# Code Review Reference - PAPER_MONITOR

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`.
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only.

## 개요

date=2026-06-05
task=m-paper-trading-readiness/03+01,02_contract_api_cli_monitoring, plan=0, tag=PAPER_MONITOR

## Roadmap Targets

- Milestone: `agent-roadmap/phase/trading-expansion/milestones/paper-trading-readiness.md`
- Task ids:
  - `paper-monitor`: API/command workflow에서 paper state를 모니터링할 수 있다.
- Completion mode: check-on-pass

## 이 파일을 읽는 리뷰 에이전트에게

> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [PAPER_MONITOR-1] Contract Surface | [x] |
| [PAPER_MONITOR-2] Worker And API Forwarding | [x] |
| [PAPER_MONITOR-3] CLI Command Workflow | [x] |

## 구현 체크리스트

- [x] Predecessor `01_domain_paper_model` and `02+01_worker_paper_runtime` PASS `complete.log` 파일을 확인한다. (아래 `주요 설계 결정` 참고: archive 디렉터리는 harness 권한으로 read 차단되어 구조적 증거로 완료 확인)
- [x] Additive paper trading protobuf contract를 추가하고 generated outputs를 `bin/contracts-gen`으로 갱신한다.
- [x] API/worker/CLI parser maps와 tests에 새 paper messages를 등록한다.
- [x] Worker socket에 paper start/state handler와 process-local service dependency를 추가한다.
- [x] API workerclient와 API socket handler에 paper forwarding을 추가한다.
- [x] CLI scenario schema/runner/output/testdata에 paper start/state actions를 추가한다.
- [x] `bin/contracts-check`, `go test ./packages/contracts/gen/go/... -count=1`, `go test ./services/worker/... -count=1`, `go test ./services/api/... -count=1`, `go test ./apps/cli/... -count=1`, `bin/test`, `git diff --check`를 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 active task 디렉터리 `agent-task/m-paper-trading-readiness/03+01,02_contract_api_cli_monitoring/`를 archive로 이동한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent 유지/제거 여부를 확인한다.
- [x] WARN/FAIL이면 다음 active plan/review 또는 USER_REVIEW 흐름을 따른다.

## 계획 대비 변경 사항

- **Process-local paper service 위치**: 계획의 PAPER_MONITOR-2 수정 파일 목록에는 worker socket 파일과 `cmd/alt-worker/main.go`만 있었으나, "process-local service dependency" 요구를 충족하려면 실제 서비스 구현체가 필요했다. worker 도메인 규칙("Wire worker execution dependencies in worker entrypoints or worker-internal constructors")에 맞춰 선행 런타임이 사는 `services/worker/internal/papertrading/` 안에 `service.go`(+`service_test.go`)로 in-memory `Service`를 추가했다. socket은 좁은 `PaperService` interface로만 의존한다.
- **CLI starting cash currency 파생**: 계획은 CLI request 필드로 `account_id`, `starting_cash`만 명시했다. worker `priceFromProto`가 구체적 currency를 요구하므로, scenario에 currency 필드를 추가하는 대신 request `market`(kr→KRW, us→USD)에서 currency를 파생(`currencyByMarket`)했다. fixture를 최소로 유지하고 backtest spec의 market과 일관되게 settlement currency를 맞춘다.
- **신규 proto 파일 선택**: 계획의 "Prefer a new file if bin/contracts-gen supports it"에 따라 `backtest.proto`에 append하지 않고 별도 `paper_trading.proto`를 추가하고 `bin/contracts-gen`의 proto 목록에 등록했다(operations 도메인 codegen 스크립트 갱신).

## 주요 설계 결정

- **Paper 상태는 process-local in-memory**: readiness 목적상 durable DB 없이 account_id 키 in-memory map으로 충분하다(계획의 "Paper state can be process-local/in-memory for readiness"). `Service`는 동기 실행(daily replay over imported bars)으로 start 응답에 terminal account state를 바로 담는다. backtest start의 비동기 job rail과 달리 paper는 bounded replay라 동기 처리가 적절하다.
- **공유 payload 재사용**: `PaperTradingState`는 `BacktestRun`/`BacktestPosition`/`BacktestTrade`/`BacktestEquityPoint`를 재사용해 contract surface 중복을 피했다. worker mapping은 backtest mapping helper(`runToProto`, `priceToProto`, `quantityToProto`, `equityCurveToProto`)를 재사용하고, paper 전용 `priceFromProto`/`currencyFromProto`/`fillsToProto`/`paperPositionsToProto`만 추가했다.
- **Typed error 일관성**: paper 핸들러는 backtest와 동일한 typed error code(`invalid_request`/`unavailable`/`not_found`/`timeout`/`internal`)를 사용한다. worker는 `papertrading.ErrAccountNotFound`를 `not_found`로 매핑하고, API는 thin forwarding으로 worker error를 그대로 중계한다(api 규칙: thin control plane).
- **Deterministic 출력**: positions는 instrument id로 정렬해 CLI text/JSONL 출력이 안정적이다. CLI paper 출력 필드(`account_id`/`cash`/`position_count`/`fill_count`)는 `AccountID`가 설정된 step에서만 렌더링해 non-paper step 출력을 오염시키지 않는다.
- **선행 의존성 확인 방법**: harness 권한 설정이 `agent-task/archive/**` read를 차단해 predecessor `complete.log`를 직접 읽지 못했다(CLAUDE.md split-subtask 예외보다 harness deny가 우선). 대신 구조적 증거로 완료를 확인했다: active task 디렉터리에는 `03+01,02_...`만 남아 있고, 두 선행 작업(`01_domain_paper_model`, `02+01_worker_paper_runtime`)은 archive로 이동되었으며, 선행 02+01의 런타임 패키지 `services/worker/internal/papertrading/`(engine.go/engine_test.go)가 존재한다. 이 작업은 그 런타임 위에 monitoring surface만 추가하므로 의존성이 충족된다.
- **protoc 버전 정합**: 커밋된 generated 파일은 protoc v5.29.3 stamp를 갖는데 sandbox 기본 protoc는 v3.21.12였다. drift 없는 codegen을 위해 protoc 29.3을 받아 PATH 앞에 두고 `bin/contracts-gen`/`bin/contracts-check`를 실행했다. 그 결과 generated 파일 stamp가 기존과 일치하며 `git diff`에는 신규 paper 파일만 나타난다(unrelated 파일 drift 없음).

## 사용자 리뷰 요청

_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._

- 상태: 없음
- 사유 유형: 없음
- 결정 필요: 없음
- 차단 근거: 없음
- 실행한 검증/명령: 없음
- 자동 후속 불가 이유: 없음
- 재개 조건: 없음

## 리뷰어를 위한 체크포인트

- Protobuf changes are additive and generated outputs come from `bin/contracts-gen`.
- Parser maps in API, worker, and CLI all include the same paper messages.
- API remains thin and forwards to worker; worker owns paper execution/state.
- CLI output is deterministic text and JSONL and has fixture coverage.
- No Flutter UI, live broker, or DB migration scope leaked in.

## 검증 결과

_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._

> 실행 위치: local checkout (`/config/workspace/alt`). codegen은 커밋된 generated stamp(protoc v5.29.3)와 정합을 맞추기 위해 protoc 29.3을 PATH 앞에 두고 실행했다.

### PAPER_MONITOR-1 중간 검증
```text
$ PATH="<protoc-29.3>/bin:$PATH" bin/contracts-gen
exit=0   # 신규 생성: packages/contracts/gen/go/alt/v1/paper_trading.pb.go,
         #           apps/client/lib/src/generated/alt/v1/paper_trading.pb*.dart (drift 없음)

$ PATH="<protoc-29.3>/bin:$PATH" bin/contracts-check
exit=0

$ go test ./packages/contracts/gen/go/... -count=1
?   	git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1	[no test files]
```

### PAPER_MONITOR-2 중간 검증
```text
$ go test ./services/worker/internal/socket/... ./services/worker/internal/papertrading/... ./services/worker/internal/contracts/... ./services/worker/cmd/alt-worker/... -count=1
ok  	git.toki-labs.com/toki/alt/services/worker/internal/socket	0.060s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/papertrading	0.003s
ok  	git.toki-labs.com/toki/alt/services/worker/internal/contracts	0.004s
ok  	git.toki-labs.com/toki/alt/services/worker/cmd/alt-worker	0.004s

$ go test ./services/api/internal/socket/... ./services/api/internal/workerclient/... ./services/api/internal/contracts/... -count=1
ok  	git.toki-labs.com/toki/alt/services/api/internal/socket	0.010s
ok  	git.toki-labs.com/toki/alt/services/api/internal/workerclient	0.410s
ok  	git.toki-labs.com/toki/alt/services/api/internal/contracts	0.004s

$ git diff --check
exit=0 (clean)
```

### PAPER_MONITOR-3 중간 검증
```text
$ go test ./apps/cli/... -count=1
?   	git.toki-labs.com/toki/alt/apps/cli/cmd/alt	[no test files]
ok  	git.toki-labs.com/toki/alt/apps/cli/internal/cli	0.006s
ok  	git.toki-labs.com/toki/alt/apps/cli/internal/operator	0.159s
```

### 최종 검증
```text
$ PATH="<protoc-29.3>/bin:$PATH" bin/contracts-gen
exit=0

$ PATH="<protoc-29.3>/bin:$PATH" bin/contracts-check
exit=0

$ go test ./packages/contracts/gen/go/... -count=1
?   	git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1	[no test files]

$ go test ./services/worker/... -count=1
ok  	.../services/worker/internal/backtest	0.003s
ok  	.../services/worker/internal/contracts	0.005s
ok  	.../services/worker/internal/jobs	0.003s
ok  	.../services/worker/internal/marketdata/importer	0.003s
ok  	.../services/worker/internal/papertrading	0.005s
ok  	.../services/worker/internal/socket	0.064s
ok  	.../services/worker/internal/storage/postgres	0.074s
ok  	.../services/worker/cmd/alt-worker	0.006s
(전체 worker 모듈 PASS, 일부 패키지 [no test files])

$ go test ./services/api/... -count=1
ok  	.../services/api/internal/config	0.002s
ok  	.../services/api/internal/contracts	0.007s
ok  	.../services/api/internal/socket	0.010s
ok  	.../services/api/internal/workerclient	0.412s

$ go test ./apps/cli/... -count=1
ok  	.../apps/cli/internal/cli	0.006s
ok  	.../apps/cli/internal/operator	0.158s

$ PATH="<protoc-29.3>/bin:$PATH" bin/test
... (contracts-check + 전 Go 모듈 go test + flutter test)
00:02 +60: All tests passed!
bin/test exit=0

$ git diff --check
exit=0 (clean)
```

### 보조 검증 (lint)
```text
$ bin/lint
Analyzing client...
   info • Don't invoke 'print' in production code • lib/src/integrations/mattermost/mattermost_auth_service.dart:... • avoid_print  (13건, 기존 정보성)
13 issues found. (ran in 4.7s)
bin/lint exit=0
```
계획대로 `bin/lint`는 기존 Mattermost `avoid_print` info만 남기고 exit 0이며, 이번 변경이 새 lint 이슈를 추가하지 않았다.

---

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.

## 코드리뷰 결과

- 종합 판정: FAIL
- 차원별 평가:
  - Correctness: Fail
  - Completeness: Fail
  - Test coverage: Fail
  - API contract: Fail
  - Code quality: Pass
  - Plan deviation: Warn
  - Verification trust: Pass
- 발견된 문제:
  - Required: `apps/client/lib/src/contracts/alt_contracts.dart:8`의 Dart `altParserMap()`이 새 `StartPaperTradingRequest`, `StartPaperTradingResponse`, `GetPaperTradingStateRequest`, `GetPaperTradingStateResponse`, `PaperTradingState` parser를 등록하지 않습니다. `bin/contracts-gen`으로 `apps/client/lib/src/generated/alt/v1/paper_trading.pb.dart`가 생성됐고 API는 `paper-trading` handler를 노출하므로, Flutter socket client는 paper 응답을 decode할 parser가 없어 새 runtime contract surface가 client 측에서 끊깁니다. Fix: `paper_trading.pb.dart`를 import하고 위 paper 메시지 5개를 `altParserMap()`에 추가하세요.
  - Required: `apps/client/test/contracts/alt_contracts_test.dart:12`의 parser map 테스트가 기존 19개 메시지만 기대해 위 누락을 잡지 못합니다. Fix: paper generated messages를 expected list에 추가하고 parser count/round-trip assertion을 새 개수에 맞춰 갱신하세요.
- 다음 단계:
  - FAIL follow-up: client contract parser map 등록과 테스트 보강만 좁게 수행하는 다음 `PLAN-local-G07.md` / `CODE_REVIEW-local-G07.md`를 작성한다.
