<!-- task=m-live-trading-boundary/02+01_order_lifecycle plan=1 tag=REVIEW_LIVE_ORDER -->

# Plan - REVIEW_LIVE_ORDER

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것은 필수다. 구현 후 검증을 실행하고 실제 변경 내용, 설계 결정, 명령 출력(stdout/stderr)을 기록한 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 최종 판정, 로그 rename, `complete.log`, archive 이동은 code-review 전용이다.

구현 중 사용자만 결정할 수 있는 사항, 사용자 소유 외부 환경/secret, 또는 계획 범위 충돌 없이는 안전하게 진행할 수 없으면 active review stub의 `사용자 리뷰 요청` 섹션에 정확한 결정, 근거, 실행 명령/출력, 재개 조건을 기록하고 중단한다. 구현 에이전트는 사용자에게 직접 묻거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

첫 리뷰(`code_review_cloud_G07_0.log`)는 live order lifecycle 구현이 큰 흐름은 잡았지만 세 가지 Required 문제로 FAIL 판정했다. 이 follow-up은 새 기능 확장이 아니라 기존 live order lifecycle의 contract registration, worker safety validation, 테스트 신뢰도를 보강하는 좁은 수정이다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 중 직접 사용자 프롬프트는 금지이며, code-review가 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성을 소유한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/trading-expansion/milestones/live-trading-boundary.md`
- Task ids:
  - `order-lifecycle`: 주문 생성, 제출, 체결, 취소, 실패 lifecycle이 정의된다.
- Completion mode: check-on-pass

## 분석 결과

### 리뷰 근거

- `apps/client/lib/src/contracts/alt_contracts.dart:59`는 live broker capability 메시지만 등록하고, 이번 subtask의 live order request/response 여섯 개를 등록하지 않았다.
- `apps/client/test/contracts/alt_contracts_test.dart:41`도 같은 누락 상태라 parser map 테스트가 통과해도 새 live order 메시지 누락을 잡지 못한다.
- `services/worker/internal/socket/live.go:99`, `services/worker/internal/socket/live_mapping.go:36`, `services/worker/internal/livetrading/service.go:105` 경로는 confirmed submit이면 malformed intent도 broker port까지 갈 수 있다.
- `services/worker/internal/livetrading/service_test.go:88`의 confirmation gate 테스트는 `callCount`가 fake broker와 연결되지 않아 실제 호출 여부를 검증하지 않는다.

### 범위 결정 근거

이 follow-up은 parser map/test, worker submit validation, confirmation gate test만 다룬다. 새 broker selector 설계, risk/kill switch, durable audit, real KIS network order placement, account sync는 여전히 sibling subtask 범위다.

### 빌드 등급

Build lane `cloud-G07`, review lane `cloud-G07`를 유지한다. 변경 범위가 protocol/client parser와 live broker submit safety gate에 걸쳐 있어 첫 리뷰와 같은 판단 강도가 적절하다.

## 구현 체크리스트

- [ ] [REVIEW_LIVE_ORDER-1] Flutter client parser map과 parser map test에 새 live order request/response 메시지를 등록한다.
- [ ] [REVIEW_LIVE_ORDER-2] worker live submit path에서 malformed confirmed intent가 broker port로 나가지 않도록 invalid_request validation을 추가한다.
- [ ] [REVIEW_LIVE_ORDER-3] operator confirmation gate 테스트가 실제 broker call count와 forwarded intent를 검증하도록 고친다.
- [ ] [REVIEW_LIVE_ORDER-4] focused verification과 affected checks를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_LIVE_ORDER-1] Client Parser Map

#### 문제

Go API/worker/CLI parser maps는 live order request/response를 등록했지만 Flutter `altParserMap()`은 capability 메시지만 등록한다. `AltSocketClient`는 이 map을 사용하므로 client가 live order response를 typed decode하지 못한다.

#### 해결 방법

`apps/client/lib/src/contracts/alt_contracts.dart`에 아래 메시지를 추가한다.

- `SubmitLiveOrderRequest`
- `SubmitLiveOrderResponse`
- `CancelLiveOrderRequest`
- `CancelLiveOrderResponse`
- `GetLiveOrderRequest`
- `GetLiveOrderResponse`

`apps/client/test/contracts/alt_contracts_test.dart`의 expected message list와 parser count를 함께 갱신한다.

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

- [ ] `apps/client/lib/src/contracts/alt_contracts.dart`에 parser 등록을 추가한다.
- [ ] `apps/client/test/contracts/alt_contracts_test.dart`에 새 메시지 기대값과 count를 추가한다.

#### 중간 검증

```bash
cd apps/client && flutter test test/contracts/alt_contracts_test.dart
```

기대 결과: exit code 0.

### [REVIEW_LIVE_ORDER-2] Worker Live Submit Validation

#### 문제

현재 worker live submit은 `account_id`와 `intent != nil`만 확인한 뒤, confirmed request면 `livetrading.Service.SubmitLiveOrder`가 broker port를 호출한다. 이러면 API/client가 빈 instrument, invalid side, missing/non-positive quantity, empty type을 보내도 broker adapter까지 도달할 수 있다.

#### 해결 방법

worker-owned boundary에서 최소 shape를 검증한다.

- `account_id` required
- `intent` required
- `instrument_id` required
- `side`는 `buy` 또는 `sell`
- `quantity.amount.value` required and greater than zero
- `type` required, but custom broker strings are allowed
- optional `limit_price`가 있으면 amount는 numeric/non-negative이고 currency mapping을 보존한다

구현 위치는 `services/worker/internal/socket/live_mapping.go`의 proto-to-domain conversion과 `services/worker/internal/livetrading/service.go`의 defensive service validation 중 하나만으로 부족하지 않게 잡는다. direct service caller도 broker before validation을 우회하지 못해야 한다. validation error는 worker socket에서 `invalid_request` typed error로 매핑한다.

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

- [ ] `services/worker/internal/livetrading/service.go`에 invalid submit sentinel/helper를 추가하거나 동등한 typed mapping이 가능하게 한다.
- [ ] `services/worker/internal/socket/live_mapping.go` 또는 `live.go`에서 malformed live intent를 invalid_request로 변환한다.
- [ ] `services/worker/internal/livetrading/service_test.go` 또는 `services/worker/internal/socket/live_test.go`에 malformed confirmed request가 broker call count 0과 invalid_request를 보장하는 테스트를 추가한다.

#### 중간 검증

```bash
go test -count=1 ./services/worker/internal/livetrading ./services/worker/internal/socket
```

기대 결과: exit code 0.

### [REVIEW_LIVE_ORDER-3] Confirmation Gate Test Trust

#### 문제

`TestSubmitLiveOrderCallsBrokerOnlyAfterConfirmation`은 `callCount` 변수를 선언하지만 fake broker 호출과 연결하지 않는다. 따라서 unconfirmed path가 broker를 호출하더라도 테스트가 잡지 못한다.

#### 해결 방법

`fakeBroker`에 `submitCalls`, `lastAccountID`, `lastIntent` 같은 관측 필드를 추가하거나 test-local fake를 별도로 만들어 실제 호출 수를 assert한다.

Before:

```go
callCount := 0
// fakeBroker.SubmitOrder does not increment callCount.
```

After:

```go
if broker.submitCalls != 0 {
    t.Fatalf("broker should not be called without confirmation")
}
if broker.submitCalls != 1 {
    t.Fatalf("broker should be called once after confirmation")
}
```

custom order type/intent preservation assertion은 기존 테스트와 충돌하지 않게 유지한다.

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

- [ ] `services/worker/internal/livetrading/service_test.go`의 fake broker가 submit call count와 intent를 기록한다.
- [ ] unconfirmed submit은 broker call count 0을 검증한다.
- [ ] confirmed submit은 broker call count 1과 forwarded custom order type을 검증한다.

#### 중간 검증

```bash
go test -count=1 ./services/worker/internal/livetrading
```

기대 결과: exit code 0.

### [REVIEW_LIVE_ORDER-4] Verification

#### 문제

첫 리뷰에서 focused Go checks, contract drift, client parser test, lint는 통과했지만, 발견된 누락을 보강한 뒤 같은 영향권을 다시 확인해야 한다.

#### 해결 방법

다음 명령을 local checkout root 기준으로 실행하고 실제 stdout/stderr를 review stub에 기록한다.

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

- [ ] `bin/contracts-check`를 실행한다.
- [ ] `go test -count=1 ./packages/contracts/gen/go/...`를 실행한다.
- [ ] `go test -count=1 ./services/worker/internal/livetrading ./services/worker/internal/socket`를 실행한다.
- [ ] `go test -count=1 ./services/api/internal/socket ./services/api/internal/workerclient`를 실행한다.
- [ ] `go test -count=1 ./apps/cli/internal/operator`를 실행한다.
- [ ] `cd apps/client && flutter test test/contracts/alt_contracts_test.dart`를 실행한다.
- [ ] `bin/lint`를 실행한다.
- [ ] `git diff --check`를 실행한다.

#### 중간 검증

```bash
bin/contracts-check
go test -count=1 ./packages/contracts/gen/go/...
go test -count=1 ./services/worker/internal/livetrading ./services/worker/internal/socket
go test -count=1 ./services/api/internal/socket ./services/api/internal/workerclient
go test -count=1 ./apps/cli/internal/operator
cd apps/client && flutter test test/contracts/alt_contracts_test.dart
bin/lint
git diff --check
```

기대 결과: 모두 exit code 0. `bin/lint`의 기존 Mattermost `avoid_print` info는 exit code 0이면 pre-existing info로 기록할 수 있다.

## 최종 검증

```bash
bin/contracts-check
go test -count=1 ./packages/contracts/gen/go/...
go test -count=1 ./services/worker/internal/livetrading ./services/worker/internal/socket
go test -count=1 ./services/api/internal/socket ./services/api/internal/workerclient
go test -count=1 ./apps/cli/internal/operator
cd apps/client && flutter test test/contracts/alt_contracts_test.dart
bin/lint
git diff --check
```

기대 결과: 모두 exit code 0. 모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다.
