diff --git a/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/code_review_cloud_G07_0.log b/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/code_review_cloud_G07_0.log new file mode 100644 index 0000000..2e3636a --- /dev/null +++ b/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/code_review_cloud_G07_0.log @@ -0,0 +1,227 @@ + + +# Code Review Reference - OPS + +> **[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-06-01 +task=m-operator-headless-workflow-validation/01_scenario_core, plan=0, tag=OPS + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Task ids: + - `scenario-format`: operator scenario를 YAML 또는 동등한 파일 형식으로 표현한다. +- 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-headless-workflow-validation/01_scenario_core/`로 이동한다. 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-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [OPS-1] CLI Command Skeleton And Scenario Parser | [x] | + +## 구현 체크리스트 + +- [x] `apps/cli`에 operator scenario schema, YAML parser, dry-run validation을 추가한다. 검증: 예시 scenario가 parser 또는 dry-run 검증을 통과한다. (`apps/cli/internal/operator/scenario.go`, `LoadScenario`/`ParseScenario`/`Validate`. `api_connection_smoke.yaml`이 dry-run 검증 통과.) +- [x] `alt operator scenario validate --file ` command를 추가하고 exit code와 summary 출력을 고정한다. (`apps/cli/internal/cli/cli.go`. exit 0/1/2, `scenario=... status=valid steps=N` / `status=parse_error` 계약.) +- [x] scenario parser와 CLI validate command 단위 테스트를 추가한다. (`scenario_test.go` 3 케이스, `cli_test.go` 3 케이스, 모두 통과.) +- [x] 원격 ALT checkout root에서 focused CLI test와 operations smoke 검증을 실행한다. focused test/`go run`은 exit 0. operations smoke `bin/test`는 첫 단계 `contracts-check`가 host `protoc-gen-go` 미설치로 차단되어, 나머지 단계(5개 Go 모듈 + Flutter)를 개별 실행해 통과를 확인했다. 상세는 `검증 결과`와 `계획 대비 변경 사항` 참고. +- [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 산출물이 추적 가능한지 확인한다. +- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [ ] PASS이면 active task 디렉터리 `agent-task/m-operator-headless-workflow-validation/01_scenario_core/`를 `agent-task/archive/YYYY/MM/m-operator-headless-workflow-validation/01_scenario_core/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [ ] PASS이고 task group이 `m-operator-headless-workflow-validation`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-operator-headless-workflow-validation/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. +- [x] 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로 이동한다. + +## 계획 대비 변경 사항 + +- `apps/cli/go.mod`: 계획 체크리스트는 `packages/contracts/gen/go`, `proto-socket/go`, `gopkg.in/yaml.v3`를 require로 적었으나, 같은 항목의 단서 "direct import만 direct require로 둔다"를 따른다. 이 subtask(`01_scenario_core`)는 scenario schema/parser/CLI skeleton만 만들고 contracts나 proto-socket을 import하지 않으므로 `go mod tidy` 결과는 `gopkg.in/yaml.v3 v3.0.1`만 direct require로 남는다. API/worker request runner를 추가하는 `02+01_api_market` subtask에서 contracts/proto-socket을 실제 import할 때 그 require를 추가한다. yaml.v3 v3.0.1은 workspace의 `services/worker`가 이미 쓰는 동일 버전이라 일관된다. +- 최종 검증의 `bin/test`는 첫 단계 `bin/contracts-check`가 host에서 차단되었다. `contracts-check`는 항상 `bin/contracts-gen`을 호출하는데 원격 macOS test host에 `protoc-gen-go`가 설치되어 있지 않다(`$HOME/go/bin` 비어 있음, `protoc`는 있으나 Go plugin 없음). 이는 이번 변경 범위인 `apps/cli`(operations)와 무관한 host toolchain 공백이다. `bin/test`를 완주하지 못한 대신 동일 host에서 `bin/test`의 나머지 단계(`packages/contracts/gen/go`, `packages/domain`, `services/api`, `services/worker`, `apps/cli` Go 모듈 테스트와 `apps/client` `flutter test`)를 개별 실행해 모두 통과함을 `검증 결과`에 남겼다. 후속 에이전트가 host에 `protoc-gen-go`를 설치하면 `bin/test`를 그대로 재실행해 공백을 닫을 수 있다(사용자 결정이 아니라 환경 준비 follow-up). + +## 주요 설계 결정 + +- exit code 계약을 `0=성공`, `1=usage/unknown-command`, `2=scenario parse/validation 실패`로 고정했다. parse 실패와 usage 오류를 분리해 스크립트가 입력 파일 문제와 CLI 사용 오류를 구분할 수 있다. +- machine-readable 출력은 stdout에 `key=value` 한 줄로 낸다: 성공은 `scenario= status=valid steps=`, 실패는 `scenario=unknown status=parse_error`. human-readable 오류 상세는 stderr로 분리해 grep 계약(`status=parse_error`)과 사람 디버깅을 모두 만족한다. +- YAML decode는 `yaml.Decoder.KnownFields(true)`로 strict하게 처리해 fixture 오타(미지의 필드)가 즉시 드러나게 했다. action 값은 닫힌 enum(`validActions`)으로 검증해 미구현 action을 참조하는 scenario를 dry-run에서 거부한다. +- `timeout`은 `5s` 같은 Go duration 문자열을 받도록 `operator.Duration` 래퍼에 `UnmarshalYAML`을 구현했다. raw nanoseconds 대신 사람이 읽고 쓰기 쉬운 형식을 쓰면서 잘못된 duration을 parse 단계에서 거부한다. +- `main.go`는 `cli.Run(args, stdout, stderr) int`에 위임하는 thin entrypoint로 두어 dispatch/exit-code/출력 계약을 테스트 가능한 in-memory buffer로 검증할 수 있게 했다. +- `invalid_request_matrix.yaml` fixture는 미지원 action을 가진 step으로 구성해 dry-run 검증 실패(`unknown action` → `status=parse_error`)를 재현하며, "아직 runner가 없는 invalid request는 참조할 수 없다"는 의도를 문서화한다. +- 도메인 경계 준수: `apps/cli`는 `services/api/internal/*`나 `services/worker/internal/*`를 import하지 않고 `internal/operator`/`internal/cli`로 자기 완결적이다(operations 도메인 규칙). + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- Scenario schema가 milestone의 YAML 또는 동등 파일 형식 요구를 만족하는지 확인한다. +- CLI validate command가 parse 실패를 typed, grep 가능한 출력과 non-zero exit code로 남기는지 확인한다. +- `apps/cli`가 `services/api/internal/*` 또는 `services/worker/internal/*`를 import하지 않는지 확인한다. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +검증 host: `ssh toki@toki-labs.com` 원격 macOS ALT checkout root(code-server volume 공유 경로). go `go1.26.3 darwin/arm64`. 모든 명령은 원격 checkout root 기준 상대로 실행했다. + +사전 준비: `apps/cli`에서 `go mod tidy` 실행으로 `go.sum` 생성(exit 0). yaml.v3 v3.0.1, 전이 의존 check.v1 해시 기록. + +### OPS-1 중간 검증 +```bash +$ (cd apps/cli && go test ./...) +? git.toki-labs.com/toki/alt/apps/cli/cmd/alt [no test files] +ok git.toki-labs.com/toki/alt/apps/cli/internal/cli 0.854s +ok git.toki-labs.com/toki/alt/apps/cli/internal/operator 0.435s +# exit code 0 +``` + +```bash +$ (cd apps/cli && go run ./cmd/alt operator scenario validate --file testdata/operator/api_connection_smoke.yaml) +scenario=api_connection_smoke status=valid steps=1 +# exit code 0 +``` + +### 최종 검증 +```bash +$ (cd apps/cli && go test ./...) +? git.toki-labs.com/toki/alt/apps/cli/cmd/alt [no test files] +ok git.toki-labs.com/toki/alt/apps/cli/internal/cli (cached) +ok git.toki-labs.com/toki/alt/apps/cli/internal/operator (cached) +# exit code 0 +``` + +```bash +$ (cd apps/cli && go run ./cmd/alt operator scenario validate --file testdata/operator/api_connection_smoke.yaml) +scenario=api_connection_smoke status=valid steps=1 +# exit code 0 +``` + +```bash +$ bin/test +contracts-gen: required command not found: protoc-gen-go +# exit code 1 — 차단: 첫 단계 bin/contracts-check가 호출하는 bin/contracts-gen이 +# 원격 macOS host에 없는 protoc-gen-go를 요구한다(contracts 도메인 codegen 도구, +# 이번 apps/cli 변경과 무관한 host toolchain 공백). 아래 대체 실행으로 나머지 단계 확인. +``` + +bin/test 대체 실행(동일 host, `bin/test`의 contracts-check 이후 단계를 그대로 분해 실행): +```bash +$ for m in packages/contracts/gen/go packages/domain services/api services/worker apps/cli; do (cd "$m" && go test ./...); done +--- packages/contracts/gen/go --- +? git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1 [no test files] +--- packages/domain --- +ok git.toki-labs.com/toki/alt/packages/domain/backtest 0.877s +ok git.toki-labs.com/toki/alt/packages/domain/market 0.449s +--- services/api --- +? git.toki-labs.com/toki/alt/services/api/cmd/alt-api [no test files] +ok git.toki-labs.com/toki/alt/services/api/internal/config 0.413s +ok git.toki-labs.com/toki/alt/services/api/internal/contracts 0.644s +ok git.toki-labs.com/toki/alt/services/api/internal/socket 1.097s +ok git.toki-labs.com/toki/alt/services/api/internal/workerclient 1.685s +--- services/worker --- +ok git.toki-labs.com/toki/alt/services/worker/cmd/alt-worker-migrate 1.720s +ok git.toki-labs.com/toki/alt/services/worker/internal/backtest 0.441s +ok git.toki-labs.com/toki/alt/services/worker/internal/config 3.372s +ok git.toki-labs.com/toki/alt/services/worker/internal/contracts 1.310s +ok git.toki-labs.com/toki/alt/services/worker/internal/jobs 0.858s +ok git.toki-labs.com/toki/alt/services/worker/internal/marketdata/datacheck 2.962s +ok git.toki-labs.com/toki/alt/services/worker/internal/marketdata/importer 2.133s +ok git.toki-labs.com/toki/alt/services/worker/internal/providers/kis 2.552s +ok git.toki-labs.com/toki/alt/services/worker/internal/rediskeys 3.647s +ok git.toki-labs.com/toki/alt/services/worker/internal/socket 4.170s +ok git.toki-labs.com/toki/alt/services/worker/internal/storage/postgres 4.276s +--- apps/cli --- +ok git.toki-labs.com/toki/alt/apps/cli/internal/cli (cached) +ok git.toki-labs.com/toki/alt/apps/cli/internal/operator (cached) +# 모든 모듈 exit 0 (test 없는 패키지는 [no test files]) + +$ (cd apps/client && flutter test) +... +00:01 +27: All tests passed! +# exit 0, 27 테스트 통과 +``` + +미실행/차단 요약: +- 차단: `bin/test` 완주 — host `protoc-gen-go` 미설치로 `contracts-check` 단계에서 멈춤. 사용자 결정 사안이 아니라 후속 에이전트가 host 도구를 갖추면 재실행으로 닫을 수 있는 환경 follow-up. +- 대체 증거: contracts-check를 제외한 `bin/test`의 모든 후속 단계(5개 Go 모듈, Flutter 27 테스트)를 개별 실행해 통과 확인. +- 남은 위험: 생성물 drift 검출(`contracts-check`)은 이번 subtask가 contracts/proto/generated 출력을 수정하지 않으므로 실질 위험 낮음. + +--- + +> **[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. + +## 코드리뷰 결과 + +- 종합 판정: FAIL + +### 차원별 평가 + +- Correctness: Pass +- Completeness: Fail +- Test coverage: Fail +- API contract: Pass +- Code quality: Pass +- Plan deviation: Fail +- Verification trust: Fail + +### 발견된 문제 + +- Required: `apps/cli/internal/operator/scenario_test.go:29`에는 plan의 테스트 커버리지 공백(`plan_cloud_G07_0.log:102`)이 요구한 invalid duration 거부 테스트가 없다. `ParseScenario`에 `timeout: nope` 또는 동등한 invalid duration fixture를 넣고 `invalid timeout` 계열 오류를 검증하는 테스트를 추가해 duration schema 회귀를 잠가야 한다. +- Required: `code_review_cloud_G07_0.log:151`에 기록된 필수 smoke `bin/test`가 exit code 1로 실패했다. `plan_cloud_G07_0.log:238`의 최종 검증은 `bin/test` exit code 0을 요구하므로, 원격 ALT checkout에서 `protoc-gen-go` 사용 가능 여부를 확인하고 필요한 toolchain 준비 후 `bin/test`를 그대로 재실행해 실제 stdout/stderr와 exit code 0 증거를 남겨야 한다. + +### 다음 단계 + +- FAIL follow-up: 현재 plan/review를 archive한 뒤 `REVIEW_OPS` 후속 `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.md`를 작성한다. USER_REVIEW gate는 트리거하지 않는다. diff --git a/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/code_review_cloud_G07_1.log b/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/code_review_cloud_G07_1.log new file mode 100644 index 0000000..a786268 --- /dev/null +++ b/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/code_review_cloud_G07_1.log @@ -0,0 +1,225 @@ + + +# Code Review Reference - REVIEW_OPS + +> **[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-06-01 +task=m-operator-headless-workflow-validation/01_scenario_core, plan=1, tag=REVIEW_OPS + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Task ids: + - `scenario-format`: operator scenario를 YAML 또는 동등한 파일 형식으로 표현한다. +- 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-headless-workflow-validation/01_scenario_core/`로 이동한다. 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-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [REVIEW_OPS-1] Invalid Duration Test Coverage | [x] | +| [REVIEW_OPS-2] Operations Smoke Verification Trust | [x] | + +## 구현 체크리스트 + +- [x] `apps/cli/internal/operator/scenario_test.go`에 invalid duration rejection test를 추가하고 원격 focused CLI test에서 통과를 확인한다. (`TestParseScenarioRejectsInvalidDuration`. `go test -count=1 ./...` exit 0.) +- [x] 원격 ALT checkout root에서 `protoc-gen-go`를 확인/준비한 뒤 `bin/test`를 exit code 0으로 완주하고 stdout/stderr를 기록한다. (host에 `protoc-gen-go v1.36.11` 설치 후 `bin/test` exit 0 완주.) +- [x] 원격 ALT checkout root에서 scenario validate command를 재실행해 출력 계약을 재확인한다. (`scenario=api_connection_smoke status=valid steps=1`, exit 0.) +- [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-headless-workflow-validation/01_scenario_core/`를 `agent-task/archive/YYYY/MM/m-operator-headless-workflow-validation/01_scenario_core/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [x] PASS이고 task group이 `m-operator-headless-workflow-validation`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-operator-headless-workflow-validation/`를 제거하거나, 남은 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로 이동한다. + +## 계획 대비 변경 사항 + +- 계획대로 진행했고 repo source 측 계획 외 변경은 없다. 유일한 source 변경은 `apps/cli/internal/operator/scenario_test.go`에 `TestParseScenarioRejectsInvalidDuration` 추가다(REVIEW_OPS-1). `bin/test`는 source 수정 없이 통과해 REVIEW_OPS-2의 "필요한 최소 수정" 조항은 발동하지 않았다. +- REVIEW_OPS-2 toolchain 준비: 원격 macOS test host에 `protoc-gen-go`가 없어 `go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11`로 설치했다. 버전은 generated 파일 헤더(`packages/contracts/gen/go/**` = `protoc-gen-go v1.36.11`, `protoc v5.29.3`)와 일치시켜 `bin/contracts-check`의 regenerate-and-diff에서 drift가 나지 않게 했다. host `protoc`는 이미 `libprotoc 29.3`(= protoc v5.29.3)으로 헤더와 일치했고, dart plugin wrapper `apps/client/tool/protoc-gen-dart`도 실행 가능 상태였다. 이는 repo source가 아니라 test host toolchain 준비이며, 권한/정책 차단 없이 설치되어 `사용자 리뷰 요청`은 발생하지 않았다. +- 설치 결과 `bin/test`가 contracts check, 5개 Go 모듈 test, Flutter 27 test를 모두 통과하고 exit code 0으로 완주했다. 첫 리뷰에서 지적된 "대체 분해 실행" 대신 `bin/test` 자체의 exit 0 증거를 `검증 결과`에 남겼다. + +## 주요 설계 결정 + +- invalid duration 테스트는 `ParseScenario`에 `timeout: not-a-duration` YAML을 직접 입력해 `operator.Duration.UnmarshalYAML` 거부 경로를 직접 탄다. 입력에 유효한 step(`id`+known `action`)을 둬서 step 검증이 아니라 duration parse가 실패 원인임을 분리했고, error 문자열에 `invalid timeout`이 포함되는지로 단언해 schema 회귀를 잡는다. +- toolchain 준비는 host 환경 변경에 그치고 repo source(`bin/*`, contracts, generated 출력)는 건드리지 않았다. 검증 host 도구 공백은 source 문제가 아니므로 generated 출력 재커밋이나 스크립트 수정이 필요 없다. + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- Invalid duration rejection test가 실제로 `operator.Duration.UnmarshalYAML` 경로를 타는지 확인한다. +- `bin/test`가 대체 분해 실행이 아니라 원격 ALT checkout root에서 그대로 exit code 0을 반환했는지 확인한다. +- follow-up이 `apps/cli/internal/operator/scenario_test.go`와 원격 검증 evidence 외의 source를 불필요하게 수정하지 않았는지 확인한다. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +검증 host: `ssh toki@toki-labs.com` 원격 macOS ALT checkout root(code-server volume 공유 경로). go `go1.26.3 darwin/arm64`. 모든 명령은 원격 checkout root 기준으로 실행했다. PATH에 `$HOME/go/bin`(protoc-gen-go)과 flutter/dart bin을 포함했다. + +toolchain 준비(REVIEW_OPS-2): +```bash +$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11 +# exit 0 +$ protoc-gen-go --version +protoc-gen-go v1.36.11 +``` + +### REVIEW_OPS-1 중간 검증 +```bash +$ (cd apps/cli && go test -count=1 ./...) +? git.toki-labs.com/toki/alt/apps/cli/cmd/alt [no test files] +ok git.toki-labs.com/toki/alt/apps/cli/internal/cli 0.809s +ok git.toki-labs.com/toki/alt/apps/cli/internal/operator 0.427s +# exit code 0 — internal/operator가 cache 없이 실행되어 새 TestParseScenarioRejectsInvalidDuration 포함 통과 +``` + +### REVIEW_OPS-2 중간 검증 +```bash +$ command -v protoc-gen-go +/Users/toki/go/bin/protoc-gen-go +# exit code 0 + +$ bin/test +# exit code 0 (전체 출력은 아래 최종 검증과 동일) +``` + +### 최종 검증 +```bash +$ export PATH="$HOME/SDK/flutter/bin:$HOME/SDK/flutter/bin/cache/dart-sdk/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:$HOME/go/bin:$PATH" + +$ command -v protoc-gen-go +/Users/toki/go/bin/protoc-gen-go +# exit code 0 + +$ (cd apps/cli && go test -count=1 ./...) +? git.toki-labs.com/toki/alt/apps/cli/cmd/alt [no test files] +ok git.toki-labs.com/toki/alt/apps/cli/internal/cli 0.809s +ok git.toki-labs.com/toki/alt/apps/cli/internal/operator 0.427s +# exit code 0 + +$ (cd apps/cli && go run ./cmd/alt operator scenario validate --file testdata/operator/api_connection_smoke.yaml) +scenario=api_connection_smoke status=valid steps=1 +# exit code 0 + +$ bin/test +? git.toki-labs.com/toki/alt/packages/contracts/gen/go/alt/v1 [no test files] +ok git.toki-labs.com/toki/alt/packages/domain/backtest (cached) +ok git.toki-labs.com/toki/alt/packages/domain/market (cached) +? git.toki-labs.com/toki/alt/services/api/cmd/alt-api [no test files] +ok git.toki-labs.com/toki/alt/services/api/internal/config (cached) +ok git.toki-labs.com/toki/alt/services/api/internal/contracts (cached) +ok git.toki-labs.com/toki/alt/services/api/internal/socket (cached) +ok git.toki-labs.com/toki/alt/services/api/internal/workerclient (cached) +? 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 (cached) +ok git.toki-labs.com/toki/alt/services/worker/internal/backtest (cached) +ok git.toki-labs.com/toki/alt/services/worker/internal/config (cached) +ok git.toki-labs.com/toki/alt/services/worker/internal/contracts (cached) +ok git.toki-labs.com/toki/alt/services/worker/internal/jobs (cached) +ok git.toki-labs.com/toki/alt/services/worker/internal/marketdata/datacheck (cached) +ok git.toki-labs.com/toki/alt/services/worker/internal/marketdata/importer (cached) +ok git.toki-labs.com/toki/alt/services/worker/internal/providers/kis (cached) +ok git.toki-labs.com/toki/alt/services/worker/internal/rediskeys (cached) +ok git.toki-labs.com/toki/alt/services/worker/internal/socket (cached) +? 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 (cached) +? git.toki-labs.com/toki/alt/services/worker/internal/storage/postgres/sqlc [no test files] +? git.toki-labs.com/toki/alt/apps/cli/cmd/alt [no test files] +ok git.toki-labs.com/toki/alt/apps/cli/internal/cli (cached) +ok git.toki-labs.com/toki/alt/apps/cli/internal/operator 0.236s +# (flutter test 단계) +00:00 +0: loading .../apps/client/test/app/bootstrap_test.dart +00:00 +1: .../test/widget_test.dart: shows ALT dashboard shell with default disconnected socket state +... (중략) +00:00 +27: All tests passed! +# exit code 0 — contracts check + 5개 Go 모듈 + Flutter 27 test 모두 통과 +``` + +검증 요약: +- 첫 리뷰의 Required 2건이 모두 닫혔다: invalid duration test 추가(통과), `bin/test` 자체 exit 0 완주(대체 분해 실행 아님). +- 차단/생략 없음. toolchain 준비는 권한/정책 차단 없이 host에 protoc-gen-go 설치로 해소됨. +- 남은 위험: 없음. contracts-check가 drift 없이 통과해 generated 출력과 host toolchain 버전이 정합한다. + +--- + +> **[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: active plan/review를 archive하고 `complete.log`를 작성한 뒤 task directory를 `agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/`로 이동한다. diff --git a/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/complete.log b/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/complete.log new file mode 100644 index 0000000..c3f836d --- /dev/null +++ b/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/complete.log @@ -0,0 +1,44 @@ +# Complete - m-operator-headless-workflow-validation/01_scenario_core + +## 완료 일시 + +2026-06-01 + +## 요약 + +OPS scenario core task completed after 2 review loops; final verdict PASS. + +## 루프 이력 + +| Plan | Review | Verdict | 메모 | +|------|--------|---------|------| +| `plan_cloud_G07_0.log` | `code_review_cloud_G07_0.log` | FAIL | invalid duration test coverage and `bin/test` verification trust were missing. | +| `plan_cloud_G07_1.log` | `code_review_cloud_G07_1.log` | PASS | invalid duration test added and remote `bin/test` completed with exit code 0. | + +## 구현/정리 내용 + +- Added the operator scenario YAML schema, strict parser, dry-run validation, and `alt operator scenario validate --file ` CLI path. +- Added parser and CLI tests, including invalid duration rejection coverage. +- Prepared the remote test host `protoc-gen-go` toolchain and verified the operations smoke command directly. + +## 최종 검증 + +- `command -v protoc-gen-go` - PASS; returned `/Users/toki/go/bin/protoc-gen-go`. +- `(cd apps/cli && go test -count=1 ./...)` - PASS; `cmd/alt` had no tests, `internal/cli` and `internal/operator` passed. +- `(cd apps/cli && go run ./cmd/alt operator scenario validate --file testdata/operator/api_connection_smoke.yaml)` - PASS; output `scenario=api_connection_smoke status=valid steps=1`. +- `bin/test` - PASS; contracts check, Go module tests, and Flutter tests completed with exit code 0. + +## Roadmap Completion + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Completed task ids: + - `scenario-format`: PASS; evidence=`agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/plan_cloud_G07_1.log`, `agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/code_review_cloud_G07_1.log`; verification=`bin/test`, `(cd apps/cli && go test -count=1 ./...)`, `(cd apps/cli && go run ./cmd/alt operator scenario validate --file testdata/operator/api_connection_smoke.yaml)` +- Not completed task ids: 없음 + +## 잔여 Nit + +- 없음 + +## 후속 작업 + +- 없음 diff --git a/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/plan_cloud_G07_0.log b/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/plan_cloud_G07_0.log new file mode 100644 index 0000000..99f9553 --- /dev/null +++ b/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/plan_cloud_G07_0.log @@ -0,0 +1,246 @@ + + +# Plan - OPS Scenario Core + +## 이 파일을 읽는 구현 에이전트에게 + +`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 채우고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니다. + +## 배경 + +현재 마일스톤은 Flutter 화면 전에 terminal/YAML 기반 운영 흐름을 먼저 검증해야 한다. `apps/cli/cmd/alt/main.go:8`은 `version`과 banner만 처리하므로 operator scenario 파일을 표현하거나 dry-run 검증할 경로가 없다. 이 작업은 API/worker 실행 전에도 검증 가능한 scenario schema와 CLI command skeleton을 먼저 만든다. + +## 사용자 리뷰 요청 흐름 + +구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. code-review가 해당 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다. + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Task ids: + - `scenario-format`: operator scenario를 YAML 또는 동등한 파일 형식으로 표현한다. +- 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/operations/rules.md` +- `agent-ops/rules/project/domain/api/rules.md` +- `agent-ops/rules/project/domain/worker/rules.md` +- `agent-ops/rules/project/domain/contracts/rules.md` +- `agent-ops/skills/common/router.md` +- `agent-ops/skills/common/plan/SKILL.md` +- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` +- `agent-roadmap/current.md` +- `agent-roadmap/phase/operator-surface/PHASE.md` +- `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- `agent-test/local/rules.md` +- `agent-test/local/operations-smoke.md` +- `agent-test/local/api-smoke.md` +- `agent-test/local/worker-smoke.md` +- `agent-test/local/contracts-smoke.md` +- `.gitignore` +- `go.work` +- `go.work.sum` +- `bin/test` +- `bin/lint` +- `bin/build` +- `bin/dev` +- `apps/cli/go.mod` +- `apps/cli/cmd/alt/main.go` +- `packages/contracts/proto/alt/v1/common.proto` +- `packages/contracts/proto/alt/v1/market.proto` +- `packages/contracts/proto/alt/v1/backtest.proto` +- `packages/contracts/gen/go/go.mod` +- `services/api/go.mod` +- `services/api/internal/config/config.go` +- `services/api/internal/contracts/parser_map.go` +- `services/api/internal/contracts/parser_map_test.go` +- `services/api/internal/socket/handlers.go` +- `services/api/internal/socket/server.go` +- `services/api/internal/socket/server_test.go` +- `services/api/internal/socket/market.go` +- `services/api/internal/socket/market_test.go` +- `services/api/internal/socket/backtest.go` +- `services/api/internal/socket/backtest_test.go` +- `services/api/internal/workerclient/client.go` +- `services/api/internal/workerclient/client_test.go` +- `services/worker/go.mod` +- `services/worker/internal/contracts/parser_map.go` +- `services/worker/internal/contracts/parser_map_test.go` +- `services/worker/internal/socket/handlers.go` +- `services/worker/internal/socket/server.go` +- `services/worker/internal/socket/market.go` +- `services/worker/internal/socket/market_test.go` +- `services/worker/internal/socket/backtest.go` +- `services/worker/internal/socket/backtest_test.go` +- `services/worker/internal/socket/market_mapping.go` +- `services/worker/internal/socket/backtest_mapping.go` +- `services/worker/internal/storage/ports.go` +- `../proto-socket/go/go.mod` +- `../proto-socket/go/ws_client.go` +- `../proto-socket/go/communicator.go` +- `../proto-socket/go/base_client.go` +- `../proto-socket/go/examples/ws_echo/main.go` + +### 테스트 환경 규칙 + +- `test_env=local`. +- `agent-test/local/rules.md`는 존재하고 전문을 읽었다. +- local 검증은 현재 shell에서 직접 실행 금지다. 모든 검증 명령은 `ssh toki@toki-labs.com` 접속 후 원격 ALT checkout root에서 실행해야 한다. +- 적용 profile: `agent-test/local/operations-smoke.md`. `apps/cli/**`는 operations domain이고 profile의 smoke는 `bin/test`, full-cycle은 `bin/build`다. +- 참고 profile: `api-smoke`, `worker-smoke`, `contracts-smoke`를 읽었다. 이번 subtask는 API/worker/contracts source를 수정하지 않으므로 필수 검증으로 적용하지 않는다. +- focused command `(cd apps/cli && go test ./...)`는 `bin/test`가 `apps/cli`에서 실행하는 module test를 분리한 fallback이다. 원격 root 기준 실행한다. +- `<확인 필요>` 값은 없었다. + +### 테스트 커버리지 공백 + +- Scenario YAML schema parse/dry-run: 기존 테스트 없음. 새 `apps/cli/internal/operator/scenario_test.go`가 정상/누락 필드/unknown action/invalid duration을 덮어야 한다. +- CLI command dispatch and exit code: 기존 `apps/cli` 테스트 없음. 새 `apps/cli/internal/cli/cli_test.go`가 validate 성공, parse 실패 exit code, 기존 `version` 호환을 덮어야 한다. +- API/worker runtime request behavior: 기존 API/worker socket tests가 handler registration, typed errors, forwarding을 덮는다. 이 subtask에서는 runtime request 실행을 만들지 않는다. + +### 심볼 참조 + +- renamed/removed symbol: none. + +### 분할 판단 + +- split decision policy를 plan 파일 선택 전에 평가했다. +- shared task group: `agent-task/m-operator-headless-workflow-validation/`. +- sibling subtasks: + - `01_scenario_core`: scenario format, parser, CLI skeleton. no dependencies. + - `02+01_api_market`: API connection and market scenario runner. depends on `01`. + - `03+02_backtest_workflows`: backtest actions, polling, result summary, typed error matrix. depends on `02`. + - `04+02,03_evidence_handoff`: fixture/expected-output handoff and dev entrypoint notes. depends on `02`, `03`. +- 이 plan은 `01_scenario_core`라 predecessor가 없다. + +### 범위 결정 근거 + +- `services/api/**`, `services/worker/**`, `packages/contracts/proto/**`는 수정하지 않는다. 기존 proto-socket handler surface가 current milestone scenario에 필요한 request/response를 이미 제공한다. +- Flutter UI, chart, dashboard layout, push notification은 milestone 범위 제외와 Phase gate에 따라 제외한다. +- live provider/KIS credential 흐름은 제외한다. scenario format은 secret-free fixture로 검증한다. + +### 빌드 등급 + +- build lane: `cloud-G07`, review lane: `cloud-G07`. +- 이유: CLI workflow, YAML parsing, stdout/exit-status contract를 설계하는 terminal-agent 작업이다. + +## 구현 체크리스트 + +- [ ] `apps/cli`에 operator scenario schema, YAML parser, dry-run validation을 추가한다. 검증: 예시 scenario가 parser 또는 dry-run 검증을 통과한다. +- [ ] `alt operator scenario validate --file ` command를 추가하고 exit code와 summary 출력을 고정한다. +- [ ] scenario parser와 CLI validate command 단위 테스트를 추가한다. +- [ ] 원격 ALT checkout root에서 focused CLI test와 operations smoke 검증을 실행한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +### [OPS-1] CLI Command Skeleton And Scenario Parser + +#### 문제 + +`apps/cli/cmd/alt/main.go:8`은 `version`만 특별 처리하고 나머지는 `ALT operator CLI` banner를 출력한다. 현재 `apps/cli/go.mod:1`에는 module/go version만 있어 contracts, proto-socket, YAML parser 의존성이 없다. + +Before: + +```go +// apps/cli/cmd/alt/main.go:8 +func main() { + if len(os.Args) > 1 && os.Args[1] == "version" { + fmt.Println("alt dev") + return + } + fmt.Println("ALT operator CLI") +} +``` + +#### 해결 방법 + +- `apps/cli/cmd/alt/main.go`를 thin entrypoint로 바꾸고 `internal/cli.Run(args, stdout, stderr) int`로 위임한다. +- `apps/cli/internal/cli/cli.go`를 추가한다. +- `apps/cli/internal/operator/scenario.go`를 추가해 YAML scenario schema를 정의한다. +- YAML schema는 최소한 아래 필드를 지원한다. + +```yaml +name: api_connection_smoke +timeout: 5s +steps: + - id: hello + action: hello + expect: + status: ok + capabilities: + - hello + - request-response +``` + +After: + +```go +// apps/cli/cmd/alt/main.go:8 +func main() { + os.Exit(cli.Run(os.Args[1:], os.Stdout, os.Stderr)) +} +``` + +#### 수정 파일 및 체크리스트 + +- [ ] `apps/cli/go.mod`: `git.toki-labs.com/toki/alt/packages/contracts/gen/go`, `git.toki-labs.com/toki/proto-socket/go`, `gopkg.in/yaml.v3` 의존성을 추가한다. direct import만 direct require로 둔다. +- [ ] `apps/cli/go.sum`: `go mod tidy` 결과를 반영한다. +- [ ] `apps/cli/cmd/alt/main.go`: `internal/cli` 위임 entrypoint로 변경한다. +- [ ] `apps/cli/internal/cli/cli.go`: `version`, `operator scenario validate` dispatch와 usage/error 출력 계약을 구현한다. +- [ ] `apps/cli/internal/operator/scenario.go`: scenario struct, strict decode, validation, action enum을 구현한다. +- [ ] `apps/cli/testdata/operator/api_connection_smoke.yaml`: 정상 예시 fixture를 추가한다. +- [ ] `apps/cli/testdata/operator/invalid_request_matrix.yaml`: validation-only 예시 fixture를 추가한다. + +#### 테스트 작성 + +- 작성: `apps/cli/internal/operator/scenario_test.go` + - `TestLoadScenarioValidatesExample`: `testdata/operator/api_connection_smoke.yaml` 로드와 dry-run 검증 성공. + - `TestLoadScenarioRejectsUnknownAction`: unknown `action`이 parse/validation error. + - `TestLoadScenarioRejectsMissingStepID`: missing `id`가 validation error. +- 작성: `apps/cli/internal/cli/cli_test.go` + - `TestRunVersion`: 기존 `version` 출력 유지. + - `TestRunScenarioValidate`: valid fixture exit code 0 and summary output. + - `TestRunScenarioValidateParseError`: invalid fixture exit code 2 and grep 가능한 `status=parse_error`. + +#### 중간 검증 + +원격 ALT checkout root 기준: + +```bash +(cd apps/cli && go test ./...) +(cd apps/cli && go run ./cmd/alt operator scenario validate --file testdata/operator/api_connection_smoke.yaml) +``` + +기대 결과: 두 명령 모두 exit code 0. validate 출력에 `scenario=api_connection_smoke`, `status=valid`, `steps=1` 또는 동등한 machine-readable field가 포함된다. + +## 수정 파일 요약 + +| 파일 | 항목 | +|------|------| +| `apps/cli/go.mod` | OPS-1 | +| `apps/cli/go.sum` | OPS-1 | +| `apps/cli/cmd/alt/main.go` | OPS-1 | +| `apps/cli/internal/cli/cli.go` | OPS-1 | +| `apps/cli/internal/cli/cli_test.go` | OPS-1 | +| `apps/cli/internal/operator/scenario.go` | OPS-1 | +| `apps/cli/internal/operator/scenario_test.go` | OPS-1 | +| `apps/cli/testdata/operator/api_connection_smoke.yaml` | OPS-1 | +| `apps/cli/testdata/operator/invalid_request_matrix.yaml` | OPS-1 | + +## 최종 검증 + +원격 ALT checkout root 기준으로 실행한다. Go test cache output은 이 parser/CLI skeleton 단계에서는 허용한다. fresh execution이 필요하다고 판단되면 구현 에이전트가 `-count=1`로 대체하고 `계획 대비 변경 사항`에 남긴다. + +```bash +(cd apps/cli && go test ./...) +(cd apps/cli && go run ./cmd/alt operator scenario validate --file testdata/operator/api_connection_smoke.yaml) +bin/test +``` + +기대 결과: 모든 명령 exit code 0. `bin/test`가 contracts check, Go module tests, Flutter tests를 통과한다. 원격 host나 checkout 접근 불가 시 pass가 아니라 차단 사유와 미실행 명령을 review stub에 남긴다. + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/plan_cloud_G07_1.log b/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/plan_cloud_G07_1.log new file mode 100644 index 0000000..6b47d91 --- /dev/null +++ b/agent-task/archive/2026/06/m-operator-headless-workflow-validation/01_scenario_core/plan_cloud_G07_1.log @@ -0,0 +1,142 @@ + + +# Plan - REVIEW_OPS Scenario Core Follow-up + +## 이 파일을 읽는 구현 에이전트에게 + +`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 채우고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니다. + +## 배경 + +첫 구현은 `apps/cli` scenario parser와 validate command의 핵심 동작을 추가했고 focused CLI 검증은 원격에서 통과했다. 코드 리뷰는 두 가지 Required 공백을 발견했다. + +- `plan_cloud_G07_0.log`의 테스트 커버리지 분석은 invalid duration 거부 테스트를 요구했지만 `apps/cli/internal/operator/scenario_test.go`에 해당 테스트가 없다. +- 필수 operations smoke인 `bin/test`는 원격 host에서 `protoc-gen-go` 미설치로 exit code 1을 반환했으므로, milestone task를 PASS 처리할 검증 신뢰도가 아직 닫히지 않았다. + +## 사용자 리뷰 요청 흐름 + +구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. code-review가 해당 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다. + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Task ids: + - `scenario-format`: operator scenario를 YAML 또는 동등한 파일 형식으로 표현한다. +- Completion mode: check-on-pass + +## 리뷰 근거 + +- Archived plan: `agent-task/m-operator-headless-workflow-validation/01_scenario_core/plan_cloud_G07_0.log` +- Archived review: `agent-task/m-operator-headless-workflow-validation/01_scenario_core/code_review_cloud_G07_0.log` +- Verdict: FAIL +- Required issues: + - invalid duration test coverage missing. + - `bin/test` required smoke not yet passing because `protoc-gen-go` is absent on the remote test host PATH. + +## 범위 결정 근거 + +- 이 follow-up은 `apps/cli` test coverage와 원격 검증 신뢰도만 닫는다. +- `services/api/**`, `services/worker/**`, `packages/contracts/proto/**`, `apps/client/**`는 수정하지 않는다. +- `protoc-gen-go` 준비가 필요하면 repo source가 아니라 원격 test host toolchain 준비로 처리한다. toolchain 준비가 사용자만 할 수 있는 정책/권한 문제로 막힐 때만 `사용자 리뷰 요청`을 채운다. +- `bin/test`가 toolchain 준비 뒤 source 문제로 실패하면 해당 실패를 숨기지 말고 정확한 stdout/stderr와 함께 필요한 최소 코드 수정으로 처리한다. + +## 빌드 등급 + +- build lane: `cloud-G07`, review lane: `cloud-G07`. +- 이유: `bin/test`, codegen toolchain, stdout/stderr evidence, exit-status contract를 다루는 terminal-agent follow-up이다. + +## 구현 체크리스트 + +- [ ] `apps/cli/internal/operator/scenario_test.go`에 invalid duration rejection test를 추가하고 원격 focused CLI test에서 통과를 확인한다. +- [ ] 원격 ALT checkout root에서 `protoc-gen-go`를 확인/준비한 뒤 `bin/test`를 exit code 0으로 완주하고 stdout/stderr를 기록한다. +- [ ] 원격 ALT checkout root에서 scenario validate command를 재실행해 출력 계약을 재확인한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 변경 및 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +### [REVIEW_OPS-1] Invalid Duration Test Coverage + +#### 문제 + +`plan_cloud_G07_0.log`의 테스트 커버리지 분석은 invalid duration을 덮어야 한다고 기록했지만 현재 테스트는 정상 fixture, unknown action, missing step id만 검증한다. `operator.Duration.UnmarshalYAML`이 실제로 invalid duration을 거부하더라도 테스트가 없어 schema 회귀를 잡지 못한다. + +#### 해결 방법 + +- `apps/cli/internal/operator/scenario_test.go`에 invalid timeout YAML을 `ParseScenario`로 입력하는 테스트를 추가한다. +- 테스트 이름 예: `TestParseScenarioRejectsInvalidDuration`. +- 입력 예: + +```yaml +name: invalid_timeout +timeout: not-a-duration +steps: + - id: hello + action: hello +``` + +- 기대: error가 nil이 아니고 `invalid timeout` 또는 duration parse 실패를 식별할 수 있는 문자열을 포함한다. + +#### 수정 파일 및 체크리스트 + +- [ ] `apps/cli/internal/operator/scenario_test.go`: invalid duration rejection test를 추가한다. + +#### 중간 검증 + +원격 ALT checkout root 기준: + +```bash +(cd apps/cli && go test -count=1 ./...) +``` + +기대 결과: exit code 0. `internal/operator` 테스트가 cache 없이 실행된다. + +### [REVIEW_OPS-2] Operations Smoke Verification Trust + +#### 문제 + +첫 구현의 최종 검증에서 `bin/test`는 `contracts-gen: required command not found: protoc-gen-go`로 실패했다. 대체 모듈 테스트는 유용한 근거지만, operations smoke profile의 필수 command인 `bin/test` 자체가 exit code 0으로 완주했다는 증거를 대체하지 못한다. + +#### 해결 방법 + +- 원격 ALT checkout root에서 테스트용 PATH를 잡고 `command -v protoc-gen-go`를 먼저 실행한다. +- 없으면 generated Go 파일에 기록된 버전과 맞춰 `go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11`로 host toolchain을 준비하고 `$HOME/go/bin`이 PATH에 포함됐는지 확인한다. +- 이후 `bin/test`를 그대로 재실행한다. +- `bin/test`가 drift나 source 문제로 실패하면 실패 stdout/stderr를 기록하고, 이 follow-up 범위에서 필요한 최소 수정만 수행한다. + +#### 수정 파일 및 체크리스트 + +- [ ] 원격 host에서 `command -v protoc-gen-go` 결과를 기록한다. +- [ ] 필요한 경우 `go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11`로 `protoc-gen-go`를 준비한다. +- [ ] `bin/test`를 exit code 0으로 완주하고 실제 stdout/stderr를 review stub에 붙인다. + +#### 중간 검증 + +원격 ALT checkout root 기준: + +```bash +command -v protoc-gen-go +bin/test +``` + +기대 결과: `command -v protoc-gen-go`는 경로를 출력하고 exit code 0. `bin/test`는 contracts check, Go module tests, Flutter tests를 모두 통과하고 exit code 0. + +## 수정 파일 요약 + +| 파일 | 항목 | +|------|------| +| `apps/cli/internal/operator/scenario_test.go` | REVIEW_OPS-1 | +| 원격 test host toolchain evidence | REVIEW_OPS-2 | + +## 최종 검증 + +모든 명령은 원격 ALT checkout root 기준으로 실행한다. 현재 local shell에서 직접 실행하지 않는다. + +```bash +export PATH="$HOME/SDK/flutter/bin:$HOME/SDK/flutter/bin/cache/dart-sdk/bin:/opt/homebrew/bin:/usr/local/bin:/usr/local/go/bin:$HOME/go/bin:$PATH" +command -v protoc-gen-go +(cd apps/cli && go test -count=1 ./...) +(cd apps/cli && go run ./cmd/alt operator scenario validate --file testdata/operator/api_connection_smoke.yaml) +bin/test +``` + +기대 결과: 모든 명령 exit code 0. `bin/test`가 contracts check, Go module tests, Flutter tests를 통과한다. 원격 host toolchain 준비가 권한/정책상 불가능하면 pass가 아니라 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 사용자에게 필요한 결정, 재개 조건을 남긴다. + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/m-operator-headless-workflow-validation/02+01_api_market/CODE_REVIEW-cloud-G07.md b/agent-task/m-operator-headless-workflow-validation/02+01_api_market/CODE_REVIEW-cloud-G07.md new file mode 100644 index 0000000..867fbf9 --- /dev/null +++ b/agent-task/m-operator-headless-workflow-validation/02+01_api_market/CODE_REVIEW-cloud-G07.md @@ -0,0 +1,141 @@ + + +# Code Review Reference - OPS + +> **[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-06-01 +task=m-operator-headless-workflow-validation/02+01_api_market, plan=0, tag=OPS + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Task ids: + - `terminal-run`: terminal에서 scenario를 실행하거나 대표 조회를 수행할 수 있다. + - `market-check`: market data status/import 관련 결과를 화면 없이 확인할 수 있다. +- 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-headless-workflow-validation/02+01_api_market/`로 이동한다. 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-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [OPS-1] CLI Parser Map And API Client | [ ] | +| [OPS-2] Run Command, Market Actions, And Output Contract | [ ] | + +## 구현 체크리스트 + +- [ ] `01_scenario_core`의 `complete.log`가 active 또는 archive에 있는지 확인하고, 없으면 구현을 시작하지 않고 review stub에 blocker를 기록한다. +- [ ] CLI-local ALT parser map과 API URL dialer를 추가한다. +- [ ] `alt operator scenario run --file --api-url ` command를 추가하고 exit code와 JSONL/grep 가능한 summary 출력을 고정한다. +- [ ] `api_connection_smoke`와 `market_data_status_query` scenario action을 구현한다. 검증: CLI 또는 script가 exit code와 요약 출력을 남기고 fixture/local smoke가 market 상태 출력을 확인한다. +- [ ] connection failure, typed ErrorInfo, expectation mismatch가 구분되는 출력 테스트를 추가한다. +- [ ] 원격 ALT checkout root에서 focused CLI test와 operations smoke 검증을 실행한다. +- [ ] 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-headless-workflow-validation/02+01_api_market/`를 `agent-task/archive/YYYY/MM/m-operator-headless-workflow-validation/02+01_api_market/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [ ] PASS이고 task group이 `m-operator-headless-workflow-validation`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-operator-headless-workflow-validation/`를 제거하거나, 남은 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 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- `01_scenario_core` predecessor completion이 확인되었는지 확인한다. +- CLI parser map이 API/worker current parser surface와 필요한 runner messages를 포함하는지 확인한다. +- Terminal output이 transport error, typed ErrorInfo, expectation mismatch를 구분하는지 확인한다. +- `apps/cli`가 API/worker internal packages를 import하지 않는지 확인한다. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +### OPS-1 중간 검증 +```bash +$ (cd apps/cli && go test ./internal/operator -run 'Test(ParserMap|APIClient)') +(output) +``` + +### OPS-2 중간 검증 +```bash +$ (cd apps/cli && go test ./...) +(output) +``` + +### 최종 검증 +```bash +$ (cd apps/cli && go test -count=1 ./...) +(output) +``` + +```bash +$ bin/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. diff --git a/agent-task/m-operator-headless-workflow-validation/02+01_api_market/PLAN-cloud-G07.md b/agent-task/m-operator-headless-workflow-validation/02+01_api_market/PLAN-cloud-G07.md new file mode 100644 index 0000000..5b0a68d --- /dev/null +++ b/agent-task/m-operator-headless-workflow-validation/02+01_api_market/PLAN-cloud-G07.md @@ -0,0 +1,237 @@ + + +# Plan - OPS API And Market Scenario Runner + +## 이 파일을 읽는 구현 에이전트에게 + +`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 채우고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니다. + +## 배경 + +`01_scenario_core`가 scenario file과 validate command를 만든 뒤에는 실제 API control plane에 연결해 hello와 market read를 실행해야 한다. API는 `services/api/internal/socket/handlers.go:55`, `market.go:43`, `market.go:68`에서 이미 Hello, ListInstruments, ListBars를 처리하지만 CLI는 `services/api/internal/contracts`를 import할 수 없는 `internal` 경계 밖에 있다. 따라서 CLI-local parser map과 proto-socket API client를 만들어 기존 API/worker rail 위에 얇게 붙인다. + +## 사용자 리뷰 요청 흐름 + +구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. code-review가 해당 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다. + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Task ids: + - `terminal-run`: terminal에서 scenario를 실행하거나 대표 조회를 수행할 수 있다. + - `market-check`: market data status/import 관련 결과를 화면 없이 확인할 수 있다. +- 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/operations/rules.md` +- `agent-ops/rules/project/domain/api/rules.md` +- `agent-ops/rules/project/domain/worker/rules.md` +- `agent-ops/rules/project/domain/contracts/rules.md` +- `agent-ops/skills/common/plan/SKILL.md` +- `agent-roadmap/current.md` +- `agent-roadmap/phase/operator-surface/PHASE.md` +- `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- `agent-test/local/rules.md` +- `agent-test/local/operations-smoke.md` +- `agent-test/local/api-smoke.md` +- `agent-test/local/worker-smoke.md` +- `agent-test/local/contracts-smoke.md` +- `go.work` +- `apps/cli/go.mod` +- `apps/cli/cmd/alt/main.go` +- `packages/contracts/proto/alt/v1/common.proto` +- `packages/contracts/proto/alt/v1/market.proto` +- `packages/contracts/proto/alt/v1/backtest.proto` +- `services/api/internal/contracts/parser_map.go` +- `services/api/internal/contracts/parser_map_test.go` +- `services/api/internal/socket/handlers.go` +- `services/api/internal/socket/server.go` +- `services/api/internal/socket/server_test.go` +- `services/api/internal/socket/market.go` +- `services/api/internal/socket/market_test.go` +- `services/api/internal/workerclient/client.go` +- `services/api/internal/workerclient/client_test.go` +- `services/worker/internal/socket/market.go` +- `services/worker/internal/socket/market_test.go` +- `services/worker/internal/contracts/parser_map.go` +- `services/worker/internal/contracts/parser_map_test.go` +- `../proto-socket/go/ws_client.go` +- `../proto-socket/go/communicator.go` +- `../proto-socket/go/base_client.go` +- `../proto-socket/go/examples/ws_echo/main.go` + +### 테스트 환경 규칙 + +- `test_env=local`. +- `agent-test/local/rules.md`는 존재하고 전문을 읽었다. 현재 shell 직접 테스트 금지, 원격 ALT checkout root에서 실행한다. +- 적용 profile: `agent-test/local/operations-smoke.md` because `apps/cli/**` 변경. +- 참고 profile: `api-smoke`, `worker-smoke`, `contracts-smoke`. API/worker/contracts runtime source는 수정하지 않지만, CLI runner가 이 surface를 호출하므로 final `bin/test`가 회귀 baseline이다. +- focused command `(cd apps/cli && go test ./...)`는 `bin/test`에서 앱 모듈 test만 분리한 fallback이다. + +### 테스트 커버리지 공백 + +- CLI-local parser map: 기존 coverage 없음. 새 test가 Hello/ListInstruments/ListBars request/response parser를 검증해야 한다. +- API URL parsing and proto-socket dial: workerclient는 유사 구현을 `services/api/internal/workerclient/client.go:63`에서 갖지만 CLI에서는 재사용할 수 없다. 새 unit test와 in-process fake server test가 필요하다. +- Market scenario output: 기존 API/worker tests는 typed errors와 forwarding을 덮지만 CLI stdout/status contract는 없음. 새 runner/output tests가 필요하다. + +### 심볼 참조 + +- renamed/removed symbol: none. + +### 분할 판단 + +- split decision policy를 plan 파일 선택 전에 평가했다. +- shared task group: `agent-task/m-operator-headless-workflow-validation/`. +- 이 plan의 directory `02+01_api_market`는 predecessor index `01`을 요구한다. +- predecessor `01`: active `agent-task/m-operator-headless-workflow-validation/01_scenario_core/complete.log` 없음, archive complete.log 없음. runtime은 `01_scenario_core` PASS 후 이 plan을 구현해야 한다. + +### 범위 결정 근거 + +- API/worker socket handlers는 기존 request/response와 typed error를 제공하므로 수정하지 않는다. +- 이 plan은 hello, list_instruments, list_bars, market-side unavailable/disconnected output까지만 다룬다. backtest start/list/detail/result/polling은 `03+02_backtest_workflows`로 분리한다. +- Flutter client와 provider live import는 제외한다. + +### 빌드 등급 + +- build lane: `cloud-G07`, review lane: `cloud-G07`. +- 이유: proto-socket connection, terminal command, stdout/error/exit code contract가 있는 CLI workflow 작업이다. + +## 구현 체크리스트 + +- [ ] `01_scenario_core`의 `complete.log`가 active 또는 archive에 있는지 확인하고, 없으면 구현을 시작하지 않고 review stub에 blocker를 기록한다. +- [ ] CLI-local ALT parser map과 API URL dialer를 추가한다. +- [ ] `alt operator scenario run --file --api-url ` command를 추가하고 exit code와 JSONL/grep 가능한 summary 출력을 고정한다. +- [ ] `api_connection_smoke`와 `market_data_status_query` scenario action을 구현한다. 검증: CLI 또는 script가 exit code와 요약 출력을 남기고 fixture/local smoke가 market 상태 출력을 확인한다. +- [ ] connection failure, typed ErrorInfo, expectation mismatch가 구분되는 출력 테스트를 추가한다. +- [ ] 원격 ALT checkout root에서 focused CLI test와 operations smoke 검증을 실행한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 의존 관계 및 구현 순서 + +- 이 directory 이름의 runtime dependency는 `02+01_api_market`이다. +- 구현 시작 전 `agent-task/m-operator-headless-workflow-validation/01_scenario_core/complete.log` 또는 `agent-task/archive/*/*/m-operator-headless-workflow-validation/01_scenario_core/complete.log`가 있어야 한다. +- 추가 hidden dependency를 만들지 않는다. + +### [OPS-1] CLI Parser Map And API Client + +#### 문제 + +`services/api/internal/contracts/parser_map.go:14`와 `services/worker/internal/contracts/parser_map.go:10`은 ALT message parser list를 갖고 있지만 Go `internal` 경계 때문에 `apps/cli`가 import할 수 없다. proto-socket client dial API는 `../proto-socket/go/ws_client.go:58`과 `:62`에 있고, typed request는 `../proto-socket/go/communicator.go:357`에 있다. + +#### 해결 방법 + +- `apps/cli/internal/operator/parser_map.go`에 CLI-local `ParserMap()`을 추가한다. API/worker parser map의 current ALT message set을 복제하되, CLI package 안에서 관리한다. +- `apps/cli/internal/operator/client.go`에 `APIClient`를 추가한다. +- URL parsing은 `services/api/internal/workerclient/client.go:71`의 방식과 같게 `net/url`, `net.SplitHostPort`, default port를 사용한다. + +#### 수정 파일 및 체크리스트 + +- [ ] `apps/cli/internal/operator/parser_map.go`: Hello, market, backtest message parser factories 추가. +- [ ] `apps/cli/internal/operator/client.go`: ws/wss URL parsing, heartbeat config, `Close`, `Hello`, `ListInstruments`, `ListBars` methods 추가. +- [ ] `apps/cli/internal/operator/client_test.go`: fake proto-socket server로 hello/list market round-trip 검증. +- [ ] `apps/cli/go.mod`, `apps/cli/go.sum`: direct imports에 맞게 `nhooyr.io/websocket`, `google.golang.org/protobuf` 필요 여부 반영. + +#### 테스트 작성 + +- 작성: `apps/cli/internal/operator/parser_map_test.go` + - `TestParserMapIncludesRunnerMessages`: Hello/ListInstruments/ListBars와 backtest messages parser coverage. +- 작성: `apps/cli/internal/operator/client_test.go` + - `TestAPIClientHello`: fake server hello round-trip. + - `TestAPIClientListInstruments`: market request fields round-trip. + - `TestAPIClientConnectUnavailable`: refused port maps to transport status. + +#### 중간 검증 + +원격 ALT checkout root 기준: + +```bash +(cd apps/cli && go test ./internal/operator -run 'Test(ParserMap|APIClient)') +``` + +기대 결과: exit code 0. fake server tests pass without external services. + +### [OPS-2] Run Command, Market Actions, And Output Contract + +#### 문제 + +`apps/cli/cmd/alt/main.go:13` currently prints a static banner for non-version commands. There is no terminal entrypoint that turns a scenario into API requests or outputs machine-readable status keys. + +#### 해결 방법 + +- `apps/cli/internal/operator/runner.go`를 추가해 scenario steps를 실행한다. +- action support: + - `hello` + - `list_instruments` + - `list_bars` +- `apps/cli/internal/operator/output.go`는 JSONL을 기본으로 출력한다. +- Exit codes: + - `0`: all steps matched expectations. + - `1`: typed `ErrorInfo` or expectation mismatch when not expected. + - `2`: flags/scenario parse/validation error. + - `3`: transport connect/disconnect/timeout before typed response. +- `invalid_request_matrix`처럼 expected error를 선언한 scenario는 expected error가 오면 exit code 0이다. + +#### 수정 파일 및 체크리스트 + +- [ ] `apps/cli/internal/cli/cli.go`: `operator scenario run` flags `--file`, `--api-url`, `--timeout`, `--output` 추가. +- [ ] `apps/cli/internal/operator/scenario.go`: market action fields and expectations 추가. +- [ ] `apps/cli/internal/operator/runner.go`: hello/list_instruments/list_bars 실행과 expectation evaluation 구현. +- [ ] `apps/cli/internal/operator/output.go`: JSONL event and summary writer 구현. +- [ ] `apps/cli/testdata/operator/market_data_status_query.yaml`: market fixture 추가. +- [ ] `apps/cli/internal/cli/cli_test.go`: run command output/exit code tests 추가. +- [ ] `apps/cli/internal/operator/runner_market_test.go`: market response success, empty, typed error, transport error tests 추가. + +#### 테스트 작성 + +- 작성/확장: + - `TestRunScenarioAPISmoke`: fake API server hello response produces `status=ok`. + - `TestRunScenarioMarketDataStatus`: list_instruments/list_bars output includes `scenario`, `step`, `action`, `status`, `count`. + - `TestRunScenarioTransportFailure`: unavailable socket exits 3 and prints `status=transport_error`. + - `TestRunScenarioExpectedTypedError`: expected `invalid_request` exits 0 and prints `error.code`. + +#### 중간 검증 + +원격 ALT checkout root 기준: + +```bash +(cd apps/cli && go test ./...) +``` + +기대 결과: exit code 0. CLI tests prove run command, output contract, and market scenarios without live worker credentials. + +## 수정 파일 요약 + +| 파일 | 항목 | +|------|------| +| `apps/cli/go.mod` | OPS-1 | +| `apps/cli/go.sum` | OPS-1 | +| `apps/cli/internal/operator/parser_map.go` | OPS-1 | +| `apps/cli/internal/operator/parser_map_test.go` | OPS-1 | +| `apps/cli/internal/operator/client.go` | OPS-1 | +| `apps/cli/internal/operator/client_test.go` | OPS-1 | +| `apps/cli/internal/cli/cli.go` | OPS-2 | +| `apps/cli/internal/cli/cli_test.go` | OPS-2 | +| `apps/cli/internal/operator/scenario.go` | OPS-2 | +| `apps/cli/internal/operator/runner.go` | OPS-2 | +| `apps/cli/internal/operator/output.go` | OPS-2 | +| `apps/cli/internal/operator/runner_market_test.go` | OPS-2 | +| `apps/cli/testdata/operator/market_data_status_query.yaml` | OPS-2 | + +## 최종 검증 + +원격 ALT checkout root 기준으로 실행한다. CLI output/exit code tests should be fresh; use `-count=1`. + +```bash +(cd apps/cli && go test -count=1 ./...) +bin/test +``` + +기대 결과: 모든 명령 exit code 0. 원격 host나 checkout 접근 불가 시 pass가 아니라 차단 사유와 미실행 명령을 review stub에 남긴다. + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/m-operator-headless-workflow-validation/03+02_backtest_workflows/CODE_REVIEW-cloud-G07.md b/agent-task/m-operator-headless-workflow-validation/03+02_backtest_workflows/CODE_REVIEW-cloud-G07.md new file mode 100644 index 0000000..43058d9 --- /dev/null +++ b/agent-task/m-operator-headless-workflow-validation/03+02_backtest_workflows/CODE_REVIEW-cloud-G07.md @@ -0,0 +1,140 @@ + + +# Code Review Reference - OPS + +> **[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-06-01 +task=m-operator-headless-workflow-validation/03+02_backtest_workflows, plan=0, tag=OPS + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Task ids: + - `backtest-check`: backtest list/detail/result/start 관련 결과를 화면 없이 확인할 수 있다. + - `error-output`: unavailable/error/disconnected 상태가 화면 없이도 판별 가능한 출력으로 남는다. +- Completion mode: check-on-pass + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[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-headless-workflow-validation/03+02_backtest_workflows/`로 이동한다. 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-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [OPS-1] Backtest Request Actions And Polling | [ ] | +| [OPS-2] Invalid Request Matrix And Error Output | [ ] | + +## 구현 체크리스트 + +- [ ] `02+01_api_market`의 `complete.log`가 active 또는 archive에 있는지 확인하고, 없으면 구현을 시작하지 않고 review stub에 blocker를 기록한다. +- [ ] Backtest start/list/detail/result/compare/poll actions를 scenario schema와 runner에 추가한다. +- [ ] `backtest_run_request`, `backtest_run_polling`, `backtest_result_summary`, `invalid_request_matrix` fixture를 추가한다. 검증: fixture 또는 local smoke가 성공/실패 결과를 확인한다. +- [ ] typed error, unavailable, disconnected, expectation mismatch 출력이 grep 가능한 field로 남는지 테스트한다. 검증: typed error 또는 상태 코드 출력 테스트가 통과한다. +- [ ] 원격 ALT checkout root에서 focused CLI test와 operations smoke 검증을 실행한다. +- [ ] 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-headless-workflow-validation/03+02_backtest_workflows/`를 `agent-task/archive/YYYY/MM/m-operator-headless-workflow-validation/03+02_backtest_workflows/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [ ] PASS이고 task group이 `m-operator-headless-workflow-validation`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-operator-headless-workflow-validation/`를 제거하거나, 남은 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 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- `02+01_api_market` predecessor completion이 확인되었는지 확인한다. +- Backtest actions가 existing contract fields를 사용하고 schema/contract source를 임의 변경하지 않았는지 확인한다. +- Polling timeout과 expected typed errors가 deterministic tests로 검증되었는지 확인한다. +- Error output이 typed error와 transport failure를 구분하는지 확인한다. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +### OPS-1 중간 검증 +```bash +$ (cd apps/cli && go test -count=1 ./internal/operator -run 'TestRunBacktest') +(output) +``` + +### OPS-2 중간 검증 +```bash +$ (cd apps/cli && go test -count=1 ./internal/operator -run 'Test(ExpectedTypedError|UnexpectedTypedError|TransportFailure|ExpectationMismatch)') +(output) +``` + +### 최종 검증 +```bash +$ (cd apps/cli && go test -count=1 ./...) +(output) +``` + +```bash +$ bin/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. diff --git a/agent-task/m-operator-headless-workflow-validation/03+02_backtest_workflows/PLAN-cloud-G07.md b/agent-task/m-operator-headless-workflow-validation/03+02_backtest_workflows/PLAN-cloud-G07.md new file mode 100644 index 0000000..b04af0e --- /dev/null +++ b/agent-task/m-operator-headless-workflow-validation/03+02_backtest_workflows/PLAN-cloud-G07.md @@ -0,0 +1,229 @@ + + +# Plan - OPS Backtest Workflow Scenarios + +## 이 파일을 읽는 구현 에이전트에게 + +`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 채우고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니다. + +## 배경 + +Market scenario runner가 준비되면 backtest list/detail/result/start/polling과 invalid request matrix를 같은 headless contract로 검증해야 한다. API는 `services/api/internal/socket/backtest.go:74`부터 start/list/detail/result/compare handlers를 갖고, worker는 `services/worker/internal/socket/backtest.go:112`부터 worker-owned execution/read handlers를 갖고 있다. 이 작업은 서비스 내부를 바꾸지 않고 CLI runner에 backtest actions, polling, summary output, typed error matrix를 추가한다. + +## 사용자 리뷰 요청 흐름 + +구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. code-review가 해당 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다. + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Task ids: + - `backtest-check`: backtest list/detail/result/start 관련 결과를 화면 없이 확인할 수 있다. + - `error-output`: unavailable/error/disconnected 상태가 화면 없이도 판별 가능한 출력으로 남는다. +- Completion mode: check-on-pass + +## 분석 결과 + +### 읽은 파일 + +- `agent-ops/rules/project/rules.md` +- `agent-ops/rules/project/domain/operations/rules.md` +- `agent-ops/rules/project/domain/api/rules.md` +- `agent-ops/rules/project/domain/worker/rules.md` +- `agent-ops/rules/project/domain/contracts/rules.md` +- `agent-ops/skills/common/plan/SKILL.md` +- `agent-roadmap/current.md` +- `agent-roadmap/phase/operator-surface/PHASE.md` +- `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- `agent-test/local/rules.md` +- `agent-test/local/operations-smoke.md` +- `agent-test/local/api-smoke.md` +- `agent-test/local/worker-smoke.md` +- `agent-test/local/contracts-smoke.md` +- `apps/cli/go.mod` +- `apps/cli/cmd/alt/main.go` +- `packages/contracts/proto/alt/v1/common.proto` +- `packages/contracts/proto/alt/v1/market.proto` +- `packages/contracts/proto/alt/v1/backtest.proto` +- `services/api/internal/socket/backtest.go` +- `services/api/internal/socket/backtest_test.go` +- `services/api/internal/socket/market.go` +- `services/api/internal/socket/market_test.go` +- `services/api/internal/contracts/parser_map.go` +- `services/api/internal/contracts/parser_map_test.go` +- `services/api/internal/workerclient/client.go` +- `services/api/internal/workerclient/client_test.go` +- `services/worker/internal/socket/backtest.go` +- `services/worker/internal/socket/backtest_test.go` +- `services/worker/internal/socket/backtest_mapping.go` +- `services/worker/internal/socket/backtest_mapping_test.go` +- `services/worker/internal/storage/ports.go` +- `services/worker/internal/contracts/parser_map.go` +- `services/worker/internal/contracts/parser_map_test.go` +- `../proto-socket/go/ws_client.go` +- `../proto-socket/go/communicator.go` + +### 테스트 환경 규칙 + +- `test_env=local`. +- `agent-test/local/rules.md`는 존재하고 전문을 읽었다. 현재 shell 직접 테스트 금지, 원격 ALT checkout root에서 실행한다. +- 적용 profile: `agent-test/local/operations-smoke.md` because `apps/cli/**` 변경. +- 참고 profile: `api-smoke`, `worker-smoke`, `contracts-smoke`. Backtest runner가 API/worker contract surface를 호출하므로 final `bin/test`로 baseline 회귀를 확인한다. +- focused command `(cd apps/cli && go test ./...)`는 `bin/test`의 CLI module subset fallback이다. + +### 테스트 커버리지 공백 + +- Existing API/worker tests cover backtest handler registration, validation, forwarding, unavailable/not_found/internal typed errors. +- CLI runner does not yet build backtest protobuf requests, poll run status, summarize result metrics, or print invalid request matrix output. New tests must cover these. +- Real infra-backed completed backtest result is not required in this subtask; fake proto-socket server tests are the deterministic verification path. + +### 심볼 참조 + +- renamed/removed symbol: none. + +### 분할 판단 + +- split decision policy를 plan 파일 선택 전에 평가했다. +- shared task group: `agent-task/m-operator-headless-workflow-validation/`. +- 이 plan의 directory `03+02_backtest_workflows`는 predecessor index `02`를 요구한다. +- predecessor `02`: active `agent-task/m-operator-headless-workflow-validation/02+01_api_market/complete.log` 없음, archive complete.log 없음. runtime은 `02+01_api_market` PASS 후 이 plan을 구현해야 한다. + +### 범위 결정 근거 + +- `services/api/**`, `services/worker/**`, `packages/contracts/**`는 수정하지 않는다. Existing handlers already cover start/list/detail/result/compare. +- Strategy execution correctness, storage migration, provider import, real PostgreSQL/Redis smoke는 worker milestone 범위로 보며 이 CLI runner subtask에서는 fake socket 검증으로 제한한다. +- Flutter UI handoff 문서는 `04+02,03_evidence_handoff`로 분리한다. + +### 빌드 등급 + +- build lane: `cloud-G07`, review lane: `cloud-G07`. +- 이유: terminal scenario execution, polling timeout, stdout/exit-status/error contract가 있는 CLI workflow 작업이다. + +## 구현 체크리스트 + +- [ ] `02+01_api_market`의 `complete.log`가 active 또는 archive에 있는지 확인하고, 없으면 구현을 시작하지 않고 review stub에 blocker를 기록한다. +- [ ] Backtest start/list/detail/result/compare/poll actions를 scenario schema와 runner에 추가한다. +- [ ] `backtest_run_request`, `backtest_run_polling`, `backtest_result_summary`, `invalid_request_matrix` fixture를 추가한다. 검증: fixture 또는 local smoke가 성공/실패 결과를 확인한다. +- [ ] typed error, unavailable, disconnected, expectation mismatch 출력이 grep 가능한 field로 남는지 테스트한다. 검증: typed error 또는 상태 코드 출력 테스트가 통과한다. +- [ ] 원격 ALT checkout root에서 focused CLI test와 operations smoke 검증을 실행한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 의존 관계 및 구현 순서 + +- 이 directory 이름의 runtime dependency는 `03+02_backtest_workflows`이다. +- 구현 시작 전 `agent-task/m-operator-headless-workflow-validation/02+01_api_market/complete.log` 또는 `agent-task/archive/*/*/m-operator-headless-workflow-validation/02+01_api_market/complete.log`가 있어야 한다. +- 추가 hidden dependency를 만들지 않는다. + +### [OPS-1] Backtest Request Actions And Polling + +#### 문제 + +`packages/contracts/proto/alt/v1/backtest.proto:35` defines `StartBacktestRequest`; `:98`, `:107`, `:89`, `:117` define list/detail/result/compare requests. Existing API handlers expose those messages, but the CLI runner from prior subtasks only handles hello/market actions. + +#### 해결 방법 + +- Extend scenario step actions: + - `start_backtest` + - `list_backtest_runs` + - `get_backtest_run_detail` + - `get_backtest_result` + - `compare_backtest_runs` + - `poll_backtest_run` +- Support `save_as` and interpolation from prior step response for run id, using explicit `{{steps..run.id}}` paths only. +- Poll action repeats detail/list request until status is in expected terminal set or timeout expires. + +#### 수정 파일 및 체크리스트 + +- [ ] `apps/cli/internal/operator/scenario.go`: backtest fields, run spec, polling config, expected status/result fields 추가. +- [ ] `apps/cli/internal/operator/runner.go`: backtest action dispatch와 saved value lookup 추가. +- [ ] `apps/cli/internal/operator/output.go`: backtest run/result summary fields 추가. +- [ ] `apps/cli/internal/operator/runner_backtest_test.go`: fake server start/list/detail/result/compare/poll tests 추가. +- [ ] `apps/cli/testdata/operator/backtest_run_request.yaml`: start fixture 추가. +- [ ] `apps/cli/testdata/operator/backtest_run_polling.yaml`: polling fixture 추가. +- [ ] `apps/cli/testdata/operator/backtest_result_summary.yaml`: result fixture 추가. + +#### 테스트 작성 + +- 작성: `apps/cli/internal/operator/runner_backtest_test.go` + - `TestRunBacktestRequestOutputsRunID`: start response run id/status output. + - `TestRunBacktestPollingStopsOnSucceeded`: polling uses saved run id and terminal status. + - `TestRunBacktestResultSummaryOutputsMetrics`: summary fields output. + - `TestRunBacktestMissingRunIDTypedError`: missing run id expected error path. + +#### 중간 검증 + +원격 ALT checkout root 기준: + +```bash +(cd apps/cli && go test -count=1 ./internal/operator -run 'TestRunBacktest') +``` + +기대 결과: exit code 0. Fake API server tests prove request build, polling, and summary output. + +### [OPS-2] Invalid Request Matrix And Error Output + +#### 문제 + +Milestone requires unavailable/error/disconnected to remain machine-readable without a UI. Existing API/worker handlers return typed `ErrorInfo`, but CLI must normalize typed errors and transport failures into stable terminal output and exit codes. + +#### 해결 방법 + +- Add scenario expectation fields: + - `expect.error_code` + - `expect.transport_status` + - `expect.exit_code` +- Add invalid matrix fixture with missing spec, missing run id, empty compare id, invalid market/timeframe/date range. +- Output JSONL must include at least `scenario`, `step`, `action`, `status`, `error.code`, `error.message`, `exit_code` in summary. + +#### 수정 파일 및 체크리스트 + +- [ ] `apps/cli/internal/operator/scenario.go`: expected error/status validation 추가. +- [ ] `apps/cli/internal/operator/runner.go`: typed error expectation and matrix execution behavior 추가. +- [ ] `apps/cli/internal/operator/output.go`: transport/typed/expectation status fields 고정. +- [ ] `apps/cli/testdata/operator/invalid_request_matrix.yaml`: full invalid matrix로 확장. +- [ ] `apps/cli/internal/operator/runner_error_test.go`: typed error expected/unexpected, transport failure, disconnected test 추가. + +#### 테스트 작성 + +- 작성: `apps/cli/internal/operator/runner_error_test.go` + - `TestExpectedTypedErrorExitsZero` + - `TestUnexpectedTypedErrorExitsOne` + - `TestTransportFailureExitsThree` + - `TestExpectationMismatchExitsOne` + +#### 중간 검증 + +원격 ALT checkout root 기준: + +```bash +(cd apps/cli && go test -count=1 ./internal/operator -run 'Test(ExpectedTypedError|UnexpectedTypedError|TransportFailure|ExpectationMismatch)') +``` + +기대 결과: exit code 0. + +## 수정 파일 요약 + +| 파일 | 항목 | +|------|------| +| `apps/cli/internal/operator/scenario.go` | OPS-1, OPS-2 | +| `apps/cli/internal/operator/runner.go` | OPS-1, OPS-2 | +| `apps/cli/internal/operator/output.go` | OPS-1, OPS-2 | +| `apps/cli/internal/operator/runner_backtest_test.go` | OPS-1 | +| `apps/cli/internal/operator/runner_error_test.go` | OPS-2 | +| `apps/cli/testdata/operator/backtest_run_request.yaml` | OPS-1 | +| `apps/cli/testdata/operator/backtest_run_polling.yaml` | OPS-1 | +| `apps/cli/testdata/operator/backtest_result_summary.yaml` | OPS-1 | +| `apps/cli/testdata/operator/invalid_request_matrix.yaml` | OPS-2 | + +## 최종 검증 + +원격 ALT checkout root 기준으로 실행한다. CLI output/exit code tests should be fresh; use `-count=1`. + +```bash +(cd apps/cli && go test -count=1 ./...) +bin/test +``` + +기대 결과: 모든 명령 exit code 0. 원격 host나 checkout 접근 불가 시 pass가 아니라 차단 사유와 미실행 명령을 review stub에 남긴다. + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/m-operator-headless-workflow-validation/04+02,03_evidence_handoff/CODE_REVIEW-cloud-G07.md b/agent-task/m-operator-headless-workflow-validation/04+02,03_evidence_handoff/CODE_REVIEW-cloud-G07.md new file mode 100644 index 0000000..0420853 --- /dev/null +++ b/agent-task/m-operator-headless-workflow-validation/04+02,03_evidence_handoff/CODE_REVIEW-cloud-G07.md @@ -0,0 +1,154 @@ + + +# Code Review Reference - OPS + +> **[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-06-01 +task=m-operator-headless-workflow-validation/04+02,03_evidence_handoff, plan=0, tag=OPS + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Task ids: + - `handoff`: Flutter 화면 MVP로 넘길 수 있는 검증 evidence와 남은 wireframe 의존성을 정리한다. +- 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-headless-workflow-validation/04+02,03_evidence_handoff/`로 이동한다. 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-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [OPS-1] Fixture Evidence Matrix | [ ] | +| [OPS-2] Developer Entrypoint Notes | [ ] | + +## 구현 체크리스트 + +- [ ] `02+01_api_market`와 `03+02_backtest_workflows`의 `complete.log`가 active 또는 archive에 있는지 확인하고, 없으면 구현을 시작하지 않고 review stub에 blocker를 기록한다. +- [ ] `apps/cli/testdata/operator/` 아래에 scenario별 command/input/expected output/exit code/protobuf field handoff artifact를 추가한다. +- [ ] `bin/dev`에 headless scenario validate/run entrypoint를 추가하되 secret, remote path, personal endpoint를 쓰지 않는다. +- [ ] Handoff artifact completeness를 검증하는 focused test 또는 deterministic shell check를 추가한다. +- [ ] 원격 ALT checkout root에서 focused CLI test와 operations smoke 검증을 실행한다. +- [ ] 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-headless-workflow-validation/04+02,03_evidence_handoff/`를 `agent-task/archive/YYYY/MM/m-operator-headless-workflow-validation/04+02,03_evidence_handoff/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [ ] PASS이고 task group이 `m-operator-headless-workflow-validation`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-operator-headless-workflow-validation/`를 제거하거나, 남은 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 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- `02+01_api_market`와 `03+02_backtest_workflows` predecessor completion이 확인되었는지 확인한다. +- Handoff matrix가 milestone handoff table의 six scenarios를 모두 포함하는지 확인한다. +- Expected output fixtures가 secret-free이고 machine-readable인지 확인한다. +- `bin/dev` examples가 local defaults and fixture paths만 포함하는지 확인한다. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +### OPS-1 중간 검증 +```bash +$ (cd apps/cli && go test -count=1 ./internal/operator -run 'TestHeadlessValidationHandoff|TestExpectedOutputFixtures') +(output) +``` + +### OPS-2 중간 검증 +```bash +$ bin/dev +(output) +``` + +```bash +$ (cd apps/cli && go test -count=1 ./...) +(output) +``` + +### 최종 검증 +```bash +$ bin/dev +(output) +``` + +```bash +$ (cd apps/cli && go test -count=1 ./...) +(output) +``` + +```bash +$ bin/test +(output) +``` + +```bash +$ bin/build +(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. diff --git a/agent-task/m-operator-headless-workflow-validation/04+02,03_evidence_handoff/PLAN-cloud-G07.md b/agent-task/m-operator-headless-workflow-validation/04+02,03_evidence_handoff/PLAN-cloud-G07.md new file mode 100644 index 0000000..09b4f57 --- /dev/null +++ b/agent-task/m-operator-headless-workflow-validation/04+02,03_evidence_handoff/PLAN-cloud-G07.md @@ -0,0 +1,221 @@ + + +# Plan - OPS Evidence Handoff + +## 이 파일을 읽는 구현 에이전트에게 + +`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 근거를 채우고 멈춘다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니다. + +## 배경 + +마일스톤의 마지막 task는 Flutter 화면 MVP로 넘길 검증 evidence와 남은 wireframe 의존성을 정리하는 것이다. `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md:69`의 handoff table은 scenario별 command/input/output/exit code/protobuf field evidence를 요구한다. 이 작업은 구현된 CLI fixtures와 expected output을 정리해 후속 Flutter Operator Console MVP가 화면 구현 전에 검증할 수 있는 산출물을 남긴다. + +## 사용자 리뷰 요청 흐름 + +구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. code-review가 해당 요청을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다. + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- Task ids: + - `handoff`: Flutter 화면 MVP로 넘길 수 있는 검증 evidence와 남은 wireframe 의존성을 정리한다. +- Completion mode: check-on-pass + +## 분석 결과 + +### 읽은 파일 + +- `agent-ops/rules/project/rules.md` +- `agent-ops/rules/project/domain/operations/rules.md` +- `agent-ops/rules/project/domain/api/rules.md` +- `agent-ops/rules/project/domain/worker/rules.md` +- `agent-ops/rules/project/domain/contracts/rules.md` +- `agent-ops/skills/common/plan/SKILL.md` +- `agent-roadmap/current.md` +- `agent-roadmap/phase/operator-surface/PHASE.md` +- `agent-roadmap/phase/operator-surface/milestones/operator-headless-workflow-validation.md` +- `agent-test/local/rules.md` +- `agent-test/local/operations-smoke.md` +- `agent-test/local/api-smoke.md` +- `agent-test/local/worker-smoke.md` +- `agent-test/local/contracts-smoke.md` +- `bin/dev` +- `bin/test` +- `bin/build` +- `apps/cli/go.mod` +- `apps/cli/cmd/alt/main.go` +- `packages/contracts/proto/alt/v1/common.proto` +- `packages/contracts/proto/alt/v1/market.proto` +- `packages/contracts/proto/alt/v1/backtest.proto` +- `services/api/internal/socket/handlers.go` +- `services/api/internal/socket/market.go` +- `services/api/internal/socket/backtest.go` +- `services/worker/internal/socket/market.go` +- `services/worker/internal/socket/backtest.go` + +### 테스트 환경 규칙 + +- `test_env=local`. +- `agent-test/local/rules.md`는 존재하고 전문을 읽었다. 현재 shell 직접 테스트 금지, 원격 ALT checkout root에서 실행한다. +- 적용 profile: `agent-test/local/operations-smoke.md` because `apps/cli/testdata/**` and `bin/dev` are operations surface. +- focused fixture command는 `bin/test`의 CLI module subset fallback이다. + +### 테스트 커버리지 공백 + +- Handoff artifact completeness has no existing test. Add a test or deterministic check that each scenario listed in milestone lines 73-80 has fixture, expected output, and command record. +- `bin/dev` currently lists only generic CLI command at `bin/dev:17`; it does not show scenario validate/run entrypoints. + +### 심볼 참조 + +- renamed/removed symbol: none. + +### 분할 판단 + +- split decision policy를 plan 파일 선택 전에 평가했다. +- shared task group: `agent-task/m-operator-headless-workflow-validation/`. +- 이 plan의 directory `04+02,03_evidence_handoff`는 predecessor indices `02`, `03`을 요구한다. +- predecessor `02`: active `agent-task/m-operator-headless-workflow-validation/02+01_api_market/complete.log` 없음, archive complete.log 없음. +- predecessor `03`: active `agent-task/m-operator-headless-workflow-validation/03+02_backtest_workflows/complete.log` 없음, archive complete.log 없음. +- runtime은 `02+01_api_market`와 `03+02_backtest_workflows` PASS 후 이 plan을 구현해야 한다. + +### 범위 결정 근거 + +- Roadmap/milestone 문서는 이 plan에서 직접 수정하지 않는다. PASS 완료 이벤트 후 roadmap 업데이트는 runtime 책임이다. +- tracked `docs/`는 사람용 최신 가이드만 두는 규칙이 있으므로 handoff 산출물은 CLI fixture 영역인 `apps/cli/testdata/operator/` 아래에 둔다. +- Flutter 화면 wireframe 자체는 제외하고, 남은 wireframe 의존성만 handoff에 기록한다. + +### 빌드 등급 + +- build lane: `cloud-G07`, review lane: `cloud-G07`. +- 이유: terminal smoke evidence, expected output, exit-status contract를 후속 UI 구현 gate로 넘기는 작업이다. + +## 구현 체크리스트 + +- [ ] `02+01_api_market`와 `03+02_backtest_workflows`의 `complete.log`가 active 또는 archive에 있는지 확인하고, 없으면 구현을 시작하지 않고 review stub에 blocker를 기록한다. +- [ ] `apps/cli/testdata/operator/` 아래에 scenario별 command/input/expected output/exit code/protobuf field handoff artifact를 추가한다. +- [ ] `bin/dev`에 headless scenario validate/run entrypoint를 추가하되 secret, remote path, personal endpoint를 쓰지 않는다. +- [ ] Handoff artifact completeness를 검증하는 focused test 또는 deterministic shell check를 추가한다. +- [ ] 원격 ALT checkout root에서 focused CLI test와 operations smoke 검증을 실행한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 의존 관계 및 구현 순서 + +- 이 directory 이름의 runtime dependencies는 `04+02,03_evidence_handoff`이다. +- 구현 시작 전 `agent-task/m-operator-headless-workflow-validation/02+01_api_market/complete.log`와 `agent-task/m-operator-headless-workflow-validation/03+02_backtest_workflows/complete.log`, 또는 대응 archive complete.log가 있어야 한다. +- 추가 hidden dependency를 만들지 않는다. + +### [OPS-1] Fixture Evidence Matrix + +#### 문제 + +Milestone handoff 기준은 `api_connection_smoke`, `market_data_status_query`, `backtest_run_request`, `backtest_run_polling`, `backtest_result_summary`, `invalid_request_matrix` 각각의 command/input/output/exit code/protobuf field evidence를 요구한다. Prior subtasks add fixtures, but no single artifact proves matrix completeness. + +#### 해결 방법 + +- `apps/cli/testdata/operator/headless_validation.md`를 추가한다. +- 각 scenario마다: + - command + - input fixture path + - expected output fixture path + - expected exit code + - checked protobuf/view-model field + - remaining Flutter wireframe dependency + 를 기록한다. +- `apps/cli/testdata/operator/expected/*.jsonl` expected output fixtures를 추가한다. + +#### 수정 파일 및 체크리스트 + +- [ ] `apps/cli/testdata/operator/headless_validation.md`: scenario evidence matrix 추가. +- [ ] `apps/cli/testdata/operator/expected/api_connection_smoke.jsonl`: expected output 추가. +- [ ] `apps/cli/testdata/operator/expected/market_data_status_query.jsonl`: expected output 추가. +- [ ] `apps/cli/testdata/operator/expected/backtest_run_request.jsonl`: expected output 추가. +- [ ] `apps/cli/testdata/operator/expected/backtest_run_polling.jsonl`: expected output 추가. +- [ ] `apps/cli/testdata/operator/expected/backtest_result_summary.jsonl`: expected output 추가. +- [ ] `apps/cli/testdata/operator/expected/invalid_request_matrix.jsonl`: expected output 추가. +- [ ] `apps/cli/internal/operator/handoff_test.go`: matrix references all required scenario fixture and expected output paths. + +#### 테스트 작성 + +- 작성: `apps/cli/internal/operator/handoff_test.go` + - `TestHeadlessValidationHandoffCoversRequiredScenarios`: six required scenarios present. + - `TestExpectedOutputFixturesAreValidJSONLines`: expected output fixtures parse as JSON lines and include scenario/status/action fields. + +#### 중간 검증 + +원격 ALT checkout root 기준: + +```bash +(cd apps/cli && go test -count=1 ./internal/operator -run 'TestHeadlessValidationHandoff|TestExpectedOutputFixtures') +``` + +기대 결과: exit code 0. + +### [OPS-2] Developer Entrypoint Notes + +#### 문제 + +`bin/dev:17` currently only shows `cd apps/cli && go run ./cmd/alt`. It does not reveal scenario validate/run commands, so the headless gate remains hard to rediscover. + +Before: + +```bash +# bin/dev:17 + CLI: + cd apps/cli && go run ./cmd/alt +``` + +#### 해결 방법 + +- `bin/dev` CLI block에 scenario validate/run examples를 추가한다. +- Examples use localhost defaults and fixture paths only; no remote checkout path, secret, account, provider credential, or personal endpoint. + +#### 수정 파일 및 체크리스트 + +- [ ] `bin/dev`: CLI section에 `operator scenario validate`와 `operator scenario run` examples 추가. +- [ ] `apps/cli/internal/cli/cli_test.go` 또는 `apps/cli/internal/operator/handoff_test.go`: documented fixture path가 존재하는지 확인. + +#### 테스트 작성 + +- 작성 또는 확장: `apps/cli/internal/operator/handoff_test.go` + - `TestHandoffDocumentedScenarioFixturesExist`. +- 별도 shell test는 만들지 않는다. `bin/dev`는 static output script이고 operations smoke `bin/test`가 workspace baseline이다. + +#### 중간 검증 + +원격 ALT checkout root 기준: + +```bash +bin/dev +(cd apps/cli && go test -count=1 ./...) +``` + +기대 결과: `bin/dev` exit code 0 and output includes `operator scenario validate`. CLI tests exit code 0. + +## 수정 파일 요약 + +| 파일 | 항목 | +|------|------| +| `apps/cli/testdata/operator/headless_validation.md` | OPS-1 | +| `apps/cli/testdata/operator/expected/api_connection_smoke.jsonl` | OPS-1 | +| `apps/cli/testdata/operator/expected/market_data_status_query.jsonl` | OPS-1 | +| `apps/cli/testdata/operator/expected/backtest_run_request.jsonl` | OPS-1 | +| `apps/cli/testdata/operator/expected/backtest_run_polling.jsonl` | OPS-1 | +| `apps/cli/testdata/operator/expected/backtest_result_summary.jsonl` | OPS-1 | +| `apps/cli/testdata/operator/expected/invalid_request_matrix.jsonl` | OPS-1 | +| `apps/cli/internal/operator/handoff_test.go` | OPS-1, OPS-2 | +| `bin/dev` | OPS-2 | + +## 최종 검증 + +원격 ALT checkout root 기준으로 실행한다. Handoff fixture checks should be fresh; use `-count=1`. + +```bash +bin/dev +(cd apps/cli && go test -count=1 ./...) +bin/test +bin/build +``` + +기대 결과: 모든 명령 exit code 0. `bin/dev` output includes scenario validate/run examples. 원격 host나 checkout 접근 불가 시 pass가 아니라 차단 사유와 미실행 명령을 review stub에 남긴다. + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/apps/cli/cmd/alt/main.go b/apps/cli/cmd/alt/main.go index 50212de..f3b9b11 100644 --- a/apps/cli/cmd/alt/main.go +++ b/apps/cli/cmd/alt/main.go @@ -1,14 +1,11 @@ package main import ( - "fmt" "os" + + "git.toki-labs.com/toki/alt/apps/cli/internal/cli" ) func main() { - if len(os.Args) > 1 && os.Args[1] == "version" { - fmt.Println("alt dev") - return - } - fmt.Println("ALT operator CLI") + os.Exit(cli.Run(os.Args[1:], os.Stdout, os.Stderr)) } diff --git a/apps/cli/go.mod b/apps/cli/go.mod index ea0f1f7..1b22efb 100644 --- a/apps/cli/go.mod +++ b/apps/cli/go.mod @@ -1,3 +1,5 @@ module git.toki-labs.com/toki/alt/apps/cli go 1.22 + +require gopkg.in/yaml.v3 v3.0.1 diff --git a/apps/cli/go.sum b/apps/cli/go.sum new file mode 100644 index 0000000..a62c313 --- /dev/null +++ b/apps/cli/go.sum @@ -0,0 +1,4 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/apps/cli/internal/cli/cli.go b/apps/cli/internal/cli/cli.go new file mode 100644 index 0000000..c35db68 --- /dev/null +++ b/apps/cli/internal/cli/cli.go @@ -0,0 +1,78 @@ +// Package cli implements the ALT operator CLI dispatch. It is a thin command +// layer: argument parsing and exit-code/output contracts live here, while the +// scenario schema and validation live in the operator package. +package cli + +import ( + "flag" + "fmt" + "io" + + "git.toki-labs.com/toki/alt/apps/cli/internal/operator" +) + +// Exit codes form the CLI contract consumed by scripts and tests. +const ( + exitOK = 0 // success + exitUsage = 1 // usage or unknown-command error + exitParseError = 2 // scenario parse/validation failure +) + +// Run dispatches the operator CLI and returns a process exit code. stdout +// carries machine-readable status lines; stderr carries human-readable +// usage and error detail. +func Run(args []string, stdout, stderr io.Writer) int { + if len(args) == 0 { + usage(stderr) + return exitUsage + } + + switch args[0] { + case "version": + fmt.Fprintln(stdout, "alt dev") + return exitOK + case "operator": + return runOperator(args[1:], stdout, stderr) + default: + fmt.Fprintf(stderr, "unknown command %q\n", args[0]) + usage(stderr) + return exitUsage + } +} + +// runOperator handles the `operator scenario validate --file ` command. +func runOperator(args []string, stdout, stderr io.Writer) int { + if len(args) < 2 || args[0] != "scenario" || args[1] != "validate" { + fmt.Fprintln(stderr, "usage: alt operator scenario validate --file ") + return exitUsage + } + + fs := flag.NewFlagSet("operator scenario validate", flag.ContinueOnError) + fs.SetOutput(stderr) + file := fs.String("file", "", "path to operator scenario YAML file") + if err := fs.Parse(args[2:]); err != nil { + return exitUsage + } + if *file == "" { + fmt.Fprintln(stderr, "scenario file is required: --file ") + return exitUsage + } + + sc, err := operator.LoadScenario(*file) + if err != nil { + fmt.Fprintln(stdout, "scenario=unknown status=parse_error") + fmt.Fprintf(stderr, "validate failed: %v\n", err) + return exitParseError + } + + fmt.Fprintf(stdout, "scenario=%s status=valid steps=%d\n", sc.Name, len(sc.Steps)) + return exitOK +} + +// usage prints the top-level command summary. +func usage(w io.Writer) { + fmt.Fprintln(w, "usage: alt ") + fmt.Fprintln(w, "commands:") + fmt.Fprintln(w, " version") + fmt.Fprintln(w, " operator scenario validate --file ") +} diff --git a/apps/cli/internal/cli/cli_test.go b/apps/cli/internal/cli/cli_test.go new file mode 100644 index 0000000..4ffabd9 --- /dev/null +++ b/apps/cli/internal/cli/cli_test.go @@ -0,0 +1,46 @@ +package cli + +import ( + "bytes" + "path/filepath" + "strings" + "testing" +) + +func TestRunVersion(t *testing.T) { + var stdout, stderr bytes.Buffer + code := Run([]string{"version"}, &stdout, &stderr) + if code != 0 { + t.Fatalf("exit code = %d, want 0 (stderr=%q)", code, stderr.String()) + } + if got := strings.TrimSpace(stdout.String()); got != "alt dev" { + t.Errorf("stdout = %q, want \"alt dev\"", got) + } +} + +func TestRunScenarioValidate(t *testing.T) { + path := filepath.Join("..", "..", "testdata", "operator", "api_connection_smoke.yaml") + var stdout, stderr bytes.Buffer + code := Run([]string{"operator", "scenario", "validate", "--file", path}, &stdout, &stderr) + if code != 0 { + t.Fatalf("exit code = %d, want 0 (stderr=%q)", code, stderr.String()) + } + out := stdout.String() + for _, want := range []string{"scenario=api_connection_smoke", "status=valid", "steps=1"} { + if !strings.Contains(out, want) { + t.Errorf("stdout %q missing %q", out, want) + } + } +} + +func TestRunScenarioValidateParseError(t *testing.T) { + path := filepath.Join("..", "..", "testdata", "operator", "invalid_request_matrix.yaml") + var stdout, stderr bytes.Buffer + code := Run([]string{"operator", "scenario", "validate", "--file", path}, &stdout, &stderr) + if code != 2 { + t.Fatalf("exit code = %d, want 2 (stdout=%q stderr=%q)", code, stdout.String(), stderr.String()) + } + if !strings.Contains(stdout.String(), "status=parse_error") { + t.Errorf("stdout %q missing status=parse_error", stdout.String()) + } +} diff --git a/apps/cli/internal/operator/scenario.go b/apps/cli/internal/operator/scenario.go new file mode 100644 index 0000000..4ce1654 --- /dev/null +++ b/apps/cli/internal/operator/scenario.go @@ -0,0 +1,123 @@ +// Package operator defines the headless operator scenario schema and the +// dry-run validation used before the API/worker runtime exists. Keeping the +// scenario format and its checks here lets the CLI verify operator workflows +// as plain YAML fixtures, in line with ALT's headless-first operations gate. +package operator + +import ( + "bytes" + "fmt" + "os" + "time" + + "gopkg.in/yaml.v3" +) + +// Action enumerates the operator step actions the headless validator +// understands. The set is intentionally small: it grows as later subtasks add +// real API/worker request runners. Any action outside this set is rejected at +// dry-run time so scenario files cannot reference unimplemented behaviour. +type Action string + +const ( + // ActionHello validates a basic API connection handshake. + ActionHello Action = "hello" +) + +// validActions is the closed set used for strict dry-run validation. +var validActions = map[Action]bool{ + ActionHello: true, +} + +// Duration wraps time.Duration so scenario YAML can express timeouts as Go +// duration strings (for example "5s") rather than raw nanoseconds. +type Duration time.Duration + +// UnmarshalYAML decodes a duration string such as "5s" into a Duration. +func (d *Duration) UnmarshalYAML(value *yaml.Node) error { + var s string + if err := value.Decode(&s); err != nil { + return fmt.Errorf("timeout must be a duration string: %w", err) + } + parsed, err := time.ParseDuration(s) + if err != nil { + return fmt.Errorf("invalid timeout %q: %w", s, err) + } + *d = Duration(parsed) + return nil +} + +// Expect captures the assertions a step makes against its response. The fields +// are descriptive only at this stage; later subtasks compare them with real +// API/worker responses. +type Expect struct { + Status string `yaml:"status"` + Capabilities []string `yaml:"capabilities"` +} + +// Step is a single operator action within a scenario. +type Step struct { + ID string `yaml:"id"` + Action Action `yaml:"action"` + Expect Expect `yaml:"expect"` +} + +// Scenario is the top-level operator scenario document. +type Scenario struct { + Name string `yaml:"name"` + Timeout Duration `yaml:"timeout"` + Steps []Step `yaml:"steps"` +} + +// LoadScenario reads a scenario file and returns it after strict decode and +// dry-run validation. +func LoadScenario(path string) (*Scenario, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("read scenario: %w", err) + } + return ParseScenario(data) +} + +// ParseScenario strictly decodes scenario YAML and validates it. Unknown YAML +// fields are rejected so typos in fixtures surface immediately. +func ParseScenario(data []byte) (*Scenario, error) { + dec := yaml.NewDecoder(bytes.NewReader(data)) + dec.KnownFields(true) + + var s Scenario + if err := dec.Decode(&s); err != nil { + return nil, fmt.Errorf("decode scenario: %w", err) + } + if err := s.Validate(); err != nil { + return nil, err + } + return &s, nil +} + +// Validate runs the dry-run checks: a named scenario, at least one step, and +// every step having a unique id and a known action. +func (s *Scenario) Validate() error { + if s.Name == "" { + return fmt.Errorf("scenario name is required") + } + if len(s.Steps) == 0 { + return fmt.Errorf("scenario %q has no steps", s.Name) + } + + seen := make(map[string]bool, len(s.Steps)) + for i, step := range s.Steps { + if step.ID == "" { + return fmt.Errorf("step %d: id is required", i) + } + if seen[step.ID] { + return fmt.Errorf("step %q: duplicate id", step.ID) + } + seen[step.ID] = true + + if !validActions[step.Action] { + return fmt.Errorf("step %q: unknown action %q", step.ID, step.Action) + } + } + return nil +} diff --git a/apps/cli/internal/operator/scenario_test.go b/apps/cli/internal/operator/scenario_test.go new file mode 100644 index 0000000..0378fe7 --- /dev/null +++ b/apps/cli/internal/operator/scenario_test.go @@ -0,0 +1,60 @@ +package operator + +import ( + "path/filepath" + "strings" + "testing" +) + +func fixture(t *testing.T, name string) string { + t.Helper() + return filepath.Join("..", "..", "testdata", "operator", name) +} + +func TestLoadScenarioValidatesExample(t *testing.T) { + sc, err := LoadScenario(fixture(t, "api_connection_smoke.yaml")) + if err != nil { + t.Fatalf("LoadScenario returned error: %v", err) + } + if sc.Name != "api_connection_smoke" { + t.Errorf("name = %q, want api_connection_smoke", sc.Name) + } + if len(sc.Steps) != 1 { + t.Fatalf("steps = %d, want 1", len(sc.Steps)) + } + if sc.Steps[0].Action != ActionHello { + t.Errorf("step action = %q, want %q", sc.Steps[0].Action, ActionHello) + } +} + +func TestLoadScenarioRejectsUnknownAction(t *testing.T) { + _, err := LoadScenario(fixture(t, "invalid_request_matrix.yaml")) + if err == nil { + t.Fatal("expected error for unknown action, got nil") + } + if !strings.Contains(err.Error(), "unknown action") { + t.Errorf("error = %v, want it to mention unknown action", err) + } +} + +func TestLoadScenarioRejectsMissingStepID(t *testing.T) { + data := []byte("name: missing_id\ntimeout: 1s\nsteps:\n - action: hello\n") + _, err := ParseScenario(data) + if err == nil { + t.Fatal("expected error for missing step id, got nil") + } + if !strings.Contains(err.Error(), "id is required") { + t.Errorf("error = %v, want it to mention id is required", err) + } +} + +func TestParseScenarioRejectsInvalidDuration(t *testing.T) { + data := []byte("name: invalid_timeout\ntimeout: not-a-duration\nsteps:\n - id: hello\n action: hello\n") + _, err := ParseScenario(data) + if err == nil { + t.Fatal("expected error for invalid timeout, got nil") + } + if !strings.Contains(err.Error(), "invalid timeout") { + t.Errorf("error = %v, want it to mention invalid timeout", err) + } +} diff --git a/apps/cli/testdata/operator/api_connection_smoke.yaml b/apps/cli/testdata/operator/api_connection_smoke.yaml new file mode 100644 index 0000000..1a8e84c --- /dev/null +++ b/apps/cli/testdata/operator/api_connection_smoke.yaml @@ -0,0 +1,10 @@ +name: api_connection_smoke +timeout: 5s +steps: + - id: hello + action: hello + expect: + status: ok + capabilities: + - hello + - request-response diff --git a/apps/cli/testdata/operator/invalid_request_matrix.yaml b/apps/cli/testdata/operator/invalid_request_matrix.yaml new file mode 100644 index 0000000..b5e7863 --- /dev/null +++ b/apps/cli/testdata/operator/invalid_request_matrix.yaml @@ -0,0 +1,11 @@ +# Validation-only fixture. Each step uses an action the dry-run validator must +# reject, so `alt operator scenario validate` exits non-zero with +# status=parse_error. It documents that unimplemented request actions cannot be +# referenced until a later subtask adds their runner. +name: invalid_request_matrix +timeout: 5s +steps: + - id: unsupported_request + action: send_unsupported_request + expect: + status: error diff --git a/apps/client/ios/Flutter/Debug.xcconfig b/apps/client/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/apps/client/ios/Flutter/Debug.xcconfig +++ b/apps/client/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/apps/client/ios/Flutter/Release.xcconfig b/apps/client/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/apps/client/ios/Flutter/Release.xcconfig +++ b/apps/client/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/apps/client/ios/Podfile b/apps/client/ios/Podfile new file mode 100644 index 0000000..620e46e --- /dev/null +++ b/apps/client/ios/Podfile @@ -0,0 +1,43 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '13.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/apps/client/macos/Flutter/Flutter-Debug.xcconfig b/apps/client/macos/Flutter/Flutter-Debug.xcconfig index c2efd0b..4b81f9b 100644 --- a/apps/client/macos/Flutter/Flutter-Debug.xcconfig +++ b/apps/client/macos/Flutter/Flutter-Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/apps/client/macos/Flutter/Flutter-Release.xcconfig b/apps/client/macos/Flutter/Flutter-Release.xcconfig index c2efd0b..5caa9d1 100644 --- a/apps/client/macos/Flutter/Flutter-Release.xcconfig +++ b/apps/client/macos/Flutter/Flutter-Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/apps/client/macos/Podfile b/apps/client/macos/Podfile new file mode 100644 index 0000000..ff5ddb3 --- /dev/null +++ b/apps/client/macos/Podfile @@ -0,0 +1,42 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/apps/client/pubspec.lock b/apps/client/pubspec.lock index 4a38741..2d95a1a 100644 --- a/apps/client/pubspec.lock +++ b/apps/client/pubspec.lock @@ -362,10 +362,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" mime: dependency: transitive description: @@ -589,26 +589,26 @@ packages: dependency: transitive description: name: test - sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7" + sha256: "8d9ceddbab833f180fbefed08afa76d7c03513dfdba87ffcec2718b02bbcbf20" url: "https://pub.dev" source: hosted - version: "1.30.0" + version: "1.31.0" test_api: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.11" test_core: dependency: transitive description: name: test_core - sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51" + sha256: "1991d4cfe85d5043241acac92962c3977c8d2f2add1ee73130c7b286417d1d34" url: "https://pub.dev" source: hosted - version: "0.6.16" + version: "0.6.17" typed_data: dependency: transitive description: