---
read_when:
    - qa-lab 또는 qa-channel 확장하기
    - 리포지토리 기반 QA 시나리오 추가하기
    - Gateway 대시보드를 중심으로 더 높은 현실성의 QA 자동화 구축하기
summary: qa-lab, qa-channel, 시드된 시나리오, 프로토콜 보고서를 위한 비공개 QA 자동화 구조
title: QA E2E 자동화
x-i18n:
    generated_at: "2026-04-26T11:27:36Z"
    model: gpt-5.4
    provider: openai
    source_hash: 3803f2bc5cdf2368c3af59b412de8ef732708995a54f7771d3f6f16e8be0592b
    source_path: concepts/qa-e2e-automation.md
    workflow: 15
---

비공개 QA 스택은 단일 단위 테스트보다 더 현실적이고 채널 형태에 가까운 방식으로 OpenClaw를 검증하도록 설계되었습니다.

현재 구성 요소:

- `extensions/qa-channel`: DM, 채널, 스레드, 반응, 편집, 삭제 표면을 갖춘 합성 메시지 채널
- `extensions/qa-lab`: transcript를 관찰하고, 수신 메시지를 주입하고, Markdown 보고서를 내보내기 위한 디버거 UI 및 QA bus
- `qa/`: kickoff 작업과 기본 QA 시나리오를 위한 리포지토리 기반 시드 자산

현재 QA 운영자 흐름은 2단 구성의 QA 사이트입니다:

- 왼쪽: 에이전트가 있는 Gateway 대시보드(Control UI)
- 오른쪽: Slack 스타일 transcript와 시나리오 계획을 보여주는 QA Lab

다음으로 실행합니다:

```bash
pnpm qa:lab:up
```

이 명령은 QA 사이트를 빌드하고, Docker 기반 Gateway 레인을 시작하며, 운영자나 자동화 루프가 에이전트에 QA 미션을 부여하고, 실제 채널 동작을 관찰하며, 무엇이 작동했는지, 실패했는지, 또는 계속 막혀 있었는지를 기록할 수 있는 QA Lab 페이지를 노출합니다.

매번 Docker 이미지를 다시 빌드하지 않고 QA Lab UI를 더 빠르게 반복 개발하려면, bind mount된 QA Lab 번들로 스택을 시작하세요:

```bash
pnpm openclaw qa docker-build-image
pnpm qa:lab:build
pnpm qa:lab:up:fast
pnpm qa:lab:watch
```

`qa:lab:up:fast`는 미리 빌드된 이미지에서 Docker 서비스를 유지하고 `extensions/qa-lab/web/dist`를 `qa-lab` 컨테이너에 bind mount합니다. `qa:lab:watch`는 변경 시 해당 번들을 다시 빌드하며, QA Lab asset hash가 바뀌면 브라우저가 자동으로 새로고침됩니다.

로컬 OpenTelemetry trace smoke를 실행하려면 다음을 사용하세요:

```bash
pnpm qa:otel:smoke
```

이 스크립트는 로컬 OTLP/HTTP trace 수신기를 시작하고, `diagnostics-otel` Plugin이 활성화된 상태로 `otel-trace-smoke` QA 시나리오를 실행한 다음, 내보낸 protobuf span을 디코딩하고 릴리스 중요 형상을 검증합니다. `openclaw.run`, `openclaw.harness.run`, `openclaw.model.call`, `openclaw.context.assembled`, `openclaw.message.delivery`가 반드시 존재해야 하며, 모델 호출은 성공한 턴에서 `StreamAbandoned`를 내보내면 안 됩니다. 원시 진단 ID와 `openclaw.content.*` 속성은 trace에 포함되면 안 됩니다. 결과는 QA 스위트 아티팩트 옆의 `otel-smoke-summary.json`에 기록됩니다.

실제 전송을 사용하는 Matrix smoke 레인을 실행하려면 다음을 사용하세요:

```bash
pnpm openclaw qa matrix
```

이 레인은 Docker에서 일회용 Tuwunel homeserver를 프로비저닝하고, 임시 driver, SUT, observer 사용자를 등록하고, 하나의 비공개 방을 만든 다음, QA Gateway child 내부에서 실제 Matrix Plugin을 실행합니다. live transport 레인은 child config를 테스트 중인 transport에만 한정하므로, Matrix는 child config에 `qa-channel` 없이 실행됩니다. 구조화된 보고서 아티팩트와 결합된 stdout/stderr 로그를 선택한 Matrix QA 출력 디렉터리에 기록합니다. 바깥쪽 `scripts/run-node.mjs` 빌드/런처 출력까지 캡처하려면 `OPENCLAW_RUN_NODE_OUTPUT_LOG=<path>`를 리포지토리 로컬 로그 파일로 설정하세요.
Matrix 진행 상황은 기본적으로 출력됩니다. `OPENCLAW_QA_MATRIX_TIMEOUT_MS`는 전체 실행 시간을 제한하고, `OPENCLAW_QA_MATRIX_CLEANUP_TIMEOUT_MS`는 정리 시간을 제한하여, Docker teardown이 멈췄을 때 멈춰 있는 대신 정확한 복구 명령을 보고합니다.

실제 전송을 사용하는 Telegram smoke 레인을 실행하려면 다음을 사용하세요:

```bash
pnpm openclaw qa telegram
```

이 레인은 일회용 서버를 프로비저닝하는 대신 실제 비공개 Telegram 그룹 하나를 대상으로 합니다. `OPENCLAW_QA_TELEGRAM_GROUP_ID`, `OPENCLAW_QA_TELEGRAM_DRIVER_BOT_TOKEN`, `OPENCLAW_QA_TELEGRAM_SUT_BOT_TOKEN`이 필요하며, 동일한 비공개 그룹에 있는 두 개의 서로 다른 봇이 있어야 합니다. SUT 봇은 Telegram 사용자 이름이 있어야 하며, 두 봇 모두 `@BotFather`에서 Bot-to-Bot Communication Mode를 활성화했을 때 봇 간 관찰이 가장 잘 작동합니다.
어떤 시나리오라도 실패하면 명령은 0이 아닌 종료 코드를 반환합니다. 실패 종료 코드 없이 아티팩트만 원하면 `--allow-failures`를 사용하세요.
Telegram 보고서와 요약에는 canary부터 시작하여 driver 메시지 전송 요청 시점부터 관찰된 SUT 응답까지의 응답별 RTT가 포함됩니다.

풀링된 live 자격 증명을 사용하기 전에 다음을 실행하세요:

```bash
pnpm openclaw qa credentials doctor
```

doctor는 Convex broker 환경 변수를 검사하고, 엔드포인트 설정을 검증하며, maintainer secret이 있을 경우 admin/list 접근 가능성도 확인합니다. secret에 대해서는 설정됨/누락됨 상태만 보고합니다.

실제 전송을 사용하는 Discord smoke 레인을 실행하려면 다음을 사용하세요:

```bash
pnpm openclaw qa discord
```

이 레인은 두 개의 봇이 있는 실제 비공개 Discord guild 채널 하나를 대상으로 합니다. 하나는 harness가 제어하는 driver 봇이고, 다른 하나는 번들 Discord Plugin을 통해 child OpenClaw Gateway가 시작하는 SUT 봇입니다. 환경 변수 자격 증명을 사용하는 경우 `OPENCLAW_QA_DISCORD_GUILD_ID`, `OPENCLAW_QA_DISCORD_CHANNEL_ID`, `OPENCLAW_QA_DISCORD_DRIVER_BOT_TOKEN`, `OPENCLAW_QA_DISCORD_SUT_BOT_TOKEN`, `OPENCLAW_QA_DISCORD_SUT_APPLICATION_ID`가 필요합니다.
이 레인은 채널 mention 처리를 검증하고, SUT 봇이 Discord에 기본 `/help` 명령을 등록했는지도 확인합니다.
어떤 시나리오라도 실패하면 명령은 0이 아닌 종료 코드를 반환합니다. 실패 종료 코드 없이 아티팩트만 원하면 `--allow-failures`를 사용하세요.

live transport 레인은 이제 각자 자체 시나리오 목록 형식을 만드는 대신 하나의 더 작은 공통 계약을 공유합니다:

`qa-channel`은 여전히 광범위한 합성 제품 동작 스위트이며, live transport 커버리지 matrix의 일부는 아닙니다.

| 레인     | Canary | Mention gating | Allowlist block | Top-level reply | Restart resume | Thread follow-up | Thread isolation | Reaction observation | Help command | Native command registration |
| -------- | ------ | -------------- | --------------- | --------------- | -------------- | ---------------- | ---------------- | -------------------- | ------------ | --------------------------- |
| Matrix   | x      | x              | x               | x               | x              | x                | x                | x                    |              |                             |
| Telegram | x      | x              |                 |                 |                |                  |                  |                      | x            |                             |
| Discord  | x      | x              |                 |                 |                |                  |                  |                      |              | x                           |

이렇게 하면 `qa-channel`은 광범위한 제품 동작 스위트로 유지되고, Matrix, Telegram 및 향후 live transport는 하나의 명시적 transport 계약 체크리스트를 공유할 수 있습니다.

Docker를 QA 경로에 포함하지 않고 일회용 Linux VM 레인을 실행하려면 다음을 사용하세요:

```bash
pnpm openclaw qa suite --runner multipass --scenario channel-chat-baseline
```

