feat: operator runtime refactor - API lifecycle capabilities, client bootstrap, and socket handler updates
This commit is contained in:
parent
c4a457c5c4
commit
24983ad72a
12 changed files with 1450 additions and 15 deletions
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
## 활성 Milestone
|
||||
|
||||
- [계획] Operator Runtime Reliability Refactor
|
||||
- [진행중] Operator Runtime Reliability Refactor
|
||||
- Phase: `agent-roadmap/phase/operator-surface/PHASE.md`
|
||||
- 경로: `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ Flutter client를 ALT의 공식 operator surface로 삼아 market data 상태와
|
|||
- [완료] API-Centered Proto-Socket Rail
|
||||
- 경로: `agent-roadmap/archive/phase/operator-surface/milestones/api-centered-proto-socket-rail.md`
|
||||
- 요약: API 서버를 control plane으로 두고 client-api, api-worker 내부 통신을 proto-socket과 ALT protobuf contracts로 통일한다.
|
||||
- [계획] Operator Runtime Reliability Refactor
|
||||
- [진행중] Operator Runtime Reliability Refactor
|
||||
- 경로: `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- 요약: Flutter operator console/headless validation 전에 API-worker lifecycle, worker execution wiring, capability reporting, client bootstrap, 원격 테스트 룰을 정리한다.
|
||||
- [계획] Flutter Operator Console UX Plan
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ Flutter operator console 작업을 더 진행하기 전에 API-worker runtime ra
|
|||
|
||||
## 상태
|
||||
|
||||
[계획]
|
||||
[진행중]
|
||||
|
||||
## 구현 잠금
|
||||
|
||||
|
|
@ -24,7 +24,8 @@ Flutter operator console 작업을 더 진행하기 전에 API-worker runtime ra
|
|||
- worker의 backtest start/execution wiring 또는 명시적 unavailable 상태 정리
|
||||
- hello capability와 handler registration test를 실제 surface와 일치
|
||||
- `../nexo/agent-test/local/rules.md`의 원격 실행 기준을 참고해 ALT local test profile을 원격 테스트 host 기준으로 정리
|
||||
- Flutter app bootstrap에서 operator shell과 push/Firebase/Mattermost 초기화를 분리
|
||||
- Flutter app
|
||||
- bootstrap에서 operator shell과 push/Firebase/Mattermost 초기화를 분리
|
||||
- remote verification evidence, generated drift check, skipped integration 조건을 plan/review 루프에서 재현 가능하게 정리
|
||||
|
||||
## 기능
|
||||
|
|
@ -38,13 +39,13 @@ Operator console과 headless validation이 의존할 API-worker-client rail을 s
|
|||
- [x] [test-rules] `agent-test/local` 규칙을 `../nexo/agent-test/local/rules.md`의 원격 실행 방식에 맞춰 `ssh toki@toki-labs.com` 기준으로 정리하고, ALT smoke profile이 local shell 실행을 암시하지 않게 한다.
|
||||
- [ ] [capabilities] API/worker `HelloResponse.capabilities`와 handler coverage test가 market/backtest/worker availability를 실제 등록 surface와 일치하게 검증한다.
|
||||
- [ ] [client-bootstrap] Flutter operator shell이 push/Firebase/Mattermost 초기화 없이도 시작 가능한 경계로 분리되고, push-specific integration은 후속 push Milestone 범위로 남는다.
|
||||
- [ ] [verification-handoff] 원격 ALT checkout에서 실행할 검증 명령, `bin/contracts-check`, `bin/worker-storage-check`, 통합 테스트 skip 조건과 실제 stdout/stderr 기록 기준을 plan/review 산출물에 남길 수 있게 정리한다.
|
||||
- [x] [verification-handoff] 원격 ALT checkout에서 실행할 검증 명령, `bin/contracts-check`, `bin/worker-storage-check`, 통합 테스트 skip 조건과 실제 stdout/stderr 기록 기준을 plan/review 산출물에 남길 수 있게 정리한다.
|
||||
|
||||
## 완료 리뷰
|
||||
|
||||
- 상태: 없음
|
||||
- 요청일: 없음
|
||||
- 완료 근거: `test-rules`는 Milestone 생성 시점에 반영했다. runtime/client refactor 기능 Task는 아직 충족되지 않았다.
|
||||
- 완료 근거: `test-rules`는 Milestone 생성 시점에 반영했고, `verification-handoff`는 2026-05-31에 `agent-test/local` handoff 문구와 `m-operator-runtime-refactor` plan/review 산출물로 반영했다. runtime/client refactor 기능 Task는 아직 충족되지 않았다.
|
||||
- 리뷰 필요:
|
||||
- [ ] 사용자가 완료 결과를 확인했다
|
||||
- [ ] archive 이동을 승인했다
|
||||
|
|
@ -62,6 +63,7 @@ Operator console과 headless validation이 의존할 API-worker-client rail을 s
|
|||
- 관련 경로: `services/api/`, `services/worker/`, `apps/client/`, `agent-test/local/`, `bin/`
|
||||
- 표준선(선택): API는 thin control plane으로 유지하고 worker execution/storage는 worker에 둔다. 로컬 테스트 profile의 명령은 로컬 shell이 아니라 원격 테스트 host의 ALT checkout에서 실행한다. Client operator shell은 push integration 없이도 기본 실행 가능해야 한다.
|
||||
- 초기 반영: 2026-05-31에 `../nexo/agent-test/local/rules.md`를 참고해 `agent-test/local/rules.md`와 smoke profile의 host/blocker 문구를 원격 테스트 host 기준으로 갱신했다.
|
||||
- 진행 반영: 2026-05-31에 `agent-test/local` handoff 문구를 보강하고 `agent-task/m-operator-runtime-refactor/` 아래 worker/API/client split plan을 작성했다.
|
||||
- 선행 작업: API-Centered Proto-Socket Rail
|
||||
- 후속 작업: Flutter Operator Console UX Plan, Operator Headless Workflow Validation, Flutter Operator Console MVP
|
||||
- 확인 필요: 없음
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
<!-- task=m-operator-runtime-refactor/01_worker_execution plan=0 tag=WORKER -->
|
||||
|
||||
# Code Review Reference - WORKER
|
||||
|
||||
> **[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.
|
||||
> 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-05-31
|
||||
task=m-operator-runtime-refactor/01_worker_execution, plan=0, tag=WORKER
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- Task ids:
|
||||
- `worker-exec`: `services/worker` main에서 `BacktestStarter`, concrete `KindRunBacktest` handler, engine/store dependency를 연결하거나, 아직 실행 불가인 부분은 capability와 typed error로 명시한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-cloud-G07.md` -> `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` -> `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-operator-runtime-refactor/01_worker_execution/`로 이동한다. 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` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [WORKER-1] Worker Capability Truth Source | [x] |
|
||||
| [WORKER-2] Typed Unavailable And Handler Coverage | [x] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [x] [WORKER-1] worker hello capabilities를 `Deps` 기반으로 계산해 start surface가 unavailable일 때 execution capability를 광고하지 않게 한다.
|
||||
- [x] [WORKER-2] worker tests를 보강해 nil starter unavailable response와 hello capability/handler coverage가 실제 registered surface와 일치함을 검증한다.
|
||||
- [x] 원격 ALT checkout root에서 `go test -count=1 ./services/worker/...`를 실행하고 실제 stdout/stderr를 review stub에 기록한다.
|
||||
- [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_G07_N.log`로 아카이브한다.
|
||||
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하여 plan/review/archive 산출물이 추적 가능한지 확인한다.
|
||||
- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [x] PASS이면 active task 디렉터리 `agent-task/m-operator-runtime-refactor/01_worker_execution/`를 `agent-task/archive/YYYY/MM/m-operator-runtime-refactor/01_worker_execution/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [x] PASS이고 task group이 `m-operator-runtime-refactor`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-operator-runtime-refactor/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.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로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
- 없음. 계획된 범위와 명세에 맞춰 정확하게 구현을 완료하였습니다.
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
- `socket.Deps` 객체를 `helloHandler`에 클로저 형태로 주입하여 `protoSocket` 패키지의 형식이 지정된 리퀘스트 리스너 시그니처를 깨뜨리지 않으면서, `helloHandler`와 `handleHello`가 dynamic하게 `Deps` 필드들의 non-nil 여부에 따라 worker의 capabilities 목록을 필터링하도록 설계하였습니다.
|
||||
- `capabilitiesForDeps` 헬퍼 함수를 추가하여, standard capabilities인 `hello`를 기본으로 포함하고, `Instruments`와 `Bars`가 모두 존재할 때 `market-read`, `Analysis`와 `Results`가 모두 존재할 때 `backtest-read`, `Starter`가 존재할 때 `backtest-start` 및 `worker-execution`을 추가하도록 매핑하였습니다.
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- Worker hello capability가 `Deps` 실제 availability에서 계산되는지 확인한다.
|
||||
- `StartBacktestRequest` handler는 계속 등록되지만 nil starter에서는 typed unavailable을 반환하는지 확인한다.
|
||||
- production engine/store wiring을 억지로 연결하지 않고 unavailable path를 명시했다는 범위 결정을 유지했는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### WORKER-1 중간 검증
|
||||
```text
|
||||
$ go test -count=1 ./services/worker/internal/socket
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/socket 0.060s
|
||||
```
|
||||
|
||||
### WORKER-2 중간 검증
|
||||
```text
|
||||
$ go test -count=1 ./services/worker/internal/socket ./services/worker/internal/jobs
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/socket 0.057s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/jobs 0.002s
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```text
|
||||
$ go test -count=1 ./services/worker/...
|
||||
? git.toki-labs.com/toki/alt/services/worker/cmd/alt-worker [no test files]
|
||||
? git.toki-labs.com/toki/alt/services/worker/cmd/alt-worker-data-check [no test files]
|
||||
ok git.toki-labs.com/toki/alt/services/worker/cmd/alt-worker-migrate 0.003s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/backtest 0.002s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/config 0.004s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/contracts 0.006s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/jobs 0.004s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/marketdata/datacheck0.004s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/marketdata/importer0.004s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/providers/kis 0.008s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/rediskeys 0.004s
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/socket 0.058s
|
||||
? git.toki-labs.com/toki/alt/services/worker/internal/storage [no test files]
|
||||
ok git.toki-labs.com/toki/alt/services/worker/internal/storage/postgres 0.096s
|
||||
? git.toki-labs.com/toki/alt/services/worker/internal/storage/postgres/sqlc [no test files]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
||||
## 코드리뷰 결과
|
||||
|
||||
- 종합 판정: PASS
|
||||
- 차원별 평가:
|
||||
- correctness: Pass
|
||||
- completeness: Pass
|
||||
- test coverage: Pass
|
||||
- API contract: Pass
|
||||
- code quality: Pass
|
||||
- plan deviation: Pass
|
||||
- verification trust: Pass
|
||||
- 발견된 문제: 없음
|
||||
- 다음 단계: PASS이므로 `complete.log`를 작성하고 active task 디렉터리를 archive로 이동한다. `m-operator-runtime-refactor` 완료 이벤트 메타데이터를 보고하되 roadmap 수정은 런타임 책임으로 남긴다.
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
# Complete - m-operator-runtime-refactor/01_worker_execution
|
||||
|
||||
## 완료 일시
|
||||
|
||||
2026-05-31
|
||||
|
||||
## 요약
|
||||
|
||||
Worker hello capability truth source를 `socket.Deps` 기반으로 정리하고 capability/test coverage를 보강했다. loop=1, final verdict=PASS.
|
||||
|
||||
## 루프 이력
|
||||
|
||||
| Plan | Review | Verdict | 메모 |
|
||||
|------|--------|---------|------|
|
||||
| `plan_cloud_G07_0.log` | `code_review_cloud_G07_0.log` | PASS | worker capability가 unavailable start를 광고하지 않고 원격 worker test가 통과함 |
|
||||
|
||||
## 구현/정리 내용
|
||||
|
||||
- `services/worker/internal/socket/handlers.go`에서 hello handler가 `Deps`를 받아 `hello`, `market-read`, `backtest-read`, `backtest-start` capability를 실제 dependency availability 기준으로 계산한다.
|
||||
- `services/worker/internal/socket/server_test.go`에 capability helper와 worker socket hello regression coverage를 추가했다.
|
||||
|
||||
## 최종 검증
|
||||
|
||||
- `go test -count=1 ./services/worker/...` - PASS; 원격 ALT checkout에서 재실행했고 worker 전체 패키지가 exit code 0으로 통과했다.
|
||||
- `git diff --check -- services/worker/internal/socket/handlers.go services/worker/internal/socket/server_test.go` - PASS; whitespace error 없음.
|
||||
|
||||
## Roadmap Completion
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- Completed task ids:
|
||||
- `worker-exec`: PASS; evidence=`agent-task/archive/2026/05/m-operator-runtime-refactor/01_worker_execution/plan_cloud_G07_0.log`, `agent-task/archive/2026/05/m-operator-runtime-refactor/01_worker_execution/code_review_cloud_G07_0.log`; verification=`go test -count=1 ./services/worker/...`
|
||||
- Not completed task ids: 없음
|
||||
|
||||
## 잔여 Nit
|
||||
|
||||
- 없음
|
||||
|
||||
## 후속 작업
|
||||
|
||||
- 없음
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
<!-- task=m-operator-runtime-refactor/01_worker_execution plan=0 tag=WORKER -->
|
||||
|
||||
# PLAN - WORKER
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것이 필수다. 구현 후 검증을 실행하고, 실제 변경 내용과 stdout/stderr를 review stub에 남긴 뒤 active 파일을 유지하고 리뷰 준비 상태로 보고한다. 사용자 결정, 사용자 소유 외부 환경, 범위 충돌 없이는 진행할 수 없을 때만 review stub의 `사용자 리뷰 요청`을 채우고 멈춘다. 단순 증거 공백은 후속 에이전트가 재실행으로 메울 수 있으므로 사용자 리뷰 요청이 아니다.
|
||||
|
||||
## 배경
|
||||
|
||||
현재 worker process는 `KindRunBacktest` placeholder를 등록하고 store-backed read surface만 연결한다. `StartBacktest`는 `Deps.Starter == nil`이면 typed unavailable을 반환하지만, worker hello capability는 항상 `worker-execution`을 광고한다. production `StrategyPort`와 실행 대상 universe adapter가 아직 없으므로, 이번 단계는 실행 가능하다고 속이지 않고 start surface unavailable을 capability와 테스트로 고정한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 차단 사유는 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식을 따르며, 실제 `USER_REVIEW.md` 작성 여부는 code-review가 판단한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- Task ids:
|
||||
- `worker-exec`: `services/worker` main에서 `BacktestStarter`, concrete `KindRunBacktest` handler, engine/store dependency를 연결하거나, 아직 실행 불가인 부분은 capability와 typed error로 명시한다.
|
||||
- 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/rules/project/domain/worker/rules.md`
|
||||
- `agent-ops/rules/project/domain/operations/rules.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/worker-smoke.md`
|
||||
- `agent-test/local/operations-smoke.md`
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/operator-surface/PHASE.md`
|
||||
- `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- `services/worker/cmd/alt-worker/main.go`
|
||||
- `services/worker/internal/socket/server.go`
|
||||
- `services/worker/internal/socket/handlers.go`
|
||||
- `services/worker/internal/socket/backtest.go`
|
||||
- `services/worker/internal/socket/market.go`
|
||||
- `services/worker/internal/socket/server_test.go`
|
||||
- `services/worker/internal/socket/backtest_test.go`
|
||||
- `services/worker/internal/socket/market_test.go`
|
||||
- `services/worker/internal/socket/backtest_mapping.go`
|
||||
- `services/worker/internal/socket/backtest_mapping_test.go`
|
||||
- `services/worker/internal/jobs/builtin.go`
|
||||
- `services/worker/internal/jobs/runner.go`
|
||||
- `services/worker/internal/jobs/backtest_starter.go`
|
||||
- `services/worker/internal/jobs/backtest_jobs.go`
|
||||
- `services/worker/internal/jobs/runner_test.go`
|
||||
- `services/worker/internal/jobs/backtest_starter_test.go`
|
||||
- `services/worker/internal/jobs/backtest_jobs_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/store.go`
|
||||
- `services/worker/internal/storage/postgres/store_test.go`
|
||||
- `services/worker/go.mod`
|
||||
- `packages/domain/backtest/types.go`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- `test_env=local`.
|
||||
- `agent-test/local/rules.md`는 존재하고 읽었다. local 검증은 현재 shell에서 직접 실행 금지이며, `ssh toki@toki-labs.com` 접속 후 원격 ALT checkout root에서 profile 명령을 실행한다.
|
||||
- 매칭 profile은 `agent-test/local/worker-smoke.md`다. worker-only 변경의 기본 명령은 원격 checkout root 기준 `go test ./services/worker/...`이며, fresh run이 필요하므로 plan에서는 `go test -count=1 ./services/worker/...`를 사용한다.
|
||||
- storage migration/query/generated behavior를 바꾸지 않으면 `bin/worker-storage-check`는 필수 검증이 아니다. 실행이 필요해졌는데 infra나 원격 host가 막히면 pass가 아니라 blocker/skip 근거와 미실행 명령을 기록한다.
|
||||
- `agent-test/local/operations-smoke.md`는 이번에 작은 verification-handoff 문서 변경을 확인하기 위해 읽었지만, 이 worker plan의 code path 검증 명령은 worker profile을 따른다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `handleStartBacktest`의 nil starter typed unavailable은 `services/worker/internal/socket/backtest_test.go:112`에서 이미 직접 검증한다.
|
||||
- worker hello capability가 실제 `Deps`와 일치하는지는 현재 coverage가 없다. `services/worker/internal/socket/server_test.go:59`는 server name/protocol만 확인하고 capability 내용은 검증하지 않는다.
|
||||
- `KindRunBacktest` concrete handler는 `services/worker/internal/jobs/backtest_jobs_test.go:308`에서 placeholder override를 검증하지만, process main에서는 production engine dependency가 없어서 연결되어 있지 않다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
- `handleHello` reference: `services/worker/internal/socket/handlers.go:48`.
|
||||
- `helloHandler` reference: `services/worker/internal/socket/handlers.go:19`.
|
||||
- `RegisterBuiltins` reference: `services/worker/cmd/alt-worker/main.go:25`, `services/worker/internal/jobs/runner_test.go:104`, `services/worker/internal/jobs/backtest_jobs_test.go:310`.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 먼저 평가했다.
|
||||
- shared task group: `m-operator-runtime-refactor`.
|
||||
- sibling plans:
|
||||
- `01_worker_execution`: worker execution availability and worker capability truth source. no predecessors.
|
||||
- `02+01_api_lifecycle_capabilities`: API lifecycle/capability work. depends on `01_worker_execution` so API capability names can align with worker truth.
|
||||
- `03_client_bootstrap`: Flutter bootstrap separation. independent of worker/API implementation.
|
||||
- 이 plan은 worker domain 안에서 끝나며 API/client call-site rollout을 포함하지 않는다. storage SQL or schema 변경도 하지 않는다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- production `BacktestStarter`/`Engine` wiring is intentionally excluded because `backtest.Engine` needs a `StrategyPort` and `BarSource` (`services/worker/internal/backtest/engine.go:20`, `services/worker/internal/backtest/engine.go:31`), while current `RunSpec` has no instrument/universe field and `storage.Store.GetBars` reads by instrument id (`services/worker/internal/storage/ports.go:22`).
|
||||
- The acceptable milestone path is explicit unavailable: keep `Deps.Starter == nil`, keep typed `unavailable` response, and ensure hello capabilities no longer advertise execution.
|
||||
- API lifecycle and client bootstrap are covered by sibling plans.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- build lane: `cloud-G07`; review lane: `cloud-G07`.
|
||||
- Worker runtime capability reporting affects proto-socket session semantics and milestone completion, while integration evidence may depend on remote-only verification.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] [WORKER-1] worker hello capabilities를 `Deps` 기반으로 계산해 start surface가 unavailable일 때 execution capability를 광고하지 않게 한다.
|
||||
- [ ] [WORKER-2] worker tests를 보강해 nil starter unavailable response와 hello capability/handler coverage가 실제 registered surface와 일치함을 검증한다.
|
||||
- [ ] 원격 ALT checkout root에서 `go test -count=1 ./services/worker/...`를 실행하고 실제 stdout/stderr를 review stub에 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
### [WORKER-1] Worker Capability Truth Source
|
||||
|
||||
#### 문제
|
||||
|
||||
`services/worker/internal/socket/handlers.go:58` always returns `worker-execution`, even when `services/worker/cmd/alt-worker/main.go:37` builds `socket.Deps` without `Starter`. This conflicts with `services/worker/internal/socket/backtest.go:117`, where start requests return typed unavailable when no starter exists.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
Make hello registration close over `Deps`, and compute capabilities from actual dependency availability.
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
// services/worker/internal/socket/handlers.go:18
|
||||
handlers := []sessionHandler{
|
||||
helloHandler(),
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```go
|
||||
handlers := []sessionHandler{
|
||||
helloHandler(deps),
|
||||
}
|
||||
```
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
// services/worker/internal/socket/handlers.go:53
|
||||
func handleHello(req *altv1.HelloRequest) (*altv1.HelloResponse, error) {
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```go
|
||||
func handleHello(deps Deps, req *altv1.HelloRequest) (*altv1.HelloResponse, error) {
|
||||
```
|
||||
|
||||
Capabilities should always include `hello`. Add `market-read` only when both `deps.Instruments` and `deps.Bars` are non-nil. Add `backtest-read` only when `deps.Analysis` and `deps.Results` are non-nil. Add `backtest-start` only when `deps.Starter` is non-nil. Do not add `worker-execution` while start is unavailable.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `services/worker/internal/socket/handlers.go`: change `helloHandler` and `handleHello` to accept `Deps`.
|
||||
- [ ] `services/worker/internal/socket/handlers.go`: add a small `capabilitiesForDeps(deps Deps) []string` helper.
|
||||
- [ ] `services/worker/cmd/alt-worker/main.go`: leave `Starter` nil and keep existing typed unavailable path; no fake starter or placeholder execution should be wired.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
Write tests in `services/worker/internal/socket/server_test.go`:
|
||||
|
||||
- `TestWorkerHelloCapabilitiesReflectDeps`: no deps => only `hello`; market deps => `market-read`; backtest read deps => `backtest-read`; starter => `backtest-start`.
|
||||
- Keep existing socket hello test and assert it does not advertise `backtest-start` when `BacktestDeps{}` is used.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
go test -count=1 ./services/worker/internal/socket
|
||||
```
|
||||
|
||||
Expected: exit code 0, no missing handler/capability assertions.
|
||||
|
||||
### [WORKER-2] Typed Unavailable And Handler Coverage
|
||||
|
||||
#### 문제
|
||||
|
||||
`services/worker/internal/socket/backtest_test.go:112` covers the direct nil-starter response, but `services/worker/internal/socket/server_test.go:67` only checks handler uniqueness/hello presence. It does not tie the handler registry to capability claims.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
Extend registry tests to compare actual request handlers with capability names. Keep `StartBacktestRequest` registered, because registered start requests should return typed unavailable while `Deps.Starter` is nil. The distinction is capability truth: registered handler exists, execution capability does not.
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
// services/worker/internal/socket/server_test.go:67
|
||||
func TestSessionHandlers(t *testing.T) {
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```go
|
||||
func TestSessionHandlers(t *testing.T) {
|
||||
// keep requestType uniqueness checks
|
||||
}
|
||||
|
||||
func TestWorkerCapabilitiesDoNotClaimUnavailableStart(t *testing.T) {
|
||||
// validate capability names from Deps{}
|
||||
}
|
||||
```
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `services/worker/internal/socket/server_test.go`: assert capabilities for `Deps{}` omit `backtest-start`.
|
||||
- [ ] `services/worker/internal/socket/backtest_test.go`: keep `TestHandleStartBacktestUnavailable`; update only if helper names change.
|
||||
- [ ] `services/worker/internal/socket/market_test.go`: no change expected unless capability helper uses fake stores from this file.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
Tests are required because this is the regression that completes the roadmap task: the worker must not advertise unavailable execution.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
go test -count=1 ./services/worker/internal/socket ./services/worker/internal/jobs
|
||||
```
|
||||
|
||||
Expected: exit code 0. Existing job tests should still show `RegisterRunBacktestHandler` can override the built-in placeholder when a future production adapter is ready.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `services/worker/internal/socket/handlers.go` | WORKER-1 |
|
||||
| `services/worker/internal/socket/server_test.go` | WORKER-1, WORKER-2 |
|
||||
| `services/worker/internal/socket/backtest_test.go` | WORKER-2 |
|
||||
| `services/worker/cmd/alt-worker/main.go` | WORKER-1 scope check only |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
go test -count=1 ./services/worker/...
|
||||
```
|
||||
|
||||
Expected: exit code 0. Go test cache output is not acceptable for this plan because capability assertions are new.
|
||||
|
||||
If implementation changes storage SQL, migrations, or generated sqlc output, also run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
bin/worker-storage-check
|
||||
```
|
||||
|
||||
Expected: exit code 0, or a recorded blocker/skip with actual stdout/stderr if remote infra is unavailable.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
<!-- task=m-operator-runtime-refactor/02+01_api_lifecycle_capabilities 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.
|
||||
> 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-05-31
|
||||
task=m-operator-runtime-refactor/02+01_api_lifecycle_capabilities, plan=0, tag=API
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- Task ids:
|
||||
- `api-worker-lifecycle`: `services/api`의 worker client 전역 상태와 one-shot connect 흐름을 주입 가능한 lifecycle로 정리하고, connect failure, timeout, disconnect behavior를 테스트한다.
|
||||
- `capabilities`: API/worker `HelloResponse.capabilities`와 handler coverage test가 market/backtest/worker availability를 실제 등록 surface와 일치하게 검증한다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-cloud-G07.md` -> `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` -> `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-operator-runtime-refactor/02+01_api_lifecycle_capabilities/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다.
|
||||
4. PASS이고 task group이 `m-operator-runtime-refactor`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [API-1] Inject Worker Lifecycle | [ ] |
|
||||
| [API-2] Reconnect And Error Mapping | [ ] |
|
||||
| [API-3] API Capability Alignment | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] [API-1] API server construction에서 package global `socket.Worker`를 제거하고 worker client를 `Server`/handler dependency로 주입한다.
|
||||
- [ ] [API-2] worker connect failure, timeout, disconnect/reconnect behavior를 handler/lifecycle tests로 고정한다.
|
||||
- [ ] [API-3] API `HelloResponse.capabilities`를 registered handlers와 worker availability 상태에 맞춰 검증한다.
|
||||
- [ ] 원격 ALT checkout root에서 `go test -count=1 ./services/api/...`를 실행하고 실제 stdout/stderr를 review stub에 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하여 plan/review/archive 산출물이 추적 가능한지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/m-operator-runtime-refactor/02+01_api_lifecycle_capabilities/`를 `agent-task/archive/YYYY/MM/m-operator-runtime-refactor/02+01_api_lifecycle_capabilities/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-operator-runtime-refactor`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-operator-runtime-refactor/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.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로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `socket.Worker` package global이 제거되거나 더 이상 runtime state source가 아닌지 확인한다.
|
||||
- Worker connect failure/timeout/disconnect가 typed response로 매핑되고 reconnect path가 테스트되는지 확인한다.
|
||||
- API hello capabilities가 registered API handler surface와 worker availability를 반영하는지 확인한다.
|
||||
- `01_worker_execution`의 completed capability naming과 불일치가 없는지 확인한다.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
|
||||
### API-1 중간 검증
|
||||
```text
|
||||
$ go test -count=1 ./services/api/internal/socket
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-2 중간 검증
|
||||
```text
|
||||
$ go test -count=1 ./services/api/internal/socket ./services/api/internal/workerclient
|
||||
(output)
|
||||
```
|
||||
|
||||
### API-3 중간 검증
|
||||
```text
|
||||
$ go test -count=1 ./services/api/internal/socket
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```text
|
||||
$ go test -count=1 ./services/api/...
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
|
@ -0,0 +1,327 @@
|
|||
<!-- task=m-operator-runtime-refactor/02+01_api_lifecycle_capabilities plan=0 tag=API -->
|
||||
|
||||
# PLAN - API
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 반드시 채운다. 구현 후 검증 명령의 실제 stdout/stderr를 붙이고, active plan/review 파일을 그대로 둔 채 리뷰 준비 상태로 보고한다. 사용자 결정, 사용자 소유 외부 환경, 범위 충돌 없이는 안전하게 진행할 수 없을 때만 review stub의 `사용자 리뷰 요청`을 채우고 멈춘다. 후속 에이전트가 명령 재실행으로 해소할 수 있는 증거 공백은 사용자 리뷰 요청이 아니다.
|
||||
|
||||
## 배경
|
||||
|
||||
현재 API server construction이 package global `socket.Worker`를 덮어쓰고, process main은 worker connect error를 goroutine 안에서 버린다. Handler tests도 global swap에 의존해 병렬성/격리가 약하다. 이 계획은 API를 thin control plane으로 유지하면서 worker client lifecycle을 주입 가능하게 만들고, hello capability가 registered handler와 worker availability를 반영하게 한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 차단 사유는 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 정당성을 확인한 뒤에만 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- Task ids:
|
||||
- `api-worker-lifecycle`: `services/api`의 worker client 전역 상태와 one-shot connect 흐름을 주입 가능한 lifecycle로 정리하고, connect failure, timeout, disconnect behavior를 테스트한다.
|
||||
- `capabilities`: API/worker `HelloResponse.capabilities`와 handler coverage test가 market/backtest/worker availability를 실제 등록 surface와 일치하게 검증한다.
|
||||
- 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/rules/project/domain/api/rules.md`
|
||||
- `agent-ops/rules/project/domain/worker/rules.md`
|
||||
- `agent-ops/rules/project/domain/operations/rules.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/api-smoke.md`
|
||||
- `agent-test/local/worker-smoke.md`
|
||||
- `agent-test/local/contracts-smoke.md`
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/operator-surface/PHASE.md`
|
||||
- `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- `services/api/cmd/alt-api/main.go`
|
||||
- `services/api/internal/config/config.go`
|
||||
- `services/api/internal/config/config_test.go`
|
||||
- `services/api/internal/socket/server.go`
|
||||
- `services/api/internal/socket/handlers.go`
|
||||
- `services/api/internal/socket/backtest.go`
|
||||
- `services/api/internal/socket/market.go`
|
||||
- `services/api/internal/socket/server_test.go`
|
||||
- `services/api/internal/socket/backtest_test.go`
|
||||
- `services/api/internal/socket/market_test.go`
|
||||
- `services/api/internal/workerclient/client.go`
|
||||
- `services/api/internal/workerclient/client_test.go`
|
||||
- `services/api/go.mod`
|
||||
- `services/worker/internal/socket/handlers.go`
|
||||
- `services/worker/internal/socket/server_test.go`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- `test_env=local`.
|
||||
- `agent-test/local/rules.md`는 존재하고 읽었다. 검증 명령은 현재 shell에서 직접 실행하지 않고 remote host에서 ALT checkout root 기준으로 실행한다.
|
||||
- 매칭 profile은 `agent-test/local/api-smoke.md`다. API-only 변경 기본 명령은 `go test ./services/api/...`; fresh run이 필요하므로 `go test -count=1 ./services/api/...`를 사용한다.
|
||||
- Parser/contract schema를 바꾸지 않으므로 `agent-test/local/contracts-smoke.md`의 `bin/contracts-check`는 필수 검증이 아니다. Protobuf schema/generated output을 건드리면 이 plan 범위 이탈로 기록하고 contracts check를 추가해야 한다.
|
||||
- Worker capability names depend on predecessor `01_worker_execution`; this dependent task must not start before that subtask has `complete.log`.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `services/api/internal/workerclient/client_test.go:17` covers unavailable connect, and `services/api/internal/workerclient/client_test.go:209` covers request timeout.
|
||||
- Disconnect/reconnect behavior is not covered. `sendTyped` returns `ErrUnavailable` when `wsClient` is nil/dead (`services/api/internal/workerclient/client.go:163`) but no API handler tries to reconnect before forwarding.
|
||||
- `services/api/internal/socket/server_test.go:60` only asserts capabilities are non-empty; it does not verify registered API handlers or worker availability capability.
|
||||
- Handler tests mutate global `Worker` through `withWorker` (`services/api/internal/socket/backtest_test.go:71`), so dependency injection/lifecycle behavior is not isolated.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- removed/renamed symbol candidate: package global `socket.Worker`.
|
||||
- References found:
|
||||
- `services/api/internal/socket/server.go:18`
|
||||
- `services/api/internal/socket/server.go:21`
|
||||
- `services/api/cmd/alt-api/main.go:23`
|
||||
- `services/api/cmd/alt-api/main.go:38`
|
||||
- `services/api/internal/socket/backtest.go:68`, `84`, `103`, `122`, `146`
|
||||
- `services/api/internal/socket/market.go:43`, `76`
|
||||
- tests: `services/api/internal/socket/backtest_test.go:74`, `75`, `76`
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 먼저 평가했다.
|
||||
- shared task group: `m-operator-runtime-refactor`.
|
||||
- this subtask: `02+01_api_lifecycle_capabilities`.
|
||||
- predecessor `01`: expected active path `agent-task/m-operator-runtime-refactor/01_worker_execution/complete.log`; currently missing because the predecessor plan was just created. Runtime dependency comes from the directory name, so implementation must wait until predecessor PASS writes `complete.log`.
|
||||
- API work is split from worker because it removes package global state and changes lifecycle semantics across handlers/tests. Client bootstrap is independent and remains in `03_client_bootstrap`.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- Do not change protobuf schema or generated code. `HelloResponse.capabilities` already exists in generated contracts.
|
||||
- Do not make API run backtest execution or import storage dependencies; API remains a thin forwarder.
|
||||
- Do not implement Flutter UI changes here.
|
||||
- Do not make worker availability a hard API startup requirement. API should start and report worker unavailable when connect fails.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- build lane: `cloud-G07`; review lane: `cloud-G07`.
|
||||
- This changes lifecycle/concurrency boundaries, removes global state, and affects client-visible capability semantics.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] [API-1] API server construction에서 package global `socket.Worker`를 제거하고 worker client를 `Server`/handler dependency로 주입한다.
|
||||
- [ ] [API-2] worker connect failure, timeout, disconnect/reconnect behavior를 handler/lifecycle tests로 고정한다.
|
||||
- [ ] [API-3] API `HelloResponse.capabilities`를 registered handlers와 worker availability 상태에 맞춰 검증한다.
|
||||
- [ ] 원격 ALT checkout root에서 `go test -count=1 ./services/api/...`를 실행하고 실제 stdout/stderr를 review stub에 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
`02+01_api_lifecycle_capabilities`는 `01_worker_execution`의 `complete.log`가 있어야 구현을 시작한다. worker side capability names and unavailable/start semantics must be known before API hello capability assertions are finalized.
|
||||
|
||||
### [API-1] Inject Worker Lifecycle
|
||||
|
||||
#### 문제
|
||||
|
||||
`services/api/internal/socket/server.go:18` declares package global `Worker`, and `NewServer` overwrites it at `services/api/internal/socket/server.go:21`. `services/api/cmd/alt-api/main.go:22` starts a goroutine that ignores `socket.Worker.Connect(ctx)` errors. This hides connect failures and makes tests mutate shared state.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
Introduce an API `Server` wrapper and dependency injection.
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
// services/api/internal/socket/server.go:18
|
||||
var Worker workerclient.WorkerClient
|
||||
|
||||
func NewServer(cfg config.Config) *protoSocket.WsServer {
|
||||
Worker = workerclient.New(cfg.WorkerSocketURL)
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```go
|
||||
type Server struct {
|
||||
wsServer *protoSocket.WsServer
|
||||
worker workerclient.WorkerClient
|
||||
}
|
||||
|
||||
func NewServer(cfg config.Config) *Server {
|
||||
return NewServerWithWorker(cfg, workerclient.New(cfg.WorkerSocketURL))
|
||||
}
|
||||
```
|
||||
|
||||
Register handlers with the injected worker:
|
||||
|
||||
```go
|
||||
server.wsServer.OnClientConnected = registerSessionHandlers(worker)
|
||||
```
|
||||
|
||||
Move close ownership into `Server.Stop`, and remove `socket.Worker.Connect/Close` from `cmd/alt-api/main.go`. API startup may attempt eager connect and log failure, but handlers must still operate when worker is unavailable.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `services/api/internal/socket/server.go`: add `Server`, `NewServerWithWorker`, `Start`, `Stop`.
|
||||
- [ ] `services/api/internal/socket/handlers.go`: make `sessionHandlers(worker)` and `registerSessionHandlers(worker)` close over injected worker.
|
||||
- [ ] `services/api/internal/socket/backtest.go`: pass `workerclient.WorkerClient` into handlers instead of reading global `Worker`.
|
||||
- [ ] `services/api/internal/socket/market.go`: same injection change.
|
||||
- [ ] `services/api/cmd/alt-api/main.go`: remove direct global connect/close usage.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
Update tests to use `NewServerWithWorker(cfg, fake)` and remove `withWorker` global swap where possible. Add a test that two servers can be built with different fake workers without cross-talk.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
go test -count=1 ./services/api/internal/socket
|
||||
```
|
||||
|
||||
Expected: exit code 0 and no package global worker dependency.
|
||||
|
||||
### [API-2] Reconnect And Error Mapping
|
||||
|
||||
#### 문제
|
||||
|
||||
`services/api/internal/workerclient/client.go:56` has idempotent `Connect`, but API handlers never call it after startup. If a worker disconnects, `sendTyped` returns `ErrUnavailable` (`services/api/internal/workerclient/client.go:163`) until an external reconnect happens. There is no test for reconnect behavior.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
Before each forwarded worker request, ensure the injected worker is connected with the request timeout context. Since `Connect` is already idempotent, calling it in the request path handles initial connect failure and later disconnects without a second lifecycle manager.
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
// services/api/internal/socket/backtest.go:71
|
||||
ctx, cancel := context.WithTimeout(context.Background(), workerRequestTimeout)
|
||||
defer cancel()
|
||||
res, err := Worker.StartBacktest(ctx, req)
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```go
|
||||
ctx, cancel := context.WithTimeout(context.Background(), workerRequestTimeout)
|
||||
defer cancel()
|
||||
if err := worker.Connect(ctx); err != nil {
|
||||
return &altv1.StartBacktestResponse{Error: workerErrorInfo(err)}, nil
|
||||
}
|
||||
res, err := worker.StartBacktest(ctx, req)
|
||||
```
|
||||
|
||||
Apply the same helper path for market and backtest forwarding to keep error mapping consistent.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `services/api/internal/socket/backtest.go`: add shared worker connect/forward helper or call `worker.Connect(ctx)` consistently.
|
||||
- [ ] `services/api/internal/socket/market.go`: same behavior.
|
||||
- [ ] `services/api/internal/workerclient/client.go`: add `IsConnected() bool` only if API hello capability needs it; keep `Connect` idempotent.
|
||||
- [ ] `services/api/internal/workerclient/client_test.go`: add reconnect after close/dead connection coverage if implementation changes client state methods.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
Add fake worker fields for connect count and connect error. Required tests:
|
||||
|
||||
- connect failure returns typed unavailable without forwarding request.
|
||||
- context timeout maps to timeout.
|
||||
- after fake disconnect state, next valid handler calls `Connect` again before forwarding.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
go test -count=1 ./services/api/internal/socket ./services/api/internal/workerclient
|
||||
```
|
||||
|
||||
Expected: exit code 0; timeout/unavailable mapping remains typed response payloads.
|
||||
|
||||
### [API-3] API Capability Alignment
|
||||
|
||||
#### 문제
|
||||
|
||||
`services/api/internal/socket/handlers.go:66` returns static capabilities with `hello`, `request-response`, and `market-read`. The API now registers backtest handlers (`services/api/internal/socket/backtest_test.go:442`) but does not advertise them, and it cannot represent worker availability.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
Build capabilities from registered handler groups and worker connection state. Minimum expected capability set:
|
||||
|
||||
- always: `hello`, `request-response`
|
||||
- registered API surface: `market-read`, `backtest-read`, `backtest-start`
|
||||
- worker state: `worker-available` when connected/alive, otherwise `worker-unavailable`
|
||||
|
||||
Keep names aligned with `01_worker_execution`. If predecessor chose different exact names, use those names here and record the change in review.
|
||||
|
||||
Before:
|
||||
|
||||
```go
|
||||
// services/api/internal/socket/handlers.go:70
|
||||
Capabilities: []string{
|
||||
"hello",
|
||||
"request-response",
|
||||
"market-read",
|
||||
},
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```go
|
||||
Capabilities: capabilitiesForSession(worker)
|
||||
```
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `services/api/internal/socket/handlers.go`: make `helloHandler(worker)` and `handleHello(worker, req)`.
|
||||
- [ ] `services/api/internal/socket/server_test.go`: assert hello capabilities include registered market/backtest API surface and worker availability state.
|
||||
- [ ] `services/api/internal/socket/backtest_test.go` and `market_test.go`: keep handler coverage tests aligned with capability helper.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
Tests are required:
|
||||
|
||||
- `TestServerRespondsToHelloRequest` should assert exact capability set for an unavailable fake worker.
|
||||
- A unit test should assert capability names and handler registration stay in sync.
|
||||
- If `workerclient.IsConnected()` is added, test both true and false fake states.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
go test -count=1 ./services/api/internal/socket
|
||||
```
|
||||
|
||||
Expected: exit code 0 and deterministic capability assertions.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `services/api/internal/socket/server.go` | API-1 |
|
||||
| `services/api/internal/socket/handlers.go` | API-1, API-3 |
|
||||
| `services/api/internal/socket/backtest.go` | API-1, API-2 |
|
||||
| `services/api/internal/socket/market.go` | API-1, API-2 |
|
||||
| `services/api/cmd/alt-api/main.go` | API-1 |
|
||||
| `services/api/internal/workerclient/client.go` | API-2, API-3 if needed |
|
||||
| `services/api/internal/socket/server_test.go` | API-1, API-3 |
|
||||
| `services/api/internal/socket/backtest_test.go` | API-1, API-2, API-3 |
|
||||
| `services/api/internal/socket/market_test.go` | API-1, API-2, API-3 |
|
||||
| `services/api/internal/workerclient/client_test.go` | API-2 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
go test -count=1 ./services/api/...
|
||||
```
|
||||
|
||||
Expected: exit code 0. Go test cache output is not acceptable because lifecycle and capability tests are new.
|
||||
|
||||
If implementation changes protobuf schema or generated contract outputs, stop and record scope change; then run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
bin/contracts-check
|
||||
```
|
||||
|
||||
Expected: exit code 0 with actual stdout/stderr, or documented blocker. Schema/generated changes are not expected in this plan.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<!-- task=m-operator-runtime-refactor/03_client_bootstrap plan=0 tag=CLIENT -->
|
||||
|
||||
# Code Review Reference - CLIENT
|
||||
|
||||
> **[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.
|
||||
> 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-05-31
|
||||
task=m-operator-runtime-refactor/03_client_bootstrap, plan=0, tag=CLIENT
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- Task ids:
|
||||
- `client-bootstrap`: Flutter operator shell이 push/Firebase/Mattermost 초기화 없이도 시작 가능한 경계로 분리되고, push-specific integration은 후속 push Milestone 범위로 남는다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-cloud-G07.md` -> `code_review_cloud_G07_N.log`, `PLAN-cloud-G07.md` -> `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/m-operator-runtime-refactor/03_client_bootstrap/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다.
|
||||
4. PASS이고 task group이 `m-operator-runtime-refactor`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [CLIENT-1] Core Shell Bootstrap | [ ] |
|
||||
| [CLIENT-2] App Shell Starts Without Push | [ ] |
|
||||
| [CLIENT-3] Push Opt-In Boundary | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] [CLIENT-1] core operator shell bootstrap을 Firebase/Mattermost push initialization과 분리한다.
|
||||
- [ ] [CLIENT-2] bootstrap tests를 추가해 기본 `runAltClient` path가 push/Firebase 없이 app shell을 시작할 수 있음을 검증한다.
|
||||
- [ ] [CLIENT-3] push-specific initialization은 explicit opt-in API로 남기고 후속 push Milestone 범위임을 코드 경계로 드러낸다.
|
||||
- [ ] 원격 ALT checkout root에서 `cd apps/client && flutter test`를 실행하고 실제 stdout/stderr를 review stub에 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
|
||||
- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하여 plan/review/archive 산출물이 추적 가능한지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/m-operator-runtime-refactor/03_client_bootstrap/`를 `agent-task/archive/YYYY/MM/m-operator-runtime-refactor/03_client_bootstrap/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-operator-runtime-refactor`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-operator-runtime-refactor/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.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로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- Default `runAltClient`/`bootstrapAltClient` path does not require Firebase or Mattermost push readiness.
|
||||
- Push initialization is explicit opt-in and remains outside dashboard/widget presentation code.
|
||||
- New tests avoid platform Firebase initialization by using injected hooks/fakes.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
|
||||
### CLIENT-1 중간 검증
|
||||
```text
|
||||
$ cd apps/client && flutter test test/app/bootstrap_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
### CLIENT-2 중간 검증
|
||||
```text
|
||||
$ cd apps/client && flutter test test/widget_test.dart test/app/bootstrap_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
### CLIENT-3 중간 검증
|
||||
```text
|
||||
$ cd apps/client && flutter test test/integrations/mattermost_push_host_integration_test.dart
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```text
|
||||
$ cd apps/client && flutter test
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
<!-- task=m-operator-runtime-refactor/03_client_bootstrap plan=0 tag=CLIENT -->
|
||||
|
||||
# PLAN - CLIENT
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 구현이 막히면 active review stub의 `사용자 리뷰 요청`에 정확한 결정/근거/실행 명령을 기록하고 멈춘다. 후속 에이전트가 재실행으로 메울 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니다.
|
||||
|
||||
## 배경
|
||||
|
||||
Flutter app bootstrap currently initializes Firebase and Mattermost push before rendering the operator shell. The current Milestone requires the operator shell to start without push/Firebase/Mattermost readiness, while push behavior remains a later Milestone. This plan separates core shell bootstrap from optional push integration without changing dashboard UI or generated contracts.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 사용자 결정이 필요한 경우 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션을 채우고 멈춘다. code-review가 검증한 경우에만 `USER_REVIEW.md`를 작성한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- Task ids:
|
||||
- `client-bootstrap`: Flutter operator shell이 push/Firebase/Mattermost 초기화 없이도 시작 가능한 경계로 분리되고, push-specific integration은 후속 push Milestone 범위로 남는다.
|
||||
- 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/rules/project/domain/client/rules.md`
|
||||
- `agent-ops/rules/project/domain/operations/rules.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/client-smoke.md`
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/operator-surface/PHASE.md`
|
||||
- `agent-roadmap/phase/operator-surface/milestones/operator-runtime-refactor.md`
|
||||
- `apps/client/lib/main.dart`
|
||||
- `apps/client/lib/src/app/bootstrap.dart`
|
||||
- `apps/client/lib/src/app/app.dart`
|
||||
- `apps/client/test/widget_test.dart`
|
||||
- `apps/client/test/integrations/mattermost_push_host_integration_test.dart`
|
||||
- `apps/client/integration_test/socket_runtime_smoke_test.dart`
|
||||
- `apps/client/pubspec.yaml`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- `test_env=local`.
|
||||
- `agent-test/local/rules.md`는 존재하고 읽었다. local shell에서 테스트를 직접 실행하지 않고 remote host의 ALT checkout root에서 실행한다.
|
||||
- 매칭 profile은 `agent-test/local/client-smoke.md`다. client-only 변경 기본 명령은 `cd apps/client && flutter test`.
|
||||
- Socket runtime/integration smoke는 API socket/platform runtime이 필요하면 실행 가능 여부를 확인하고, 실행할 수 없으면 pass가 아니라 skip/blocker와 미실행 명령을 기록한다.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `apps/client/test/widget_test.dart:18` already proves `AltClientApp` can render without a `mattermostHost`, but it bypasses `runAltClient` and `bootstrapAltClient`.
|
||||
- `apps/client/lib/src/app/bootstrap.dart:25` has no unit test proving Firebase/push are optional.
|
||||
- Mattermost host integration tests cover host initialization behavior in isolation (`apps/client/test/integrations/mattermost_push_host_integration_test.dart:91`) but not app bootstrap separation.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol candidate: none required.
|
||||
- `bootstrapAltClient` references: `apps/client/lib/src/app/bootstrap.dart:25`, `apps/client/lib/src/app/bootstrap.dart:33`.
|
||||
- `runAltClient` references: `apps/client/lib/main.dart:3`, `apps/client/lib/src/app/bootstrap.dart:31`.
|
||||
- `MattermostPushHostIntegration` app references: `apps/client/lib/src/app/bootstrap.dart:6`, `apps/client/lib/src/app/app.dart:6`.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 먼저 평가했다.
|
||||
- shared task group: `m-operator-runtime-refactor`.
|
||||
- this subtask: `03_client_bootstrap`, no predecessors.
|
||||
- It is independent because it touches Flutter bootstrap/test boundaries only. It must not wait for worker/API runtime plans and must not implement push notification behavior itself.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- Do not change dashboard layout, navigation depth, chart/form wireframe, or production operator screens.
|
||||
- Do not edit generated Dart protobuf files.
|
||||
- Do not remove Mattermost/Firebase integration packages; only make them optional at app bootstrap.
|
||||
- Do not implement push notification behavior. Push-specific integration remains in the later push Milestone.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- build lane: `cloud-G07`; review lane: `cloud-G07`.
|
||||
- Flutter bootstrap touches platform/Firebase startup and optional external integration boundaries; tests are runnable but runtime smoke may be environment-gated.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] [CLIENT-1] core operator shell bootstrap을 Firebase/Mattermost push initialization과 분리한다.
|
||||
- [ ] [CLIENT-2] bootstrap tests를 추가해 기본 `runAltClient` path가 push/Firebase 없이 app shell을 시작할 수 있음을 검증한다.
|
||||
- [ ] [CLIENT-3] push-specific initialization은 explicit opt-in API로 남기고 후속 push Milestone 범위임을 코드 경계로 드러낸다.
|
||||
- [ ] 원격 ALT checkout root에서 `cd apps/client && flutter test`를 실행하고 실제 stdout/stderr를 review stub에 기록한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
### [CLIENT-1] Core Shell Bootstrap
|
||||
|
||||
#### 문제
|
||||
|
||||
`apps/client/lib/src/app/bootstrap.dart:25` calls `Firebase.initializeApp()` and `_mattermostHost.initialize()` before `runApp`. This makes optional push readiness a prerequisite for the operator shell, contrary to the client domain rule and current Milestone.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
Split bootstrap into core shell setup and optional push setup. Keep `applyFullscreenMode` in the core path. Return or pass a nullable `MattermostPushHostIntegration` only when push is explicitly enabled.
|
||||
|
||||
Before:
|
||||
|
||||
```dart
|
||||
// apps/client/lib/src/app/bootstrap.dart:25
|
||||
Future<void> bootstrapAltClient() async {
|
||||
await applyFullscreenMode();
|
||||
await Firebase.initializeApp();
|
||||
await _mattermostHost.initialize();
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```dart
|
||||
class AltClientBootstrapOptions {
|
||||
const AltClientBootstrapOptions({this.enablePush = false});
|
||||
final bool enablePush;
|
||||
}
|
||||
|
||||
Future<MattermostPushHostIntegration?> bootstrapAltClient({
|
||||
AltClientBootstrapOptions options = const AltClientBootstrapOptions(),
|
||||
}) async {
|
||||
await applyFullscreenMode();
|
||||
if (!options.enablePush) return null;
|
||||
await Firebase.initializeApp();
|
||||
await _mattermostHost.initialize();
|
||||
return _mattermostHost;
|
||||
}
|
||||
```
|
||||
|
||||
Allow injectable functions or host factory if needed for tests; do not call Firebase in the default test path.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/client/lib/src/app/bootstrap.dart`: introduce bootstrap options/result and default push-off core bootstrap.
|
||||
- [ ] `apps/client/lib/main.dart`: keep `main` simple; default `runAltClient()` should render the core shell.
|
||||
- [ ] `apps/client/lib/src/app/app.dart`: no change expected unless constructor docs or nullable host behavior needs tightening.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
Add `apps/client/test/app/bootstrap_test.dart` or equivalent:
|
||||
|
||||
- default `bootstrapAltClient` does not call injected Firebase/push functions.
|
||||
- opt-in push path calls injected Firebase and host initialize once.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
cd apps/client && flutter test test/app/bootstrap_test.dart
|
||||
```
|
||||
|
||||
Expected: exit code 0.
|
||||
|
||||
### [CLIENT-2] App Shell Starts Without Push
|
||||
|
||||
#### 문제
|
||||
|
||||
`apps/client/test/widget_test.dart:19` instantiates `AltClientApp` directly, so it does not protect the `runAltClient` bootstrap path.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
Make `runAltClient` accept injectable `runApp`/bootstrap hooks or a testable `buildAltClientApp` helper. Keep tests simple and avoid launching platform Firebase.
|
||||
|
||||
Before:
|
||||
|
||||
```dart
|
||||
// apps/client/lib/src/app/bootstrap.dart:31
|
||||
Future<void> runAltClient() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await bootstrapAltClient();
|
||||
runApp(ProviderScope(child: AltClientApp(mattermostHost: _mattermostHost)));
|
||||
}
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```dart
|
||||
Future<void> runAltClient({
|
||||
AltClientBootstrapOptions options = const AltClientBootstrapOptions(),
|
||||
void Function(Widget app)? runAppOverride,
|
||||
}) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
final mattermostHost = await bootstrapAltClient(options: options);
|
||||
(runAppOverride ?? runApp)(
|
||||
ProviderScope(child: AltClientApp(mattermostHost: mattermostHost)),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
If `runAppOverride` creates awkward production API, prefer a private testable helper that returns the root widget after bootstrap.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/client/lib/src/app/bootstrap.dart`: make default run path pass `mattermostHost: null`.
|
||||
- [ ] `apps/client/test/widget_test.dart`: keep direct shell tests passing.
|
||||
- [ ] `apps/client/test/app/bootstrap_test.dart`: assert default run path builds `AltClientApp` without a Mattermost host.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
Required. Use injected callbacks/fakes so no Firebase platform initialization is needed.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
cd apps/client && flutter test test/widget_test.dart test/app/bootstrap_test.dart
|
||||
```
|
||||
|
||||
Expected: exit code 0.
|
||||
|
||||
### [CLIENT-3] Push Opt-In Boundary
|
||||
|
||||
#### 문제
|
||||
|
||||
`apps/client/lib/src/app/bootstrap.dart:10` creates a package-level `_mattermostHost` and immediately uses it during default bootstrap. This hides the optional boundary and invites later push work to leak into the operator shell.
|
||||
|
||||
#### 해결 방법
|
||||
|
||||
Keep the host factory private but only instantiate or initialize it when push is explicitly enabled. Existing `MattermostPushHostIntegration` tests remain the behavior owner for push-specific logic.
|
||||
|
||||
Before:
|
||||
|
||||
```dart
|
||||
// apps/client/lib/src/app/bootstrap.dart:10
|
||||
final _mattermostHost = MattermostPushHostIntegration(
|
||||
pushClient: MattermostPushPluginClient(),
|
||||
);
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```dart
|
||||
MattermostPushHostIntegration createMattermostPushHost() {
|
||||
return MattermostPushHostIntegration(
|
||||
pushClient: MattermostPushPluginClient(),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Use the factory only in opt-in push bootstrap. If a singleton is retained, ensure it is not initialized on the default path.
|
||||
|
||||
#### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/client/lib/src/app/bootstrap.dart`: isolate push host creation/initialization behind explicit opt-in.
|
||||
- [ ] `apps/client/test/integrations/mattermost_push_host_integration_test.dart`: no behavior change expected; run to confirm.
|
||||
- [ ] Do not edit `apps/client/lib/src/integrations/mattermost/*` unless a compile error requires a narrow import/API update.
|
||||
|
||||
#### 테스트 작성
|
||||
|
||||
No new Mattermost behavior test is needed beyond existing host tests, but run them because imports/factory usage may change.
|
||||
|
||||
#### 중간 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
cd apps/client && flutter test test/integrations/mattermost_push_host_integration_test.dart
|
||||
```
|
||||
|
||||
Expected: exit code 0.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `apps/client/lib/src/app/bootstrap.dart` | CLIENT-1, CLIENT-2, CLIENT-3 |
|
||||
| `apps/client/lib/main.dart` | CLIENT-2 |
|
||||
| `apps/client/lib/src/app/app.dart` | CLIENT-1 if nullable host docs/types need tightening |
|
||||
| `apps/client/test/app/bootstrap_test.dart` | CLIENT-1, CLIENT-2 |
|
||||
| `apps/client/test/widget_test.dart` | CLIENT-2 |
|
||||
| `apps/client/test/integrations/mattermost_push_host_integration_test.dart` | CLIENT-3 verification only |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
Run from the remote ALT checkout root:
|
||||
|
||||
```bash
|
||||
cd apps/client && flutter test
|
||||
```
|
||||
|
||||
Expected: exit code 0. Flutter test cache is not accepted as evidence; record actual stdout/stderr.
|
||||
|
||||
If runtime socket smoke is attempted, use the client profile rules and record actual skip/blocker conditions instead of treating skipped integration as pass.
|
||||
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -16,7 +16,7 @@ type sessionHandler struct {
|
|||
// through the same registry instead of bespoke registration paths.
|
||||
func sessionHandlers(deps Deps) []sessionHandler {
|
||||
handlers := []sessionHandler{
|
||||
helloHandler(),
|
||||
helloHandler(deps),
|
||||
}
|
||||
handlers = append(handlers, marketHandlers(deps)...)
|
||||
handlers = append(handlers, backtestHandlers(deps)...)
|
||||
|
|
@ -41,16 +41,21 @@ func registerHandlers(client *protoSocket.WsClient, handlers []sessionHandler) {
|
|||
}
|
||||
}
|
||||
|
||||
func helloHandler() sessionHandler {
|
||||
func helloHandler(deps Deps) sessionHandler {
|
||||
return sessionHandler{
|
||||
requestType: protoSocket.TypeNameOf(&altv1.HelloRequest{}),
|
||||
register: func(client *protoSocket.WsClient) {
|
||||
protoSocket.AddRequestListenerTyped[*altv1.HelloRequest, *altv1.HelloResponse](&client.Communicator, handleHello)
|
||||
protoSocket.AddRequestListenerTyped[*altv1.HelloRequest, *altv1.HelloResponse](
|
||||
&client.Communicator,
|
||||
func(req *altv1.HelloRequest) (*altv1.HelloResponse, error) {
|
||||
return handleHello(deps, req)
|
||||
},
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func handleHello(req *altv1.HelloRequest) (*altv1.HelloResponse, error) {
|
||||
func handleHello(deps Deps, req *altv1.HelloRequest) (*altv1.HelloResponse, error) {
|
||||
protocolVersion := req.GetAltProtocolVersion()
|
||||
if protocolVersion == "" {
|
||||
protocolVersion = defaultAltProtocolVersion
|
||||
|
|
@ -59,10 +64,20 @@ func handleHello(req *altv1.HelloRequest) (*altv1.HelloResponse, error) {
|
|||
ServerName: serverName,
|
||||
ServerVersion: serverVersion,
|
||||
AltProtocolVersion: protocolVersion,
|
||||
Capabilities: []string{
|
||||
"hello",
|
||||
"market-read",
|
||||
"worker-execution",
|
||||
},
|
||||
Capabilities: capabilitiesForDeps(deps),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func capabilitiesForDeps(deps Deps) []string {
|
||||
caps := []string{"hello"}
|
||||
if deps.Instruments != nil && deps.Bars != nil {
|
||||
caps = append(caps, "market-read")
|
||||
}
|
||||
if deps.Analysis != nil && deps.Results != nil {
|
||||
caps = append(caps, "backtest-read")
|
||||
}
|
||||
if deps.Starter != nil {
|
||||
caps = append(caps, "backtest-start", "worker-execution")
|
||||
}
|
||||
return caps
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,13 @@ func TestWorkerSocketServerHello(t *testing.T) {
|
|||
if res.AltProtocolVersion != "alt.v1" {
|
||||
t.Errorf("expected AltProtocolVersion to be %q, got %q", "alt.v1", res.AltProtocolVersion)
|
||||
}
|
||||
|
||||
// Assert no backtest-start or worker-execution since BacktestDeps{} has no Starter.
|
||||
for _, cap := range res.Capabilities {
|
||||
if cap == "backtest-start" || cap == "worker-execution" {
|
||||
t.Errorf("expected capability %q to be omitted when Starter is nil", cap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionHandlers(t *testing.T) {
|
||||
|
|
@ -104,3 +111,74 @@ func TestRegisterHandlers(t *testing.T) {
|
|||
registerHandlers(nil, handlers)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkerHelloCapabilitiesReflectDeps(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
deps Deps
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "no deps",
|
||||
deps: Deps{},
|
||||
expected: []string{"hello"},
|
||||
},
|
||||
{
|
||||
name: "market deps",
|
||||
deps: Deps{
|
||||
Instruments: &fakeInstrumentStore{},
|
||||
Bars: &fakeBarStore{},
|
||||
},
|
||||
expected: []string{"hello", "market-read"},
|
||||
},
|
||||
{
|
||||
name: "backtest read deps",
|
||||
deps: Deps{
|
||||
Analysis: &fakeAnalysisStore{},
|
||||
Results: &fakeResultStore{},
|
||||
},
|
||||
expected: []string{"hello", "backtest-read"},
|
||||
},
|
||||
{
|
||||
name: "starter deps",
|
||||
deps: Deps{
|
||||
Starter: &fakeStarter{},
|
||||
},
|
||||
expected: []string{"hello", "backtest-start", "worker-execution"},
|
||||
},
|
||||
{
|
||||
name: "all deps",
|
||||
deps: Deps{
|
||||
Instruments: &fakeInstrumentStore{},
|
||||
Bars: &fakeBarStore{},
|
||||
Analysis: &fakeAnalysisStore{},
|
||||
Results: &fakeResultStore{},
|
||||
Starter: &fakeStarter{},
|
||||
},
|
||||
expected: []string{"hello", "market-read", "backtest-read", "backtest-start", "worker-execution"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
caps := capabilitiesForDeps(tt.deps)
|
||||
if len(caps) != len(tt.expected) {
|
||||
t.Fatalf("expected caps %v, got %v", tt.expected, caps)
|
||||
}
|
||||
for i, v := range tt.expected {
|
||||
if caps[i] != v {
|
||||
t.Errorf("expected cap at %d to be %q, got %q", i, v, caps[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkerCapabilitiesDoNotClaimUnavailableStart(t *testing.T) {
|
||||
caps := capabilitiesForDeps(Deps{})
|
||||
for _, cap := range caps {
|
||||
if cap == "backtest-start" || cap == "worker-execution" {
|
||||
t.Errorf("capabilities for empty Deps{} should not claim unavailable start, but got %q", cap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue