QA test your code changes by reading your git diff, choosing the right validation path for frontend/browser and backend changes, and reporting pass/fail with ev
复制下面这句话,粘贴给 Claude Code、Codex、Cursor 等 AI 编程工具,它会读取安装说明并在你确认后完成安装。
请阅读 https://ai.atlankj.com/install/asset/gh-qa-36d64db844b4 ,按照其中的说明把「qa」安装到你(当前 AI 工具)中。执行前先告诉我将运行的命令和写入的位置,等我确认。
查看 AI 将读取的安装说明正在读取 GitHub 原文…
内容来自 GitHub 原始文件,由原作者维护。在 GitHub 查看
Read the diff, classify what changed, and run the right validation path: browser QA for frontend/browser changes, API validation for backend surface changes, repo-native validation for backend-internal changes, and both for mixed changes.
You changed code. This skill is diff-driven first: it reads what changed, understands the affected behavior, and validates that behavior with the right tools. It is not a generic website crawler, and it should not invent random API checks that are unrelated to the diff.
/qa # Diff-based: choose the right validation path automatically
/qa http://localhost:3000 # Same, explicit frontend URL
/qa -- validate the workflow filters API
git difffrontend/browser, backend API, backend-internal, or mixed# What files changed?
git diff --name-only HEAD~1 # vs last commit (if changes are committed)
git diff --name-only # vs working tree (if uncommitted)
# Full diff for context
git diff HEAD~1 # or git diff for uncommitted
Pick whichever diff has content. If both are empty, there is nothing diff-driven to QA.
Read the full contents of every changed file that affects behavior:
.tsx, .jsx, .ts, .js, .css, .htmlLook for:
| Mode | Trigger | Primary validation |
|---|---|---|
| Frontend/browser | UI/routes/components/styles changed | Browser QA against the dev server |
| Backend API | Route handlers, request/response schemas, or externally visible API behavior changed | Start backend locally and run targeted API requests |
| Backend-internal | Services/workers/business logic changed without public API surface changes | Repo-native fast checks plus targeted tests |
| Mixed | Frontend/browser and backend changed together | Backend validation first, then frontend/browser QA |
Use these rules:
Mixed.backend-internal.Use browser automation against the dev server. Validate the specific UI changes plus 1-2 adjacent regression checks.
Use the repo's documented local startup and auth instructions, start the backend if needed, identify the changed endpoint(s), and run targeted HTTP requests to validate the changed contract.
Run the repo's fast verification commands first, then targeted unit/integration/scenario tests for the changed logic. Only start the backend and do live API calls if the change affects exposed behavior.
Validate the backend first, then run frontend/browser QA against the flow that depends on it. If the backend contract is broken, frontend results are not trustworthy.
If the user provided a URL, use it. Otherwise auto-detect common local ports:
5173, 3000, 3001, 8080, 8000, 4200
If none respond, start the most direct repo-documented local command for the changed surface. If the diff needs both frontend and backend running together and the repo provides a combined frontend/backend dev script, prefer that. Only ask the user to start something manually if the repo has no documented command or startup fails.
Try these in order:
skyvern_browser_session_create(local=true, headless=false, timeout=15)
Use local=true so the browser can reach localhost.
If local session creation fails because the MCP server is remote, the cloud browser cannot reach
localhost. Tell the user to run:
# Terminal 1: Launch a local browser with CDP exposed
skyvern browser serve --port 9222
# Terminal 2: Tunnel it to the internet
ngrok http 9222
Then connect:
skyvern_browser_session_connect(cdp_url="wss://<ngrok-subdomain>.ngrok-free.app/devtools/browser/<id>")
The user can get the browser ID from the skyvern browser serve output or by calling the
ngrok URL's /json endpoint.
skyvern_browser_session_create(timeout=15)
Only works for publicly reachable URLs. localhost URLs will not work here.
For each changed frontend file, create targeted checks. Examples:
Test 1: Settings page renders the new "Retry failed run" button
- Navigate to /settings/runs
- Assert: button with text "Retry failed run" exists
- Click it
- Assert: success toast appears
Test 2: Adjacent regression
- Verify the existing "Delete run" action still works or is still visible
Be specific. Do not write "verify the page works."
For each test case:
skyvern_navigate(url="http://localhost:<port>/<route>")
Health gate after navigation:
skyvern_evaluate(expression="(() => {
const errors = [];
const body = document.body?.innerText || '';
if (body.includes('Something went wrong')) errors.push('error_message');
if (body.includes('Cannot read properties')) errors.push('js_error_in_ui');
if (/\\bundefined\\b/.test(body) && !/\\bif\\b|\\btypeof\\b|\\bdocument|tutorial|example/i.test(body) && body.length < 5000) errors.push('undefined_text');
if (body.includes('connection refused')) errors.push('connection_refused');
if (/sign.?in|log.?in|auth/i.test(window.location.pathname)) errors.push('auth_redirect');
if (document.querySelector('[role=\"alert\"]')) errors.push('alert_element');
if (!document.querySelector('main, [role=\"main\"], nav, header, h1, h2, [class*=\"layout\" i], [class*=\"page\" i], [class*=\"app\" i]'))
errors.push('blank_page');
return JSON.stringify({ pass: errors.length === 0, errors });
})()")
Prefer deterministic DOM assertions:
skyvern_evaluate(expression="!!document.querySelector('button')")
skyvern_evaluate(expression="document.querySelector('h1')?.textContent?.trim()")
skyvern_evaluate(expression="window.location.pathname")
Use interaction tools when needed:
skyvern_act(prompt="Click the 'Retry failed run' button")
skyvern_act(prompt="Fill the email field with 'test@example.com' and click Submit")
skyvern_validate(prompt="The page shows the success toast and the form is no longer loading")
skyvern_screenshot()
Also check for failed network requests once per page:
skyvern_evaluate(expression="(() => {
const entries = performance.getEntriesByType('resource').filter(e => e.responseStatus >= 400);
return JSON.stringify({ failed: entries.map(e => ({ url: e.name, status: e.responseStatus })).slice(0, 5) });
})()")
Before starting the server or sending requests, read the repo's local instructions:
README, AGENTS.md, CLAUDE.md, Makefile, package.json, pyproject.tomlDo not guess the startup command if the repo already documents one.
If the backend is not already responding on the expected local port:
If the repo requires background processes, start them in the background and keep notes on how you did it.
Use the diff to answer:
Do not stop at the route file. Read the full handler, schema, and any changed tests.
For each changed endpoint, create targeted checks:
Examples:
Test 1: GET /api/runs returns the new field in the response body
Test 2: GET /api/runs?status=missing returns an empty list, not a 500
Test 3: POST /api/runs rejects invalid payload with a 4xx validation error
Test 4: PATCH /api/runs/:id updates the record and a follow-up GET shows the change
Use the repo's documented auth scheme and local base URL. Use curl, the repo SDK, or a small
one-off client if that is clearer than shell quoting. Prefer simple, inspectable commands.
Examples:
curl -sS -H "Authorization: Bearer <token>" \
"http://localhost:<port>/api/..."
curl -sS -X POST \
-H "Content-Type: application/json" \
-H "<auth-header>: <token>" \
-d '{"example":"value"}' \
"http://localhost:<port>/api/..."
Capture:
If the endpoint is authenticated and you cannot obtain local credentials from repo docs, say so clearly and stop rather than faking coverage.
If the diff is backend-only but does not change an exposed endpoint or UI flow:
Examples of appropriate checks:
pytest, npm test, go test, or equivalentExamples of inappropriate checks:
## QA Report
### Validation Mode
- Mode: Backend API
- Scope: `routes/runs.py`, `schemas/run_response.py`
### Changes Tested
- Added `retryable` field to run responses
- Updated `status` filter handling
### Results
| # | Test | Result | Evidence |
|---|------|--------|----------|
| 1 | GET /api/runs returns `retryable` for valid runs | PASS | HTTP 200, field present in response |
| 2 | GET /api/runs?status=missing returns empty list | PASS | HTTP 200, `[]` |
| 3 | GET /api/runs?status=invalid returns validation error | PASS | HTTP 422 |
| 4 | Frontend runs page still renders filter state | PASS | screenshot_3 |
### Issues Found
1. `retryable` is missing from one branch of the response serializer.
### Verdict
3/4 tests passed. 1 issue found.
Report the evidence that actually matters:
After generating the QA report, persist it to the pull request as a sticky comment so the evidence survives beyond the conversation.
Write the full report markdown from Step 5 to .qa/latest-report.md with a filesystem editing tool.
Do not place report text in a shell command, variable assignment, heredoc, or command substitution.
Then run the fixed command below. It reads .qa/latest-report.md and passes it to gh as a literal
argument vector without shell evaluation, updating this user's own <!-- skyvern-qa-report --> comment
when one already exists:
skyvern skill post-qa-report
If no PR exists for the current branch, the command leaves the report at .qa/latest-report.md.
Tell the user to run /qa again after creating a PR. Do not create a PR just to post a QA report.
Screenshots taken during QA (via skyvern_screenshot()) are saved locally for the agent's
verification. They are not uploaded to the PR comment because GitHub's API does not support
image uploads in issue comments. The text report describes what was observed.
If the user asks to preserve screenshots, save them to .qa/screenshots/ and tell the user
the local path. Do not include local file paths in the PR comment — they are meaningless to
other reviewers.
skyvern skill post-qa-report; do not reconstruct its gh calls in a shell.<!-- skyvern-qa-report --> marker, short commit hash, and UTC timestamp.gh is not available or not authenticated, fall back to saving the report locally and tell the user.| Problem | Action |
|---|---|
| No git diff found | Ask what behavior to validate, then fall back to explore mode |
| Frontend dev server not running | Start the most direct repo-documented local command for the changed surface; prefer a combined dev command only when the validation needs both frontend and backend; only ask the user if no documented command exists or startup fails |
| Backend server not running | Start the most direct repo-documented local command for the changed surface; prefer a combined dev environment command only when the validation needs both sides |
| Cannot identify changed endpoint | Read changed routes, schemas, and tests before proceeding |
| Auth required but no local creds available | Report the blocker clearly; do not fake coverage |
| Component does not render | Capture screenshot and specific UI error |
| API returns unexpected 5xx | Save request/response evidence and report the regression |
Before closing, fetch the session recording so you can include it in the QA report.
skyvern_browser_session_get(session_id="pbs_xxx")
→ Returns app_url (watch in browser) and recordings (download URLs)
Or simply close — skyvern_browser_session_close() now returns recording data too:
skyvern_browser_session_close()
→ { session_id, closed, app_url, recordings: [{url, filename}], downloaded_files: [{url, filename}] }
skyvern browser session get --session pbs_xxx --json
# → { "app_url": "https://...", "recordings": [...] }
skyvern browser session close --session pbs_xxx --json
# → { "session_id": "pbs_xxx", "closed": true, "app_url": "https://...", "recordings": [...] }
Include in the QA report:
app_url valuerecordings[].urlAlways close browser sessions when done:
skyvern_browser_session_close()
If you started local servers or background processes, leave the user a clear note about what is still running.
If there is no useful diff, fall back to explicit exploration:
The primary mode is still diff-driven. Always try to understand the code changes first.