所有权
- OpenClaw (
extensions/qa-lab/src/mantis/*): 场景运行时、pnpm openclaw qa mantis <command>CLI、证据 schema。 - QA Lab (
extensions/qa-lab/src/live-transports/*): 实时传输测试框架、driver/SUT bots、报告/证据写入器。 - Crabbox (
openclaw/crabbox): 预热的 Linux 机器、租约、VNC、crabbox media preview。 - GitHub Actions (
.github/workflows/mantis-*.yml): 远程入口点、artifact 保留。 - ClawSweeper: 解析维护者 PR 命令、分发工作流、发布最终 PR 评论。
CLI 命令
所有命令均为pnpm openclaw qa mantis <command>,定义在
extensions/qa-lab/src/mantis/cli.ts。构建/运行时需要 OPENCLAW_ENABLE_PRIVATE_QA_CLI=1
(打包后的工作流会在构建前设置 OPENCLAW_BUILD_PRIVATE_QA=1 和
OPENCLAW_ENABLE_PRIVATE_QA_CLI=1)。
每个命令都接受
--repo-root <path> 和 --output-dir <path>;Crabbox
命令还接受 --crabbox-bin、--provider、--machine-class/--class、
--lease-id、--idle-timeout、--ttl 和 --keep-lease。除非另有说明,本地 CLI 的 provider/class 默认值为
hetzner/beast;CI 工作流通常会同时覆盖这两项。
discord-smoke
https://discord.com/api/v10)获取机器人
用户、guild、guild 的 channels 以及目标 channel,断言该 channel
属于该 guild,然后(除非使用 --skip-post)发送一条消息并添加
👀 反应。写出 mantis-discord-smoke-summary.json 和
mantis-discord-smoke-report.md。
Token 解析顺序:--token-file 的值,然后是
OPENCLAW_QA_DISCORD_MANTIS_BOT_TOKEN(可用 --token-env 覆盖),然后是由
OPENCLAW_QA_DISCORD_MANTIS_BOT_TOKEN_FILE 指定的文件(可用 --token-file-env 覆盖)。
Guild/channel id 来自 OPENCLAW_QA_DISCORD_GUILD_ID / OPENCLAW_QA_DISCORD_CHANNEL_ID(可用
--guild-id / --channel-id 覆盖),且必须是 17-20 位数字的 Discord snowflake。设置
OPENCLAW_QA_REDACT_PUBLIC_METADATA=1 可在发布的 summary 和 report 中将 bot/guild/channel/message ids
以及名称替换为 <redacted>。
run
--transport 目前只接受 discord。--scenario 是两个内置 id 之一,每个都有自己默认的 baseline ref 和
期望的前后标签(extensions/qa-lab/src/mantis/run.runtime.ts):
--candidate 默认是 HEAD。其他参数:--credential-source
(默认 convex)、--credential-role(默认 ci)、--provider-mode
(默认 live-frontier)、--fast(默认开启)、--skip-install、--skip-build。
运行器会在 <output-dir>/worktrees/ 下为 baseline 和 candidate 创建分离的
git worktree 检出,在每个工作区中运行 pnpm install/pnpm build
(除非被跳过),然后针对每个 worktree 运行
pnpm openclaw qa discord --scenario <id> --model openai/gpt-5.4 --alt-model openai/gpt-5.4 --allow-failures。
每个 lane 会写入 discord-qa-reaction-timelines.json 以及一对
<scenario-id>-timeline.html/.png;运行器会将这些证据复制回
baseline//candidate/ 下,在输出目录中写入 comparison.json、
mantis-report.md 和 mantis-evidence.json,并在比较未通过时以非零退出
(baseline fail 且 candidate pass)退出。
第二个 Discord 场景(discord-thread-reply-filepath-attachment)使用 driver 机器人
发布一个父消息,创建一个真实 thread,带着 repo 本地的 filePath 调用 SUT 的
message.thread-reply 动作,然后轮询 thread 以获取回复和附件文件名。它期望一个名为
mantis-thread-report.md 的附件。
desktop-browser-smoke
--browser-url(默认 https://openclaw.ai)或渲染后的
--html-file 的 VNC 会话中启动浏览器,等待后使用 scrot 截图,可选地用
ffmpeg 录制 MP4,并将 desktop-browser-smoke.png / .mp4 / remote-metadata.json
rsync 回 --output-dir。
标志:
--lease-id <cbx_...>复用一个已加热的桌面,而不是创建新的。--browser-profile-dir <remote-path>复用远程 Chrome user-data-dir,以便持久化桌面在多次运行之间保持登录状态(用于长期存在的 Discord Web viewer 配置文件)。--browser-profile-archive-env <name>在启动前从该环境变量恢复一个 base64 的.tgzChrome profile 归档(默认OPENCLAW_MANTIS_BROWSER_PROFILE_TGZ_B64);用于 Discord Web 等已登录的 witness。--video-duration <seconds>控制 MP4 录制时长(默认 10 秒)。--keep-lease(或OPENCLAW_MANTIS_KEEP_VM=1)会保留本次运行创建的 lease 以便进行 VNC 检查;创建 lease 的失败运行也会默认保留它。
qa discord)仍然是权威来源;当
设置 OPENCLAW_QA_DISCORD_CAPTURE_UI_METADATA=1 时,该场景还会写出一个
Discord Web URL 证据文件,而 OPENCLAW_QA_DISCORD_KEEP_THREADS=1 会让 thread 保持打开足够长的时间,以便浏览器能够打开它。
GitHub workflow 优先使用持久化的 viewer profile,通过
MANTIS_DISCORD_VIEWER_CHROME_PROFILE_DIR(完整的 profile 归档可能会超过
GitHub secret 大小限制);对于较小/引导型 profile,它也可以改为恢复
来自 MANTIS_DISCORD_VIEWER_CHROME_PROFILE_TGZ_B64 的 base64 .tgz。如果这两种来源都未配置,workflow 仍会发布确定性的
baseline/candidate 截图,并记录已跳过登录 witness。
slack-desktop-smoke
pnpm openclaw qa slack,在 VNC 浏览器中打开 Slack Web,
捕获桌面,并将 Slack QA 产物(slack-qa/)和 VNC 截图/视频一起复制回本地。
这是唯一一种 SUT gateway 和浏览器都在同一个 VM 中运行的 Mantis 形态。
使用 --gateway-setup 时,命令会在 VM 中创建一个持久但可丢弃的 OpenClaw
home:$HOME/.openclaw-mantis/slack-openclaw,为目标 channel 打补丁 Slack
Socket Mode 配置,启动
openclaw gateway run --dev --allow-unconfigured --port 38973,并让 Chrome
在 VNC 会话中继续运行;省略 --gateway-setup 则会改为运行常规的 bot-to-bot Slack QA lane。
--credential-source env 所需的环境变量(本地默认是 env;role 默认是 maintainer):
OPENCLAW_QA_SLACK_CHANNEL_IDOPENCLAW_QA_SLACK_DRIVER_BOT_TOKENOPENCLAW_QA_SLACK_SUT_BOT_TOKENOPENCLAW_QA_SLACK_SUT_APP_TOKEN- 远程 model lane 需要
OPENCLAW_LIVE_OPENAI_KEY(如果本地只设置了OPENAI_API_KEY,Mantis 会在调用 Crabbox 之前将其复制到OPENCLAW_LIVE_OPENAI_KEY)
--credential-source convex 时,Mantis 会在创建 VM 之前从共享池中租用 Slack SUT 凭据,并将 channel id、app token 和 bot token 以 OPENCLAW_MANTIS_SLACK_* 环境变量传入 VM,因此 GitHub workflow 只需要 Convex broker secret,而不需要原始 Slack token。
其他标志:--slack-url <url> 打开特定 URL(否则 Mantis 会从 auth.test 推导出
https://app.slack.com/client/<team>/<channel>);--slack-channel-id <id> 设置 gateway 允许列表的 channel;
OPENCLAW_MANTIS_SLACK_BROWSER_PROFILE_DIR 控制 VM 内持久化的 Chrome profile(默认 $HOME/.config/openclaw-mantis/slack-chrome-profile);
--approval-checkpoints 运行原生 Slack approval 场景
(slack-approval-exec-native、slack-approval-plugin-native)并渲染
pending/resolved checkpoint 截图,而不是进行 gateway setup(与 --gateway-setup 互斥);--hydrate-mode source|prehydrated、
--provider-mode、--model、--alt-model 和 --fast 会透传到
Slack live lane。
approval checkpoint 截图是根据场景观察到的 Slack API 消息渲染出来的,而不是来自实时 Slack UI;只有当 lease 的浏览器 profile 已经登录时,slack-desktop-smoke.png 才能作为 Slack Web 本身的证据。
telegram-desktop-builder
openclaw gateway run --dev --allow-unconfigured --port 38974,向租用的私有群组发送一条 driver-bot 就绪消息,然后捕获截图和 MP4。bot token 只用于配置 OpenClaw;它不会用于登录 Telegram Desktop。桌面 viewer 是一个独立的 Telegram 用户会话,可以通过 --telegram-profile-archive-env <name> 恢复,或者通过 VNC 手动登录后再使用 --keep-lease 保持在线。
标志:--lease-id <cbx_...> 针对一个已经登录到 Telegram Desktop 的 VM 重新运行;--telegram-profile-archive-env <name> 在启动前恢复一个 base64 的 .tgz profile 归档;--telegram-profile-dir <remote-path> 设置远程 profile 目录(默认 $HOME/.local/share/TelegramDesktop);--no-gateway-setup 仅安装并打开 Telegram Desktop;--credential-source/--credential-role 默认分别为 convex/maintainer。
证据清单
每个会发布到 PR 的场景都会在其报告旁边写入mantis-evidence.json:
path 是相对于清单目录的路径;targetPath 是相对于已配置的 R2/S3 制品前缀的路径。scripts/mantis/publish-pr-evidence.mjs 会拒绝路径穿越,并在文件缺失时跳过 "required": false 的条目。
制品类型:timeline(确定性的前后对比截图)、desktopScreenshot(VNC/浏览器截图)、motionPreview(录制中的内联动画 GIF)、motionClip(经过运动裁剪的 MP4)、fullVideo(完整录制)、metadata(JSON/日志伴随文件)、report(Markdown 报告)。
一次运行的磁盘制品布局:
OPENCLAW_QA_REDACT_PUBLIC_METADATA=1;在 Discord/Slack/Telegram 的 GitHub 工作流中默认已启用。
GitHub 自动化
scripts/mantis/publish-pr-evidence.mjs 是可复用的发布器。工作流会传入 manifest、目标 PR、artifact 目标根目录、评论标记、artifact URL、run URL 和请求来源来调用它。它会将声明的 artifacts 上传到 Mantis R2 存储桶,生成一个以摘要优先的 PR 评论,内联图片/预览并链接视频,然后更新现有的标记评论或创建一个新的评论。所需环境变量:
MANTIS_ARTIFACT_R2_ACCESS_KEY_IDMANTIS_ARTIFACT_R2_SECRET_ACCESS_KEYMANTIS_ARTIFACT_R2_BUCKET(工作流设置为openclaw-crabbox-artifacts)MANTIS_ARTIFACT_R2_ENDPOINTMANTIS_ARTIFACT_R2_REGION(工作流设置为auto)MANTIS_ARTIFACT_R2_PUBLIC_BASE_URL(工作流设置为https://artifacts.openclaw.ai)
MANTIS_GITHUB_APP_ID /
MANTIS_GITHUB_APP_PRIVATE_KEY)发布,而不是 github-actions[bot],并使用一个隐藏的标记评论作为 upsert 键。
Mantis Discord Status Reactions 和 Mantis Telegram Live 都接受
baseline_ref/candidate_ref(或在 PR 评论中使用 baseline=/candidate=)
,并在使用包含密钥的凭据运行前验证解析出的 SHA 是否为 origin/main 的祖先、发布标签(v*),或某个开放 PR 的 head。
来自具有 write/maintain/admin 访问权限的 PR 的评论触发器:
telegram-status-command 作为 scenario;它们接受 provider=aws|hetzner 和
lease=<cbx_...>,用于指定特定的 Crabbox provider 或预热好的桌面。Mantis Telegram Desktop Proof 仅在 PR 已经带有 mantis: telegram-visible-proof 标签时,才会响应 PR 评论。
Web UI chat 评论触发器默认使用 PR head SHA 作为 candidate。它们会运行
Control UI 模拟 Gateway chat proof 并发布浏览器 artifacts;对其他网页和原生应用界面,请使用
常规的 Playwright/browser proof、维护者截图、Crabbox 或本地 artifacts。
ClawSweeper 也可以直接分发一个场景:
机器与密钥
本地 CLI Crabbox 的默认值为--provider hetzner --class beast;可通过 --provider、--class/--machine-class,或 OPENCLAW_MANTIS_CRABBOX_PROVIDER / OPENCLAW_MANTIS_CRABBOX_CLASS 覆盖。GitHub 工作流通常会同时覆盖这两项(例如 --class standard,以及 Slack 工作流中的 aws/hetzner provider 选择输入)。如果某个 provider 过慢或不可用,请在相同的 Crabbox 接口后面添加它,而不是硬编码回退方案。
VM 基线:Linux,配备可用于桌面的 Chrome/Chromium、CDP 访问、VNC/noVNC、Node 22.22.3+、24.15+ 或 25.9+ 以及 pnpm、OpenClaw 检出目录,并且能够向目标传输通道、GitHub、模型提供方和凭据代理服务进行出站访问。
Mantis 命令和工作流中使用的凭据与环境变量名称:
OPENCLAW_QA_DISCORD_MANTIS_BOT_TOKENOPENCLAW_QA_DISCORD_GUILD_IDOPENCLAW_QA_DISCORD_CHANNEL_ID- 本地
qa mantis run --credential-source env还需要OPENCLAW_QA_DISCORD_DRIVER_BOT_TOKEN、OPENCLAW_QA_DISCORD_SUT_BOT_TOKEN和OPENCLAW_QA_DISCORD_SUT_APPLICATION_ID。GitHub 工作流通常使用--credential-source convex以及下面的 broker 凭据,而不是原始的 Discord bot token。 OPENCLAW_QA_REDACT_PUBLIC_METADATA=1用于公开制品上传OPENCLAW_QA_CONVEX_SITE_URL、OPENCLAW_QA_CONVEX_SECRET_CIOPENAI_API_KEY(或 Telegram Desktop 证据专用的OPENCLAW_MANTIS_AGENT_OPENAI_API_KEY)CRABBOX_COORDINATOR/CRABBOX_COORDINATOR_TOKEN(工作流也接受OPENCLAW_QA_MANTIS_CRABBOX_COORDINATOR/_TOKEN作为后备,并在调用 Crabbox 之前将 它们映射为普通名称)CRABBOX_ACCESS_CLIENT_ID、CRABBOX_ACCESS_CLIENT_SECRETMANTIS_GITHUB_APP_ID、MANTIS_GITHUB_APP_PRIVATE_KEY
运行结果
传输前/后场景区分了这些结果,以免将不稳定的 环境误判为产品回归:- Bug reproduced:基线以该场景预期的方式失败。
- Harness failure:在 oracle 变得有意义之前,环境设置、凭据、传输 API、浏览器或提供方就已失败。
添加一个场景
实时传输场景是按传输方式用 TypeScript 定义的(参见extensions/qa-lab/src/mantis/run.runtime.ts 中用于 Discord 的前后形态的 MANTIS_SCENARIO_CONFIGS),而不是一个独立的声明式文件格式。每个场景都需要:id 和标题、传输方式、必需凭证、基线 ref 策略、候选 ref 策略、OpenClaw 配置补丁、设置/刺激步骤、预期的基线和候选 oracle、视觉捕获目标、超时预算以及清理步骤。
聚焦的仅候选端浏览器 proof 可以使用专门的确定性 E2E 测试和工作流。保持其作用范围明确,在执行前验证候选 ref,隔离受密钥支持的发布,并输出相同的证据清单契约。
优先使用小而类型明确的 oracle,而不是视觉检查:Discord 的 reaction 状态或消息引用、Slack 线程 ts/reaction API 状态、电子邮件 message id 和 headers。仅在 UI 是唯一可靠可观测项时使用浏览器截图,并在存在平台 API oracle 的情况下,将视觉检查作为补充而不是替代。
在 Discord、Slack 和 Telegram 之后,同一 runner 形态会扩展到 WhatsApp
(QR 登录、重新识别、送达、媒体、reactions)和 Matrix
(加密房间、线程/回复关系、重启恢复);这两者都尚未实现。
未决问题
- 当复用现有的 Mantis bot 时,哪个 Discord bot 应该作为驱动方,哪个应该作为被测系统(SUT)?
- GitHub 应该将 PR 的 Mantis 产物保留多长时间?
- 什么时候 ClawSweeper 应该自动推荐一个 Mantis 场景,而不是等待维护者命令?
- 对于公开 PR,截图在上传前是否应该进行遮盖或裁剪?