fix(dev-runtime): 배포 검증 실패를 수정한다

원격 macOS에서 빈 prompt 인자가 oneshot CLI에 전달되어 cwd 테스트가 실패했고, app-server cwd 검증은 고정 sleep 레이스에 의존했다.

dev 배포가 같은 기준으로 반복되도록 inventory와 capacity smoke 절차도 함께 정리한다.
This commit is contained in:
toki 2026-06-24 15:39:43 +09:00
parent 36d8393a10
commit a47331f21d
13 changed files with 662 additions and 16 deletions

View file

@ -94,6 +94,7 @@
## 스킬 라우팅
- dev 배포, dev-runtime 배포, Edge/Node dev 환경 배포, provider pool 배포, OpenAI-compatible capacity smoke 검증: `agent-ops/skills/project/dev-runtime-deploy/SKILL.md`
- 사용자 실행 파이프라인 검증, repo 내부 edge-node 진단, 메시지 2회 왕복, edge command 응답, 보조 E2E smoke, full-cycle 실제 구동, `scripts/dev/edge.sh`/`scripts/dev/node.sh` 진단 테스트: `agent-ops/skills/project/e2e-smoke/SKILL.md`
- field 테스트 포트, artifact/bootstrap HTTP, 외부 테스트 환경: `agent-test/local/rules.md`를 따른다.
- bootstrap/install UX, Agent Bootstrap, specialized agent 등록, Control Plane enrollment: `testing` domain rule과 `agent-test/local/rules.md`를 따른다.

View file

@ -0,0 +1,130 @@
---
name: dev-runtime-deploy
version: 1.0.0
description: dev 배포, dev-runtime 배포, Edge/Node dev 환경 배포 요청에서 clean sync, 전체 테스트, rebuild, 원격 배포, OpenAI-compatible capacity smoke를 수행하는 절차
---
# dev-runtime-deploy
## 목적
dev-runtime provider pool을 제품 검증이 끝난 상태로 배포한다. 배포는 원격 checkout 동기화, 전체 테스트, 전체 rebuild, Edge/Node 재시작, provider snapshot 기반 capacity 검증까지 포함한다.
## 언제 호출할지
- 사용자가 `dev 배포`, `dev-runtime 배포`, `배포해봐`처럼 dev 환경 배포를 요청할 때
- Edge, mac CLI node, GX10 vLLM node, OneXPlayer Lemonade node로 이루어진 dev-runtime provider pool을 갱신할 때
- OpenAI-compatible `/v1/responses` 또는 `/v1/chat/completions` 경로가 provider capacity만큼 채워지는지 검증할 때
- dev 환경의 remote runner, node 접속 정보, provider capacity, model alias 변경을 배포 절차에 반영할 때
## 입력
- `env`: 배포 대상 환경. 기본값은 `dev`이다.
- `model`: OpenAI-compatible model alias. 지정하지 않으면 dev 환경 문서의 기준 model을 사용한다.
- `capacity_targets`: provider별 기대 capacity. 지정하지 않으면 dev 환경 인벤토리 또는 `agent-test/dev/*` 문서에서 읽는다.
- `source_ref`: 배포할 git ref. 지정하지 않으면 원격 runner의 기본 배포 branch 기준을 따른다.
## 먼저 확인할 것
- [ ] `agent-ops/rules/project/domain/testing/rules.md`를 읽고 사용자 실행 파이프라인 검증 기준을 확인한다.
- [ ] dev 환경은 `agent-test/dev/inventory.yaml`을 먼저 읽는다. 파일이 없으면 `agent-test/dev/rules.md`, `agent-test/dev/edge-smoke.md`, `agent-test/dev/node-smoke.md`로 fallback하고, 구조화 inventory 누락을 보고한다.
- [ ] dev-runtime provider pool과 compose/local profile을 섞지 않는다. dev-runtime은 native Edge/Node runtime과 dev-runtime config를 기준으로 한다.
- [ ] OneXPlayer SSH 접속 정보는 현재 host 기준 `ssh r0bin@192.168.0.59`이다. `toki` 사용자명 또는 remote runner를 경유한 OneXPlayer 접속을 사용하지 않는다.
- [ ] 현재 구현의 completion 검증 대상은 legacy `/v1/completions`가 아니라 `/v1/chat/completions`이다. `/v1/completions`는 route가 구현되어 있을 때만 별도 검증한다.
- [ ] token, secret header, bootstrap token, private key 경로는 최종 보고에 원문으로 출력하지 않는다.
## 실행 절차
1. **환경 인벤토리 확정**
- 배포 대상 runner, repo path, Edge id, Control Plane status URL, OpenAI base URL, Edge admin URL, Node SSH 정보를 문서에서 확정한다.
- provider pool 대상 model alias와 provider별 capacity를 확정한다.
- 필수 정보가 없거나 서로 충돌하면 배포를 시작하지 말고 누락/충돌 항목을 보고한다.
2. **원격 checkout clean sync**
- 원격 runner checkout에서 `git fetch` 후 배포 기준 ref로 `git reset --hard`를 수행한다.
- dirty 파일은 보존 대상으로 보지 않는다. 배포 전 clean 상태를 만든다.
- 기본 cleanup은 `git clean -fd`이다. `git clean -fdx`는 config, token, secret, runtime artifact까지 삭제할 수 있으므로 사용하지 않는다.
- sync 후 `git status --short --branch``git log --oneline -1`을 기록한다.
3. **빌드 전 전체 테스트**
- clean source 기준으로 `go test ./...`를 실행한다.
- client/Flutter, proto, Makefile, script, config 변경이 배포 범위에 포함되면 해당 도메인 규칙의 전체 테스트도 추가한다.
- 전체 테스트가 실패하면 build/deploy를 진행하지 않고 실패 패키지와 핵심 오류를 보고한다.
4. **전체 rebuild**
- 같은 source ref에서 dev-runtime Edge binary, mac node binary, Linux ARM64 node binary, Windows AMD64 node binary를 모두 다시 빌드한다.
- stale binary가 의심되거나 `config refresh` subcommand, admin port, version 출력이 맞지 않으면 clean sync부터 다시 시작한다.
- 빌드 산출물 경로, timestamp, 크기, 실행 가능 여부를 기록한다.
5. **빌드 후 기본 동작 테스트**
- 빌드된 Edge binary로 `config check`, `config refresh --help`, `config refresh --mode dry-run`을 실행한다.
- 빌드 후에도 `go test ./...`를 실행한다. 실행하지 못하면 사유와 남은 위험을 보고한다.
- dry-run이 `rejected` 또는 예상 밖 `restart_required`를 반환하면 배포를 멈추고 config diff를 보고한다.
6. **배포와 재시작**
- Edge와 mac CLI node는 원격 runner에서 빌드 산출물 기준으로 재시작한다.
- GX10 node는 Linux ARM64 node binary를 배포하고 기존 node process를 재시작한다.
- OneXPlayer node는 현재 host에서 `ssh r0bin@192.168.0.59`로 접속한다. artifact가 remote runner에 있으면 현재 host를 통해 전달한 뒤 Windows host에서 교체한다.
- OneXPlayer에서는 SSH 세션 안의 `Start-Process`로 장기 실행을 시작하지 않는다. `Win32_Process.Create` 또는 동등한 세션 독립 실행 방식으로 `iop-node.exe --config node.yaml serve`를 시작한다.
7. **배포 후 연결 검증**
- Edge, OpenAI-compatible listener, Node TCP, admin port, Control Plane status port가 열려 있는지 확인한다.
- Control Plane status에서 Edge가 connected이고 dev-runtime 기준 3개 node가 connected인지 확인한다.
- 각 node의 `provider_snapshots`에서 provider `id`, `capacity`, `in_flight`, `queued`, `health`, `served_models`를 확인한다.
- `/v1/models`가 대상 model alias를 노출하는지 확인한다.
8. **OpenAI-compatible capacity smoke**
- `/v1/responses``/v1/chat/completions`를 각각 검증한다. legacy `/v1/completions`는 구현되어 있지 않으면 실패로 보지 않는다.
- 표준 부하 프롬프트는 짧은 토큰 응답을 요구하지 않는다. 700~1200 token 수준의 구조화된 답변을 유도해 요청이 동시에 관측될 시간을 만든다.
- endpoint별로 총 provider capacity + 1개 요청을 동시에 보낸다. 예: capacity가 `4 + 3 = 7`이면 8개 동시 호출을 보낸다.
- 환경 문서에 더 작은 aggregate smoke 기준이 남아 있더라도, 배포 완료 판정은 이 capacity+1 기준을 우선한다.
- 요청 실행 중 Control Plane status를 반복 polling하여 대상 provider들의 `in_flight` 합이 총 capacity에 도달하고 `queued` 합이 1 이상이 되는 순간을 증거로 남긴다.
- 각 provider의 `in_flight`가 자기 capacity를 넘지 않고, 적어도 한 번은 기대 capacity까지 차는지 확인한다.
- 모든 요청 완료 후 같은 status에서 대상 provider들의 `in_flight=0`, `queued=0`으로 돌아오는지 확인한다.
- Qwen 계열 reasoning/thinking 텍스트는 정상 응답으로 허용한다. exact-output match를 smoke 성공 기준으로 삼지 않는다.
9. **결과 보고**
- source ref, clean sync 결과, 테스트 결과, 빌드 산출물, process/port 상태, connected node 목록, provider capacity snapshot, capacity smoke 관측값을 보고한다.
- 실패한 단계가 있으면 다음 단계를 진행했는지 여부를 명확히 구분한다.
- capacity smoke가 타이밍 문제로 관측 실패했으면 요청 성공과 별도로 `capacity 관측 미충족`으로 보고하고, 프롬프트 길이 또는 status polling 간격 조정을 제안한다.
## 실행 결과 검증
- [ ] 원격 runner checkout이 배포 기준 ref로 clean sync되었는가
- [ ] 빌드 전 `go test ./...`와 필요한 추가 전체 테스트가 통과했는가
- [ ] dev-runtime Edge/mac/Linux ARM64/Windows AMD64 binary가 같은 source ref에서 rebuild되었는가
- [ ] 빌드 후 config check, refresh help, refresh dry-run, 전체 테스트가 통과했는가
- [ ] Edge, mac CLI node, GX10 vLLM node, OneXPlayer Lemonade node가 재시작되고 3개 node가 connected 상태인가
- [ ] OneXPlayer 접속과 실행이 `r0bin@192.168.0.59` 및 세션 독립 실행 방식으로 수행되었는가
- [ ] `/v1/responses` capacity smoke에서 총 capacity만큼 `in_flight`가 차고 초과 요청이 queue에 잡혔는가
- [ ] `/v1/chat/completions` capacity smoke에서 총 capacity만큼 `in_flight`가 차고 초과 요청이 queue에 잡혔는가
- [ ] 완료 후 provider `in_flight=0`, `queued=0`으로 회복되었는가
- 검증 실패 시: 실패 단계, 실패한 host/provider/endpoint, 관측된 snapshot, 진행 중단 여부를 보고한다.
## 출력 형식
```text
dev-runtime 배포 결과
- Source: <branch/ref/commit>, clean=<yes|no>
- Pre-build tests: <command> - <pass|fail|not-run>
- Build: edge=<path>, mac-node=<path>, linux-arm64-node=<path>, windows-amd64-node=<path>
- Post-build checks: config-check=<pass|fail>, refresh-help=<pass|fail>, refresh-dry-run=<status>, tests=<pass|fail|not-run>
- Deployment: edge=<pid/status>, mac-node=<pid/status>, gx10=<pid/status>, onexplayer=<pid/status>
- Ports: <port summary>
- Nodes: <node_id connected summary>
- Providers: <provider_id capacity/in_flight/queued/health summary>
- OpenAI-compatible: models=<pass|fail>, responses-capacity=<pass|fail>, chat-completions-capacity=<pass|fail>
- Capacity evidence: <endpoint별 max in_flight/queued snapshot>
- Blockers/Risk: <없음 또는 내용>
```
## 금지 사항
- dirty checkout이나 stale binary를 그대로 배포하지 않는다.
- `git clean -fdx`를 기본 cleanup으로 사용하지 않는다.
- 빌드 전 전체 테스트 실패 후 배포를 계속하지 않는다.
- OneXPlayer에 `toki` 사용자명으로 접속하거나 remote runner에서 다시 OneXPlayer로 SSH 접속하지 않는다.
- OneXPlayer 장기 실행을 Windows OpenSSH 세션 내부 `Start-Process`에 의존하지 않는다.
- Qwen reasoning/thinking 출력을 실패로 판정하거나 exact-output smoke를 기본 성공 기준으로 삼지 않는다.
- OpenAI-compatible 요청 성공만으로 capacity 검증 성공을 선언하지 않는다. provider snapshot의 `in_flight/queued` 관측을 함께 남긴다.
- 이 프로젝트 전용 배포 절차를 `agent-ops/rules/common/_templates` 또는 common skill에 넣지 않는다.

56
agent-test/README.md Normal file
View file

@ -0,0 +1,56 @@
# agent-test 환경 확장 기준
이 디렉터리는 테스트 환경별 실행 기준과 도메인/검증 시나리오별 profile을 둔다.
새 환경(`qa`, `staging`, `dev-gpu`, `dev-team-a` 등)을 추가할 때는 기존 환경을 임의로 복사하지 말고 이 기준과 `_templates/`를 먼저 따른다.
## 환경 디렉터리
- 환경 디렉터리는 `agent-test/<env>/` 형식을 사용한다.
- `<env>`는 소문자 영문, 숫자, 하이픈만 사용한다.
- 각 환경에는 반드시 `agent-test/<env>/rules.md`를 둔다.
- 도메인/검증 시나리오별 문서는 `agent-test/<env>/<test-profile>.md` 형식을 사용한다.
- 기본 도메인 smoke profile은 기존 도메인 이름을 유지해 `node-smoke.md`, `edge-smoke.md`, `control-plane-smoke.md`, `client-smoke.md`, `platform-common-smoke.md`, `testing-smoke.md`처럼 둔다.
## rules.md 표준 섹션
`rules.md`는 아래 섹션 순서를 유지한다.
1. `공통 규칙`
2. `기본 환경`
3. `포트 매핑`
4. `런타임 프로필`
5. `프리플라이트`
6. `노드/Provider 인벤토리 위치`
7. `Field/bootstrap 반복 테스트 기준`
8. `라우팅`
9. `라우팅 규칙`
섹션이 해당 환경에 직접 필요하지 않더라도 삭제하지 말고 `없음`, `해당 없음`, 또는 대상 profile 링크를 남긴다.
## 확장 원칙
- 환경 공통값은 `rules.md`에 둔다.
- 실제 노드, provider, bootstrap, model endpoint, workspace, SSH user/IP 같은 상세 인벤토리는 agent가 구조적으로 읽어야 하면 `inventory.yaml`에 두고, 사람용 설명과 도메인별 판정 기준은 해당 도메인 profile에 둔다.
- 상위 `rules.md`에는 상세값을 복제하지 말고 `노드/Provider 인벤토리 위치``라우팅`으로 연결한다.
- compose, native Edge, dev-runtime provider pool처럼 같은 환경 안의 실행 모드는 `런타임 프로필`에서 분리한다.
- 같은 포트 이름은 모든 환경에서 같은 행 이름을 쓴다. 값이 없으면 `해당 없음` 또는 `후보 <port>`로 둔다.
- shared remote runner를 쓰는 환경은 `repo root`, `sync 기준`, `프리플라이트`를 반드시 명시한다.
- secret, token, API key, private credential 원문은 `agent-test/`, `docs/`, `agent-roadmap/`에 기록하지 않는다.
- private IP와 SSH user는 테스트 접속에 필요한 환경값이면 기록할 수 있다. 단, 잘못된 user가 발견되면 해당 env rules/profile과 사람용 guide를 함께 정정한다.
- `agent-test/local/`은 local/private 기준이라 git 추적 제외될 수 있다. 추적 여부와 상관없이 현재 workspace의 운영 기준으로는 같은 구조를 유지한다.
## 새 환경 추가 절차
1. `_templates/env-rules-template.md``agent-test/<env>/rules.md`로 복사한다.
2. `<env>`, 날짜, runner, repo root, 포트, runtime profile을 채운다.
3. agent가 반복해서 읽어야 하는 host/node/provider 값이 있으면 `_templates/inventory-template.yaml`을 사용해 `agent-test/<env>/inventory.yaml`을 만든다.
4. 기존 도메인 smoke profile이 필요하면 `_templates/test-profile-template.md`를 사용해 `agent-test/<env>/<domain>-smoke.md`를 만든다.
5. `rules.md``노드/Provider 인벤토리 위치``라우팅`에 생성한 inventory와 profile을 연결한다.
6. remote runner나 external provider를 쓰면 `프리플라이트`에 확인할 항목을 구체화한다.
7. local/test/dev 등 기존 환경과 공존해야 하면 `포트 매핑`에 충돌하지 않는 값을 기록한다.
8. `rg --no-ignore`로 잘못된 SSH user, workspace, port 재사용, secret 원문이 없는지 확인한다.
## 현재 표준 구현
- `agent-test/local/rules.md`
- `agent-test/dev/rules.md`

View file

@ -0,0 +1,101 @@
---
test_env: <env>
last_rule_updated_at: <YYYY-MM-DD>
---
# <env> 테스트 규칙
**현재 문서를 반드시 끝까지 정독하고 작업한다. 다 읽지 않고 즉각 작업은 금지한다.**
**<env> 테스트 환경의 핵심 실행 모드와 local/dev/qa 등 다른 환경과의 공존 기준을 이 문서에 둔다.**
## 공통 규칙
- <env> 테스트/검증은 이 파일을 기준으로 판단한다.
- 작업 완료 검증은 변경 범위 기준으로 선택한다.
- 필수 검증을 실행하지 못하면 차단 사유로 보고한다.
- 최종 보고에는 실행 명령, 결과, 생략 사유, 남은 위험을 남긴다.
- 환경값, secret, 개인 endpoint는 tracked docs/roadmap에 쓰지 않는다.
- token/secret 원문은 원격 환경에서 주입하고, shell stdout/stderr와 최종 보고에 출력하지 않는다.
- 테스트용 Docker 환경을 작성/수정할 때는 Docker Compose `networks`, IPAM subnet, static IPv4, `extra_hosts`, host publish 문서 어디에도 host LAN/VPN과 충돌하는 대역을 사용하지 않는다.
## 기본 환경
- host: <기본 실행 host 또는 runner SSH>
- repo root: <원격/로컬 repo root>
- sync 기준: <source sync 판단 기준>
- env file: <compose/env file 기준 또는 해당 없음>
- compose identity: <COMPOSE_PROJECT_NAME / network 또는 해당 없음>
- port: <핵심 포트 요약>
- optional field ports: <선택 포트 또는 해당 없음>
- runtime: <Go/Flutter/Docker/외부 runtime>
- package manager: <Go modules / Makefile / Flutter pub >
- docker: <Docker 사용 기준>
- external service: <외부 서비스 URL/host 요약>
- model endpoint: <OpenAI-compatible 모델 endpoint>
- credential: secret/token/API key 원문은 문서에 기록하지 않는다.
## 포트 매핑
| 용도 | local/test | dev | <env> |
|---|---:|---:|---:|
| Web preview | `13000-13099` | `13001` | `<port 또는 해당 없음>` |
| Control Plane HTTP | `18000` | `18001` | `<port 또는 해당 없음>` |
| CP Client WS | `19080` | `19001` | `<port 또는 해당 없음>` |
| CP-Edge wire | `19081` | `19002` | `<port 또는 해당 없음>` |
| Edge-Node TCP | `19090` | `19003` | `<port 또는 해당 없음>` |
| Edge artifact/bootstrap | `18080` | `18082` | `<port 또는 해당 없음>` |
| Edge OpenAI-compatible | `18081` | `18083` | `<port 또는 해당 없음>` |
| Edge metrics | `19092` | `19101` | `<port 또는 해당 없음>` |
| PostgreSQL host publish | `15400` | `15401` | `<port 또는 해당 없음>` |
| Redis host publish | `16300` | `16301` | `<port 또는 해당 없음>` |
<환경 안에서 compose/native/dev-runtime처럼 Edge-Node TCP가 갈라지면 여기에 명시한다.>
## 런타임 프로필
- local quick check: 현재 checkout에서 가능한 quick check를 우선 실행한다.
- remote runner: <SSH, repo root, 어떤 evidence에 쓰는지>
- compose <env> stack: <compose file/env file/project/network/주요 포트>
- native/direct Edge profile: <필요 artifact/openai/metrics/edge-node 포트>
- provider pool profile: <필요 config path, model alias, Edge-Node TCP>
- external provider field: <필요 SSH/user/IP/provider endpoint>
## 프리플라이트
- plan 작성 시 필수 검증이 현재 checkout을 벗어나 원격 runner, field/bootstrap, 외부 provider, Docker/code-server, emulator/device, 공유 장기 runtime을 사용하면 먼저 테스트 환경 프리플라이트를 계획에 기록한다.
- 테스트 환경 프리플라이트에는 runner, repo root/workdir, branch/HEAD/dirty 상태, local 변경과 원격 source 동기화 여부, binary/artifact 경로와 필요한 help/version 출력, config path, runtime identity, port/process 상태, 외부 host, OS/arch 가정을 포함한다.
- 프리플라이트에서 dirty/divergent checkout, stale artifact, 잘못된 identity, missing command, closed port, host OS 불일치, source 미동기화가 확인되면 plan은 먼저 setup/sync/rebuild 단계를 만들거나 blocker로 보고한다. remote deploy 환경에서 dirty 변경을 보존하지 않는 정책이면 배포 전 clean sync와 rebuild를 기본 단계로 둔다. profile 값을 이미 참이라고 가정한 검증 명령만 쓰지 않는다.
## 노드/Provider 인벤토리 위치
- machine-readable 인벤토리 기준: `agent-test/<env>/inventory.yaml` 또는 `해당 없음`
- Edge/OpenAI-compatible 입력 표면 상세 기준: `agent-test/<env>/edge-smoke.md`
- Node 접속과 bootstrap 상세 기준: `agent-test/<env>/node-smoke.md`
- 공통 provider config 계약 기준: `agent-test/<env>/platform-common-smoke.md`
- compose stack 기준: `agent-test/<env>/control-plane-smoke.md`, `agent-test/<env>/client-smoke.md`, `agent-test/<env>/testing-smoke.md`
## Field/bootstrap 반복 테스트 기준
- field/bootstrap 검증은 이 환경의 runner, checkout, artifact 기준으로 수행한다. 세부 Edge/Node profile은 해당 env의 `edge-smoke.md``node-smoke.md`를 따른다.
- compose stack과 native/provider-pool profile이 서로 다른 Edge-Node TCP를 쓰면 두 프로필을 섞어서 판정하지 않는다.
- Node bootstrap은 Edge의 `node register`가 출력한 OS별 완성 명령을 그대로 사용한다. 사용자에게 안내하는 명령에는 임의 placeholder, 수동 token 치환, `IOP_*=` named environment parameter를 넣지 않는다.
- token, API key, private credential 원문은 tracked docs, roadmap, 테스트 규칙에 기록하지 않는다. 실행 증거에는 token을 마스킹하거나 명령 생성 사실만 남긴다.
- Linux/macOS Node는 생성된 `curl | bash` 계열 명령을 사용한다. Windows Node는 native PowerShell bootstrap을 기본으로 사용한다.
- provider capacity, model/provider mapping의 live apply는 해당 Edge binary가 `config refresh`를 제공하는 경우에만 사용한다. refresh 지원이 필요한 환경에서 subcommand나 admin port가 없으면 stale binary로 보고 clean sync/rebuild를 먼저 수행한다.
- Edge process restart 또는 일시 단절 후에는 Node reconnect 정책을 검증한다. retry 한계를 넘겨 Node process가 종료된 경우에만 해당 Node host에서 새 bootstrap 실행이 필요하다.
## 라우팅
- node / smoke / node 실행 파이프라인 baseline: `agent-test/<env>/node-smoke.md`
- edge / smoke / edge 실행 그룹과 입력 표면 baseline: `agent-test/<env>/edge-smoke.md`
- control-plane / smoke / control-plane health와 wire baseline: `agent-test/<env>/control-plane-smoke.md`
- client / smoke / Flutter client와 IOP console package baseline: `agent-test/<env>/client-smoke.md`
- platform-common / smoke / 공통 설정과 protobuf 계약 baseline: `agent-test/<env>/platform-common-smoke.md`
- testing / smoke / 테스트 도구와 full-cycle 검증 baseline: `agent-test/<env>/testing-smoke.md`
## 라우팅 규칙
- 여러 항목이 맞으면 모두 읽는다.
- 도메인 매핑이나 domain rule이 있으면 각 도메인의 `<domain>-smoke` 문서를 기본 baseline으로 둔다.
- 도메인이 아직 없을 때만 `project-smoke`를 fallback baseline으로 둔다.
- 도메인/검증 시나리오별 문서는 다른 테스트 문서로 라우팅하지 않는다.

View file

@ -0,0 +1,57 @@
test_env: <env>
profile: <profile-name>
last_updated_at: <YYYY-MM-DD>
source:
remote_runner:
ssh: <user@host 또는 해당 없음>
repo_root: <repo root 또는 해당 없음>
clean_sync:
- <sync command 또는 해당 없음>
dirty_policy: <preserve|discard|manual-review>
edge:
id: <edge-id 또는 해당 없음>
config_path: <edge config path 또는 해당 없음>
control_plane_http: <url 또는 해당 없음>
control_plane_status_url: <url 또는 해당 없음>
bootstrap_http_public: <url 또는 해당 없음>
openai_base_url_public: <url 또는 해당 없음>
openai_base_url_runner: <url 또는 해당 없음>
edge_node_tcp_public: <host:port 또는 해당 없음>
admin_addr_runner: <host:port 또는 해당 없음>
build:
binaries:
edge: <path 또는 해당 없음>
node_macos: <path 또는 해당 없음>
node_linux_arm64: <path 또는 해당 없음>
node_windows_amd64: <path 또는 해당 없음>
model:
alias: <model alias 또는 해당 없음>
provider_capacity_total: <number 또는 0>
capacity_smoke:
endpoints:
- /v1/responses
- /v1/chat/completions
concurrent_requests: capacity_plus_one
expected_total_in_flight: <number 또는 0>
expected_min_queued: 1
prompt_policy: long_reasoning_allowed
exact_output_match: false
nodes:
- id: <node-id>
alias: <node-alias>
role: <role>
ssh: <user@host 또는 해당 없음>
ssh_origin: <current_host|remote_runner|not_applicable>
workspace: <path 또는 해당 없음>
provider_pool_candidate: <true|false>
provider:
id: <provider-id 또는 해당 없음>
type: <provider type 또는 해당 없음>
endpoint: <url 또는 해당 없음>
served_model: <served model 또는 해당 없음>
capacity: <number 또는 0>

View file

