npx claudepluginhub kylesnowschwartz/ralph-ban --plugin ralph-banThis skill uses the workspace's default tool permissions.
Drive the running service: boot it, request it, observe the full response, judge against the spec.
Verifies deployed feature behavior read-only via API queries, log scans, metrics, and degradation-aware checks. For post-deploy smoke tests, API validation, production diagnosis.
Builds API test suites for endpoint testing, contract testing, and load testing of REST/GraphQL/gRPC APIs.
Tests REST API endpoints: validates requests/responses/auth, generates curl/Postman/scripts, load tests concurrency/response times, security scans injections/XSS/CORS.
Share bugs, ideas, or general feedback.
Drive the running service: boot it, request it, observe the full response, judge against the spec.
Scope: $ARGUMENTS
If no scope was provided, read the recent changeset to determine which routes, handlers, or middleware changed.
git diff, git log — and identify the surface that changed: route paths, request schemas, response shapes, headers, status semantics.package.json scripts, bin/rails server, Procfile, Justfile, make run. Read the project's README and CLAUDE.md first.curl. Capture status, headers, and body to separate files. Capture timing.references/assertion-grammar.md) to compare observed output to the spec.references/flake-vs-defect.md..agent-history/oracle/<card-id>/<timestamp>/.SERVER_LOG="/tmp/qa-server-$$.log"
bin/rails server -p 3000 >"$SERVER_LOG" 2>&1 &
SERVER_PID=$!
trap '[ -n "$SERVER_PID" ] && kill "$SERVER_PID" 2>/dev/null' EXIT
# Poll readiness — request the actual surface, and watch for the process to die
ready=0
for i in $(seq 1 60); do
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
echo "server process died before becoming ready (see $SERVER_LOG)" >&2
exit 1
fi
if curl -sf -o /dev/null http://localhost:3000/up; then
ready=1
break
fi
sleep 0.5
done
[ "$ready" = 1 ] || { echo "server failed to become ready in 30s (see $SERVER_LOG)" >&2; exit 1; }
Two disciplines: poll the condition (endpoint responds), not elapsed time. Watch the process with kill -0 so a crashed boot fails fast instead of polling against a corpse.
Replace bin/rails server -p 3000 and /up with the project's start command and a route the spec does not depend on (/healthz, /up, /). Polling the route under test masks readiness behind the assertion.
Capture status, headers, and body separately. curl -i collapses them; the Oracle's transcript needs them apart.
TXN=.agent-history/oracle/$CARD_ID/$(date +%Y%m%dT%H%M%S)
mkdir -p "$TXN"
curl -sS -o "$TXN/body.json" \
-D "$TXN/headers.txt" \
-w '%{http_code}\n%{time_total}\n' \
-X POST http://localhost:3000/api/widgets \
-H 'Content-Type: application/json' \
-d '{"name":"test"}' \
> "$TXN/status_and_timing.txt" 2> "$TXN/curl_stderr.txt"
# Pretty-print the request for the transcript
cat > "$TXN/request.txt" <<'EOF'
POST /api/widgets HTTP/1.1
Content-Type: application/json
{"name":"test"}
EOF
-w writes status and total time to stdout; -D dumps headers; -o writes body. Three channels, three files.
Name the boundaries the request crosses; stop at the first broken one. Failure at boundary N makes boundaries > N unassertable.
| # | Boundary | Asserted by | Evidence file |
|---|---|---|---|
| 1 | Request reaches the server | curl exits 0, status_and_timing.txt has a code | curl_stderr.txt |
| 2 | Server responds with the expected status | status_and_timing.txt line 1 | status_and_timing.txt |
| 3 | Response carries the expected headers | headers.txt matches spec | headers.txt |
| 4 | Response body matches the expected shape | jq predicates over body.json | body.json + assertion log |
| 5 | Side effects (DB row, queued job, log line) match spec | per the skill for that surface (db-state-qa, log-tail-qa) | linked transcript |
If boundary 1 fails (curl error, connection refused), boundaries 2-5 are not assertable. Report and stop.
jq is universal; lean on it for body shape. See references/assertion-grammar.md for predicates over body.json, header equality, status, and timing. Orange-OpenSource/hurl ships richer first-class predicates (jsonpath, xpath, header, duration, regex) when present.
| Observation | Spec asserts what? | Verdict |
|---|---|---|
| 2xx with expected body | matching success | satisfied |
| 2xx with unexpected body | success but shape wrong | REJECT (boundary 4) |
| 4xx | spec asserts that 4xx | satisfied |
| 4xx | spec asserts 2xx | REJECT (boundary 2) |
| 5xx, deterministic across two requests | spec does not assert 5xx | REJECT (defect) |
| 5xx, intermittent across N requests | spec does not assert idempotency | ESCALATE (flake suspected — environmental, may not be the worker's fault) |
| 5xx, deterministic | spec asserts 5xx (e.g., "shall return 503 when downstream is unavailable") | satisfied |
| Timeout | any | ESCALATE (cannot determine cause from a single trial) |
See references/flake-vs-defect.md for the longer rubric and reproduction protocol.
Save artefacts under .agent-history/oracle/<card-id>/<timestamp>/:
request.txt — the request as it would appear in an HTTP/1.1 transcriptbody.json — the response body as the server returned it (no jq processing)headers.txt — -D output, full header blockstatus_and_timing.txt — -w output, two lines: status, timecurl_stderr.txt — connection errors live here; absence is significantassertions.log — one line per asserted predicate, with match / mismatch / could-not-determineverdict.md — APPROVE / REJECT / ESCALATE with the boundary-walk table filled intrap … EXIT runs on success, failure, and interrupt.curl + jq is the universal floor; hurl is a richer dialect when present.## HTTP QA Report
**Scope**: <which endpoint(s) verified>
**Verdict**: APPROVE | REJECT | ESCALATE
### Server
- [PASS/FAIL] `<server start command>` — port, ready time
### Boundary Walk
| # | Boundary | Result | Evidence |
|---|---|---|---|
| 1 | request reaches server | PASS/FAIL | path |
| 2 | status code | PASS/FAIL | path |
| 3 | headers | PASS/FAIL | path |
| 4 | body shape | PASS/FAIL | path |
| 5 | side effects | PASS/FAIL/N/A | linked transcript |
### Specifications Verified
| Spec # | Predicate | Verified by | Verdict |
|--------|-----------|-------------|---------|
| 1 | (paste from bl show) | (jq expression / file) | satisfied / unsatisfied / could-not-determine |
### Findings
1. <description with reproduction command and evidence path>
### Transcript
Path: `.agent-history/oracle/<card-id>/<timestamp>/`
Contents: <brief listing>