diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 130b9226..6d17551b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -330,3 +330,89 @@ jobs: echo " Numbered-variant class-name churn is expected; the semantic" echo " alias tests and drift-version-pin test guard the real surface." fi + + storyboard: + name: AdCP storyboard runner — examples/seller_agent.py + runs-on: ubuntu-latest + # Non-blocking until seller-agent content gaps in #304 are resolved. + # Promote to required once overall_status: passing and controller_detected: true. + continue-on-error: true + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Set up Node 22 + uses: actions/setup-node@v4 + with: + node-version: "22" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -e ".[dev]" + + - name: Start seller agent + run: | + ADCP_PORT=3001 python examples/seller_agent.py & + AGENT_PID=$! + for i in $(seq 1 60); do + # Any HTTP response (including 405 on GET to a POST-only endpoint) + # means the server is up and accepting connections. + HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 1 \ + http://127.0.0.1:3001/mcp 2>/dev/null || echo "000") + if [ "$HTTP_CODE" != "000" ]; then + echo "Seller agent ready (HTTP ${HTTP_CODE}, pid ${AGENT_PID})" + break + fi + if ! kill -0 "$AGENT_PID" 2>/dev/null; then + echo "Seller agent process died during startup" + exit 1 + fi + if [ "$i" -eq 60 ]; then + echo "Seller agent failed to start within 30s" + kill "$AGENT_PID" 2>/dev/null || true + exit 1 + fi + sleep 0.5 + done + + - name: Run storyboard suite + timeout-minutes: 5 + # @adcp/client@latest is intentionally unpinned — this is AdCP's own CI + # running AdCP's own canonical runner. Tracking latest surfaces protocol + # drift as soon as it ships, which is the point of this job. + run: | + npx -y -p @adcp/client@latest adcp storyboard run \ + http://127.0.0.1:3001/mcp media_buy_seller \ + --json --allow-http \ + > storyboard-result.json + + - name: Assert pass + run: | + python -c " + import json, sys, pathlib + p = pathlib.Path('storyboard-result.json') + if not p.exists() or p.stat().st_size == 0: + print('storyboard-result.json missing or empty — runner produced no output') + sys.exit(1) + with p.open() as f: + d = json.load(f) + if d.get('overall_status') != 'passing': + print(json.dumps(d, indent=2)) + sys.exit(1) + if not d.get('controller_detected'): + print('controller_detected was false; check DemoStore overrides (see #304)') + sys.exit(1) + " + + - if: always() + uses: actions/upload-artifact@v4 + with: + name: storyboard-result-${{ github.run_attempt }} + path: storyboard-result.json + if-no-files-found: warn