@ -0,0 +1,79 @@
---
test_env: <env>
test_profile: <test-profile>
domain: <domain|project-wide>
verification_type: <smoke|unit|integration|e2e|model|migration|manual|full-cycle>
last_rule_updated_at: <YYYY-MM-DD>
---
# <test-profile> <env> 테스트
## 읽기 조건
- < 도메인/검증 시나리오의 테스트, 검증, 실행 조건 판단이 필요한 경우>
## 적용 범위
- <검증 대상 entrypoint, command, API, UX, profile, service>
## 분류
- domain: <domain|project-wide>
- verification_type: <smoke|unit|integration|e2e|model|migration|manual|full-cycle>
- scope: <검증 대상 변경 범위 요약>
## 환경
- host:
- port:
- runtime:
- package manager:
- docker:
- external service:
- model endpoint:
- credential: secret/token/API key 원문은 문서에 기록하지 않는다.
## 명령
- setup:
- lint:
- unit:
- smoke:
- e2e:
- model:
- full-cycle:
## 필수 검증
- <작업 완료 반드시 실행하거나 판단해야 하는 검증>
## 보조 검증
- <실행하면 좋지만 필수 검증을 대체하지 않는 smoke/helper 검증>
## 판정 기준
- <통과로 판단할 출력, 상태, UX, artifact, endpoint 조건>
## 기준 출력 예시
```text
<필요한 경우 기대 출력 예시>
```
## 차단 기준
- <테스트를 계속할 없는 환경/권한/도구/서비스 조건>
## 보고 항목
- 실행한 명령:
- 성공한 검증:
- 실패/차단된 검증:
- 생략 사유:
- 남은 위험:
## 금지 사항
- secret, token, 개인 endpoint 원문은 tracked 파일에 기록하지 않는다.

View file

@ -3,7 +3,7 @@ test_env: dev
test_profile: edge-smoke
domain: edge
verification_type: smoke
last_rule_updated_at: 2026-06-12
last_rule_updated_at: 2026-06-24
---
# edge-smoke dev 테스트
@ -41,6 +41,40 @@ last_rule_updated_at: 2026-06-12
- model endpoint: dev OpenAI-compatible base URL 후보 `http://toki-labs.com:18083/v1`
- credential: token/secret 원문은 문서에 기록하지 않는다.
## dev-runtime provider pool 인벤토리
dev-runtime provider pool과 3-node 연결 상태를 점검할 때는 `agent-test/dev/inventory.yaml`의 machine-readable 값을 우선하고, 원격 runner `ssh toki@toki-labs.com``/Users/toki/agent-work/iop-dev` checkout을 기준으로 한다.
- Edge config: `build/dev-runtime/edge.yaml`
- Edge id: `edge-toki-labs-dev`
- Control Plane HTTP: `http://127.0.0.1:18001`
- bootstrap HTTP: `http://toki-labs.com:18082`
- Edge OpenAI-compatible base URL: `http://toki-labs.com:18083/v1`
- Edge-Node TCP transport: `toki-labs.com:18084`
- model alias: `qwen3.6:35b`
노드 후보:
- mac CLI node: `mac-codex-node` / `mac-codex`
- SSH/user: `ssh toki@toki-labs.com`
- 목적: `cli` adapter, `codex app-server`
- workspace: `/Users/toki/agent-work/iop-workspace/nomadcode`
- GX10 vLLM node: `gx10-vllm-node` / `gx10-vllm`
- SSH/user: `ssh toki@192.168.0.91`
- provider endpoint: `http://192.168.0.91:8001/v1`
- served model: `nvidia/Qwen3.6-35B-A3B-NVFP4`
- capacity baseline: `4`
- workspace: `/home/toki/iop-gx10-vllm`
- OneXPlayer Lemonade node: `onexplayer-lemonade-node` / `onexplayer-lemonade`
- SSH/user: `ssh r0bin@192.168.0.59`
- 접속 기준: 현재 작업 호스트에서 직접 SSH
- provider endpoint: `http://192.168.0.59:13305/v1`
- served model: `Qwen3.6-35B-A3B-MTP-GGUF`
- capacity baseline: `3`
- workspace: `C:/Users/r0bin/iop-field`
OneXPlayer Lemonade Node는 원격 runner나 Edge host에서 다시 SSH하거나 proxy process로 띄우지 않는다. 현재 작업 호스트에서 OneXPlayer Windows host에 `ssh r0bin@192.168.0.59`로 직접 접속한 뒤 generated PowerShell bootstrap을 실행한다.
## 명령
- setup:
@ -69,6 +103,9 @@ last_rule_updated_at: 2026-06-12
- node 로컬 `[node-message]` payload 라인 목록과 edge `[node-*-message]` payload 라인 목록이 run별로 내용/순서까지 동일해야 한다.
- edge complete는 같은 run의 마지막 `[node-*-message]` 이후에만 정상이다.
- OpenAI-compatible smoke에서 `/healthz`, `/v1/models`, `/v1/responses`가 기대 상태로 응답한다.
- dev-runtime capacity smoke는 `/v1/responses``/v1/chat/completions` 각각에 provider capacity 총합 + 1개 동시 요청을 보내고, Control Plane status의 `provider_snapshots`에서 총 `in_flight`가 capacity 총합에 도달하며 `queued`가 1 이상 잡히는지 확인한다. 현재 `gx10-vllm=4`, `onexplayer-lemonade=3`이면 endpoint별 8개 동시 요청에서 총 `in_flight=7`, `queued>=1` 관측을 기준으로 한다.
- capacity smoke 완료 후 대상 provider의 `in_flight=0`, `queued=0` 회복을 확인한다.
- Qwen 계열 provider-pool smoke는 thinking/reasoning 텍스트가 포함될 수 있다. 추론 출력 자체를 실패로 보지 말고 HTTP 성공, model alias, final marker 포함 여부, provider node log/run count 증가로 판정한다. 응답 전체가 특정 token과 정확히 같은지 비교하는 strict exact-match는 이 profile의 기본 판정으로 쓰지 않는다.
- bootstrap 사용자 명령은 완성된 URL과 positional token 하나만 포함한다.
## 기준 출력 예시

View file

