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

# Plan - LIVE_ORDER

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

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

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

## 배경

Live broker port가 생긴 뒤에는 주문 생성, 제출, 취소, 상태 조회 lifecycle을 operator-confirmed command로 열어야 한다. 기존 paper lifecycle은 `services/worker/internal/papertrading/service.go:286`의 in-memory submit과 `service.go:320`, `service.go:345`의 cancel/fill 흐름으로 닫혀 있고, `paper_trading.proto:89`는 paper-only request다. 이 계획은 live 전용 lifecycle을 추가하되 주문 type/capability를 설계에서 제한하지 않는다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.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

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/trading-expansion/PHASE.md`
- `agent-roadmap/phase/trading-expansion/milestones/live-trading-boundary.md`
- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/private/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/rules/project/domain/domain-model/rules.md`
- `agent-ops/rules/project/domain/contracts/rules.md`
- `agent-ops/rules/project/domain/worker/rules.md`
- `agent-ops/rules/project/domain/api/rules.md`
- `agent-ops/rules/project/domain/operations/rules.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-test/local/rules.md`
- `agent-test/local/domain-model-smoke.md`
- `agent-test/local/contracts-smoke.md`
- `agent-test/local/worker-smoke.md`
- `agent-test/local/api-smoke.md`
- `agent-test/local/client-smoke.md`
- `agent-test/local/operations-smoke.md`
- `packages/domain/backtest/types.go`
- `packages/domain/backtest/types_test.go`
- `packages/domain/market/types.go`
- `packages/contracts/proto/alt/v1/paper_trading.proto`
- `bin/contracts-gen`
- `bin/contracts-check`
- `services/worker/internal/papertrading/service.go`
- `services/worker/internal/papertrading/service_test.go`
- `services/worker/internal/contracts/parser_map.go`
- `services/worker/internal/socket/backtest.go`
- `services/worker/internal/socket/handlers.go`
- `services/worker/internal/socket/paper.go`
- `services/worker/internal/socket/paper_mapping.go`
- `services/worker/internal/socket/paper_test.go`
- `services/worker/internal/socket/server_test.go`
- `services/api/internal/contracts/parser_map.go`
- `services/api/internal/workerclient/client.go`
- `services/api/internal/socket/handlers.go`
- `services/api/internal/socket/paper.go`
- `services/api/internal/socket/paper_test.go`
- `apps/cli/internal/operator/scenario.go`
- `apps/cli/internal/operator/scenario_test.go`
- `apps/cli/internal/operator/runner.go`
- `apps/cli/internal/operator/runner_paper_test.go`
- `apps/cli/internal/operator/client.go`
- `apps/cli/internal/operator/client_test.go`
- `apps/cli/internal/operator/output.go`
- `apps/cli/internal/operator/parser_map.go`
- `apps/cli/internal/operator/parser_map_test.go`
- `apps/cli/testdata/operator/paper_order_lifecycle.yaml`
- `apps/cli/testdata/operator/expected/paper_order_lifecycle.jsonl`
- `apps/cli/testdata/operator/headless_validation.md`

### 테스트 환경 규칙

`test_env=local`이다. `agent-test/local/rules.md`와 domain/contracts/worker/api/client/operations smoke profile을 읽었다. `.proto`, worker socket, API forwarder, CLI scenario가 함께 바뀌므로 `bin/contracts-gen`, `bin/contracts-check`, `go test ./packages/domain/...`, `go test ./packages/contracts/gen/go/...`, `go test ./services/worker/...`, `go test ./services/api/...`, `go test ./apps/cli/...`, `bin/lint`를 적용한다.

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

- Live submit requires operator confirmation: 기존 테스트 없음. worker/API/CLI 모두 새 테스트가 필요하다.
- Live cancel/status lifecycle: 기존 paper tests는 paper-only `PaperOrderStatus`만 검증한다. live order status와 broker status mapping 테스트가 필요하다.
- Custom broker order type preservation: 기존 paper validator는 `market`/`limit`만 허용한다(`apps/cli/internal/operator/scenario.go:81`). live scenario validator는 이 제한을 재사용하면 안 된다.
- Headless live scenario evidence: paper fixture만 존재한다. live order lifecycle YAML/expected JSONL/handoff matrix를 추가해야 한다.

### 심볼 참조

Renamed/removed symbols: none. 새 action/message/service symbols만 추가한다. `SubmitPaperOrder`, `CancelPaperOrder`, `FillPaperOrder` 참조는 paper path로 유지한다.

### 분할 판단

