Skip to content

Commit f02ea79

Browse files
bokelleyclaude
andauthored
ci(adcp): add storyboard CI job for seller_agent.py compliance checks (#309)
* ci(adcp): add storyboard CI job for seller_agent.py compliance checks Adds a non-blocking storyboard CI job (continue-on-error: true) that boots examples/seller_agent.py and runs the @adcp/client storyboard suite on every PR. Uses curl-based HTTP readiness polling instead of lsof, bounds the wait with a 30s timeout + PID-alive-check, and guards json.load with a file-existence check. Artifact always uploaded. Non-blocking until seller-agent content gaps in #304 are resolved. Closes #305 https://claude.ai/code/session_01SSje6ebBHD85TWdQbEig9m * ci(adcp): fix storyboard CI job after pre-PR review - overall_status check: 'pass' → 'passing' (actual runner enum value) - Assert step: dump full JSON on failure instead of d[k] (avoids KeyError) - Artifact upload: add if-no-files-found: warn and run_attempt suffix - Comment: document @latest intent and 405-ok readiness check rationale https://claude.ai/code/session_01SSje6ebBHD85TWdQbEig9m * ci(adcp): use 127.0.0.1 instead of localhost for storyboard probes Acting on dx-expert review of this PR: On dual-stack hosts (and Ubuntu runners since actions/runner 2.300+), ``localhost`` resolves to ``::1`` first. uvicorn's default bind is IPv4-only, so the readiness probe and runner invocation each eat a connection-refused round-trip on ``::1`` before falling back to ``127.0.0.1``. Curl falls back automatically (so it still works), but it's wasteful and slightly fragile. Pin both call sites to ``127.0.0.1`` directly. The agent's bind address is unchanged (still ``0.0.0.0`` via ``ADCP_HOST`` default in #296); only the client-side address resolution changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 04966d7 commit f02ea79

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,89 @@ jobs:
330330
echo " Numbered-variant class-name churn is expected; the semantic"
331331
echo " alias tests and drift-version-pin test guard the real surface."
332332
fi
333+
334+
storyboard:
335+
name: AdCP storyboard runner — examples/seller_agent.py
336+
runs-on: ubuntu-latest
337+
# Non-blocking until seller-agent content gaps in #304 are resolved.
338+
# Promote to required once overall_status: passing and controller_detected: true.
339+
continue-on-error: true
340+
341+
steps:
342+
- uses: actions/checkout@v4
343+
344+
- name: Set up Python 3.12
345+
uses: actions/setup-python@v5
346+
with:
347+
python-version: "3.12"
348+
349+
- name: Set up Node 22
350+
uses: actions/setup-node@v4
351+
with:
352+
node-version: "22"
353+
354+
- name: Install dependencies
355+
run: |
356+
python -m pip install --upgrade pip
357+
pip install -e ".[dev]"
358+
359+
- name: Start seller agent
360+
run: |
361+
ADCP_PORT=3001 python examples/seller_agent.py &
362+
AGENT_PID=$!
363+
for i in $(seq 1 60); do
364+
# Any HTTP response (including 405 on GET to a POST-only endpoint)
365+
# means the server is up and accepting connections.
366+
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 1 \
367+
http://127.0.0.1:3001/mcp 2>/dev/null || echo "000")
368+
if [ "$HTTP_CODE" != "000" ]; then
369+
echo "Seller agent ready (HTTP ${HTTP_CODE}, pid ${AGENT_PID})"
370+
break
371+
fi
372+
if ! kill -0 "$AGENT_PID" 2>/dev/null; then
373+
echo "Seller agent process died during startup"
374+
exit 1
375+
fi
376+
if [ "$i" -eq 60 ]; then
377+
echo "Seller agent failed to start within 30s"
378+
kill "$AGENT_PID" 2>/dev/null || true
379+
exit 1
380+
fi
381+
sleep 0.5
382+
done
383+
384+
- name: Run storyboard suite
385+
timeout-minutes: 5
386+
# @adcp/client@latest is intentionally unpinned — this is AdCP's own CI
387+
# running AdCP's own canonical runner. Tracking latest surfaces protocol
388+
# drift as soon as it ships, which is the point of this job.
389+
run: |
390+
npx -y -p @adcp/client@latest adcp storyboard run \
391+
http://127.0.0.1:3001/mcp media_buy_seller \
392+
--json --allow-http \
393+
> storyboard-result.json
394+
395+
- name: Assert pass
396+
run: |
397+
python -c "
398+
import json, sys, pathlib
399+
p = pathlib.Path('storyboard-result.json')
400+
if not p.exists() or p.stat().st_size == 0:
401+
print('storyboard-result.json missing or empty — runner produced no output')
402+
sys.exit(1)
403+
with p.open() as f:
404+
d = json.load(f)
405+
if d.get('overall_status') != 'passing':
406+
print(json.dumps(d, indent=2))
407+
sys.exit(1)
408+
if not d.get('controller_detected'):
409+
print('controller_detected was false; check DemoStore overrides (see #304)')
410+
sys.exit(1)
411+
"
412+
413+
- if: always()
414+
uses: actions/upload-artifact@v4
415+
with:
416+
name: storyboard-result-${{ github.run_attempt }}
417+
path: storyboard-result.json
418+
if-no-files-found: warn

0 commit comments

Comments
 (0)