@ -0,0 +1,80 @@
test_env: dev
profile: dev-runtime-provider-pool
last_updated_at: "2026-06-24"
source:
remote_runner:
ssh: toki@toki-labs.com
repo_root: /Users/toki/agent-work/iop-dev
clean_sync:
- git fetch origin main
- git reset --hard origin/main
- git clean -fd
dirty_policy: discard
edge:
id: edge-toki-labs-dev
config_path: build/dev-runtime/edge.yaml
control_plane_http: http://127.0.0.1:18001
control_plane_status_url: http://127.0.0.1:18001/edges/edge-toki-labs-dev/status
bootstrap_http_public: http://toki-labs.com:18082
openai_base_url_public: http://toki-labs.com:18083/v1
openai_base_url_runner: http://127.0.0.1:18083/v1
edge_node_tcp_public: toki-labs.com:18084
admin_addr_runner: 127.0.0.1:19093
build:
binaries:
edge: build/dev-runtime/bin/edge
node_macos: build/dev-runtime/bin/iop-node
node_linux_arm64: build/dev-runtime/bin/iop-node-linux-arm64
node_windows_amd64: build/dev-runtime/bin/iop-node-windows-amd64.exe
model:
alias: qwen3.6:35b
provider_capacity_total: 7
capacity_smoke:
endpoints:
- /v1/responses
- /v1/chat/completions
concurrent_requests: capacity_plus_one
expected_total_in_flight: 7
expected_min_queued: 1
prompt_policy: long_reasoning_allowed
exact_output_match: false
nodes:
- id: mac-codex-node
alias: mac-codex
role: cli
ssh: toki@toki-labs.com
workspace: /Users/toki/agent-work/iop-workspace/nomadcode
provider_pool_candidate: false
adapters:
- cli
- id: gx10-vllm-node
alias: gx10-vllm
role: vllm-provider
ssh: toki@192.168.0.91
workspace: /home/toki/iop-gx10-vllm
provider_pool_candidate: true
provider:
id: gx10-vllm
type: vllm
endpoint: http://192.168.0.91:8001/v1
served_model: nvidia/Qwen3.6-35B-A3B-NVFP4
capacity: 4
- id: onexplayer-lemonade-node
alias: onexplayer-lemonade
role: lemonade-provider
ssh: r0bin@192.168.0.59
ssh_origin: current_host
workspace: C:/Users/r0bin/iop-field
provider_pool_candidate: true
provider:
id: onexplayer-lemonade
type: lemonade
endpoint: http://192.168.0.59:13305/v1
served_model: Qwen3.6-35B-A3B-MTP-GGUF
capacity: 3
windows_process_start: Win32_Process.Create

View file

@ -3,7 +3,7 @@ test_env: dev
test_profile: node-smoke
domain: node
verification_type: smoke
last_rule_updated_at: 2026-06-12
last_rule_updated_at: 2026-06-24
---
# node-smoke dev 테스트
@ -40,6 +40,30 @@ last_rule_updated_at: 2026-06-12
- model endpoint: dev OpenAI-compatible base URL 후보 `http://toki-labs.com:18083/v1`
- credential: token/secret 원문은 문서에 기록하지 않는다.
## dev-runtime Node 접속 기준
dev-runtime의 실제 3-node 연결을 점검할 때는 원격 runner `ssh toki@toki-labs.com``/Users/toki/agent-work/iop-dev` checkout과 `build/dev-runtime/edge.yaml`을 기준으로 한다. Node는 dev-runtime provider pool 기준에서 Edge-Node TCP `toki-labs.com:18084`로 붙는다.
- mac CLI node: `mac-codex-node` / `mac-codex`
- SSH/user: `ssh toki@toki-labs.com`
- 목적: `cli` adapter, `codex app-server`
- workspace: `/Users/toki/agent-work/iop-workspace/nomadcode`
- GX10 vLLM node: `gx10-vllm-node` / `gx10-vllm`
- SSH/user: `ssh toki@192.168.0.91`
- provider endpoint: `http://192.168.0.91:8001/v1`
- served model: `nvidia/Qwen3.6-35B-A3B-NVFP4`
- workspace: `/home/toki/iop-gx10-vllm`
- OneXPlayer Lemonade node: `onexplayer-lemonade-node` / `onexplayer-lemonade`
- SSH/user: `ssh r0bin@192.168.0.59`
- 접속 기준: 현재 작업 호스트에서 직접 SSH
- provider endpoint: `http://192.168.0.59:13305/v1`
- served model: `Qwen3.6-35B-A3B-MTP-GGUF`
- workspace: `C:/Users/r0bin/iop-field`
GX10은 Linux/ARM64 bootstrap, OneXPlayer는 Windows native PowerShell bootstrap을 기본으로 한다. OneXPlayer는 현재 작업 호스트에서 직접 접속해 세팅하며, 원격 runner나 Edge host에서 `node-onexplayer-lemonade.yaml`로 proxy 실행하지 않는다.
OneXPlayer에서 SSH 세션 안의 `Start-Process``iop-node.exe`를 띄우면 SSH 세션 종료와 함께 process가 정리될 수 있다. dev 반복 배포에서는 `Win32_Process.Create` 또는 동등한 세션 독립 실행 방식으로 `C:/Users/r0bin/iop-field`에서 `iop-node.exe --config node.yaml serve`를 시작하고, WMI/process query와 `iop-node.log``connected to edge` 로그로 유지 여부를 확인한다.
## 명령
- setup:

View file