이 명령은 새 Multipass guest를 부팅하고, 의존성을 설치하고, guest 내부에서 OpenClaw를 빌드하고, `qa suite`를 실행한 다음, 일반 QA 보고서와 요약을 host의 `.artifacts/qa-e2e/...`로 다시 복사합니다.
시나리오 선택 동작은 host의 `qa suite`와 동일합니다.
host와 Multipass 스위트 실행은 기본적으로 격리된 Gateway worker와 함께 선택된 여러 시나리오를 병렬로 실행합니다. `qa-channel`의 기본 동시성은 4이며, 선택한 시나리오 수를 상한으로 가집니다. worker 수를 조정하려면 `--concurrency <count>`를 사용하고, 직렬 실행을 원하면 `--concurrency 1`을 사용하세요.
어떤 시나리오라도 실패하면 명령은 0이 아닌 종료 코드를 반환합니다. 실패 종료 코드 없이 아티팩트만 원하면 `--allow-failures`를 사용하세요.
live 실행은 guest에서 실용적인 지원 QA 인증 입력을 전달합니다: 환경 변수 기반 provider 키, QA live provider config 경로, 그리고 존재할 경우 `CODEX_HOME`. guest가 마운트된 워크스페이스를 통해 다시 쓸 수 있도록 `--output-dir`은 리포지토리 루트 아래에 두세요.

## 리포지토리 기반 시드

시드 자산은 `qa/`에 있습니다:

- `qa/scenarios/index.md`
- `qa/scenarios/<theme>/*.md`

이들은 QA 계획이 사람과 에이전트 모두에게 보이도록 의도적으로 git에 포함되어 있습니다.

`qa-lab`은 범용 Markdown 실행기로 유지되어야 합니다. 각 시나리오 Markdown 파일은 하나의 테스트 실행에 대한 단일 진실 공급원이어야 하며 다음을 정의해야 합니다:

- 시나리오 메타데이터
- 선택적 category, capability, lane, risk 메타데이터
- docs 및 code 참조
- 선택적 Plugin 요구 사항
- 선택적 Gateway config patch
- 실행 가능한 `qa-flow`

`qa-flow`를 지원하는 재사용 가능한 런타임 표면은 범용적이고 교차 기능적으로 유지될 수 있습니다. 예를 들어, Markdown 시나리오는 특수한 실행기를 추가하지 않고도 Gateway `browser.request` seam을 통해 내장된 Control UI를 구동하는 브라우저 측 helper와 transport 측 helper를 결합할 수 있습니다.

시나리오 파일은 소스 트리 폴더가 아니라 제품 기능별로 그룹화해야 합니다. 파일이 이동하더라도 시나리오 ID는 안정적으로 유지하고, 구현 추적 가능성을 위해 `docsRefs`와 `codeRefs`를 사용하세요.

기본 목록은 다음을 포괄할 만큼 충분히 넓어야 합니다:

- DM 및 채널 채팅
- 스레드 동작
- 메시지 작업 수명 주기
- Cron 콜백
- 메모리 회상
- 모델 전환
- subagent handoff
- 리포지토리 읽기 및 문서 읽기
- Lobster Invaders 같은 작은 빌드 작업 하나

## Provider mock 레인

`qa suite`에는 두 개의 로컬 provider mock 레인이 있습니다:

- `mock-openai`는 시나리오 인식 OpenClaw mock입니다. 리포지토리 기반 QA 및 parity gate를 위한 기본 결정적 mock 레인으로 유지됩니다.
- `aimock`은 실험적 프로토콜, fixture, record/replay, chaos 커버리지를 위한 AIMock 기반 provider 서버를 시작합니다. 이는 추가 기능이며 `mock-openai` 시나리오 디스패처를 대체하지 않습니다.

provider 레인 구현은 `extensions/qa-lab/src/providers/` 아래에 있습니다. 각 provider는 자체 기본값, 로컬 서버 시작, Gateway 모델 config, auth-profile 준비 요구 사항, live/mock capability 플래그를 담당합니다. 공유 스위트 및 Gateway 코드는 provider 이름으로 분기하지 말고 provider registry를 통해 라우팅해야 합니다.

## 전송 어댑터

`qa-lab`은 Markdown QA 시나리오를 위한 범용 transport seam을 소유합니다.
`qa-channel`은 그 seam의 첫 번째 어댑터이지만, 설계 목표는 더 넓습니다. 미래의 실제 또는 합성 채널은 transport 전용 QA 실행기를 추가하는 대신 동일한 suite runner에 연결되어야 합니다.

아키텍처 수준에서의 분리는 다음과 같습니다:

- `qa-lab`은 범용 시나리오 실행, worker 동시성, 아티팩트 기록, 보고를 담당합니다.
- transport 어댑터는 Gateway config, 준비 상태, 수신 및 발신 관찰, transport 작업, 정규화된 transport 상태를 담당합니다.
- `qa/scenarios/` 아래의 Markdown 시나리오 파일은 테스트 실행을 정의하고, `qa-lab`은 이를 실행하는 재사용 가능한 런타임 표면을 제공합니다.

새 채널 어댑터에 대한 maintainer용 도입 가이드는
[테스트](/ko/help/testing#adding-a-channel-to-qa)에 있습니다.

## 보고

`qa-lab`은 관찰된 bus 타임라인에서 Markdown 프로토콜 보고서를 내보냅니다.
보고서는 다음 질문에 답해야 합니다:

- 무엇이 작동했는가
- 무엇이 실패했는가
- 무엇이 계속 막혀 있었는가
- 어떤 후속 시나리오를 추가할 가치가 있는가

캐릭터 및 스타일 검사를 위해, 동일한 시나리오를 여러 live 모델 참조로 실행하고 판정된 Markdown 보고서를 기록하세요:

```bash
pnpm openclaw qa character-eval \
  --model openai/gpt-5.5,thinking=medium,fast \
  --model openai/gpt-5.2,thinking=xhigh \
  --model openai/gpt-5,thinking=xhigh \
  --model anthropic/claude-opus-4-6,thinking=high \
  --model anthropic/claude-sonnet-4-6,thinking=high \
  --model zai/glm-5.1,thinking=high \
  --model moonshot/kimi-k2.5,thinking=high \
  --model google/gemini-3.1-pro-preview,thinking=high \
  --judge-model openai/gpt-5.5,thinking=xhigh,fast \
  --judge-model anthropic/claude-opus-4-6,thinking=high \
  --blind-judge-models \
  --concurrency 16 \
  --judge-concurrency 16
```

이 명령은 Docker가 아니라 로컬 QA Gateway child 프로세스를 실행합니다. character eval 시나리오는 `SOUL.md`를 통해 페르소나를 설정한 다음, 채팅, 워크스페이스 도움말, 작은 파일 작업 같은 일반 사용자 턴을 실행해야 합니다. 후보 모델에게는 평가 중이라는 사실을 알려주면 안 됩니다. 이 명령은 각 전체 transcript를 보존하고, 기본 실행 통계를 기록한 다음, 지원되는 경우 `xhigh` 추론과 fast 모드로 judge 모델에 자연스러움, 분위기, 유머를 기준으로 실행 결과의 순위를 매기도록 요청합니다.
provider를 비교할 때는 `--blind-judge-models`를 사용하세요. judge 프롬프트는 여전히 모든 transcript와 실행 상태를 받지만, 후보 참조는 `candidate-01` 같은 중립 라벨로 대체됩니다. 보고서는 파싱 후 순위를 실제 참조로 다시 매핑합니다.
후보 실행은 기본적으로 `high` thinking을 사용하며, GPT-5.5는 `medium`, 이를 지원하는 오래된 OpenAI 평가 참조는 `xhigh`를 사용합니다. 특정 후보를 재정의하려면 `--model provider/model,thinking=<level>`을 인라인으로 사용하세요. `--thinking <level>`은 여전히 전역 fallback을 설정하며, 이전 형식인 `--model-thinking <provider/model=level>`도 호환성을 위해 유지됩니다.
OpenAI 후보 참조는 provider가 이를 지원하는 경우 priority processing을 사용하도록 기본적으로 fast 모드를 사용합니다. 단일 후보 또는 judge에 대해 재정의가 필요하면 `,fast`, `,no-fast`, 또는 `,fast=false`를 인라인으로 추가하세요. 모든 후보 모델에 대해 fast 모드를 강제로 켜려는 경우에만 `--fast`를 전달하세요. 후보와 judge의 실행 시간은 벤치마크 분석을 위해 보고서에 기록되지만, judge 프롬프트에는 속도를 기준으로 순위를 매기지 말라고 명시되어 있습니다.
후보 및 judge 모델 실행은 모두 기본적으로 동시성 16을 사용합니다. provider 제한이나 로컬 Gateway 부하로 인해 실행이 너무 시끄러우면 `--concurrency` 또는 `--judge-concurrency`를 낮추세요.
후보 `--model`을 전달하지 않으면 character eval은 기본적으로 `openai/gpt-5.5`, `openai/gpt-5.2`, `openai/gpt-5`, `anthropic/claude-opus-4-6`, `anthropic/claude-sonnet-4-6`, `zai/glm-5.1`, `moonshot/kimi-k2.5`, `google/gemini-3.1-pro-preview`를 사용합니다.
`--judge-model`을 전달하지 않으면 judge는 기본적으로 `openai/gpt-5.5,thinking=xhigh,fast`와 `anthropic/claude-opus-4-6,thinking=high`를 사용합니다.

## 관련 문서

- [테스트](/ko/help/testing)
- [QA Channel](/ko/channels/qa-channel)
- [대시보드](/ko/web/dashboard)
