<!-- task=m-operator-headless-workflow-validation/03+02_backtest_workflows plan=2 tag=REVIEW_REVIEW_OPS -->

# Plan - REVIEW_REVIEW_OPS Backtest Matrix Completion

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 채우고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

두 번째 리뷰에서 CLI tests와 `bin/test`는 통과했지만, 첫 FAIL 후속 plan의 Required test scope가 아직 다 닫히지 않았다. `invalid_request_matrix.yaml`은 runtime typed-error row 두 개만 담고 있어 missing spec/run id, empty compare id, invalid market/timeframe/date range coverage를 증명하지 못한다. JSONL `exit_code`도 success path만 테스트되어 non-zero summary contract가 검증되지 않았다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. code-review가 해당 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md`
- Task ids:
  - `backtest-check`: backtest list/detail/result/start 관련 결과를 화면 없이 확인할 수 있다.
  - `error-output`: unavailable/error/disconnected 상태가 화면 없이도 판별 가능한 출력으로 남는다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-task/m-operator-headless-workflow-validation/03+02_backtest_workflows/plan_cloud_G07_1.log`
- `agent-task/m-operator-headless-workflow-validation/03+02_backtest_workflows/code_review_cloud_G07_1.log`
- `apps/cli/internal/operator/runner_error_test.go`
- `apps/cli/internal/operator/runner_backtest_test.go`
- `apps/cli/internal/operator/scenario.go`
- `apps/cli/internal/operator/scenario_test.go`
- `apps/cli/internal/operator/output.go`
- `apps/cli/internal/operator/runner.go`
- `apps/cli/internal/cli/cli_test.go`
- `apps/cli/testdata/operator/invalid_request_matrix.yaml`
- `apps/cli/testdata/operator/malformed_scenario.yaml`
- `apps/cli/testdata/operator/backtest_run_polling.yaml`
- `.gitignore`

### 테스트 환경 규칙

- `test_env=local`.
- 현재 shell 직접 테스트는 금지이며, 검증은 원격 테스트 host의 ALT checkout root에서 실행한다.
- 적용 profile: operations smoke because `apps/cli/**` and task artifacts changed.
- 리뷰 중 원격 code-server 컨테이너에서 focused operator test, `(cd apps/cli && go test -count=1 ./...)`, `bin/test`를 재실행했고 모두 exit code 0이었다.
- 추가로 `git diff --check`를 실행했고 `apps/cli/internal/operator/scenario_test.go:95: new blank line at EOF`를 확인했다.

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

- `invalid_request_matrix.yaml` only covers `invalid_run_id` and `compare_missing_runs`.
- Missing planned invalid cases: missing spec/run id, empty compare id in the matrix/evidence set, invalid market, invalid timeframe, invalid date range.
- `TestJSONLSummaryIncludesExitCode` only covers exit code 0 and does not prove non-zero JSONL summary output.

### 심볼 참조

- renamed/removed symbol: none.

### 분할 판단

- split decision policy를 평가했다.
- 기존 split subtask `03+02_backtest_workflows` 안의 리뷰 보완이며 새 sibling subtask를 만들지 않는다.
- predecessor `02`는 `agent-task/archive/2026/06/m-operator-headless-workflow-validation/02+01_api_market/complete.log`로 이미 충족되어 있다.

### 범위 결정 근거

- API/worker/contracts implementation은 수정하지 않는다.
- Backtest runner behavior already has follow-up fixes for output and polling; this plan only completes missing test/fixture coverage and whitespace cleanup.
- Flutter UI and real infra-backed backtest execution remain out of scope.

### 빌드 등급

- build lane: `cloud-G07`, review lane: `cloud-G07`.
- 이유: terminal scenario matrix, JSONL stdout contract, exit-status contract evidence를 보완한다.

## 구현 체크리스트

- [ ] `invalid_request_matrix` runtime fixture and companion validation tests cover every planned invalid case or explicitly classify why a case is dry-run-only.
- [ ] JSONL summary `exit_code` test covers at least one non-zero failure path.
- [ ] `git diff --check`가 통과하도록 whitespace를 정리한다.
- [ ] 원격 ALT checkout root에서 focused CLI test, `git diff --check`, `(cd apps/cli && go test -count=1 ./...)`, `bin/test`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_OPS-1] Complete Invalid Matrix Coverage

#### 문제