@ -1,6 +1,6 @@
---
test_env: dev
last_rule_updated_at: 2026-06-12
last_rule_updated_at: 2026-06-24
---
# dev 테스트 규칙
@ -13,6 +13,7 @@ last_rule_updated_at: 2026-06-12
- dev 테스트/검증은 이 파일을 기준으로 판단한다.
- 작업 완료 검증은 변경 범위 기준으로 선택한다.
- 필수 검증을 실행하지 못하면 차단 사유로 보고한다.
- 새 테스트 환경을 추가하거나 환경 구조를 바꿀 때는 `agent-test/README.md``agent-test/_templates/`의 확장 기준을 따른다.
- 최종 보고에는 실행 명령, 결과, 생략 사유, 남은 위험을 남긴다.
- 환경값, secret, 개인 endpoint는 tracked docs/roadmap에 쓰지 않는다.
- token/secret 원문은 원격 환경에서 주입하고, shell stdout/stderr와 최종 보고에 출력하지 않는다.
@ -23,7 +24,7 @@ last_rule_updated_at: 2026-06-12
- host: dev runtime evidence는 원격 runner `ssh toki@toki-labs.com``/Users/toki/agent-work/iop-dev` 기준으로 수행한다.
- repo root: 명령은 원격 checkout `/Users/toki/agent-work/iop-dev` 기준으로 실행한다.
- sync 기준: 원격 runner를 사용하는 경우 local uncommitted 변경은 원격 repo에 자동 반영되지 않는다. 동기화되지 않았으면 현재 변경분 검증으로 보지 않는다.
- sync 기준: dev 배포 전 원격 runner checkout은 `git fetch origin main`, `git reset --hard origin/main`, `git clean -fd`로 clean 상태를 만든 뒤 빌드한다. dev runner의 dirty 변경은 보존 대상으로 보지 않는다.
- env file: dev stack은 `docker compose --env-file .env.dev.example ...`로 명시한다.
- compose identity: `COMPOSE_PROJECT_NAME=iop-dev-agent`, `IOP_COMPOSE_NETWORK=iop-dev-agent-net`.
- port: web/dev preview `13001`, Control Plane HTTP `18001`, Portal/Control Plane wire test endpoint `19001`, CP-Edge wire `19002`, Edge-Node TCP transport `19003`, Postgres host publish `15401`, Redis host publish `16301`.
@ -50,10 +51,47 @@ last_rule_updated_at: 2026-06-12
| PostgreSQL host publish | `15400` | `15401` |
| Redis host publish | `16300` | `16301` |
dev-runtime provider pool은 compose Edge-Node TCP `19003`이 아니라 native Edge listen `18084`를 사용한다. 세부 기준은 `agent-test/dev/edge-smoke.md``agent-test/dev/node-smoke.md`를 따른다.
## 런타임 프로필
- local quick check: 현재 checkout에서 Go quick check를 우선 실행한다.
- remote runner: dev runtime evidence, Flutter client, Docker compose, field/bootstrap, 외부 runtime evidence는 `ssh toki@toki-labs.com``/Users/toki/agent-work/iop-dev` 기준으로 수행한다.
- compose dev stack: `.env.dev.example`, `COMPOSE_PROJECT_NAME=iop-dev-agent`, `IOP_COMPOSE_NETWORK=iop-dev-agent-net`, Edge-Node TCP `19003`을 사용한다.
- Edge direct dev profile: artifact/bootstrap `18082`, OpenAI-compatible `18083`, metrics `19101`을 필요할 때만 사용한다.
- dev-runtime provider pool: `/Users/toki/agent-work/iop-dev/build/dev-runtime/edge.yaml`과 Edge-Node TCP `toki-labs.com:18084`를 사용한다. 3-node/provider 세부는 `agent-test/dev/edge-smoke.md``agent-test/dev/node-smoke.md`를 따른다.
- external provider field: GX10 vLLM은 `ssh toki@192.168.0.91`, OneXPlayer Lemonade는 현재 작업 호스트에서 `ssh r0bin@192.168.0.59`로 직접 접속해 확인한다. OneXPlayer 접속은 원격 runner 경유를 필수 조건으로 보지 않는다.
## 프리플라이트
- plan 작성 시 필수 검증이 현재 checkout을 벗어나 원격 runner, field/bootstrap, 외부 provider, Docker/code-server, emulator/device, 공유 장기 runtime을 사용하면 먼저 테스트 환경 프리플라이트를 계획에 기록한다.
- 테스트 환경 프리플라이트에는 runner, repo root/workdir, branch/HEAD/dirty 상태, local 변경과 원격 source 동기화 여부, binary/artifact 경로와 필요한 help/version 출력, config path, runtime identity, port/process 상태, 외부 host, OS/arch 가정을 포함한다.
- 프리플라이트에서 dirty/divergent checkout 또는 stale artifact가 확인되면 dev 배포에서는 먼저 원격 checkout을 clean sync하고 dev-runtime 바이너리를 rebuild한다. 잘못된 identity, missing command, closed port, host OS 불일치, source 미동기화가 확인되면 plan은 setup/sync/rebuild 단계를 만들거나 blocker로 보고한다. profile 값을 이미 참이라고 가정한 검증 명령만 쓰지 않는다.
## 노드/Provider 인벤토리 위치
- dev-runtime provider pool machine-readable 기준: `agent-test/dev/inventory.yaml`
- dev-runtime provider pool과 Edge/OpenAI-compatible 입력 표면 상세 기준: `agent-test/dev/edge-smoke.md`
- dev-runtime Node 접속과 bootstrap 상세 기준: `agent-test/dev/node-smoke.md`
- 공통 provider config 계약 기준: `agent-test/dev/platform-common-smoke.md`
- compose dev stack 기준: `agent-test/dev/control-plane-smoke.md`, `agent-test/dev/client-smoke.md`, `agent-test/dev/testing-smoke.md`
## Field/bootstrap 반복 테스트 기준
- field/bootstrap 검증은 원격 runner의 `/Users/toki/agent-work/iop-dev` checkout과 dev-runtime artifact를 기준으로 수행한다. 세부 Edge/Node profile은 `agent-test/dev/edge-smoke.md``agent-test/dev/node-smoke.md`를 따른다.
- compose dev stack은 Edge-Node TCP `19003`을 사용하고, dev-runtime provider pool은 native Edge listen `18084`를 사용한다. 두 프로필을 섞어서 판정하지 않는다.
- Node bootstrap은 Edge의 `node register`가 출력한 OS별 완성 명령을 그대로 사용한다. 사용자에게 안내하는 명령에는 임의 placeholder, 수동 token 치환, `IOP_*=` named environment parameter를 넣지 않는다.
- token, API key, private credential 원문은 tracked docs, roadmap, 테스트 규칙에 기록하지 않는다. 실행 증거에는 token을 마스킹하거나 명령 생성 사실만 남긴다.
- Linux/macOS Node는 생성된 `curl | bash` 계열 명령을 사용한다. Windows Node는 native PowerShell bootstrap을 기본으로 사용한다.
- dev-runtime 배포는 clean sync 후 `build/dev-runtime/bin/edge`, mac node, Linux/Windows node binary를 같은 source 기준으로 rebuild한다. 배포 후 `edge config refresh --help`, `19093` port, `config refresh --mode dry-run`을 확인한다.
- provider capacity, model/provider mapping은 `config check``config refresh --mode dry-run`을 통과한 뒤 `config refresh --mode apply`로 반영한다. refresh subcommand 또는 `19093` admin port가 없으면 바이너리 rebuild 누락으로 보고 먼저 rebuild한다.
- Edge process restart 또는 일시 단절 후에는 Node reconnect 정책을 검증한다. retry 한계를 넘겨 Node process가 종료된 경우에만 해당 Node host에서 새 bootstrap 실행이 필요하다.
## 라우팅
- node / smoke / node 실행 파이프라인 baseline: `agent-test/dev/node-smoke.md`
- edge / smoke / edge 실행 그룹과 입력 표면 baseline: `agent-test/dev/edge-smoke.md`
- dev-runtime provider pool, 3-node 연결, GX10 vLLM, OneXPlayer Lemonade, mac CLI node 점검: `agent-test/dev/edge-smoke.md`, `agent-test/dev/node-smoke.md`
- control-plane / smoke / control-plane health와 wire baseline: `agent-test/dev/control-plane-smoke.md`
- client / smoke / Flutter client와 IOP console package baseline: `agent-test/dev/client-smoke.md`
- platform-common / smoke / 공통 설정과 protobuf 계약 baseline: `agent-test/dev/platform-common-smoke.md`

View file

@ -1077,13 +1077,8 @@ echo '{"id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{}}}'
}
defer proc.close()
time.Sleep(150 * time.Millisecond)
markerPath := filepath.Join(workspace, "cwd.txt")
cwdBytes, err := os.ReadFile(markerPath)
if err != nil {
t.Fatalf("failed to read marker file: %v", err)
}
cwdBytes := readFileEventually(t, markerPath, 2*time.Second)
cwd := strings.TrimSpace(string(cwdBytes))
resolvedWorkspace, err := os.Readlink(workspace)
@ -1095,3 +1090,21 @@ echo '{"id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{}}}'
t.Errorf("expected process cwd to be %q or %q, got %q", workspace, resolvedWorkspace, cwd)
}
}
func readFileEventually(t *testing.T, path string, timeout time.Duration) []byte {
t.Helper()
deadline := time.Now().Add(timeout)
var lastErr error
for {
b, err := os.ReadFile(path)
if err == nil {
return b
}
lastErr = err
if time.Now().After(deadline) {
t.Fatalf("failed to read file %s within %s: %v", path, timeout, lastErr)
}
time.Sleep(10 * time.Millisecond)
}
}

