docs: record dev deployment blockers

This commit is contained in:
toki 2026-08-02 13:39:22 +09:00
parent 04b8baceff
commit 459c3cb312
4 changed files with 64 additions and 18 deletions

View file

@ -1,6 +1,6 @@
---
name: dev-runtime-deploy
version: 1.0.7
version: 1.0.8
description: dev 배포, dev-runtime 배포, Edge/Node dev 환경 배포 요청에서 dev commit count 기반 git-flow release를 만들고 clean sync, 전체 테스트, rebuild, 원격 배포, OpenAI-compatible capacity smoke, release finish와 tag push를 수행하는 절차
---
@ -23,6 +23,7 @@ dev-runtime provider pool을 `dev` 기준 git-flow release로 배포한다. `dev
- `model`: OpenAI-compatible model alias. 지정하지 않으면 dev 환경 문서의 기준 model을 사용한다.
- `capacity_targets`: provider별 기대 capacity. 지정하지 않으면 dev 환경 인벤토리 또는 `agent-test/dev/*` 문서에서 읽는다.
- 배포 기준 branch는 항상 `dev`이며 다른 `source_ref`로 대체하지 않는다.
- dev-runtime 배포 범위에서 `apps/agent/**`와 정확한 package `packages/go/agenttask`는 제외한다. `packages/go/agentprovider/**`, `packages/go/agentruntime`, `packages/go/credentiallease`는 Edge/mac Node가 사용하므로 제외하지 않는다.
## Git Flow release 규칙
@ -46,6 +47,7 @@ dev-runtime provider pool을 `dev` 기준 git-flow release로 배포한다. `dev
- model: `/tmp/iop-inventory-query --env dev --model <model-alias>`
- [ ] 직접 실행 exit 2가 schema/file 오류일 때만 canonical inventory 전체 읽기로 fallback하고, exit 1은 zero-match로 보고한다.
- [ ] dev-runtime provider pool과 compose/local profile을 섞지 않는다. dev-runtime은 native Edge/Node runtime과 dev-runtime config를 기준으로 한다.
- [ ] 원격 runner의 non-login SSH는 Go/Flutter 경로가 비어 있을 수 있다. 실행 전에 `PATH=/opt/homebrew/bin:/Users/toki/SDK/flutter/bin:/Users/toki/go/bin:/Users/toki/.pub-cache/bin:$PATH`를 설정하거나 `/bin/zsh -lc`로 toolchain 경로를 확인한다.
- [ ] provider runtime option은 `agent-test/inventory-dev.yaml`의 model family별 값을 우선한다. 현재 Qwen은 mac-mlx-vllm의 `tool_call_parser=qwen`, `reasoning_parser=qwen3`, `default_chat_template_kwargs.enable_thinking=true`를 사용한다. GX10 Laguna는 `tool_call_parser=poolside_v1`, `reasoning_parser=poolside_v1`, `default_chat_template_kwargs.enable_thinking=true`, Pi `preserve_thinking=true`와 host `/home/toki/iop-gx10-vllm/laguna-s-2.1-thinking.jinja``<think>\n` generation prefix를 사용한다. Gemma/Qwen/Laguna parser와 chat template profile을 서로 복사하지 않는다.
- [ ] OneXPlayer SSH 접속 정보는 현재 host 기준 `ssh r0bin@192.168.0.59`이다. `toki` 사용자명 또는 remote runner를 경유한 OneXPlayer 접속을 사용하지 않는다.
- [ ] RTX5090 SSH는 현재 작업 호스트의 local SSH config alias `ssh iop-dev-rtx5090`을 사용한다. 이 alias는 public-key batch 인증을 사용하며, host identity와 공개키 지문은 inventory의 `rtx5090-lemonade-node.ssh_access`에서 확인한다.
@ -83,27 +85,51 @@ dev-runtime provider pool을 `dev` 기준 git-flow release로 배포한다. `dev
- 빌드·배포할 release HEAD를 `DEPLOY_SHA`, 그 tree를 `DEPLOY_TREE`로 기록한다.
4. **빌드 전 전체 테스트**
- clean source 기준으로 `go test ./...`를 실행한다.
- clean source 기준으로 Control Plane, Edge, Node와 공용 runtime package를 테스트한다. `apps/agent/**`와 정확한 package `packages/go/agenttask`는 dev-runtime 범위에서 제외한다.
- zsh의 newline word splitting이나 package-parallel 실행에 의존하지 않는다. 아래 package 목록을 한 package씩 순차 실행한다. `agentprovider/catalog`의 짧은 process probe는 package 병렬 부하에서 timeout이 날 수 있으므로 병렬 실패만으로 source 실패를 단정하지 않고 순차 실행 결과를 기준으로 한다.
```bash
while IFS= read -r package; do
go test -count=1 "$package"
done < <(
go list ./apps/control-plane/... ./apps/edge/... ./apps/node/... ./cmd/... ./packages/go/... ./scripts/... |
sed '/^iop\/packages\/go\/agenttask$/d'
)
```
- client/Flutter, proto, Makefile, script, config 변경이 배포 범위에 포함되면 해당 도메인 규칙의 전체 테스트도 추가한다.
- 전체 테스트가 실패하면 build/deploy를 진행하지 않고 실패 패키지와 핵심 오류를 보고한다.
5. **전체 rebuild**
- 같은 source ref에서 dev-runtime Edge binary, mac node binary, Linux ARM64 node binary, Windows AMD64 node binary를 모두 다시 빌드한다.
- 네 binary 모두 `-trimpath`로 빌드한다.
```bash
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
```
- stale binary가 의심되거나 `config refresh` subcommand, admin port, version 출력이 맞지 않으면 clean sync부터 다시 시작한다.
- 빌드 산출물 경로, timestamp, 크기, 실행 가능 여부를 기록한다.
- `go version -m`의 module source commit, SHA-256, 산출물 경로, timestamp, 크기, 실행 가능 여부를 기록하고 네 binary가 `DEPLOY_SHA`를 가리키는지 확인한다.
6. **빌드 후 기본 동작 테스트**
- 빌드된 Edge binary로 `config check`, `config refresh --help`, `config refresh --mode dry-run`을 실행한다.
- 빌드 후에도 `go test ./...`를 실행한다. 실행하지 못하면 사유와 남은 위험을 보고한다.
- `openai.enabled=true`이면 `openai.provider_id`가 비어 있지 않아야 한다. 누락 시 runtime의 ignored `edge.yaml`을 현재 provider identity에 맞게 보완하고 원본을 release별 경로로 백업한 뒤 다시 `config check`한다. token과 header는 출력하지 않는다.
- 빌드 후에도 4단계의 동일한 순차 package 테스트를 실행한다. 실행하지 못하면 사유와 남은 위험을 보고한다.
- dry-run이 `rejected` 또는 예상 밖 `restart_required`를 반환하면 배포를 멈추고 config diff를 보고한다.
- Flutter test/build가 tracked `ios/Flutter/*.xcconfig`, `macos/Flutter/*.xcconfig`에 CocoaPods include를 추가하거나 untracked `ios/Podfile`, `macos/Podfile`을 만들 수 있다. proto diff와 구분해 확인한 뒤 이번 검증이 만든 정확한 파일만 원복·제거하고 checkout을 clean으로 되돌린다.
7. **배포와 재시작**
- candidate Edge를 먼저 재시작해 `18082`, `18083`, `18084`, `19093` listener를 확인한 뒤 Node를 시작한다. 잘못된 config payload로 Node가 non-retryable `internal config error`를 받고 종료된 상태에서는 Edge만 고쳐도 Node가 자동 복귀하지 않는다.
- Edge와 mac-codex-node(CLI adapter + mac-mlx-vllm provider vllm-mlx process)는 원격 runner에서 빌드 산출물 기준으로 재시작한다.
- GX10 node는 Linux ARM64 node binary를 배포하고 기존 node process를 재시작한다.
- 처음 교체할 때 원본은 `pre-<release>`로 보존한다. 같은 release의 후속 candidate를 재배포하면 원본 backup을 덮어쓰지 말고 현재 실패 candidate를 `pre-<release>-<short-sha>`로 옮긴 뒤 교체한다.
- GX10 Laguna vLLM container를 재생성할 때 host template `/home/toki/iop-gx10-vllm/laguna-s-2.1-thinking.jinja`를 container `/run/iop/laguna-s-2.1-thinking.jinja`에 read-only bind하고 `--chat-template`로 지정한다. stock template로 되돌리면 enabled 요청이 첫 생성 토큰으로 `</think>`를 내보내 Pi thinking이 비는 회귀가 생길 수 있다.
- 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`를 시작한다.
- RTX5090 node는 현재 host에서 `ssh iop-dev-rtx5090`으로 접속한다. 2026-07-26 기준 IOP 관련 Windows 부팅 owner는 없고, operator는 `C:/Users/r0bin/iop-field/remote-llm-toggle.ps1` 또는 이를 한 번 호출하는 `RemoteLLM_mode.ahk`로 수동 전환한다. IOP dev 배포는 Startup shortcut, Run entry, Task Scheduler, Windows service를 생성하거나 복원하지 않는다. 기본 Toggle은 상태를 반전시키므로 배포 자동화에서 호출하지 말고 `-Action Status|Up|Down`을 명시한다. Node binary만 즉시 재시작할 때는 `Win32_Process.Create` 또는 동등한 세션 독립 방식을 사용하고 SSH 세션 내부의 `Start-Process`에 장기 실행을 의존하지 않는다.
- RTX5090 node는 현재 host에서 `ssh iop-dev-rtx5090`으로 접속한다. IOP 관련 Windows 부팅 owner는 두지 않고, operator는 `C:/Users/r0bin/iop-field/remote-llm-toggle.ps1` 또는 이를 한 번 호출하는 `RemoteLLM_mode.ahk`로 수동 전환한다. IOP dev 배포는 Startup shortcut, Run entry, Task Scheduler, Windows service를 생성하거나 복원하지 않는다. 기본 Toggle은 상태를 반전시키므로 배포 자동화에서 호출하지 말고 `-Action Status|Up|Down`을 명시한다. Node binary만 즉시 재시작할 때는 `Win32_Process.Create` 또는 동등한 세션 독립 실행 방식을 사용하고 SSH 세션 내부의 `Start-Process`에 장기 실행을 의존하지 않는다.
- RTX5090 Lemonade는 `host=0.0.0.0`, CUDA backend, Q5 GGUF + Q8 KV, context `262144` 기준을 inventory와 대조한다. localhost-only bind는 Node에서 provider endpoint에 접속할 수 없으므로 배포 완료로 보지 않는다.
8. **배포 후 연결 검증**
@ -128,11 +154,12 @@ dev-runtime provider pool을 `dev` 기준 git-flow release로 배포한다. `dev
- 하나라도 실패했거나 필수 검증이 실행되지 않았으면 finish하지 않고 `release/dev-<count>` branch를 유지한다.
- 현재 release HEAD가 `DEPLOY_SHA`와 같은지 확인한다. 달라졌으면 배포 산출물과 source가 달라진 것이므로 finish하지 않는다.
- finish 직전에 `git fetch origin dev main --tags`를 다시 실행하고 `origin/dev=DEV_BASE_SHA`, `origin/main=MAIN_BASE_SHA`, remote tag 없음이 모두 유지되는지 확인한다. 하나라도 달라졌으면 finish하지 않고 release branch를 유지한다.
- 모든 검증과 ref 고정이 성공하면 `git flow release finish --keepremote -m "Release dev-<count>" dev-<count>`로 local finish와 tag 생성을 수행한다.
- `git flow release finish -h`에서 `--keepremote` 지원 여부를 먼저 확인한다. 지원하면 `git flow release finish --keepremote -m "Release dev-<count>" dev-<count>`를 사용한다. 지원하지 않는 구현에서는 `-p/--push`를 주지 않은 `git flow release finish -m "Release dev-<count>" dev-<count>`가 원격 release branch를 건드리지 않는지 help로 확인한 뒤 사용한다. 옵션 파싱 실패는 ref mutation 전 실패인지 확인하고 같은 명령을 추측해 반복하지 않는다.
- 생성된 local `dev-<count>` tag의 tree가 `DEPLOY_TREE`와 같은지 확인한다. 다르면 원격에 push하지 않는다.
- tag tree가 같으면 `git push --atomic origin refs/heads/main:refs/heads/main refs/heads/dev:refs/heads/dev refs/tags/dev-<count>:refs/tags/dev-<count> :refs/heads/release/dev-<count>`로 production/develop/tag 반영과 remote release branch 삭제를 한 번에 수행한다.
- local finish 검증이나 atomic push가 실패하면 `DEPLOY_SHA`로 detach한 뒤 local `main=MAIN_BASE_SHA`, `dev=DEV_BASE_SHA`, `release/dev-<count>=DEPLOY_SHA`를 복원하고 생성된 local tag를 삭제한 다음 release branch로 돌아간다. remote는 atomic push 전 상태여야 한다.
- 성공 후 local과 origin의 `main`, `dev` 반영 상태, local/remote tag 존재, local/remote release branch 삭제를 확인한다.
- 원격 runner도 `git fetch --prune origin dev main --tags`, clean `dev` sync, local release branch 삭제 순서로 정리한다. ignored runtime config와 build artifact는 삭제하지 않는다.
11. **결과 보고**
- source ref, clean sync 결과, 테스트 결과, 빌드 산출물, process/port 상태, connected node 목록, provider capacity snapshot, capacity smoke 관측값을 보고한다.
@ -140,13 +167,27 @@ dev-runtime provider pool을 `dev` 기준 git-flow release로 배포한다. `dev
- 실패한 단계가 있으면 다음 단계를 진행했는지 여부를 명확히 구분한다.
- capacity smoke가 타이밍 문제로 관측 실패했으면 요청 성공과 별도로 `capacity 관측 미충족`으로 보고하고, 프롬프트 길이 또는 status polling 간격 조정을 제안한다.
## 재발 차단 진단
| 증상 | 원인 판정 | 조치 |
|---|---|---|
| non-login SSH에서 `go: command not found` | runner PATH 미초기화 | PATH를 명시하거나 login zsh에서 `command -v go/flutter/protoc`를 확인한다. |
| `agentprovider/catalog` version probe timeout | 여러 package 동시 실행에 따른 짧은 probe 경합 가능성 | 전체 package를 순차 재실행한다. 순차 실패일 때만 source blocker로 판정한다. |
| Windows cross-build가 Unix file owner 타입에서 실패 | OS별 credential key-file 검증 분리 누락 | `packages/go/credentiallease`의 Unix/Windows test와 실제 Windows ACL smoke를 통과시킨 뒤 다시 빌드한다. |
| Edge 로그의 `openai_compat adapter instance key "<provider-id>" conflicts with provider id` | profile-backed provider와 같은 이름의 정상 backing을 중복 emit하는 mapper 또는 stale Edge | provider/adapter 설정이나 agent orchestration을 삭제하지 않는다. `go test -race ./apps/edge/internal/node`와 candidate Edge source identity를 확인하고 Edge를 rebuild/restart한다. |
| Node가 `internal config error` 후 재접속하지 않음 | config rejection은 일시 네트워크 단절과 달리 process를 종료할 수 있음 | fixed Edge를 먼저 올린 뒤 mac/GX10/OneXPlayer/RTX5090 Node를 각각 다시 시작한다. |
| Edge service test 종료 후 `in_flight`가 남음 | 같은 clock 값에서 run ID가 충돌한 회귀 가능성 | `go test -race ./apps/edge/internal/service`와 run ID 동시성 회귀 테스트를 확인한다. |
| `git flow release finish --keepremote`가 option parse 단계에서 실패 | 설치된 git-flow 구현이 해당 long option을 지원하지 않음 | ref 무변경을 확인하고 help에 따라 no-push finish를 사용한 뒤 atomic push에서 release 삭제를 수행한다. |
위 오류는 dev-runtime Edge/Node 배포 문제다. 이를 통과시키기 위해 `agent-ops/rules/common/**`, common orchestration `dispatch.py`, `apps/agent/**`를 수정·삭제하지 않는다.
## 실행 결과 검증
- [ ] 원격 runner local `dev``origin/dev`로 clean sync되었는가
- [ ] `origin/main``origin/dev`의 ancestor이고 local `main`/`dev`가 원격 기준과 일치하는가
- [ ] `dev` HEAD commit count로 `dev-<count>` version을 계산했는가
- [ ] `release/dev-<count>` branch를 새로 게시했거나 동일 version의 기존 branch를 안전하게 재개했는가
- [ ] 빌드 전 `go test ./...`와 필요한 추가 전체 테스트가 통과했는가
- [ ] 빌드 전 dev-runtime 범위의 순차 Go 테스트와 필요한 추가 전체 테스트가 통과했는가
- [ ] dev-runtime Edge/mac/Linux ARM64/Windows AMD64 binary가 같은 release branch commit에서 rebuild되었는가
- [ ] 빌드 후 config check, refresh help, refresh dry-run, 전체 테스트가 통과했는가
- [ ] Edge, mac-codex-node, GX10 vLLM node, OneXPlayer Lemonade node, RTX5090 Lemonade node가 재시작되고 4개 node(mac-codex, gx10-vllm, onexplayer-lemonade, rtx5090-lemonade)가 connected 상태인가

View file

@ -101,8 +101,7 @@ dev-runtime provider pool과 4-node 연결 상태를 점검할 때는 `agent-tes
- priority baseline: `0`
- load baseline: backend `cuda`, Q5 GGUF, Q8 KV, ctx size `262144`, `llamacpp_args="--chat-template-kwargs '{\"preserve_thinking\":true}' --kv-unified --min-p 0.00 --repeat-penalty 1.0 --spec-type none --temp 0.6 --top-k 20 --top-p 0.95 -b 512 -cb -ctk q8_0 -ctv q8_0 -fa on -np 1 -ub 256"`, Lemonade `host=0.0.0.0`
- long-context admission baseline: `total_context_tokens=262144`, `long_context_capacity=1`
- process baseline: 2026-07-26 기준 IOP 관련 Windows 부팅 자동 실행은 비활성이고, 수동 `remote-llm-toggle.ps1`이 Lemonade Server, Ornith, IOP Node를 순차 UP/DOWN한다. 제거된 Startup shortcut, Run entry, Task Scheduler, Windows service는 IOP dev 배포가 생성·재생성하지 않는다.
- sampling rollout: 2026-07-23 saved recipe와 실제 llama-server process에서 `--temp 0.6 --top-p 0.95 --top-k 20` 적용을 확인했다.
- process baseline: IOP 관련 Windows 부팅 자동 실행은 비활성이고, 수동 `remote-llm-toggle.ps1`이 Lemonade Server, Ornith, IOP Node를 순차 UP/DOWN한다. Startup shortcut, Run entry, Task Scheduler, Windows service는 IOP dev 배포가 생성·재생성하지 않는다.
- workspace: `C:/Users/r0bin/iop-field`
GX10은 Laguna S 2.1 NVFP4 + DFlash NVFP4를 사용한다. OneXPlayer와 RTX5090 Lemonade는 Ornith Q5 GGUF를 사용하고 runtime speculative decoding은 `--spec-type none`으로 끈다. provider family별 parser/template을 섞지 않는다.
@ -130,6 +129,7 @@ GX10 Laguna 공식 vLLM baseline은 `temperature=0.7`, `top_p=0.95`, model `gene
- 변경한 edge 패키지 또는 `go test ./apps/edge/...`를 실행한다.
- registry, service, transport, console, HTTP/A2A 입력 표면을 바꾼 경우 edge-node 메시지 2회 왕복과 command 응답을 확인한다.
- OpenAI-compatible 경계를 바꾼 경우 dev `18083` 기준 `iop-edge smoke openai` 또는 동등한 `/healthz`, `/v1/models`, `/v1/responses` 확인으로 edge service와 node adapter 경로 수렴을 확인한다.
- runtime `edge.yaml`에서 `openai.enabled=true`이면 `openai.provider_id`가 현재 CLI/provider identity와 일치하는지 `config check`로 검증한다. 이 값이 빠진 candidate는 배포하지 않는다.
- OpenAI-compatible tool-call 경계(`apps/edge/internal/openai/**`의 text tool-call 합성/validation)를 바꾼 경우 dev `18083` `/v1/chat/completions`에 Pi/Cline형 `tools[]` 요청을 non-stream/stream 각각 최소 1회 보내 raw text tool-call boundary smoke를 수행한다. token은 원격 환경 변수에서 주입하고 원문을 명령/로그/보고에 남기지 않는다.
- dev provider-pool tool-call drift를 확인할 때는 active model group별 provider만 섞는다. Laguna group은 `gx10-vllm` direct와 Edge `laguna-s:2.1`, Ornith group은 `onexplayer-lemonade`/`rtx5090-lemonade` direct와 Edge `ornith:35b`, Qwen group은 `mac-mlx-vllm` direct와 Edge `qwen3.6:35b`를 각각 검증한다.
- bootstrap/artifact 경계를 바꾼 경우 dev artifact/base URL 후보 `18082`가 local/test `18080` field baseline을 덮어쓰지 않는지 확인한다.
@ -149,10 +149,9 @@ GX10 Laguna 공식 vLLM baseline은 `temperature=0.7`, `top_p=0.95`, model `gene
- raw boundary smoke evidence는 tracked 문서가 아니라 ignored run 위치(`agent-test/runs/**`)나 code-review output path에 저장하고, 저장물에도 token 원문을 남기지 않는다.
- provider-pool dispatch는 `in_flight >= capacity`인 provider를 후보에서 제외하고, 남은 후보 중 가장 낮은 `in_flight` 레벨을 먼저 선택한다. 같은 `in_flight` 레벨 안에서는 낮은 priority 값과 rotation으로 분산한다.
- dev-runtime capacity smoke는 model group별로 분리한다. Laguna `laguna-s:2.1``gx10-vllm=4`이므로 5개 동시 요청에서 `in_flight=4`, `queued>=1`; Ornith `ornith:35b``onexplayer-lemonade=3` + `rtx5090-lemonade=1`이므로 5개 요청에서 `in_flight=4`, `queued>=1`; Qwen `qwen3.6:35b``mac-mlx-vllm=2`이므로 3개 요청에서 `in_flight=2`, `queued>=1`을 기준으로 한다. `ornith:35b``ornith-fast`는 provider-owned shared capacity 구현 전까지 model group별 capacity를 독립 집계하므로 같은 smoke에서 두 alias를 섞지 않는다.
- 2026-07-23 `ornith:35b` 동시 긴 한국어 streaming 시도는 downstream release 전 llama-server HTTP 500 `Failed to parse input at pos`로 끝났다. 이는 provider parser 오류이며 repetition loop 또는 output filter hit으로 판정하지 않는다. 원문 prompt/SSE는 ignored run에만 보관하고, 재시도·필터 설계에는 sanitised provider-error evidence만 사용한다.
- capacity smoke 완료 후 대상 provider의 `in_flight=0`, `queued=0` 회복을 확인한다.
- long-context admission 시나리오(normal 10-way, mixed long/normal, all-long-slot-full)와 최종 회복 근거는 `agent-test/dev/long-context-admission-smoke.md``scripts/e2e-long-context-admission-smoke.sh`를 사용한다.
- 2026-06-24 dev 13-way 확장 smoke는 Mac capacity가 `3`이던 이전 관측이다. 해당 run에서는 `/v1/chat/completions` 13개 요청 성공, peak `in_flight=10`, 실제 queue 대기 `3`개, 최종 회복 `in_flight=0`, `queued=0`이 관측되었다. 현재 Mac capacity `2` 기준 13-way 확장 smoke 기대치는 peak `in_flight=9`, queue 대기 `4`개 이상이다.
- `/v1/responses``/v1/chat/completions`는 각각 700~1200 token 수준의 구조화된 응답을 유도하고 50~100ms 간격으로 status를 polling한다. 각 요청의 HTTP 200, target provider별 capacity 미초과, 합산 peak와 queue, 최종 0 회복을 별도 증거로 남긴다.
- Qwen provider-pool smoke는 thinking/reasoning 텍스트가 포함될 수 있다. 추론 출력 자체를 실패로 보지 말고 HTTP 성공, model alias, final marker 포함 여부, provider node log/run count 증가로 판정한다. 응답 전체가 특정 token과 정확히 같은지 비교하는 strict exact-match는 이 profile의 기본 판정으로 쓰지 않는다.
- Qwen provider를 agent/tool-call 용도로 검증할 때는 일반 chat smoke와 별도로 forced tool call, auto tool call, streaming `delta.tool_calls`, multi-turn tool result 후 최종 답변을 확인한다. raw native marker나 reasoning text가 assistant content로 새면 해당 model/runtime의 parser/template profile 미확정으로 보고한다.
- Qwen runtime에는 Qwen 전용 parser/template 검증값만 사용한다. dev-corp Gemma 계열의 `tool_call_parser=gemma4`, `reasoning_parser=gemma4`, Gemma4 chat template/profile을 Qwen provider에 복사하지 않는다.
@ -172,6 +171,7 @@ edge> /nodes
- dev host 또는 Edge-Node TCP port 접근이 불가능하다.
- 외부 CLI profile 검증에 필요한 CLI 설치, 계정, provider 상태가 없다.
- dev artifact/model/metrics 포트가 기존 field baseline과 충돌한다.
- Node 등록 시 `openai_compat adapter instance key "<provider-id>" conflicts with provider id`가 발생한다. 이는 agent orchestration 차단이 아니라 Edge config mapper 또는 stale binary 문제다. provider/adapter 정의를 삭제하지 말고 candidate source identity와 `go test -race ./apps/edge/internal/node`를 확인한 뒤 Edge를 먼저 교체한다.
## 보고 항목

View file

@ -3,7 +3,7 @@ test_env: dev
test_profile: node-smoke
domain: node
verification_type: smoke
last_rule_updated_at: 2026-07-30
last_rule_updated_at: 2026-08-02
---
# node-smoke dev 테스트
@ -89,8 +89,7 @@ dev-runtime의 실제 4-node 연결을 점검할 때는 원격 runner `ssh toki@
- load baseline: checkpoint `LordNeel/Ornith-1.0-35B-GGUF-llamacpp-tp1:Q5_K_M`, backend `cuda`, ctx size `262144`, `llamacpp_args="--chat-template-kwargs '{\"preserve_thinking\":true}' --kv-unified --min-p 0.00 --repeat-penalty 1.0 --spec-type none --temp 0.6 --top-k 20 --top-p 0.95 -b 512 -cb -ctk q8_0 -ctv q8_0 -fa on -np 1 -ub 256"`
- network baseline: Lemonade `host=0.0.0.0`; localhost-only bind는 Node의 provider endpoint 접속 실패를 유발한다.
- long-context admission baseline: `total_context_tokens=262144`, `long_context_capacity=1`
- process baseline: 2026-07-26 기준 IOP Node, Lemonade Server, `startup.bat -> oto -> AHK` 체인의 Windows 부팅 자동 실행은 모두 비활성이다. 수동 운영 owner는 `C:/Users/r0bin/iop-field/remote-llm-toggle.ps1`이며 UP은 Lemonade Server -> Ornith load -> Node, DOWN은 Node -> Ornith unload -> Lemonade Server 순서다. IOP dev 배포는 Startup shortcut, Run entry, Task Scheduler, Windows service를 생성하지 않는다.
- sampling rollout: 2026-07-23 saved recipe와 실제 llama-server process에서 `--temp 0.6 --top-p 0.95 --top-k 20` 적용을 확인했다.
- process baseline: IOP Node와 Lemonade Server의 Windows 부팅 자동 실행은 비활성이다. 수동 운영 owner는 `C:/Users/r0bin/iop-field/remote-llm-toggle.ps1`이며 UP은 Lemonade Server -> Ornith load -> Node, DOWN은 Node -> Ornith unload -> Lemonade Server 순서다. IOP dev 배포는 Startup shortcut, Run entry, Task Scheduler, Windows service를 생성하지 않는다.
- workspace: `C:/Users/r0bin/iop-field`
GX10은 Laguna S 2.1 NVFP4 + DFlash NVFP4를 사용한다. OneXPlayer와 RTX5090 Lemonade는 Ornith Q5 GGUF를 사용하고 runtime speculative decoding은 `--spec-type none`으로 끈다. provider family별 parser/template을 섞지 않는다.
@ -101,14 +100,16 @@ mac-mlx-vllm provider는 mac-codex-node 소속 resource로, Edge host와 같은
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` 로그로 유지 여부를 확인한다.
RTX5090은 `ssh iop-dev-rtx5090`으로 batch 접속을 먼저 확인하고 Node process, toggle status의 `edge_connected`, Control Plane의 connected node 상태를 함께 확인한다. `RemoteLLM_mode.ahk`는 모니터 profile 적용 후 수동 toggle script를 한 번만 실행하며, 이전 Task Scheduler 호출과 직접 Lemonade load 명령은 없다. `startup.lnk``C:/Users/r0bin/iop-field/startup.lnk.pre-manual-remote-llm-20260726.bak`으로 이동되어 AHK가 Windows 부팅에서 실행되지 않는다. 자동화 검증에서는 상태에 따라 반전되는 기본 Toggle 대신 `-Action Status|Up|Down`을 명시하고, `ready`는 Node의 Edge TCP `18084` 연결까지 포함해야 한다. Lemonade 복구 시에는 `0.0.0.0:13305`, 정확한 Ornith profile, llama slot `1`/`n_ctx=262144`를 확인한다. 비밀번호나 개인키 경로는 문서와 실행 로그에 기록하지 않는다.
Node binary 교체 시 최초 원본은 `pre-<release>`로 한 번만 보존한다. 같은 release에서 후속 candidate를 배포하면 원본 backup을 덮어쓰지 말고 실패 candidate를 `pre-<release>-<short-sha>`로 별도 보존한다. candidate hash를 host에서 확인한 뒤에만 current binary로 이동한다.
Edge config payload가 거부되어 Node가 `internal config error`로 종료되면 일반 reconnect 대상이 아니다. fixed Edge가 올바른 source와 listener로 실행 중인지 먼저 확인하고 mac, GX10, OneXPlayer, RTX5090 Node를 각각 명시적으로 다시 시작한다.
RTX5090은 `ssh iop-dev-rtx5090`으로 batch 접속을 먼저 확인하고 Node process, toggle status의 `edge_connected`, Control Plane의 connected node 상태를 함께 확인한다. `RemoteLLM_mode.ahk`는 모니터 profile 적용 후 수동 toggle script를 한 번만 실행하며 Windows 부팅, Task Scheduler, 직접 Lemonade load 명령에 연결하지 않는다. 자동화 검증에서는 상태에 따라 반전되는 기본 Toggle 대신 `-Action Status|Up|Down`을 명시하고, `ready`는 Node의 Edge TCP `18084` 연결까지 포함해야 한다. Lemonade 복구 시에는 `0.0.0.0:13305`, 정확한 Ornith profile, llama slot `1`/`n_ctx=262144`를 확인한다. 비밀번호나 개인키 경로는 문서와 실행 로그에 기록하지 않는다.
GX10 Laguna 공식 vLLM baseline은 `temperature=0.7`, `top_p=0.95`, model `generation_config``top_k=20`, `tool_call_parser=poolside_v1`, `reasoning_parser=poolside_v1`, `enable_thinking=true`다. 현재 dev는 stock generation prefix가 think block을 즉시 닫는 현상을 막기 위해 `<think>\n` 로컬 템플릿을 추가한다. Pi의 `high` thinking은 `chat_template_kwargs.enable_thinking=true`로 전달하고 이전 reasoning block은 `preserve_thinking=true`로 재사용한다. think smoke는 `high`에서 `thinking_start`/`thinking_delta`/`thinking_end`, `off`에서 thinking event 0개와 최종 text를 확인하고, agentic multi-turn에서는 tool-call 전후 reasoning과 최종 text까지 확인한다.
OneXPlayer/RTX5090 Ornith 공식 sampling baseline은 `temperature=0.6`, `top_p=0.95`, `top_k=20`이다. caller의 명시값이 provider 기본값보다 우선한다.
`ornith:35b` 동시 긴 한국어 streaming 시도에서 llama-server의 pre-release HTTP 500 `Failed to parse input at pos`가 관측됐다. 이는 provider parser 오류이며 repetition loop, output guard hit, 또는 반복 출력 발생을 증명하지 않는다. 원문 요청·출력은 ignored run에만 두고, tracked 문서에는 sanitised error signature만 남긴다.
Qwen provider를 agent/tool-call 용도로 검증할 때는 일반 chat smoke와 별도로 forced tool call, auto tool call, streaming `delta.tool_calls`, multi-turn tool result 후 최종 답변을 확인한다. raw native marker나 reasoning text가 assistant content로 새면 해당 model/runtime의 parser/template profile 미확정으로 보고한다.
Qwen runtime에는 Qwen 전용 parser/template 검증값만 사용한다. dev-corp Gemma 계열의 `tool_call_parser=gemma4`, `reasoning_parser=gemma4`, Gemma4 chat template/profile을 Qwen provider에 복사하지 않는다.
@ -125,6 +126,7 @@ Qwen runtime에는 Qwen 전용 parser/template 검증값만 사용한다. dev-co
## 필수 검증
- 변경한 node 패키지 또는 `go test ./apps/node/...`를 실행한다.
- Windows Node candidate는 cross-build 성공만으로 완료하지 않는다. `packages/go/credentiallease` Windows test executable을 실제 Windows host에서 실행해 현재 사용자/System/Administrators만 읽을 수 있는 key file은 허용하고 Everyone read ACE는 거부하는지 확인한다.
- 실행 요청, stream, cancel, status, session, adapter registry 경로를 바꾼 경우 repo 내부 edge-node 진단과 full-cycle 실제 구동 기준을 함께 적용한다.
- CLI profile 변경 시 `/capabilities`, `/transport`, `/sessions`, persistent profile이면 `/terminate-session`을 확인한다.
- compose Edge profile로 연결하는 경우 Node가 `19003`을 사용하고 local/test `19090` field baseline으로 붙지 않는지 확인한다. dev-runtime provider pool native Edge로 연결하는 경우에는 Node가 `18084`를 사용한다. 두 profile의 transport를 섞어 연결 성공을 판정하지 않는다.
@ -156,6 +158,7 @@ edge> Convert token iop_manual_one and reply only with converted token
- dev host 또는 Edge-Node TCP port 접근이 불가능하다.
- 외부 CLI profile 검증에 필요한 CLI 설치, 계정, provider 상태가 없다.
- Edge 로그에 provider/adapter instance key 충돌이 있고 Node가 `internal config error`로 종료된다. Node 재시작을 반복하지 말고 Edge mapper/source identity를 먼저 복구한다.
## 보고 항목

View file

@ -14,6 +14,7 @@ last_rule_updated_at: 2026-08-02
- 사용자가 테스트 환경을 별도로 지정하지 않거나 단순히 `dev`, 테스트 환경, 배포, rollout, runtime 검증이라고 말하면 이 `dev` 환경을 기본 기준으로 삼는다.
- `dev-corp``dev`의 fallback이나 자동 접근 경로가 아니다. 사용자가 `dev-corp` 또는 동등한 회사망/public dev-corp 환경을 명시하지 않은 경우 `agent-test/dev-corp/**`, `iop.ai.kr`, `fe@172.24.63.178`, dev-corp port로 접근하지 않는다.
- 작업 완료 검증은 변경 범위 기준으로 선택한다.
- dev-runtime 배포 범위는 Control Plane, Edge, Node와 이들이 사용하는 공용 package다. `apps/agent/**`와 정확한 package `packages/go/agenttask`는 제외하지만, mac-codex-node가 사용하는 `packages/go/agentprovider/**``packages/go/agentruntime`은 포함한다. 제외 범위를 이유로 common orchestration 또는 `dispatch.py`를 수정·삭제하지 않는다.
- 필수 검증을 실행하지 못하면 차단 사유로 보고한다.
- 원격 runtime, smoke, full-cycle, field/bootstrap 같은 배포형 검증은 테스트 시작 시 확정한 배포 기준 ref의 최신 build로 환경 전체를 갱신한 뒤 시작한다. dev-runtime provider pool은 Edge와 macOS/Linux ARM64/Windows AMD64 Node를 모두 같은 source ref에서 rebuild·redeploy·restart하고, compose profile은 검증에 참여하는 Control Plane/Web/Edge image를 같은 source ref에서 rebuild·redeploy·restart한다. 이미 실행 중인 build는 최신으로 추정해 재사용하지 않는다.
- 배포형 검증 전에 source commit과 각 build의 checksum 또는 동등한 build identity가 배포 기준 ref와 일치하는지 확인한다. 하나라도 갱신 또는 일치 확인을 할 수 없으면 과거 build로 테스트하지 말고 차단 사유로 보고한다. 현재 checkout에서 수행하는 source 단위/패키지 테스트와 build 전 테스트는 이 선행조건의 대상이 아니다.
@ -35,6 +36,7 @@ last_rule_updated_at: 2026-08-02
- operator notice: 기존 테스트 사용자-facing 접속점(`13001`, `18001`, Edge OpenAI-compatible 후보 `18083`)은 변경하지 않는다. 이번 profile 변경은 Control Plane/Edge 관측용 metrics/Prometheus/Grafana 포트 추가로만 공지한다. Grafana/Prometheus는 원격 runner 내부 또는 SSH 터널로 접근한다.
- optional dev field ports: artifact/bootstrap HTTP `18082`, Edge OpenAI-compatible HTTP `18083`, Edge metrics `19101`. 이 값은 compose 기본 stack에 publish되지 않으며 field/bootstrap 또는 Edge direct dev profile이 필요할 때만 사용한다.
- runtime: Go quick check는 local toolchain을 우선한다. Flutter client 포함 검증과 Docker/code-server/full-cycle runtime은 원격 runner를 사용한다.
- remote toolchain: non-login SSH에서는 Homebrew Go와 Flutter PATH가 초기화되지 않을 수 있다. 원격 검증 시작 시 `/opt/homebrew/bin/go`, `/Users/toki/SDK/flutter/bin/flutter`, `/opt/homebrew/bin/protoc`를 확인하고 PATH를 명시한다. newline package 목록을 zsh 변수로 넘기거나 package-parallel test를 사용하지 말고 package별 순차 실행을 기본으로 한다.
- package manager: Go modules / Makefile / Flutter pub
- docker: 현재 작업 컨테이너에서는 Docker-in-Docker를 사용하지 않는다. Docker compose 검증은 원격 runner 또는 code-server 환경에서 수행한다.
- external service: dev compose Control Plane HTTP `http://toki-labs.com:18001`, dev Client wire `ws://toki-labs.com:19001/client`.
@ -97,7 +99,7 @@ dev-runtime provider pool은 compose Edge-Node TCP `19003`이 아니라 native E
- 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 실행이 필요하다.
- Edge process restart 또는 일시 단절 후에는 Node reconnect 정책을 검증한다. retry 한계를 넘긴 경우뿐 아니라 Node가 `internal config error` 같은 non-retryable config rejection으로 종료된 경우에도 fixed Edge를 먼저 확인한 뒤 해당 Node를 명시적으로 다시 시작한다.
## 라우팅