`apps/cli/testdata/operator/invalid_request_matrix.yaml:4` contains only two runtime rows. The prior plan required missing spec/run id, empty compare id, invalid market/timeframe/date range, typed output, transport/disconnected or expectation output to be visible through matrix/evidence tests.

#### 해결 방법

- Keep runnable runtime rows in `invalid_request_matrix.yaml` for supported requests that can reach the fake API.
- For cases rejected by dry-run validation before socket dial, add table-driven tests or companion fixtures with names that make the matrix coverage explicit.
- Tests must list every planned invalid case by id and assert the expected status/error marker or dry-run validation message.
- Include the empty compare id case in the same coverage inventory even if it remains a dry-run validation case.

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

- [ ] `apps/cli/testdata/operator/invalid_request_matrix.yaml`: runtime invalid cases are complete and named clearly.
- [ ] `apps/cli/internal/operator/runner_error_test.go`: matrix test asserts every runtime row and expected error/status marker.
- [ ] `apps/cli/internal/operator/scenario_test.go`: dry-run-only invalid cases table covers missing spec/run id, empty compare id, invalid market/timeframe/date range.
- [ ] `apps/cli/internal/cli/cli_test.go` and `apps/cli/testdata/operator/malformed_scenario.yaml`: keep parse-error tests independent from matrix execution.

#### 테스트 작성

- Update: `TestInvalidRequestMatrixOutputsExpectedErrors`
  - Assert exact runtime row ids and expected JSONL markers, not only the presence of two error codes.
- Add or update: `TestInvalidRequestMatrixDryRunCoverage`
  - Table entries for missing spec/run id, empty compare id, invalid market, invalid timeframe, invalid date range.

#### 중간 검증

원격 ALT checkout root 기준:

```bash
(cd apps/cli && go test -count=1 ./internal/operator -run 'TestInvalidRequestMatrix')
```

기대 결과: exit code 0.

### [REVIEW_REVIEW_OPS-2] JSONL Non-Zero Exit Code Evidence

#### 문제

`apps/cli/internal/operator/runner_error_test.go:135` validates `"exit_code":0` only. The previous follow-up plan required success and failure path coverage for JSONL summary `exit_code`.

#### 해결 방법

- Extend `TestJSONLSummaryIncludesExitCode` or add a sibling test for a non-zero path.
- Use a deterministic fake API expectation mismatch or closed-port transport failure.
- Assert the summary JSONL line contains the non-zero `exit_code` matching the returned code.

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

- [ ] `apps/cli/internal/operator/runner_error_test.go`: non-zero JSONL summary assertion 추가.
- [ ] `apps/cli/internal/operator/output.go` / `runner.go`: only adjust if the test exposes a bug.

#### 테스트 작성

- Add or update: `TestJSONLSummaryIncludesExitCode`
  - Covers success `0` and one non-zero case (`1` or `3`).

#### 중간 검증

원격 ALT checkout root 기준:

```bash
(cd apps/cli && go test -count=1 ./internal/operator -run 'TestJSONLSummaryIncludesExitCode')
```

기대 결과: exit code 0.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/cli/testdata/operator/invalid_request_matrix.yaml` | REVIEW_REVIEW_OPS-1 |
| `apps/cli/internal/operator/runner_error_test.go` | REVIEW_REVIEW_OPS-1, REVIEW_REVIEW_OPS-2 |
| `apps/cli/internal/operator/scenario_test.go` | REVIEW_REVIEW_OPS-1 |
| `apps/cli/internal/cli/cli_test.go` | REVIEW_REVIEW_OPS-1 |
| `apps/cli/testdata/operator/malformed_scenario.yaml` | REVIEW_REVIEW_OPS-1 |
| `apps/cli/internal/operator/output.go` | REVIEW_REVIEW_OPS-2 only if needed |
| `apps/cli/internal/operator/runner.go` | REVIEW_REVIEW_OPS-2 only if needed |

## 최종 검증

원격 ALT checkout root 기준으로 실행한다. CLI output/exit code tests should be fresh; use `-count=1`.

```bash
(cd apps/cli && go test -count=1 ./internal/operator -run 'Test(InvalidRequestMatrix|JSONLSummaryIncludesExitCode|LoadScenarioRejectsUnknownAction|ValidateRejectsEmptyCompareRunID)')
git diff --check
(cd apps/cli && go test -count=1 ./...)
bin/test
```

기대 결과: 모든 명령 exit code 0.

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