공통 task group은 `m-live-trading-boundary`다. sibling split은 `01_broker_port`, `02+01_order_lifecycle`, `03+01,02_risk_kill_switch`, `04+01_account_sync`, `05+02,03,04_audit_trail`이다. 이 subtask는 directory name상 predecessor `01`만 필요하다. 현재 active `agent-task/m-live-trading-boundary/01_broker_port/complete.log`는 없고 archive completion도 확인되지 않았으므로 구현 시작 전 `01_broker_port` PASS `complete.log`가 필요하다.

### 범위 결정 근거

이 subtask는 risk limit/kill switch 정책을 구현하지 않는다. risk gate는 `03+01,02_risk_kill_switch`가 submit path에 추가한다. Account sync는 `04+01_account_sync`, durable audit은 `05+02,03,04_audit_trail`로 분리한다. KIS real network order placement와 credentials/account number는 제외하고, broker port는 fake broker 테스트와 default unavailable/disabled behavior로 검증한다.

### 빌드 등급

Build lane `cloud-G07`, review lane `cloud-G07`. Operator confirmation과 live order lifecycle은 protocol/schema, worker/API/CLI cross-module, money-moving boundary risk가 있어 cloud reasoning과 review가 필요하다.

## 의존 관계 및 구현 순서

Directory dependency source of truth: `02+01_order_lifecycle` means predecessor index `01` only. `agent-task/m-live-trading-boundary/01_broker_port/complete.log`가 생기기 전에는 구현을 시작하지 않는다.

## 구현 체크리스트

- [ ] [LIVE_ORDER-1] live order request/response contract를 additive하게 확장한다.
- [ ] [LIVE_ORDER-2] worker live service에 submit/cancel/status lifecycle과 operator confirmation gate를 추가한다.
- [ ] [LIVE_ORDER-3] API workerclient와 socket forwarder를 추가한다.
- [ ] [LIVE_ORDER-4] CLI headless live order scenario actions/output/fixtures를 추가한다.
- [ ] [LIVE_ORDER-5] focused verification과 full affected module tests를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [LIVE_ORDER-1] Contract

#### 문제

`packages/contracts/proto/alt/v1/paper_trading.proto:89`의 submit request는 paper-only이며 live order confirmation, broker id, client order id, broker status를 표현하지 않는다.

#### 해결 방법

`live_trading.proto`를 additive하게 확장한다. `SubmitLiveOrderRequest`, `SubmitLiveOrderResponse`, `CancelLiveOrderRequest`, `CancelLiveOrderResponse`, `GetLiveOrderRequest`, `GetLiveOrderResponse`를 추가한다. `LiveOrderIntent.type`은 string으로 두고 custom broker order type을 보존한다. Submit request에는 `operator_confirmation` 메시지를 넣고, false/empty면 worker가 typed invalid_request를 반환한다.

Before:

```proto
// packages/contracts/proto/alt/v1/paper_trading.proto:89
message SubmitPaperOrderRequest {
  string account_id = 1;
  string instrument_id = 2;
  string side = 3;
  Quantity quantity = 4;
  string type = 5;
  Price limit_price = 6;
}
```

After:

```proto
message OperatorConfirmation {
  bool confirmed = 1;
  string operator_id = 2;
  string reason = 3;
  int64 confirmed_at_unix_ms = 4;
}

message SubmitLiveOrderRequest {
  string account_id = 1;
  LiveOrderIntent intent = 2;
  OperatorConfirmation operator_confirmation = 3;
  string idempotency_key = 4;
}
```

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

- [ ] `packages/contracts/proto/alt/v1/live_trading.proto`를 확장한다.
- [ ] `bin/contracts-gen` 대상 proto 목록에 live proto가 없으면 추가한다.
- [ ] generated output은 `bin/contracts-gen`으로만 갱신한다.
- [ ] worker/API/CLI parser map에 새 request/response를 등록한다.

#### 테스트 작성

Parser map tests를 확장한다. `go test ./packages/contracts/gen/go/...`와 `bin/contracts-check`가 contract drift를 검증한다.

#### 중간 검증

```bash
bin/contracts-gen
bin/contracts-check
go test ./packages/contracts/gen/go/...
```

기대 결과: exit code 0.

### [LIVE_ORDER-2] Worker Lifecycle

#### 문제

Worker socket `Deps`에는 `services/worker/internal/socket/backtest.go:53`의 `Paper PaperService`만 있고 live order executor가 없다. Paper service는 `service.go:290`에서 submit을 저장하고 `service.go:322`에서 cancel하지만 real broker confirmation gate가 없다.

#### 해결 방법

`services/worker/internal/livetrading` 패키지를 만들거나 01에서 만든 live service를 확장한다. `Service`는 in-memory order registry와 `BrokerPort` interface를 가진다. `SubmitLiveOrder`는 operator confirmation을 먼저 검증하고, `BrokerPort`가 nil이면 unavailable/disabled error를 반환한다. 테스트 fake broker는 accepted/rejected/canceled/fill status를 반환하게 한다. Status vocabulary는 domain `trading.OrderStatus`와 broker raw status를 함께 보존한다.

