AutoGPT PR 端到端手动测试技能:基于 Docker Compose、agent-browser 与 API 证据链的 E2E 测试工作流
AutoGPT 仓库中内置了一个名为 pr-test 的 Claude Code 技能(SKILL.md),它定义了一套对 PR/分支进行端到端手动测试的完整规程:构建完整平台、通过浏览器与 API 双通道交互、逐步截取截图、校验前后状态证据,并将结果以评论和正式 Review 的形式回写到 PR。读完本文,你将掌握这套技能的"不可协商"测试准则、并行 worktree 间的锁协调机制、原生(native)与 Docker 双模式启动策略、基于 Supabase 认证的测试用户体系,以及截图上传与 PR Review 决策的完整闭环,可以直接在自己的 AutoGPT 派生项目或类似的多服务平台上复用这套方法。
技能定位与五条"不可协商"准则
pr-test 技能的元数据声明其触发条件为:当用户要求手动测试 PR、端到端测试某个功能、或对运行中的系统跑集成测试时自动触发,支持传入 worktree 路径或 PR 号,可选 --fix 标志表示发现问题后自动修复。
技能开篇列出五条 NON-NEGOTIABLE(不可协商) 要求,这是整个测试方法论的骨架:
- 每一步都要截图——不是只在测试结束时截一张,而是每个重要测试步骤都要拍。每个测试场景至少一张 BEFORE 和一张 AFTER 截图,命名规则为
{NN}-{action}-{state}.png(如01-credits-before.png、02-credits-after.png)。某个场景缺截图即判定测试"不完整",必须回头补拍。 - 截图必须回贴到 PR——所有截图推送到临时分支
test-screenshots/pr-{N},并用 GitHub raw URL 内联嵌入 PR 评论。上传失败必须重试;仍失败则在报告中列出失败文件,要求人工拖拽粘贴到 PR 评论。 - 状态变更必须有 Before/After 双重证据——对每一次状态变更操作(API 调用、用户动作),记录操作前后的实际值(例如
credits_before=100, credits_after=95),截图必须反映 UI 上的状态变化,且期望值与实际值必须显式对比,不允许"肉眼看看就行"。 - 负向测试用例强制——每个功能至少测一个负向 case(余额不足、非法输入、未授权访问),验证错误提示对用户友好且准确,并确认被拒绝操作后系统状态没有改变。
- 测试报告必须包含完整证据——每个场景的报告条目必须包含:Steps(做了什么,精确到命令/UI 操作)、Expected(预期)、Actual(实际)、API Evidence(前后 API 响应值)、Screenshot Evidence(带解释的前后截图)。
目标解析与结果目录(Step 0)
技能的第一步是把用户输入归一化为一组环境变量。若传入的是 PR 号,先用 gh pr view {N} --json headRefName 找到对应分支的 worktree;随后确定:
REPO_ROOT——根仓库目录,可用git -C "$WORKTREE_PATH" worktree list | head -1推导;WORKTREE_PATH/PLATFORM_DIR($WORKTREE_PATH/autogpt_platform)/BACKEND_DIR/FRONTEND_DIR;PR_NUMBER、PR_TITLE(标题会被 slug 化,如 "Add copilot permissions" → "add-copilot-permissions");RESULTS_DIR——统一放在$REPO_ROOT/test-results/PR-{PR_NUMBER}-{slugified-title}。
测试凭据的处理是这里值得注意的设计:PR_TEST_USER_EMAIL 与 PR_TEST_USER_PASSWORD 严禁硬编码进 SKILL 文件、PR 评论、截图或任何提交物中。技能给出的优先级是:先取环境变量(CI 或预配置 shell),缺失时才交互式向用户询问,且只对缺失的那一个变量发问,避免已导出的凭据被空输入覆盖。拿到后用 ${VAR:?message} 语法"锁死"——仍为空则带明确的变量名报错退出。文档还记录了一个教训:曾经默认共享的 test@test.com 测试账号因凭据泄漏进 SKILL 本身而被禁用(2026-05-23),此后技能明确要求每次会话都要向用户询问当前有效凭据,不得再引入任何默认账号。
理解 PR 与编写测试计划(Step 1–2)
测试前先用 gh pr view {N} --json body、git log --oneline dev..HEAD、git diff dev --stat 回答四个问题:这个 PR 为什么存在、实现了什么功能、如何实现、影响了哪些组件(backend、frontend、copilot、executor 等),进而确定关键的用户可见行为。
然后把测试计划写入 $RESULTS_DIR/test-plan.md,计划模板强制包含四部分:Scenarios、API Tests(每个端点注明 Before state / After state)、UI Tests(每个交互注明截图前后各捕获什么)、Negative Tests(REQUIRED — at least one per feature),负向用例必须写明"预期错误消息/错误码"和"验证什么状态没有改变"。
并行协调:测试锁与心跳(Step 3.0)
多个 worktree 共享同一台宿主机——Docker 基础设施(postgres、redis、clamav)、应用端口(3000/8006/…)和测试用户都是共享资源。两个 agent 并发跑 /pr-test 会互相污染状态(连接池耗尽、端口绑定静默失败、跨测试断言串味)。技能给出的方案是一个基于根 worktree 的锁文件:
- 锁路径固定为
$REPO_ROOT/.ign.testing.lock(放根 worktree 保证所有兄弟 worktree 都能看见); - 锁体是若干
key=value行:holder、pid、started、heartbeat(每约 2 分钟更新)、worktree、branch、intent(一行意图说明加大致时长)。
抢占逻辑:若锁已存在,解析 heartbeat 时间戳(兼容 BSD/macOS 的 date -j 与 GNU 的 date -d),心跳年龄超过 5 分钟视为陈旧锁并回收(打印 WARN 与旧锁内容);否则打印持锁者信息并 exit 1 等待。心跳必须在抢锁后立即作为后台进程运行——否则崩溃的 agent 会永远占着锁:
(while true; do
sleep 120
[ -f "$LOCK" ] || exit 0 # lock released → exit heartbeat
perl -i -pe "s/^heartbeat=.*/heartbeat=$(date -u +%Y-%m-%dT%H:%MZ)/" "$LOCK"
done) &
HEARTBEAT_PID=$!
释放必须无条件执行(甚至 exit 1 时也要),因此用 trap 'kill "$HEARTBEAT_PID" 2>/dev/null; rm -f "$LOCK"' EXIT INT TERM 兜底。技能特别强调了一条最容易踩的坑:锁保护的是"测试执行",不是"应用生命周期"。Step 5/6(记录结果、发 PR 评论)一完成就要立刻释放锁——即使 poetry run app / pnpm dev 还在跑、Docker 容器还开着、还在 tail 日志都不影响。应用是否继续运行与锁正交,别把两者混为一谈;兄弟 worktree 接管时会自己执行清理并释放端口。另外还有一个 append-only 的共享状态日志 $REPO_ROOT/.ign.testing.log,任何 agent 都可以往里写"I'm waiting""I'm done, resources free"之类的协调消息。
环境搭建(Step 3):.env 复制、Copilot 认证与冲突清理
3a. 从根 worktree 复制 .env
.env 文件不进 git,必须手工从根 worktree 复制到目标 worktree:
cp $REPO_ROOT/autogpt_platform/.env $PLATFORM_DIR/.env
cp $REPO_ROOT/autogpt_platform/backend/.env $BACKEND_DIR/.env
cp $REPO_ROOT/autogpt_platform/frontend/.env $FRONTEND_DIR/.env
3b. Copilot 认证:订阅模式与 OpenRouter 回退
Copilot 需要一个 LLM API 才能工作,技能给出两条路,优先订阅模式:
Option 1:订阅模式(首选,复用 Claude Max/Pro 订阅)。claude_agent_sdk Python 包自带 Claude CLI 二进制,无需通过 npm 安装 @anthropic-ai/claude-code。这一点可以从 后端 pyproject.toml 得到印证:依赖中声明了 claude-agent-sdk = "^0.1.64",其注释直接写明 "bundled CLI 2.1.116",并提到环境变量 CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 用于剥离有问题的 context-management beta。运行辅助脚本从宿主机提取 token 并自动更新 backend/.env:
bash $BACKEND_DIR/scripts/refresh_claude_token.sh --env-file $BACKEND_DIR/.env
该脚本从三个位置读取 OAuth token:macOS 系统钥匙串("Claude Code-credentials")、Linux/WSL 的 ~/.claude/.credentials.json、Windows 的 %APPDATA%/claude/.credentials.json,然后写入 CLAUDE_CODE_OAUTH_TOKEN、CLAUDE_CODE_REFRESH_TOKEN 与 CHAT_USE_CLAUDE_CODE_SUBSCRIPTION=true。容器启动时后端会用这些环境变量自动在容器内生成 ~/.claude/.credentials.json,SDK 内置的 CLI 据此认证——不需要 claude login,也不需要 npm install。注意 OAuth token 约 24 小时过期,Copilot 报认证错误时重跑脚本并重启 copilot_executor 即可。
Option 2:OpenRouter API key 模式(回退)。在 $BACKEND_DIR/.env 中设置:
CHAT_USE_CLAUDE_CODE_SUBSCRIPTION=false
CHAT_API_KEY=<同一 .env 中 OPEN_ROUTER_API_KEY 的值>
CHAT_BASE_URL=https://openrouter.ai/api/v1
CHAT_USE_CLAUDE_AGENT_SDK=true
技能给出了用 grep/perl -i -pe 原地更新或追加这些键的完整 shell 片段,并在 OPEN_ROUTER_API_KEY 缺失时直接报错退出。
3c. 停掉冲突容器
docker ps --format "{{.Names}}" | grep -E "rest_server|executor|copilot|websocket|database_manager|scheduler|notification|frontend|migrate" | while read name; do
docker stop "$name" 2>/dev/null
done
注意保留基础设施容器(supabase/db、redis、rabbitmq、clamav)。原生模式下还要杀掉宿主机上残留的应用进程并释放端口 3000/8006/8001/8002/8005/8008(pkill -9 -f "poetry run app"、lsof -ti :$port -sTCP:LISTEN | xargs -r kill -9 等)。
3e-native. 原生模式(迭代开发的默认选择)
原生模式让基础设施(postgres、supabase、redis、rabbitmq、clamav)跑在 Docker 里,而 backend 与 frontend 直接在宿主机上跑。收益是省掉每次后端改动后 3–8 分钟的 docker compose build 循环——代码修改在进程重启(秒级)后即被拾取。技能给出的选择判据是:
- 偏原生模式:迭代开发/调试循环;PR 只改 Python/TS 源码而不动 Dockerfile、compose 配置或基础设施镜像;快速复现失败场景;
- 偏 Docker 模式:测试
Dockerfile/docker-compose.yml/基础镜像本身的改动;生产一致性冒烟测试;需要精确"要发布的那个镜像"的 CI 等价运行。
对应 docker-compose.yml 的结构,原生模式的第一步是:
cd $PLATFORM_DIR && docker compose --profile local up deps --detach --remove-orphans --build
deps 是一个带 local profile 的 busybox 占位服务,其 depends_on 声明了 db、redis-0/1/2、redis-init、rabbitmq、clamav、falkordb、migrate——即"拉起全部基础设施但跳过所有应用服务"的官方组合。compose 文件里还能看到配套的 deps_backend profile 占位服务,把全部后端应用服务挂在 deps 之后。
第二步在宿主机启动后端:
cd $BACKEND_DIR && (poetry run app 2>&1 | tee .ign.application.logs) &
这与源码完全吻合:pyproject.toml 中定义了 app = "backend.app:main" 入口,而 backend/app.py 的 main() 通过 run_processes(...) 把 DatabaseManager、Scheduler、BatchExecutor、NotificationManager、PlatformLinkingManager、WebsocketServer、AgentServer、ExecutionManager、CoPilotChatBridge、CoPilotExecutor 全部作为子进程拉起来,前 N-1 个后台运行、最后一个前台运行,并在 finally 中逆序 stop() 所有进程——这就是技能所说"poetry run app 在一个父进程里生成所有应用子进程,无需单独容器或终端"的实现依据。
启动顺序有讲究:必须先等 8006 端口的后端就绪,再启动前端。原因是前端 pnpm dev 启动时会执行 generate-api-queries 脚本去拉取后端的 /openapi.json(脚本本体见 generate-api-queries.ts),后端没起来前端会直接失败。轮询等待方式:
for i in $(seq 1 60); do
if [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8006/docs 2>/dev/null)" = "200" ]; then
echo "Backend ready"; break
fi
sleep 2
done
随后 cd $FRONTEND_DIR && (pnpm dev 2>&1 | tee .ign.frontend.logs) &,再轮询 3000 端口。两者就绪后跳过 Docker 模式的 3e/3f,直接做 3g/3h(测试用户)。
3e/3f. Docker 模式(回退)与就绪轮询
cd $PLATFORM_DIR && docker compose build --no-cache 2>&1 | tail -20
if [ ${PIPESTATUS[0]} -ne 0 ]; then echo "ERROR: Docker build failed"; exit 1; fi
cd $PLATFORM_DIR && docker compose up -d 2>&1 | tail -20
若容器看起来跑的是旧代码(PR 的改动没生效),用 --no-cache 强制全量重建——BuildKit 可能从其他分支的构建复用了缓存的 COPY 层。预期耗时:普通构建 3–8 分钟,--no-cache 5–10 分钟。就绪检查是同时轮询两个端点:
for i in $(seq 1 60); do
BACKEND=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8006/docs 2>/dev/null)
FRONTEND=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null)
[ "$BACKEND" = "200" ] && [ "$FRONTEND" = "200" ] && { echo "Services ready"; break; }
sleep 5
done
3h/3i. 测试用户与 onboarding 绕过
测试用户通过 Supabase Auth(经 Kong 网关暴露在 8000 端口)注册并换取 token,注册是幂等的(已存在返回 "User already registered");若遇到 "Database error finding user",重启 supabase-auth 后重试,且必须把重试结果重新收进 $RESULT,保证后续取 token 读的是重试后的状态:
ANON_KEY=$(grep "NEXT_PUBLIC_SUPABASE_ANON_KEY=" $FRONTEND_DIR/.env | sed 's/.*=//' | tr -d '[:space:]')
SIGNUP_PAYLOAD=$(jq -nc --arg e "$PR_TEST_USER_EMAIL" --arg p "$PR_TEST_USER_PASSWORD" '{email:$e,password:$p}')
RESULT=$(curl -s -X POST 'http://localhost:8000/auth/v1/signup' \
-H "apikey: $ANON_KEY" -H 'Content-Type: application/json' -d "$SIGNUP_PAYLOAD")
TOKEN=$(curl -s -X POST 'http://localhost:8000/auth/v1/token?grant_type=password' \
-H "apikey: $ANON_KEY" -H 'Content-Type: application/json' \
-d "$SIGNUP_PAYLOAD" | jq -r '.access_token // ""')
之后所有 API 调用统一用 curl -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/...。
onboarding 绕过同样值得注意:前端在 completedSteps 中不含 ONBOARDING_COMPLETE 时会重定向到 /onboarding,因此要通过后端 API 主动把它标记完成,并验证生效,否则所有浏览器测试都会落到 onboarding 页而不是目标功能:
curl -s -X POST "http://localhost:8006/api/onboarding/step?step=ONBOARDING_COMPLETE" -H "Authorization: Bearer $TOKEN"
ONBOARDING_STATUS=$(curl -s "http://localhost:8006/api/onboarding/completed" -H "Authorization: Bearer $TOKEN" | jq -r '.is_completed')
[ "$ONBOARDING_STATUS" = "true" ] || { echo "ERROR: onboarding bypass failed"; exit 1; }
状态操控与前后证据("真实测试"的核心)
技能对"真实状态"的要求是整套方法论的灵魂:永远不要依赖 mock/注入的浏览器状态,绝不用 agent-browser eval 伪造 UI 状态,后端必须是唯一事实来源。为此给出六类手段:
- 直接写 Redis 计数器(测限流、配额等场景):
REDIS_CONTAINER=$(docker ps --format '{{.Names}}' | grep redis | head -1)
docker exec $REDIS_CONTAINER redis-cli SET "rate_limit:user:$PR_TEST_USER_EMAIL" 99 EX 3600
docker exec $REDIS_CONTAINER redis-cli GET "rate_limit:user:$PR_TEST_USER_EMAIL"
- API 前后值对比(以 credits 为例):
BEFORE=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/credits | jq '.credits')
# ...执行动作...
AFTER=$(curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8006/api/credits | jq '.credits')
echo "Delta: $(( BEFORE - AFTER ))"
- 状态变更前后都截图,UI 必须反映后端变化;
- 必要时直接查库:
docker exec supabase-db psql -U supabase_admin -d postgres -c "SELECT credits FROM user_credits WHERE user_id = '...';"; - 每次 API 测试后验证持久化:对比 API 返回与 DB 查询值,
[ "$API_CREDITS" = "$DB_CREDITS" ] && echo "CONSISTENT" || echo "MISMATCH: ..."。
通用模式封装为"记录 BEFORE → 执行动作 → 记录 AFTER → 打印对比"四步脚本,要求对每一个状态变更 API 调用使用。
执行测试(Step 4):端口表、API、浏览器与日志
端口速查表
技能给出的服务端口参照表如下(与 docker-compose.platform.yml 中各服务 ports 声明一致):
| 服务 | 端口 | URL |
|---|---|---|
| Frontend | 3000 | http://localhost:3000 |
| Backend REST | 8006 | http://localhost:8006 |
| Supabase Auth (via Kong) | 8000 | http://localhost:8000 |
| Executor | 8002 | http://localhost:8002 |
| Copilot Executor | 8008 | http://localhost:8008 |
| WebSocket | 8001 | http://localhost:8001 |
| Database Manager | 8005 | http://localhost:8005 |
| Redis | 6379 | localhost:6379 |
| RabbitMQ | 5672 | localhost:5672 |
API 测试
典型场景示例:列 agents(GET /api/graphs)、创建 agent(POST /api/graphs)、运行 agent(POST /api/graphs/{graph_id}/execute)、取执行结果(GET /api/graphs/{graph_id}/executions/{exec_id})。每个状态变更调用都套上面四步的验证模式。
agent-browser 浏览器测试
关键是 --session-name pr-test——它让 cookie 在导航间持久化,登录只需做一次:
agent-browser close 2>/dev/null || true
agent-browser --session-name pr-test open 'http://localhost:3000/login' --timeout 15000
agent-browser --session-name pr-test snapshot | grep "textbox\|button" # 拿 ref
agent-browser --session-name pr-test fill {email_ref} "$PR_TEST_USER_EMAIL"
agent-browser --session-name pr-test fill {password_ref} "$PR_TEST_USER_PASSWORD"
agent-browser --session-name pr-test click {login_button_ref}
agent-browser --session-name pr-test click 'text=Accept All' 2>/dev/null || true # 关 cookie banner
agent-browser --session-name pr-test open 'http://localhost:3000/copilot' --timeout 10000
agent-browser --session-name pr-test screenshot $RESULTS_DIR/01-page.png
技能列出的关键页面:/copilot(Copilot 聊天)、/build(Agent 构建器)、/build?flowID={id}(指定 agent)、/library 与 /library/agents/{id}(库与运行历史)、/marketplace。
日志检查
- 原生模式:所有应用日志经
tee汇入$BACKEND_DIR/.ign.application.logs(因为rest_server、executor、copilot_executor、websocket、scheduler、notification_server、database_manager都是poetry run app的单父进程子进程,输出交错在同一文件)和$FRONTEND_DIR/.ign.frontend.logs;用grep -iE "error|exception|traceback"过滤。 - Docker 模式:
docker logs autogpt_platform-rest_server-1 / -executor-1 / -copilot_executor-1 / -frontend-1 2>&1 | tail -30。
Copilot SSE 流式测试
SESSION_ID=$(curl -s -X POST 'http://localhost:8006/api/chat/sessions' \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{}' \
| jq -r '.id // .session_id // ""')
curl -N -X POST "http://localhost:8006/api/chat/sessions/$SESSION_ID/stream" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"message": "Hello, what can you help me with?"}' --max-time 60 2>/dev/null | head -50
UI 验证则优先走浏览器:打开 /copilot,填输入框回车,等 20–30 秒出响应再截图。
记录结果与报告(Step 5–6)
Step 5 强调的截图节奏:每个场景动作前后各一张,负向 case 至少一张错误态截图,测试失败时既截失败 UI 也截错误日志。命名示例:01-login-page-before.png、03-credits-page-before.png、04-credits-purchase-after.png、05-negative-insufficient-credits.png。
Step 6 要求把每张截图用 Read 工具展示给用户,并附 1–2 句解释(展示的是什么页面/状态、证明了哪个场景、UI 中值得注意的细节),然后输出汇总表格:
| # | Scenario | Result | API Evidence | Screenshot Evidence |
|---|---|---|---|---|
| 1 | {name} | PASS/FAIL | Before: X, After: Y | 01-before.png, 02-after.png |
同时把解释存入 Bash 关联数组 SCREENSHOT_EXPLANATIONS、表格存入 TEST_RESULTS_TABLE 变量——Step 7 的上传脚本依赖它们(技能特别注明 declare -A 需要 Bash 4.0+,macOS 自带 zsh 时建议用 Homebrew 的 bash 5.x,或退回普通变量加查表函数)。
截图上传与 PR 评论(Step 7)
这一步是强制的,且有两条铁律:绝不在 PR 评论里贴裸目录链接(每张图片必须 name 内联出现);绝不在评论里出现本地绝对路径(/Users/…、/home/…、C:\…、~/… 等在发布前用 grep 拦截,改写为仓库相对路径)。发布前的 sanity check:
if grep -nE '(^|[^A-Za-z])(/Users/|/home/|/tmp/|/private/|C:\\|~/)[A-Za-z0-9]' "$COMMENT_FILE" ; then
echo "ABORT: local filesystem paths detected in PR comment body."; exit 1
fi
上传走 GitHub Git API 全服务端完成——创建 blob(base64 编码,每张图重试 3 次)、tree、commit、ref,全程不碰本地 git 状态,因此对 worktree 安全、不干扰 PR 分支:
REPO="Significant-Gravitas/AutoGPT"
SCREENSHOTS_BRANCH="test-screenshots/pr-${PR_NUMBER}"
SCREENSHOTS_DIR="test-screenshots/PR-${PR_NUMBER}"
# 1) 逐图上传 blob(失败计入 FAILED_UPLOADS),累积 TREE_JSON
# 2) 创建 tree → 解析旧 ref 作为 parent(保证截图提交链式而非孤儿根提交)→ 创建 commit
# 3) 创建/更新 ref:gh api .../git/refs -f ref=... -f sha=... || gh api ... -X PATCH -F force=true
评论正文写入 mktemp 文件再 -F body=@$COMMENT_FILE 提交,避免特殊字符的 shell 解释问题。若存在上传失败的图片,报告中追加 "Failed Screenshot Uploads" 小节,列出文件并说明需人工拖拽粘贴,同时标注 Run status: INCOMPLETE。发布后还有闭环验证——回读最后一条评论,grep -q '!\[' 不通过就 exit 1,测试运行在验证通过前不算完成(注意用 --paginate | jq -r '.[-1].body' 而不是 --jq,因为 --jq 是按页应用的)。
PR 评论的最终形态必须包含:1)所有场景的 PASS/FAIL 汇总表(含 before/after API 证据);2)每张成功上传截图的内联渲染与逐图解释;3)失败上传清单与人工处理指引。
正式 Review 决策(Step 8)
评论发出后还要必须给出正式 Review 结论。评估维度是一张对照表:Coverage(PR 描述的每个功能至少一个场景)、All scenarios pass、Negative tests(每功能至少一个失败路径)、Before/after evidence(每个状态变更调用都有前后值)、Screenshots are meaningful(展示真实状态变化而非 loading spinner/空白页)、No regressions(登录、创建/运行 agent 等核心流程仍可用)。
决策逻辑简洁而严格:
全部通过 → APPROVE
任一场景 FAIL 或 PR 功能未覆盖 → REQUEST_CHANGES(列出缺口)
证据薄弱(无 before/after、截图含糊) → REQUEST_CHANGES(列出缺失)
approve / request-changes 分别用 heredoc 生成 Review 正文后调 gh pr review "$PR_NUMBER" --approve / --request-changes --body ... 发布。三条规则:任何场景失败绝不 approve(哪怕疑似 flake,先重跑);不为本轮已修复的问题 request changes;--fix 模式下先把所有失败修完,Review 反映的是修复后的最终状态。
--fix 修复模式与已知问题清单
--fix 模式把标准提高了一档:不只记录问题,而是立刻修。每个问题的修复协议是 12 步:定位根因 → 先写失败测试(后端 bug 用 pytest.mark.xfail,前端/Playwright 用 .fixme)→ 截坏状态图 → 改码 → 只重建受影响的服务(docker compose up --build -d rest_server)→ 轮询健康 → 重测同一场景 → 截修复后图 → 移除 xfail/fixme 标记并确认通过 → 冒烟验证无回归 → 立即 commit + push(每修一个提交一次,不攒批) → 继续下一场景。修复循环最后要在所有场景通过后再做一次全量复测。该模式下 UX 问题(错位、标签混乱、缺 loading 态)也按 bug 对待。
文末的"Known issues and workarounds"是该技能实战沉淀最厚的部分,值得单独摘录:
| 问题 | 原因 | 修复 |
|---|---|---|
| 注册时 "Database error finding user" | Supabase auth 迁移后 schema 缓存过期 | docker restart supabase-auth && sleep 5 后重试 |
| 订阅模式下 Copilot 报认证错误 | OAuth token 未设置或已过期 | 重跑 refresh_claude_token.sh 并重建 copilot_executor |
| agent-browser 找不到 chromium | 分支落后 dev,镜像尚未自动安装系统 chromium | which chromium 检查,缺失则 apt-get install -y chromium 并设 AGENT_BROWSER_EXECUTABLE_PATH |
选择器 text=X 命中多个元素 |
文本匹配是包含式 | 先 snapshot 拿 ref=eNN,再 click eNN |
Copilot 容器里 claude: command not found |
镜像构建时没跑 poetry install(SDK CLI 在 Poetry 依赖里,不是 npm 包) |
docker compose build --no-cache copilot_executor && docker compose up -d copilot_executor;不要 npm install -g @anthropic-ai/claude-code,那会装出 SDK 根本不会用的第二份 CLI |
| 截图挂起/超时(exit 124) | CDP 连接卡死或 Chromium 僵尸进程(macOS 常见) | pkill -9 -f "agent-browser|chromium|Chrome for Testing" 后换新 --session-name;必要时改用 eval + snapshot 验证 DOM |
| compose up 后服务不启动 | 迁移没跑完 | 看 docker logs autogpt_platform-migrate-1;db 不健康则 docker restart supabase-db |
| Docker 用了旧代码的缓存层 | COPY 层跨分支被 BuildKit 复用 |
PR 分支首次构建一律 --no-cache |
agent-browser open 丢失登录态 |
无会话持久化 | 所有命令带 --session-name pr-test,或用 eval "window.location.href=..." 在同上下文内导航 |
| "Database error querying schema" | db schema 变了但 auth 缓存未刷 | docker restart supabase-db && sleep 10 && docker restart supabase-auth && sleep 8,用户数据丢了就重新注册 |
总结
pr-test 技能把"手动 E2E 测试"从个人习惯变成了一份可执行的工程协议:以五条不可协商准则保证证据完整性(逐步骤截图、前后状态值、负向用例),以根 worktree 锁 + 心跳 + trap 释放解决多 agent 并发竞争,以原生模式(docker compose --profile local up deps + poetry run app + pnpm dev)换取秒级重启的迭代效率,以 Git API 纯服务端提交保证截图证据对 worktree 安全地回贴 PR,最后以明确的评估矩阵收敛为 approve / request-changes 的正式 Review。对任何采用多容器架构、worktree 并行开发、且希望把 AI agent 纳入 PR 验证流程的团队,这份 SKILL.md 中"证据先行、状态即事实来源、锁只护测试不护应用"的设计,都是可以直接迁移的方法论资产。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00