View file

@ -16,7 +16,10 @@ import (
func (c *CLI) executeOneShot(ctx context.Context, spec runtime.ExecutionSpec, profile config.CLIProfileConf, sink runtime.EventSink) error {
prompt := extractPrompt(spec.Input)
args := append(append([]string{}, profile.Args...), prompt)
args := append([]string{}, profile.Args...)
if prompt != "" {
args = append(args, prompt)
}
_, err := c.executeCommand(ctx, spec, profile, args, prompt, sink)
return err
}

View file

@ -103,6 +103,20 @@ curl -fsS http://toki-labs.com:18000/edges/edge-toki-labs/status
현재 GX10 vLLM과 OneXPlayer Lemonade를 같은 model alias로 묶어 검증하는 dev-runtime 기준은 원격 runner의 동기화된 iop checkout과 `build/dev-runtime/edge.yaml`이다.
배포 전 원격 runner checkout은 보존 대상이 아니다. 항상 clean sync 후 dev-runtime binary를 다시 빌드한다.
```bash
cd /Users/toki/agent-work/iop-dev
git fetch origin main
git reset --hard origin/main
git clean -fd
go build -trimpath -o build/dev-runtime/bin/edge ./apps/edge/cmd/edge
go build -trimpath -o build/dev-runtime/bin/iop-node ./apps/node/cmd/node
GOOS=linux GOARCH=arm64 go build -trimpath -o build/dev-runtime/bin/iop-node-linux-arm64 ./apps/node/cmd/node
GOOS=windows GOARCH=amd64 go build -trimpath -o build/dev-runtime/bin/iop-node-windows-amd64.exe ./apps/node/cmd/node
```
기준 주소:
- Control Plane status: 원격 host의 `http://127.0.0.1:18001`
@ -118,9 +132,17 @@ curl -fsS http://toki-labs.com:18000/edges/edge-toki-labs/status
- provider candidate: `onexplayer-lemonade` on `onexplayer-lemonade-node`, capacity `3`
- connected non-candidate: `mac-codex-node`
OneXPlayer Lemonade Node는 원격 runner나 Edge host에서 proxy process로 띄우지 않는다. Windows host에 직접 SSH로 접속한 뒤, 그 host 안에서 generated PowerShell bootstrap 명령을 실행한다. 이때 Lemonade provider endpoint는 Windows host 로컬에서 접근 가능한 값을 기준으로 검증한다.
OneXPlayer Lemonade Node는 원격 runner나 Edge host에서 다시 SSH하거나 proxy process로 띄우지 않는다. 현재 작업 호스트에서 Windows host에 `ssh r0bin@192.168.0.59`로 직접 접속한 뒤, 그 host 안에서 generated PowerShell bootstrap 명령을 실행한다. 이때 Node 작업 경로는 `$HOME\iop-field`이며, Lemonade provider endpoint는 Windows host 로컬에서 접근 가능한 값을 기준으로 검증한다.
capacity, provider mapping, model alias 변경은 실행 중 Edge에 `config refresh`로 반영한다. 후보 `edge.yaml` 수정 후 `config check`, dry-run, apply 순서로 진행한다. listen port, Edge identity, bootstrap artifact dir처럼 live apply가 위험한 값은 `restart_required`로 보고되며 Edge process 재시작이 필요하다.
SSH 세션 안의 `Start-Process`는 세션 종료와 함께 `iop-node.exe`가 정리될 수 있다. 반복 배포에서는 Windows host에서 `Win32_Process.Create` 방식으로 세션 독립 실행한다.
```powershell
$work = "$HOME\iop-field"
$cmd = 'cmd.exe /c "cd /d C:\Users\r0bin\iop-field && iop-node.exe --config node.yaml serve >> iop-node.wmi.stdout.log 2>> iop-node.wmi.stderr.log"'
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = $cmd; CurrentDirectory = $work }
```
capacity, provider mapping, model alias 변경은 후보 `edge.yaml` 수정 후 `config check`, refresh dry-run, refresh apply 순서로 반영한다. `config refresh` subcommand나 `19093` admin port가 없으면 dev-runtime binary rebuild 누락으로 보고 clean sync/rebuild부터 다시 수행한다.
dev-runtime 번들은 바이너리를 `build/dev-runtime/bin/edge`에 둔다. 명령은 그 경로를 `EDGE_BIN`으로 잡고 실행한다.
@ -140,12 +162,17 @@ Node host OS 재부팅은 필요하지 않다. Edge process 재시작이나 일
3. Edge process를 재시작하고 bootstrap 재실행 없이 3개 Node 연결이 회복되는지 확인한다.
4. `http://toki-labs.com:18083/healthz``/v1/models`를 확인한다.
5. capacity를 `gx10-vllm=4`, `onexplayer-lemonade=3`로 맞춘 후보 config를 refresh apply하고 Edge process가 유지되는지 확인한다.
6. `qwen3.6:35b`로 동시 4개 `/v1/chat/completions` 요청을 보낸다.
7. capacity `4/3` 기준이면 동시 in-flight 전제에서 aggregate 배정 기대값은 `gx10-vllm` 2개, `onexplayer-lemonade` 2개다.
6. `qwen3.6:35b``/v1/responses``/v1/chat/completions` 각각에 provider capacity 총합 + 1개 동시 요청을 보낸다.
7. capacity `gx10-vllm=4`, `onexplayer-lemonade=3` 기준이면 endpoint별 8개 동시 요청에서 Control Plane status의 provider snapshot이 총 `in_flight=7`, `queued>=1`을 한 번 이상 보여야 한다.
8. 요청 완료 후 provider snapshot이 `in_flight=0`, `queued=0`으로 회복되는지 확인한다.
Qwen 계열 모델은 thinking/reasoning 텍스트를 포함해 응답할 수 있다. 이 dev smoke에서는 thinking 출력을 실패로 보지 않고, HTTP 성공, final marker 포함, provider log/run count 증가를 기준으로 판정한다.
현재 공개 API는 개별 request의 최종 `node_id`를 응답에 노출하지 않는다. 요청별 배정을 확정해야 할 때는 Edge dispatch trace/log를 추가한 뒤 판정한다.
## 7. Smoke
## 7. 기본 Smoke
아래는 dev-runtime provider pool이 아니라 기본 field/local OpenAI-compatible profile 기준 예시다. dev-runtime provider pool 검증은 위의 `18083`/`qwen3.6:35b` 기준을 따른다.
```bash
curl -fsS http://toki-labs.com:18081/v1/models