Before:

```go
// services/worker/internal/socket/backtest.go:46
type Deps struct {
	Starter          BacktestStarter
	Analysis         storage.BacktestAnalysisStore
	Results          storage.BacktestResultStore
	Instruments      storage.InstrumentStore
	Bars             storage.BarStore
	DailyBarImporter DailyBarImporter
	Paper            PaperService
}
```

After:

```go
type Deps struct {
	Starter          BacktestStarter
	Analysis         storage.BacktestAnalysisStore
	Results          storage.BacktestResultStore
	Instruments      storage.InstrumentStore
	Bars             storage.BarStore
	DailyBarImporter DailyBarImporter
	Paper            PaperService
	Live             LiveService
}
```

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

- [ ] `services/worker/internal/livetrading/service.go`를 추가/확장한다.
- [ ] `services/worker/internal/livetrading/service_test.go`를 추가한다.
- [ ] `services/worker/internal/socket/live.go`에 submit/cancel/get handlers를 추가한다.
- [ ] `services/worker/internal/socket/live_mapping.go`에 proto/domain mapping을 추가한다.
- [ ] `services/worker/internal/socket/live_test.go`에 confirmation required, fake broker success, cancel/status tests를 추가한다.

#### 테스트 작성

작성한다. `TestSubmitLiveOrderRequiresOperatorConfirmation`, `TestSubmitLiveOrderPreservesCustomOrderType`, `TestSubmitLiveOrderCallsBrokerOnlyAfterConfirmation`, `TestCancelLiveOrderTransitionsStatus`, `TestGetLiveOrderReturnsBrokerStatus`를 포함한다.

#### 중간 검증

```bash
go test ./services/worker/internal/livetrading ./services/worker/internal/socket
```

기대 결과: exit code 0.

### [LIVE_ORDER-3] API Forwarding

#### 문제

API `WorkerClient`는 `services/api/internal/workerclient/client.go:40`부터 paper methods만 갖고 live lifecycle methods가 없다. API session handler도 `services/api/internal/socket/handlers.go:32`에서 paper까지만 append한다.

#### 해결 방법

`WorkerClient`에 submit/cancel/get live order methods를 추가하고 socket client에서 `sendTyped` one-line forwarder를 구현한다. `services/api/internal/socket/live.go`는 request shape만 검증하고 worker로 그대로 전달한다. API는 broker/risk/storage logic을 갖지 않는다.

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

- [ ] `services/api/internal/workerclient/client.go`에 live order methods를 추가한다.
- [ ] `services/api/internal/socket/live.go`를 확장한다.
- [ ] `services/api/internal/socket/handlers.go`에 live handlers를 append한다.
- [ ] `services/api/internal/socket/live_test.go`를 확장한다.
- [ ] API fake worker client test helper에 live request capture를 추가한다.

#### 테스트 작성

작성한다. Submit forward, missing account/intent validation, nil worker unavailable, timeout mapping, cancel/get forward를 포함한다.

#### 중간 검증

```bash
go test ./services/api/internal/workerclient ./services/api/internal/socket
```

기대 결과: exit code 0.

### [LIVE_ORDER-4] CLI Headless Workflow

#### 문제

CLI scenario actions는 `apps/cli/internal/operator/scenario.go:44`부터 paper actions만 정의하고, paper submit validator는 `scenario.go:81`에서 market/limit로 order type을 닫는다. Live order lifecycle evidence fixture가 없다.

#### 해결 방법

`submit_live_order`, `cancel_live_order`, `get_live_order` actions를 추가한다. Live request fields는 `broker`, `account_id`, `instrument_id`, `side`, `quantity`, `order_type`, `limit_price`, `operator_confirmed`, `operator_id`, `confirmation_reason`, `idempotency_key`를 둔다. Live order type validator는 빈 값만 막고 알려진 type으로 제한하지 않는다. Output은 `live_order_id`, `live_order_status`, `broker_order_id`, `broker_status`, `operator_confirmed`를 render한다. `apps/cli/testdata/operator/live_order_lifecycle.yaml`과 expected JSONL을 추가하고 `headless_validation.md`에 matrix row를 추가한다.

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

