feat: add backtest selector flow task files
This commit is contained in:
parent
33e6ec0254
commit
60440f83de
3 changed files with 484 additions and 0 deletions
|
|
@ -0,0 +1,168 @@
|
|||
<!-- task=m-backtest-data-collection-infrastructure/03+01,02_backtest_selector_flow plan=0 tag=API -->
|
||||
|
||||
# Code Review Reference - API
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves.
|
||||
> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record the needed decision in `사용자 리뷰 요청` and stop for code-review.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-14
|
||||
task=m-backtest-data-collection-infrastructure/03+01,02_backtest_selector_flow, plan=0, tag=API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/backtest-loop/milestones/backtest-data-collection-infrastructure.md`
|
||||
- Task ids:
|
||||
- `input-selector`: 저장된 정제 데이터에서 종목군, 기간, frequency를 선택해 backtest run input으로 넘기는 경계를 만든다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-cloud-G06.md` -> `code_review_cloud_G06_N.log`, `PLAN-cloud-G06.md` -> `plan_cloud_G06_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-backtest-data-collection-infrastructure/03+01,02_backtest_selector_flow/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] Async Run Payload Selector Preservation | [x] |
|
||||
| [API-2] Operator Start Backtest Selector | [x] |
|
||||
| [API-3] Headless Fixtures And Handoff Evidence | [x] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [x] `RunBacktestPayload`가 selector를 encode/decode하고 handler가 실행 spec에 복원하도록 구현한다.
|
||||
- [x] operator `start_backtest`가 universe/symbol/instrument_ids selector를 검증하고 `BacktestRunSpec.selector`로 전송하도록 구현한다.
|
||||
- [x] headless backtest fixtures와 handoff 문서가 import universe -> start selector -> poll/result 흐름을 증명하도록 갱신한다.
|
||||
- [x] 지정 `go test -count=1` 검증을 실행한다.
|
||||
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G06_N.log`로 아카이브한다.
|
||||
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G06_M.log`로 아카이브한다.
|
||||
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
|
||||
- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [x] PASS이면 active task 디렉터리 `agent-task/m-backtest-data-collection-infrastructure/03+01,02_backtest_selector_flow/`를 `agent-task/archive/YYYY/MM/m-backtest-data-collection-infrastructure/03+01,02_backtest_selector_flow/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [x] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-backtest-data-collection-infrastructure/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G06.md`와 `CODE_REVIEW-cloud-G06.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
- `invalid_request_matrix.yaml` 내의 `start_backtest_missing_strategy` step이 dry-run validation 단계에서 차단되지 않도록 `symbols` selector를 추가하였습니다. 이는 애초에 계획된 validation 규칙(start_backtest는 universe, symbols, 또는 instrument_ids 중 하나를 반드시 요구)을 우회하지 않으면서도 strategy id가 없다는 typed error 테스트 흐름을 그대로 유지하기 위함입니다.
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
- `RunBacktestPayload`에 중첩 DTO인 `RunBacktestSelectorPayload` 구조체를 설계하여 `backtest.InputSelector` 도메인 모델 간의 변환을 매핑하고, selector 내부에 공백이나 빈 문자열 엔트리가 들어오는 경우 명시적으로 에러를 반환하는 validation 로직을 구현하였습니다.
|
||||
- `apps/cli/internal/operator/scenario.go`의 `applyUniverseDefaults` 함수를 확장하여 `ActionStartBacktest` 에서도 universe config의 market 및 symbols 정보를 materialize하도록 하여, universe reference가 start_backtest step에서 안전하게 selector 데이터로 치환되도록 구조를 보강했습니다.
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- selector가 starter payload와 job handler spec 재구성 과정에서 손실되지 않는지 확인한다.
|
||||
- `start_backtest` selector requirement가 paper trading이나 non-backtest actions에 번지지 않았는지 확인한다.
|
||||
- headless fixtures가 import와 start 모두 같은 universe를 참조하고 expected JSONL 변경이 불필요한지 확인한다.
|
||||
- PASS 시 Roadmap Targets의 `input-selector`만 완료 이벤트 대상인지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### API-1 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./services/worker/internal/jobs ./services/worker/internal/backtest
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/jobs 0.002s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/backtest 0.002s
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/cli/internal/operator
|
||||
ok git.toki-labs.com/toki/alt/apps/cli/internal/operator 0.184s
|
||||
```
|
||||
|
||||
### API-3 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/cli/internal/operator ./apps/cli/internal/cli
|
||||
ok git.toki-labs.com/toki/alt/apps/cli/internal/operator 0.191s
|
||||
ok git.toki-labs.com/toki/alt/apps/cli/internal/cli 0.006s
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ go test -count=1 ./packages/domain/... ./services/worker/internal/backtest ./services/worker/internal/jobs ./services/worker/internal/socket ./services/api/internal/socket ./services/api/internal/workerclient ./apps/cli/internal/operator ./apps/cli/internal/cli
|
||||
ok git.toki-labs.com/toki/alt/packages/domain/backtest 0.005s
|
||||
ok git.toki-labs.com/toki/alt/packages/domain/market 0.002s
|
||||
ok git.toki-labs.com/toki/alt/packages/domain/trading 0.003s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/backtest 0.005s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/jobs 0.002s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/socket 0.065s
|
||||
ok git.toki-labs.com/toki/alt/services/api/internal/socket 0.011s
|
||||
ok git.toki-labs.com/toki/alt/services/api/internal/workerclient 0.411s
|
||||
ok git.toki-labs.com/toki/alt/apps/cli/internal/operator 0.192s
|
||||
ok git.toki-labs.com/toki/alt/apps/cli/internal/cli 0.007s
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ownership
|
||||
|
||||
| 섹션 | 소유자 |
|
||||
|------|--------|
|
||||
| 구현 항목별 완료 여부 | Implementing agent |
|
||||
| 구현 체크리스트 | Implementing agent |
|
||||
| 계획 대비 변경 사항 | Implementing agent |
|
||||
| 주요 설계 결정 | Implementing agent |
|
||||
| 사용자 리뷰 요청 | Implementing agent |
|
||||
| 검증 결과 | Implementing agent |
|
||||
| 코드리뷰 전용 체크리스트 | Review agent |
|
||||
| 코드리뷰 결과 | Review agent |
|
||||
|
||||
## 코드리뷰 결과
|
||||
|
||||
- 종합 판정: PASS
|
||||
- 차원별 평가:
|
||||
- correctness: Pass
|
||||
- completeness: Pass
|
||||
- test coverage: Pass
|
||||
- API contract: Pass
|
||||
- code quality: Pass
|
||||
- plan deviation: Pass
|
||||
- verification trust: Pass
|
||||
- 발견된 문제:
|
||||
- Nit (수정 완료): `services/worker/internal/backtest/flow_test.go:229`의 stale comment가 KindRunBacktest payload selector 보존 구현 이후에도 이전 전제를 설명하고 있어, storage-side selector test 의도로 주석을 갱신했다.
|
||||
- 다음 단계: PASS 처리로 active plan/review를 log로 아카이브하고 `complete.log` 작성 후 task directory를 archive로 이동한다.
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
# Complete - m-backtest-data-collection-infrastructure/03+01,02_backtest_selector_flow
|
||||
|
||||
## 완료 일시
|
||||
|
||||
2026-06-15
|
||||
|
||||
## 요약
|
||||
|
||||
Backtest selector flow 구현을 1회 리뷰 루프로 검토했고 최종 판정은 PASS다.
|
||||
|
||||
## 루프 이력
|
||||
|
||||
| Plan | Review | Verdict | 메모 |
|
||||
|------|--------|---------|------|
|
||||
| `plan_cloud_G06_0.log` | `code_review_cloud_G06_0.log` | PASS | selector가 operator start request, async job payload, worker execution spec, headless fixtures까지 보존됨을 확인했다. |
|
||||
|
||||
## 구현/정리 내용
|
||||
|
||||
- `RunBacktestPayload`가 selector를 encode/decode하고 `RegisterRunBacktestHandler`가 실행 `RunSpec.Selector`로 복원하도록 구현했다.
|
||||
- operator `start_backtest`가 universe, symbols, instrument_ids selector를 검증하고 `BacktestRunSpec.selector`로 전달하도록 구현했다.
|
||||
- headless backtest fixtures와 validation handoff 문서가 import universe와 start selector 흐름을 반영하도록 갱신했다.
|
||||
- 리뷰 중 `services/worker/internal/backtest/flow_test.go`의 stale selector payload comment를 현재 책임 경계에 맞게 정리했다.
|
||||
|
||||
## 최종 검증
|
||||
|
||||
- `go test -count=1 ./packages/domain/... ./services/worker/internal/backtest ./services/worker/internal/jobs ./services/worker/internal/socket ./services/api/internal/socket ./services/api/internal/workerclient ./apps/cli/internal/operator ./apps/cli/internal/cli` - PASS; domain, worker backtest/jobs/socket, API socket/workerclient, CLI operator/cli packages가 모두 exit 0.
|
||||
|
||||
## Roadmap Completion
|
||||
|
||||
- Milestone: `agent-roadmap/phase/backtest-loop/milestones/backtest-data-collection-infrastructure.md`
|
||||
- Completed task ids:
|
||||
- `input-selector`: PASS; evidence=`agent-task/archive/2026/06/m-backtest-data-collection-infrastructure/03+01,02_backtest_selector_flow/plan_cloud_G06_0.log`, `agent-task/archive/2026/06/m-backtest-data-collection-infrastructure/03+01,02_backtest_selector_flow/code_review_cloud_G06_0.log`; verification=`go test -count=1 ./packages/domain/... ./services/worker/internal/backtest ./services/worker/internal/jobs ./services/worker/internal/socket ./services/api/internal/socket ./services/api/internal/workerclient ./apps/cli/internal/operator ./apps/cli/internal/cli`
|
||||
- Not completed task ids: 없음
|
||||
|
||||
## 잔여 Nit
|
||||
|
||||
- 없음
|
||||
|
||||
## 후속 작업
|
||||
|
||||
- 없음
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
<!-- task=m-backtest-data-collection-infrastructure/03+01,02_backtest_selector_flow plan=0 tag=API -->
|
||||
# PLAN-cloud-G06: Backtest Selector Flow
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
이 계획은 `01_input_selector_contract`와 `02+01_storage_bar_selector`가 PASS되어 각각 `complete.log`를 만든 뒤에만 구현한다. 구현 후 반드시 검증을 실행하고 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 변경 내용과 stdout/stderr를 채운 뒤 active 파일을 유지한 채 리뷰 준비를 보고한다. 최종 PASS, log rename, `complete.log`, archive 이동은 code-review skill 전용이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret, 또는 범위 충돌 때문에 멈춰야 하면 사용자에게 직접 묻지 말고 review stub의 `사용자 리뷰 요청`에 증거를 채운 뒤 중단한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청 사유가 아니다.
|
||||
|
||||
## 배경
|
||||
선행 subtasks가 selector 계약과 storage filtering을 만들더라도 async job payload와 operator scenario가 selector를 잃으면 실제 backtest run input까지 이어지지 않는다. 이 subtask는 start command, async run job, operator named universe, headless handoff fixture를 연결한다. PASS되면 현재 마일스톤의 `backtest-ready/input-selector` 기능 Task를 완료할 수 있다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
구현 중 사용자 전용 결정이나 범위 충돌이 생기면 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식 그대로 기록한다. 구현 에이전트는 채팅으로 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. code-review가 사용자 리뷰 요청의 타당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/backtest-loop/milestones/backtest-data-collection-infrastructure.md`
|
||||
- Task ids:
|
||||
- `input-selector`: 저장된 정제 데이터에서 종목군, 기간, frequency를 선택해 backtest run input으로 넘기는 경계를 만든다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
### 읽은 파일
|
||||
- `agent-ops/rules/project/rules.md`
|
||||
- `agent-ops/rules/private/rules.md`
|
||||
- `agent-ops/rules/common/rules-roadmap.md`
|
||||
- `agent-ops/skills/common/router.md`
|
||||
- `agent-ops/skills/common/plan/SKILL.md`
|
||||
- `agent-ops/skills/common/update-roadmap/SKILL.md`
|
||||
- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/operations-smoke.md`
|
||||
- `agent-test/local/worker-smoke.md`
|
||||
- `agent-test/local/contracts-smoke.md`
|
||||
- `agent-test/local/domain-model-smoke.md`
|
||||
- `agent-test/local/api-smoke.md`
|
||||
- `agent-ops/rules/project/domain/operations/rules.md`
|
||||
- `agent-ops/rules/project/domain/worker/rules.md`
|
||||
- `agent-ops/rules/project/domain/contracts/rules.md`
|
||||
- `agent-ops/rules/project/domain/domain-model/rules.md`
|
||||
- `agent-ops/rules/project/domain/api/rules.md`
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/backtest-loop/PHASE.md`
|
||||
- `agent-roadmap/phase/backtest-loop/milestones/backtest-data-collection-infrastructure.md`
|
||||
- `packages/domain/backtest/types.go`
|
||||
- `services/worker/internal/jobs/backtest_starter.go`
|
||||
- `services/worker/internal/jobs/backtest_starter_test.go`
|
||||
- `services/worker/internal/jobs/backtest_jobs.go`
|
||||
- `services/worker/internal/jobs/backtest_jobs_test.go`
|
||||
- `services/worker/internal/backtest/flow_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_backtest_test.go`
|
||||
- `apps/cli/internal/operator/client.go`
|
||||
- `apps/cli/internal/operator/client_test.go`
|
||||
- `apps/cli/internal/operator/handoff_test.go`
|
||||
- `apps/cli/testdata/operator/backtest_run_request.yaml`
|
||||
- `apps/cli/testdata/operator/backtest_result_summary.yaml`
|
||||
- `apps/cli/testdata/operator/backtest_run_polling.yaml`
|
||||
- `apps/cli/testdata/operator/invalid_request_matrix.yaml`
|
||||
- `apps/cli/testdata/operator/headless_validation.md`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
- `test_env=local`로 판단했다.
|
||||
- `agent-test/local/rules.md`가 존재해 읽었다.
|
||||
- 매칭 profile로 `operations-smoke.md`, `worker-smoke.md`, `api-smoke.md`, `contracts-smoke.md`, `domain-model-smoke.md`를 읽었다.
|
||||
- operations/headless 변경이므로 CLI operator tests와 fixture handoff tests를 포함한다.
|
||||
- worker async job payload 변경이므로 worker jobs/backtest tests를 포함한다.
|
||||
- `go test -count=1` fresh 실행만 허용하고 test cache 출력은 허용하지 않는다.
|
||||
- 현재 기준으로 최종 검증 후보 `go test -count=1 ./packages/domain/... ./services/worker/internal/backtest ./services/worker/internal/jobs ./services/worker/internal/socket ./services/api/internal/socket ./services/api/internal/workerclient ./apps/cli/internal/operator ./apps/cli/internal/cli`가 exit 0임을 확인했다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
- `RunBacktestPayload`는 strategy/market/timeframe/date만 담고 selector를 보존하지 않는다.
|
||||
- `RegisterRunBacktestHandler`는 payload에서 spec을 재구성하며 기존 run spec을 덮어쓰므로 selector가 사라질 수 있다.
|
||||
- operator `start_backtest`는 named universe를 지원하지 않고 selector를 `BacktestRunSpec`에 넣지 않는다.
|
||||
- headless fixtures는 import 후 start가 같은 universe를 backtest input selector로 넘기는지 문서화하지 않는다.
|
||||
|
||||
### 심볼 참조
|
||||
- renamed/removed symbol: none.
|
||||
- `RunBacktestPayload` references: `services/worker/internal/jobs/backtest_starter.go:80`, `services/worker/internal/jobs/backtest_jobs.go:20`, `services/worker/internal/jobs/backtest_jobs.go:41`, `services/worker/internal/jobs/backtest_jobs.go:90`, `services/worker/internal/jobs/backtest_starter_test.go:66`, `services/worker/internal/jobs/backtest_jobs_test.go:350`.
|
||||
- `ActionStartBacktest` references: `apps/cli/internal/operator/scenario.go:35`, `apps/cli/internal/operator/scenario.go:638`, `apps/cli/internal/operator/runner.go:276`, `apps/cli/internal/operator/runner_backtest_test.go:27`, `apps/cli/internal/operator/runner_backtest_test.go:92`.
|
||||
|
||||
### 분할 판단
|
||||
- split decision policy를 평가했고 multi-plan을 선택했다.
|
||||
- shared task group: `agent-task/m-backtest-data-collection-infrastructure/`
|
||||
- 이 subtask: `03+01,02_backtest_selector_flow`.
|
||||
- predecessor `01_input_selector_contract`: PASS 완료. 증거: `agent-task/archive/2026/06/m-backtest-data-collection-infrastructure/01_input_selector_contract/complete.log`.
|
||||
- predecessor `02+01_storage_bar_selector`: 아직 active plan/review 상태이며 `complete.log` 미생성. 구현 시작 전 active 또는 archived complete.log가 필요하다.
|
||||
|
||||
### 범위 결정 근거
|
||||
- 새로운 universe persistence나 DB schema는 만들지 않는다. CLI scenario root `universes`를 start request selector로 materialize하는 범위까지만 다룬다.
|
||||
- paper trading start flow에는 selector requirement를 강제하지 않는다. `BacktestRunSpec`은 paper trading도 재사용하므로 이 subtask의 CLI validation은 `ActionStartBacktest`에 한정한다.
|
||||
- 실제 provider import 동작은 기존 `import_daily_bars`를 사용하고, provider client나 KIS adapter는 변경하지 않는다.
|
||||
|
||||
### 빌드 등급
|
||||
- build lane: `cloud-G06`. async job payload, worker execution handoff, CLI scenario schema, headless fixtures를 함께 건드리는 cross-surface 변경이므로 G06로 잡는다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
- `01_input_selector_contract`와 `02+01_storage_bar_selector` 완료가 선행이다.
|
||||
- `01_input_selector_contract`는 PASS 완료되었고, `02+01_storage_bar_selector`는 아직 active plan/review 상태다.
|
||||
- 구현 시작 전에 `02+01_storage_bar_selector`의 active 또는 archived `complete.log`가 없으면 이 plan을 구현하지 말고 선행 subtask를 먼저 완료한다.
|
||||
- 구현 순서: worker job payload 보존 -> CLI scenario/runner selector 생성 -> fixtures/handoff 갱신 -> 전체 검증.
|
||||
|
||||
## 구현 체크리스트
|
||||
- [ ] `RunBacktestPayload`가 selector를 encode/decode하고 handler가 실행 spec에 복원하도록 구현한다.
|
||||
- [ ] operator `start_backtest`가 universe/symbol/instrument_ids selector를 검증하고 `BacktestRunSpec.selector`로 전송하도록 구현한다.
|
||||
- [ ] headless backtest fixtures와 handoff 문서가 import universe -> start selector -> poll/result 흐름을 증명하도록 갱신한다.
|
||||
- [ ] 지정 `go test -count=1` 검증을 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
### [API-1] Async Run Payload Selector Preservation
|
||||
#### 문제
|
||||
starter payload와 job handler spec 재구성이 selector를 포함하지 않는다.
|
||||
|
||||
```go
|
||||
// services/worker/internal/jobs/backtest_starter.go:80
|
||||
payload := RunBacktestPayload{
|
||||
RunID: string(run.ID),
|
||||
StrategyID: string(spec.StrategyID),
|
||||
Market: string(spec.Market),
|
||||
Timeframe: string(spec.Timeframe),
|
||||
From: spec.From.UTC().Format(time.RFC3339),
|
||||
To: spec.To.UTC().Format(time.RFC3339),
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// services/worker/internal/jobs/backtest_jobs.go:99
|
||||
spec := backtest.RunSpec{
|
||||
StrategyID: backtest.StrategyID(p.StrategyID),
|
||||
Market: market.Market(p.Market),
|
||||
Timeframe: market.Timeframe(p.Timeframe),
|
||||
From: fromTime,
|
||||
To: toTime,
|
||||
}
|
||||
```
|
||||
|
||||
#### 해결 방법
|
||||
payload에 selector DTO를 추가하고 blank entry validation을 넣는다. handler가 payload selector를 `backtest.RunSpec.Selector`에 복원한 뒤 executor에 전달한다.
|
||||
|
||||
```go
|
||||
type RunBacktestSelectorPayload struct {
|
||||
InstrumentIDs []string `json:"instrument_ids,omitempty"`
|
||||
Symbols []string `json:"symbols,omitempty"`
|
||||
}
|
||||
|
||||
type RunBacktestPayload struct {
|
||||
RunID string `json:"run_id"`
|
||||
StrategyID string `json:"strategy_id"`
|
||||
Market string `json:"market"`
|
||||
Timeframe string `json:"timeframe"`
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Selector RunBacktestSelectorPayload `json:"selector,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
- [ ] `services/worker/internal/jobs/backtest_jobs.go`에 selector payload type, decode validation, domain conversion helper를 추가한다.
|
||||
- [ ] `services/worker/internal/jobs/backtest_starter.go`가 `spec.Selector`를 payload에 encode하도록 한다.
|
||||
- [ ] `services/worker/internal/jobs/backtest_starter_test.go`가 decoded payload selector를 assertion한다.
|
||||
- [ ] `services/worker/internal/jobs/backtest_jobs_test.go`가 executor가 받은 run spec selector와 blank selector reject를 assertion한다.
|
||||
|
||||
#### 테스트 작성
|
||||
- `TestBacktestStarterRecordsPendingAndLaunches`에 selector payload assertion 추가.
|
||||
- `TestRegisterRunBacktestHandlerTransitionsSucceeded`에 `executor.run.Spec.Selector` assertion 추가.
|
||||
- `TestDecodeRunBacktestPayloadRejectsMissingFields` 또는 새 test에 blank selector entry reject 추가.
|
||||
|
||||
#### 중간 검증
|
||||
```bash
|
||||
go test -count=1 ./services/worker/internal/jobs ./services/worker/internal/backtest
|
||||
```
|
||||
예상 결과: jobs/backtest tests exit 0.
|
||||
|
||||
### [API-2] Operator Start Backtest Selector
|
||||
#### 문제
|
||||
operator scenario root에는 universe가 있지만 `start_backtest`는 universe를 적용하지 않고 raw market/timeframe/date만 보낸다.
|
||||
|
||||
```go
|
||||
// apps/cli/internal/operator/scenario.go:495
|
||||
func applyUniverseDefaults(step *Step, universeByName map[string]UniverseConfig) error {
|
||||
if step.Action != ActionImportDailyBars && step.Action != ActionCollectionFreshness {
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// apps/cli/internal/operator/runner.go:276
|
||||
case ActionStartBacktest:
|
||||
resp, err := client.StartBacktest(stepCtx, &altv1.StartBacktestRequest{
|
||||
Spec: &altv1.BacktestRunSpec{
|
||||
StrategyId: step.Request.StrategyID,
|
||||
Market: marketByName[step.Request.Market],
|
||||
```
|
||||
|
||||
#### 해결 방법
|
||||
`Request`에 repeated `instrument_ids`를 추가하고 `start_backtest`가 `universe`, `symbols`, 또는 `instrument_ids` 중 하나를 요구하게 한다. `universe`는 scenario root에서 market/symbols를 materialize한다. runner는 `BacktestInputSelector`를 만들어 `BacktestRunSpec.selector`에 넣는다.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
- [ ] `apps/cli/internal/operator/scenario.go`의 `Request`에 `InstrumentIDs []string yaml:"instrument_ids"`를 추가한다.
|
||||
- [ ] `applyUniverseDefaults`가 `ActionStartBacktest`에도 universe defaults를 적용하도록 확장한다.
|
||||
- [ ] `ActionStartBacktest` validation에 selector requirement와 blank `symbols`/`instrument_ids` reject를 추가한다.
|
||||
- [ ] `apps/cli/internal/operator/runner.go`에 start selector helper를 추가하고 `BacktestRunSpec.selector`를 설정한다.
|
||||
- [ ] `apps/cli/internal/operator/scenario_test.go`에 valid universe start와 missing selector reject를 추가한다.
|
||||
- [ ] `apps/cli/internal/operator/runner_backtest_test.go`가 fake API에 전달된 selector를 assertion한다.
|
||||
|
||||
#### 테스트 작성
|
||||
- `TestValidateStartBacktestUniverseReference`: universe가 market/symbols로 materialize되는지 검증.
|
||||
- `TestValidateStartBacktestRejectsMissingSelector`: selector 없는 start가 dry-run에서 실패하는지 검증.
|
||||
- `TestRunBacktestRequestOutputsRunID` 또는 별도 test: fake API가 `spec.selector.symbols=["005930"]`를 받는지 검증.
|
||||
|
||||
#### 중간 검증
|
||||
```bash
|
||||
go test -count=1 ./apps/cli/internal/operator
|
||||
```
|
||||
예상 결과: operator scenario/runner tests exit 0.
|
||||
|
||||
### [API-3] Headless Fixtures And Handoff Evidence
|
||||
#### 문제
|
||||
현재 fixtures는 import와 start가 같은 종목군을 공유한다는 것을 request로 증명하지 않는다.
|
||||
|
||||
```yaml
|
||||
# apps/cli/testdata/operator/backtest_run_polling.yaml:19
|
||||
- id: start
|
||||
action: start_backtest
|
||||
request:
|
||||
strategy_id: strategy-v1
|
||||
market: kr
|
||||
timeframe: daily
|
||||
```
|
||||
|
||||
#### 해결 방법
|
||||
backtest 관련 fixtures에 root `universes`를 두고 `import_daily_bars`와 `start_backtest`가 같은 universe를 참조하게 한다. `invalid_request_matrix`는 missing-strategy typed error를 유지해야 하므로 selector를 추가하되 strategy id는 nonexistent로 둔다. `headless_validation.md`의 checked field는 import universe와 start selector handoff를 명시한다.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
- [ ] `apps/cli/testdata/operator/backtest_run_request.yaml`에 universe를 추가하고 import/start가 이를 참조하게 한다.
|
||||
- [ ] `apps/cli/testdata/operator/backtest_result_summary.yaml`에 universe를 추가하고 import/start가 이를 참조하게 한다.
|
||||
- [ ] `apps/cli/testdata/operator/backtest_run_polling.yaml`의 start request가 `universe: kr-backtest-smoke`를 사용하게 한다.
|
||||
- [ ] `apps/cli/testdata/operator/invalid_request_matrix.yaml`의 `start_backtest_missing_strategy` request에 selector를 추가해 API typed error 경로를 유지한다.
|
||||
- [ ] `apps/cli/testdata/operator/headless_validation.md` checked field에 `StartBacktestRequest.spec.selector` 증거를 추가한다.
|
||||
- [ ] expected JSONL fixture는 output key가 바뀌지 않으면 수정하지 않는다.
|
||||
|
||||
#### 테스트 작성
|
||||
- 기존 handoff tests가 fixture 존재와 promised keys를 검증한다.
|
||||
- selector가 출력되지 않는 내부 request field라 expected JSONL key 추가는 하지 않는다.
|
||||
|
||||
#### 중간 검증
|
||||
```bash
|
||||
go test -count=1 ./apps/cli/internal/operator ./apps/cli/internal/cli
|
||||
```
|
||||
예상 결과: fixture/handoff tests 포함 exit 0.
|
||||
|
||||
## 수정 파일 요약
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `services/worker/internal/jobs/backtest_jobs.go` | API-1 |
|
||||
| `services/worker/internal/jobs/backtest_jobs_test.go` | API-1 |
|
||||
| `services/worker/internal/jobs/backtest_starter.go` | API-1 |
|
||||
| `services/worker/internal/jobs/backtest_starter_test.go` | API-1 |
|
||||
| `apps/cli/internal/operator/scenario.go` | API-2 |
|
||||
| `apps/cli/internal/operator/scenario_test.go` | API-2 |
|
||||
| `apps/cli/internal/operator/runner.go` | API-2 |
|
||||
| `apps/cli/internal/operator/runner_backtest_test.go` | API-2 |
|
||||
| `apps/cli/testdata/operator/backtest_run_request.yaml` | API-3 |
|
||||
| `apps/cli/testdata/operator/backtest_result_summary.yaml` | API-3 |
|
||||
| `apps/cli/testdata/operator/backtest_run_polling.yaml` | API-3 |
|
||||
| `apps/cli/testdata/operator/invalid_request_matrix.yaml` | API-3 |
|
||||
| `apps/cli/testdata/operator/headless_validation.md` | API-3 |
|
||||
|
||||
## 최종 검증
|
||||
```bash
|
||||
go test -count=1 ./packages/domain/... ./services/worker/internal/backtest ./services/worker/internal/jobs ./services/worker/internal/socket ./services/api/internal/socket ./services/api/internal/workerclient ./apps/cli/internal/operator ./apps/cli/internal/cli
|
||||
```
|
||||
예상 결과: 모든 package exit 0. Go test cache 출력은 허용하지 않는다.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
Loading…
Reference in a new issue