<!-- task=m-backtest-analysis-surface/02+01_worker_analysis_store plan=1 tag=REVIEW_BAS-WORKER -->

# Plan - REVIEW_BAS-WORKER

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

`CODE_REVIEW-cloud-G08.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증을 실행하고, active 파일은 그대로 둔 채 리뷰 준비를 보고한다. 사용자 결정, 외부 환경 준비, 범위 충돌이 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 근거를 남기고 멈춘다. archive, `complete.log`, 최종 판정은 code-review 전용이다.

## 배경

첫 리뷰에서 worker analysis storage 구현은 단위 검증을 통과했지만, 기존 version 1 migration을 직접 수정해 이미 마이그레이션된 DB가 새 result columns를 받지 못하는 문제가 발견됐다. 또한 새 analysis read surface의 핵심 동작인 detail no-result와 compare ordering/error path가 store 경로로 검증되지 않았다. 이 follow-up은 migration/versioning과 storage regression coverage만 보완한다.

## 사용자 리뷰 요청 흐름

구현 중 차단 조건은 active `CODE_REVIEW-cloud-G08.md`의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 이를 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.

## 분석 결과

### 읽은 파일

- `agent-task/m-backtest-analysis-surface/02+01_worker_analysis_store/plan_cloud_G08_0.log`
- `agent-task/m-backtest-analysis-surface/02+01_worker_analysis_store/code_review_cloud_G08_0.log`
- `packages/domain/backtest/types.go`
- `packages/domain/backtest/types_test.go`
- `services/worker/internal/backtest/engine.go`
- `services/worker/internal/backtest/engine_test.go`
- `services/worker/internal/backtest/fixture_test.go`
- `services/worker/internal/storage/ports.go`
- `services/worker/internal/storage/postgres/migrate.go`
- `services/worker/internal/storage/postgres/migrate_test.go`
- `services/worker/internal/storage/postgres/mapping.go`
- `services/worker/internal/storage/postgres/mapping_test.go`
- `services/worker/internal/storage/postgres/migrations/000001_worker_backbone.up.sql`
- `services/worker/internal/storage/postgres/migrations/000001_worker_backbone.down.sql`
- `services/worker/internal/storage/postgres/queries/queries.sql`
- `services/worker/internal/storage/postgres/sqlc/models.go`
- `services/worker/internal/storage/postgres/sqlc/queries.sql.go`
- `services/worker/internal/storage/postgres/store.go`
- `services/worker/sqlc.yaml`
- `bin/worker-storage-gen`
- `bin/worker-storage-check`

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

- Migration upgrade path: 현재 tests는 migration 파일 존재/순서만 확인한다. 이미 version 1이 적용된 DB에 result analysis columns를 추가하는 v2 path를 확인하지 않는다.
- Store analysis reads: `ListRuns`, `GetRunDetail`, `CompareResults`가 compile은 되지만, detail no-result branch, compare input order preservation, missing result propagation은 test로 고정되지 않았다.

### 심볼 참조

- Renamed/removed symbols: none.
- Added surface references found under `services/worker/internal/storage/ports.go` and `services/worker/internal/storage/postgres/store.go`; no existing caller rename is required.

### 분할 판단

- Split policy evaluated before writing this follow-up.
- Existing task directory `02+01_worker_analysis_store` is already the worker storage split unit.
- Additional split is not needed because both fixes share the same Postgres storage ownership boundary and the same verification commands.

### 범위 결정 근거

- Include migration files, SQLC generation outputs, Postgres store tests, and any minimal store test helper needed inside `services/worker/internal/storage/postgres`.
- Exclude domain result shape, backtest engine calculation, protobuf/contracts, API parser maps, and Flutter client files; those are not required to resolve the review findings.
- Do not modify roadmap files from this follow-up.

### 빌드 등급

- build lane: `cloud-G08`; review lane: `cloud-G08`.
- Rationale: follow-up fixes storage migration/versioning and store behavior test gaps from a failed cloud review.

## 구현 체크리스트

- [ ] [REVIEW_BAS-WORKER-1] backtest analysis result columns를 새 versioned migration으로 이동하고 sqlc generated files를 갱신한다.
- [ ] [REVIEW_BAS-WORKER-2] analysis store detail/compare regression tests를 추가한다.
- [ ] [REVIEW_BAS-WORKER-3] `go test ./services/worker/internal/storage/postgres/...`, `go test ./services/worker/...`, `bin/worker-storage-check`, `git diff --check`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_BAS-WORKER-1] Move analysis columns to a versioned migration

문제: `services/worker/internal/storage/postgres/migrations/000001_worker_backbone.up.sql:35`의 기존 version 1 migration에 `total_return`, `trade_count`, `equity_curve`가 직접 추가됐다. `services/worker/internal/storage/postgres/migrate.go:65`는 version 1이 이미 적용된 DB를 건너뛰므로 기존 DB에서는 새 `UpsertResult` SQL이 없는 column 때문에 실패한다.

해결 방법:

Before:

```sql
35 CREATE TABLE IF NOT EXISTS backtest_results (
...
41     total_return NUMERIC NOT NULL,
42     trade_count INTEGER NOT NULL,
43     trades JSONB NOT NULL,
44     positions JSONB NOT NULL,
45     equity_curve JSONB NOT NULL
46 );
```

After:

```sql
-- 000001 keeps the originally applied backbone columns.
CREATE TABLE IF NOT EXISTS backtest_results (
    run_id TEXT PRIMARY KEY REFERENCES backtest_runs(id) ON DELETE CASCADE,
    starting_cash_currency TEXT NOT NULL,
    starting_cash_amount NUMERIC NOT NULL,
    ending_equity_currency TEXT NOT NULL,
    ending_equity_amount NUMERIC NOT NULL,
    trades JSONB NOT NULL,
    positions JSONB NOT NULL
);