- [ ] `apps/cli/internal/operator/scenario.go`에 live actions/request/expect validation을 추가한다.
- [ ] `apps/cli/internal/operator/runner.go`에 live action execution/evaluation을 추가한다.
- [ ] `apps/cli/internal/operator/client.go`에 live order methods를 추가한다.
- [ ] `apps/cli/internal/operator/output.go`에 live output fields를 추가한다.
- [ ] `apps/cli/internal/operator/client_test.go` fake API에 live handlers를 추가한다.
- [ ] `apps/cli/internal/operator/runner_live_test.go`를 추가한다.
- [ ] `apps/cli/internal/operator/scenario_test.go`에 live validation tests를 추가한다.
- [ ] `apps/cli/testdata/operator/live_order_lifecycle.yaml`과 expected JSONL을 추가한다.
- [ ] `apps/cli/testdata/operator/headless_validation.md`에 live order row를 추가한다.

#### 테스트 작성

작성한다. Confirmation missing rejected, custom order type forwarded, submit->cancel/get interpolation, expected fixture JSONL parse를 포함한다.

#### 중간 검증

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

기대 결과: exit code 0.

### [LIVE_ORDER-5] Verification

#### 문제

이 subtask는 contract generation, worker lifecycle, API forwarder, CLI headless workflow를 함께 바꾼다. 한 패키지 테스트만으로는 누락을 잡기 어렵다.

#### 해결 방법

profile 기준 focused commands를 모두 실행하고, generated drift는 `bin/contracts-check`로 고정한다.

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

- [ ] `bin/contracts-check`를 실행한다.
- [ ] `go test ./packages/domain/...`를 실행한다.
- [ ] `go test ./packages/contracts/gen/go/...`를 실행한다.
- [ ] `go test ./services/worker/...`를 실행한다.
- [ ] `go test ./services/api/...`를 실행한다.
- [ ] `go test ./apps/cli/...`를 실행한다.

#### 테스트 작성

추가 테스트는 LIVE_ORDER-1부터 LIVE_ORDER-4에 포함한다.

#### 중간 검증

```bash
bin/contracts-check
go test ./packages/domain/...
go test ./packages/contracts/gen/go/...
go test ./services/worker/...
go test ./services/api/...
go test ./apps/cli/...
bin/lint
```

기대 결과: 모두 exit code 0. Go test cache output은 최종 검증에서 허용한다. Flutter/Dart toolchain 부재로 `bin/lint`가 실행 불가하면 pass가 아니며 차단 근거를 기록한다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `packages/contracts/proto/alt/v1/live_trading.proto` | LIVE_ORDER-1 |
| `bin/contracts-gen` | LIVE_ORDER-1 |
| `services/worker/internal/contracts/parser_map.go` | LIVE_ORDER-1 |
| `services/api/internal/contracts/parser_map.go` | LIVE_ORDER-1 |
| `apps/cli/internal/operator/parser_map.go` | LIVE_ORDER-1 |
| `services/worker/internal/livetrading/service.go` | LIVE_ORDER-2 |
| `services/worker/internal/livetrading/service_test.go` | LIVE_ORDER-2 |
| `services/worker/internal/socket/live.go` | LIVE_ORDER-2 |
| `services/worker/internal/socket/live_mapping.go` | LIVE_ORDER-2 |
| `services/worker/internal/socket/live_test.go` | LIVE_ORDER-2 |
| `services/api/internal/workerclient/client.go` | LIVE_ORDER-3 |
| `services/api/internal/socket/live.go` | LIVE_ORDER-3 |
| `services/api/internal/socket/handlers.go` | LIVE_ORDER-3 |
| `services/api/internal/socket/live_test.go` | LIVE_ORDER-3 |
| `apps/cli/internal/operator/scenario.go` | LIVE_ORDER-4 |
| `apps/cli/internal/operator/runner.go` | LIVE_ORDER-4 |
| `apps/cli/internal/operator/client.go` | LIVE_ORDER-4 |
| `apps/cli/internal/operator/output.go` | LIVE_ORDER-4 |
| `apps/cli/internal/operator/client_test.go` | LIVE_ORDER-4 |
| `apps/cli/internal/operator/runner_live_test.go` | LIVE_ORDER-4 |
| `apps/cli/internal/operator/scenario_test.go` | LIVE_ORDER-4 |
| `apps/cli/testdata/operator/live_order_lifecycle.yaml` | LIVE_ORDER-4 |
| `apps/cli/testdata/operator/expected/live_order_lifecycle.jsonl` | LIVE_ORDER-4 |
| `apps/cli/testdata/operator/headless_validation.md` | LIVE_ORDER-4 |

## 최종 검증

```bash
bin/contracts-check
go test ./packages/domain/...
go test ./packages/contracts/gen/go/...
go test ./services/worker/...
go test ./services/api/...
go test ./apps/cli/...
bin/lint
```

기대 결과: 모든 명령 exit code 0. `bin/contracts-check` 실행 불가 시 pass가 아니며 `command -v protoc`, `command -v protoc-gen-go` 결과를 기록한다. `bin/lint` 실행 불가 시 `command -v flutter` 결과를 기록한다.

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