<!-- task=m-backtest-scenario-automation/07+05_result_poll_remote plan=1 tag=REVIEW_TEST -->

# Result Poll Remote Evidence Follow-up Plan

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

검증을 실행한 뒤 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 stdout/stderr를 채운다. active 파일을 유지하고 리뷰 준비를 보고한다. 최종 판정, archive, `complete.log` 작성은 code-review 전용이다.

구현 중 사용자 결정, 사용자 소유 외부 환경/secret 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 사용자에게 직접 묻지 말고 review stub의 `사용자 리뷰 요청`에 근거를 남긴다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다.

## 배경

첫 리뷰 루프는 원격 `backtest_result_summary.yaml` 실행이 exit code 0으로 끝난 것은 확인했지만, result line에 필수 evidence key인 `trade_count`가 없었다. 원인은 원격 protobuf encoding이 아니라 ALT CLI output layer가 `TradeCount > 0`일 때만 key를 출력하는 조건이다. 이 follow-up은 zero-trade result summary도 안정적인 JSONL/text evidence를 남기도록 고치고, 현재 checkout에서 로컬/원격 검증을 다시 신뢰 가능하게 기록한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/backtest-loop/milestones/backtest-scenario-automation.md`
- Task ids:
  - `result-poll`: `backtest_result_summary.yaml`이 terminal status 도달 후 result를 조회하도록 flaky 구간을 제거하고 원격 JSONL evidence를 기록한다.
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Archived plan: `agent-task/m-backtest-scenario-automation/07+05_result_poll_remote/plan_cloud_G06_0.log`
- Archived review: `agent-task/m-backtest-scenario-automation/07+05_result_poll_remote/code_review_cloud_G06_0.log`
- Verdict: FAIL
- Required issue 1: [apps/cli/internal/operator/output.go:198](/config/workspace/alt/apps/cli/internal/operator/output.go:198) outputs `trade_count` only when `TradeCount > 0`. The archived remote JSONL result line has `starting_cash`, `ending_equity`, and `total_return`, but no `trade_count`, so it does not satisfy [PLAN-cloud-G06.md:75](/config/workspace/alt/agent-task/m-backtest-scenario-automation/07+05_result_poll_remote/plan_cloud_G06_0.log:75) or [headless_validation.md:37](/config/workspace/alt/apps/cli/testdata/operator/headless_validation.md:37).
- Required issue 2: The archived review records `go test -count=1 ./apps/cli/internal/operator ./apps/cli/internal/cli` as passing, but reviewer rerun failed in the current checkout:
  ```text
  FAIL	git.toki-labs.com/toki/alt/apps/cli/internal/operator [build failed]
  FAIL	git.toki-labs.com/toki/alt/apps/cli/internal/cli [build failed]
  FAIL
  # git.toki-labs.com/toki/proto-socket/go
  ../proto-socket/go/communicator.go:331:18: assignment mismatch: 1 variable but c.nextNonce returns 2 values
  ../proto-socket/go/communicator.go:627:19: multiple-value c.nextNonce() (value of type (int32, error)) in single-value context
  ```
- Reviewer local checks: `git diff --check` exited 0 with no output; predecessor complete log exists at `agent-task/archive/2026/06/m-backtest-scenario-automation/05+04_runtime_startup/complete.log`; `rg --sort path -n "poll_backtest_run|run_status: succeeded|get_backtest_result" ...` found the expected YAML order.
- Narrow archive reread allowed if needed: read only the archived plan/review paths listed above, not `agent-task/archive/**` broadly.

## 분석 결과

### 읽은 파일

- `apps/cli/internal/operator/output.go`
- `apps/cli/internal/operator/runner.go`
- `apps/cli/internal/operator/runner_backtest_test.go`
- `apps/cli/internal/operator/handoff_test.go`
- `apps/cli/testdata/operator/backtest_result_summary.yaml`
- `apps/cli/testdata/operator/expected/backtest_result_summary.jsonl`
- `apps/cli/testdata/operator/headless_validation.md`
- archived plan/review paths in `Archive Evidence Snapshot`

### 범위 결정 근거

이 follow-up은 `result-poll` Task의 미충족 evidence를 닫기 위한 최소 수리다. API/worker/protobuf schema 변경은 기본 범위가 아니며, 원격 result line의 missing `trade_count`는 CLI output presence 표현 문제로 먼저 해결한다.

### 빌드 등급

`cloud-G06`을 유지한다. 원격 evidence와 검증 신뢰도는 중요하지만, 수정 범위는 `apps/cli/internal/operator` 출력 계약과 테스트로 좁고 명령도 결정적이다.

## 구현 체크리스트

- [ ] [REVIEW_TEST-1] `apps/cli/internal/operator/output.go`와 필요한 caller에서 result summary가 존재하면 `trade_count`가 `0`이어도 JSONL/text output에 출력되도록 수정한다.
- [ ] [REVIEW_TEST-1] `apps/cli/internal/operator/runner_backtest_test.go` 또는 동등한 operator test에 zero-trade result summary case를 추가해 `trade_count=0` 또는 `"trade_count":0`이 출력되는지 검증한다.
- [ ] [REVIEW_TEST-1] `apps/cli/testdata/operator/backtest_result_summary.yaml`, `expected/backtest_result_summary.jsonl`, `headless_validation.md`가 `start -> poll -> result` 순서와 promised keys를 계속 일관되게 표현하는지 확인하고 필요한 경우만 갱신한다.
- [ ] [REVIEW_TEST-2] `go test -count=1 ./apps/cli/internal/operator ./apps/cli/internal/cli`를 현재 checkout에서 재실행하고 실제 stdout/stderr를 기록한다. 같은 `../proto-socket/go` build failure가 남으면 `command -v go`와 `git -C ../proto-socket/go status --short` 결과를 함께 기록하고, 사용자 소유 외부 상태 없이는 해소할 수 없을 때만 `사용자 리뷰 요청`을 채운다.
- [ ] [REVIEW_TEST-2] 원격 runner에서 `backtest_result_summary.yaml --output jsonl`을 다시 실행해 exit code 0, `run_status=succeeded`, `starting_cash`, `ending_equity`, `total_return`, `trade_count`를 실제 출력으로 기록한다.
- [ ] `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_TEST-1] Preserve zero trade_count in result output

#### 문제

원격 백테스트 결과가 trade 0건이면 `StepEvent.TradeCount`가 0으로 채워지지만, writer가 `TradeCount > 0` 조건으로 출력하기 때문에 `trade_count` key가 사라진다. `headless_validation.md`와 roadmap task는 key 존재 자체를 evidence로 사용하므로 zero-trade 성공 run도 `trade_count: 0`을 출력해야 한다.

#### 해결 방법

`StepEvent`에 result summary 또는 trade count presence를 표현하는 필드를 추가하거나 동등한 구조로 zero value와 absence를 구분한다. `evaluateBacktest`에서 summary가 있을 때 presence를 설정하고, JSONL/text writer가 presence 기준으로 `trade_count`를 출력하게 한다. 기존 non-zero output과 다른 step output은 유지한다.

#### 테스트 결정

operator 테스트에 zero-trade result summary case를 추가한다. 가능하면 실제 runner path를 사용해 `get_backtest_result` 출력에 `trade_count=0` 또는 JSONL `"trade_count":0`이 포함되는지 확인한다.

#### 중간 검증

```bash
go test -count=1 ./apps/cli/internal/operator
```

### [REVIEW_TEST-2] Restore verification trust and remote evidence

#### 문제

첫 루프의 로컬 검증 PASS output은 현재 checkout에서 재현되지 않는다. 또한 원격 command는 exit code 0이지만 필수 key 누락 때문에 완료 evidence가 아니다.

#### 해결 방법

현재 checkout에서 로컬 검증을 새로 실행해 실제 stdout/stderr를 기록한다. sibling `../proto-socket/go` build failure가 남으면 상태를 좁게 수집하고, repo/checkout에서 해결 가능한지 판단한다. 이후 원격 runner에서 동일 scenario를 다시 실행해 result line에 `trade_count`까지 포함되는지 기록한다. raw endpoint, token, secret 값은 기록하지 않는다.

#### 테스트 결정

최종 검증 명령 전체를 실행한다. 원격 명령은 sanitized command/output만 review stub에 남긴다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/cli/internal/operator/output.go` | REVIEW_TEST-1 |
| `apps/cli/internal/operator/runner.go` | REVIEW_TEST-1, 필요 시 |
| `apps/cli/internal/operator/runner_backtest_test.go` | REVIEW_TEST-1 |
| `apps/cli/testdata/operator/backtest_result_summary.yaml` | REVIEW_TEST-1 확인, 필요 시 |
| `apps/cli/testdata/operator/expected/backtest_result_summary.jsonl` | REVIEW_TEST-1 확인, 필요 시 |
| `apps/cli/testdata/operator/headless_validation.md` | REVIEW_TEST-1 확인, 필요 시 |
| active review stub | REVIEW_TEST-2 evidence 기록 |

## 최종 검증

```bash
git diff --check
go test -count=1 ./apps/cli/internal/operator ./apps/cli/internal/cli
test -f agent-task/m-backtest-scenario-automation/05+04_runtime_startup/complete.log || find agent-task/archive -path '*/m-backtest-scenario-automation/05+04_runtime_startup/complete.log' -print -quit
rg --sort path -n "poll_backtest_run|run_status: succeeded|get_backtest_result|trade_count" apps/cli/testdata/operator/backtest_result_summary.yaml apps/cli/testdata/operator/expected/backtest_result_summary.jsonl apps/cli/testdata/operator/headless_validation.md apps/cli/internal/operator/output.go apps/cli/internal/operator/runner_backtest_test.go
ssh "$ALT_REMOTE_SSH" "set -euo pipefail; cd \"$ALT_REMOTE_ALT_ROOT/apps/cli\"; go run ./cmd/alt operator scenario run --file testdata/operator/backtest_result_summary.yaml --api-url \"ws://127.0.0.1:18030/socket\" --output jsonl"
```

기대 결과: local commands exit code 0. Remote command exit code 0 and JSONL contains `run_status=succeeded`, `starting_cash`, `ending_equity`, `total_return`, `trade_count`. Go test cache output은 허용하지 않고 `-count=1` fresh run을 사용한다.