-- 000002_backtest_analysis_results.up.sql
ALTER TABLE backtest_results
    ADD COLUMN IF NOT EXISTS total_return NUMERIC NOT NULL DEFAULT 0,
    ADD COLUMN IF NOT EXISTS trade_count INTEGER NOT NULL DEFAULT 0,
    ADD COLUMN IF NOT EXISTS equity_curve JSONB NOT NULL DEFAULT '[]'::jsonb;

ALTER TABLE backtest_results
    ALTER COLUMN total_return DROP DEFAULT,
    ALTER COLUMN trade_count DROP DEFAULT,
    ALTER COLUMN equity_curve DROP DEFAULT;
```

수정 파일 및 체크리스트:

- [ ] `services/worker/internal/storage/postgres/migrations/000001_worker_backbone.up.sql` restores the original `backtest_results` columns.
- [ ] Add `services/worker/internal/storage/postgres/migrations/000002_backtest_analysis_results.up.sql`.
- [ ] Add `services/worker/internal/storage/postgres/migrations/000002_backtest_analysis_results.down.sql`.
- [ ] `services/worker/internal/storage/postgres/migrate_test.go` still verifies sequential migration pairs.
- [ ] Run `bin/worker-storage-gen` and keep `services/worker/internal/storage/postgres/sqlc/models.go` / `queries.sql.go` in sync with final schema.

테스트 작성: update migration tests only if the existing sequential-pair test does not fail on missing `000002` down file. Add a small assertion that `000002_backtest_analysis_results.up.sql` is embedded and contains the three new column names.

중간 검증:

```bash
bin/worker-storage-gen
bin/worker-storage-check
go test ./services/worker/internal/storage/postgres/... -run 'TestMigrations|TestMigration'
```

Expected: all commands exit 0 and sqlc output has no drift after generation.

### [REVIEW_BAS-WORKER-2] Add analysis store regression tests

문제: `services/worker/internal/storage/postgres/store.go:158`의 `GetRunDetail` missing-result branch와 `services/worker/internal/storage/postgres/store.go:174`의 `CompareResults` ordered result path are untested. Mapping tests prove row conversion, but they do not prove the public `BacktestAnalysisStore` behavior promised by `services/worker/internal/storage/ports.go:47`.

해결 방법:

- Add a store-level test file under `services/worker/internal/storage/postgres`.
- Prefer the existing `Store` path with a real `pgxpool.Pool` when a local test database URL is available.
- If local Postgres is unavailable, skip the integration test with a clear `t.Skip` message, but keep deterministic unit tests for any helper extracted to make the behavior testable without database access.
- Seed at least two runs and two results, then assert:
  - `ListRuns(ctx, "")` returns newest `CreatedAt` first and can filter by status.
  - `GetRunDetail(ctx, runIDWithoutResult)` returns `HasResult=false` and no error.
  - `CompareResults(ctx, []RunID{second, first})` preserves that input order.
  - `CompareResults` returns an error wrapping `storage.ErrResultNotFound` for a missing result.

수정 파일 및 체크리스트:

- [ ] Add or update `services/worker/internal/storage/postgres/store_test.go`.
- [ ] Test data uses unique run IDs and cleans up its rows, or uses an isolated schema/database.
- [ ] Assertions check `errors.Is(err, storage.ErrResultNotFound)` for missing compare results.
- [ ] Do not add new Go dependencies unless the existing pgx/pool path cannot support the test.

테스트 작성: required. Test names should include `TestStoreBacktestAnalysisReads` or equivalent and must cover the four assertions above.

중간 검증:

```bash
go test ./services/worker/internal/storage/postgres/... -run 'TestStoreBacktestAnalysis|TestBacktestResultMapping|TestBacktestRunListRowMapping'
```

Expected: exits 0. If the store integration test skips because no local PostgreSQL URL is configured, the skip output must name the env var or local setup condition.

### [REVIEW_BAS-WORKER-3] Worker storage verification

문제: The follow-up changes affect migration ordering, generated SQLC output, and worker storage behavior, so focused and worker-wide checks must be rerun.

해결 방법: run the focused storage package tests, worker tests, storage generation drift check, and whitespace check.

수정 파일 및 체크리스트:

- [ ] Run `go test ./services/worker/internal/storage/postgres/...`.
- [ ] Run `go test ./services/worker/...`.
- [ ] Run `bin/worker-storage-check`.
- [ ] Run `git diff --check`.

테스트 작성: no additional tests beyond REVIEW_BAS-WORKER-1..2.

중간 검증:

```bash
go test ./services/worker/internal/storage/postgres/...
go test ./services/worker/...
bin/worker-storage-check
git diff --check
```

Expected: all commands exit 0. Go test cache output is acceptable after changed packages have run at least once in this implementation.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/worker/internal/storage/postgres/migrations/000001_worker_backbone.up.sql` | REVIEW_BAS-WORKER-1 |
| `services/worker/internal/storage/postgres/migrations/000002_backtest_analysis_results.up.sql` | REVIEW_BAS-WORKER-1 |
| `services/worker/internal/storage/postgres/migrations/000002_backtest_analysis_results.down.sql` | REVIEW_BAS-WORKER-1 |
| `services/worker/internal/storage/postgres/migrate_test.go` | REVIEW_BAS-WORKER-1 |
| `services/worker/internal/storage/postgres/sqlc/models.go` | REVIEW_BAS-WORKER-1 |
| `services/worker/internal/storage/postgres/sqlc/queries.sql.go` | REVIEW_BAS-WORKER-1 |
| `services/worker/internal/storage/postgres/store_test.go` | REVIEW_BAS-WORKER-2 |

## 최종 검증

```bash
go test ./services/worker/internal/storage/postgres/...
go test ./services/worker/...
bin/worker-storage-check
git diff --check
```

Expected: all commands exit 0. If a database-backed store test is skipped, the output must clearly state the missing local setup condition and the review stub must preserve that stdout.

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