Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
161 commits
Select commit Hold shift + click to select a range
ef120f1
fix: tolerate AdCP version-negotiation envelope and normalize bearer …
numarasSigmaSoftware Jul 6, 2026
c2956d7
fix: strip AdCP negotiation/envelope fields on A2A dispatch (#1512)
numarasSigmaSoftware Jul 7, 2026
fdb4b10
test: fix forward-compat fixture colliding with version negotiation (…
numarasSigmaSoftware Jul 7, 2026
5ca6271
fix: complete version negotiation per spec and consolidate bearer par…
numarasSigmaSoftware Jul 7, 2026
691a4fc
fix: align version negotiation with spec classification and cross-tra…
numarasSigmaSoftware Jul 7, 2026
ff0b356
refactor: address #1546 round-3 should-fixes (DRY, typed errors, BDD …
numarasSigmaSoftware Jul 9, 2026
388c2ef
fix: enforce AUTH-before-VERSION precedence + grade bearer on every t…
numarasSigmaSoftware Jul 9, 2026
7e1f9a3
fix: apply REST v2 compat on the wire + reject malformed version pins…
numarasSigmaSoftware Jul 9, 2026
4cbd4c1
Merge remote-tracking branch 'upstream/main' into pr/adcp-version-neg…
numarasSigmaSoftware Jul 9, 2026
21fed1d
fix: translate MCP auth errors to the two-layer envelope at the middl…
numarasSigmaSoftware Jul 9, 2026
7cdd966
Merge branch 'prebid:main' into pr/adcp-version-negotiation
numarasSigmaSoftware Jul 9, 2026
53ad45f
fix: harden AdCP version-negotiation review round-4 findings (#1512)
numarasSigmaSoftware Jul 13, 2026
e2a4e90
Merge remote-tracking branch 'upstream/main' into HEAD
numarasSigmaSoftware Jul 13, 2026
b16d2c5
fix: route A2A auth-failure boundary logging through the identity-awa…
numarasSigmaSoftware Jul 13, 2026
19553b9
fix: route the generic A2A message-processing error through the ident…
numarasSigmaSoftware Jul 13, 2026
dc35ae8
fix: normalize advertised AdCP version to release-precision, preservi…
numarasSigmaSoftware Jul 13, 2026
823ed8c
fix: validate and gate A2A push callbacks to close anonymous-discover…
numarasSigmaSoftware Jul 13, 2026
15bda32
fix: stop collapsing BILLING_NOT_SUPPORTED to UNSUPPORTED_FEATURE (#1…
numarasSigmaSoftware Jul 13, 2026
fdd5ecb
fix: gate REST response compat on the negotiated release, not the bod…
numarasSigmaSoftware Jul 13, 2026
1311af4
fix: resolve advertised versions module-qualified so policy overrides…
numarasSigmaSoftware Jul 13, 2026
e1cd411
refactor: thread REST negotiated version via dependency return, not r…
numarasSigmaSoftware Jul 13, 2026
ade8276
fix: echo request context on get_adcp_capabilities responses (#1512)
numarasSigmaSoftware Jul 13, 2026
6f18a64
fix: preserve client idempotency_key on sync_accounts across MCP and …
numarasSigmaSoftware Jul 13, 2026
c2a9c3b
fix: harden A2A push-callback SSRF and surface rejection as a typed e…
numarasSigmaSoftware Jul 13, 2026
4ced37a
fix: stop tolerating revision so a stale-update concurrency pin fails…
numarasSigmaSoftware Jul 13, 2026
df97c3b
fix: forward context on REST get_products so the response echoes it (…
numarasSigmaSoftware Jul 13, 2026
c85bd5c
fix: allow private/loopback callback targets in test/dev so E2E webho…
numarasSigmaSoftware Jul 13, 2026
454ca4b
fix: harden webhook SSRF with dedicated flag, all-record validation, …
numarasSigmaSoftware Jul 14, 2026
df11f90
fix: reach REST transport parity for sync_accounts idempotency_key (#…
numarasSigmaSoftware Jul 14, 2026
3f24147
Merge upstream/main: canonical error-code reconciliation (#1417) into…
numarasSigmaSoftware Jul 14, 2026
fe8c772
Merge upstream/main: transport-aware BDD harness (#1418) — compose WI…
numarasSigmaSoftware Jul 14, 2026
ca0ba61
fix: finish AUTH_REQUIRED reconciliation + restore A2A synthesized-en…
numarasSigmaSoftware Jul 14, 2026
7582b8a
fix: UC-010 harness uses _db_scope_for so e2e_rest variants share the…
numarasSigmaSoftware Jul 14, 2026
d0ab183
fix: drop CapabilitiesEnv.setup_default_data override — base get-or-c…
numarasSigmaSoftware Jul 14, 2026
b485781
Merge upstream/main: adcp 5.7.0 → 6.6.0 (spec 3.1.1) (#1567)
numarasSigmaSoftware Jul 14, 2026
0b6301f
test: re-ground prerelease-normalization pin guard on spec 3.1.1
numarasSigmaSoftware Jul 14, 2026
c069a0b
test: bound webhook-capture teardown so a shutdown race cannot eat th…
numarasSigmaSoftware Jul 14, 2026
4277a67
feat: persist media_buy revision counter with atomic lock/compare
numarasSigmaSoftware Jul 15, 2026
47fac97
refactor: extract backstop-agnostic idempotency-replay primitives
numarasSigmaSoftware Jul 15, 2026
d9239b1
feat: enforce required idempotency_key + verbatim replay on sync_acco…
numarasSigmaSoftware Jul 15, 2026
951539c
fix: complete SSRF hardening for webhook URL validation + delivery
numarasSigmaSoftware Jul 15, 2026
66b206e
test: harden webhook-capture teardown with socket timeout + fail-loud
numarasSigmaSoftware Jul 15, 2026
7813f79
docs: refresh stale spec-version commentary to 3.1.1 / adcp 6.6.0
numarasSigmaSoftware Jul 15, 2026
75ce2b8
feat: enforce atomic revision optimistic-concurrency on update_media_…
numarasSigmaSoftware Jul 15, 2026
0867257
fix: enforce required idempotency_key on sync_creatives (3.1.1)
numarasSigmaSoftware Jul 15, 2026
084a658
feat: echo context on REST get_capabilities + wire UC-010 @context sc…
numarasSigmaSoftware Jul 15, 2026
f51a37e
test: split A2A wire-error evidence from synthesized envelope (3.1.1)
numarasSigmaSoftware Jul 15, 2026
843d9a9
fix: thread capabilities protocols param into the request (3.1.1)
numarasSigmaSoftware Jul 15, 2026
ad65c76
feat: forward, SSRF-validate, and register sync_accounts push callbac…
numarasSigmaSoftware Jul 15, 2026
228ebcc
test: supply required idempotency_key on sync_creatives/sync_accounts…
numarasSigmaSoftware Jul 15, 2026
95428f5
fix: always-block 0.0.0.0/8 + AWS IPv6 metadata and stop SSRF error i…
numarasSigmaSoftware Jul 15, 2026
cf2e230
fix: sign and transmit the exact same webhook body bytes
numarasSigmaSoftware Jul 15, 2026
d540756
fix: stop webhook-capture teardown from leaking a stalled handler int…
numarasSigmaSoftware Jul 15, 2026
0309338
fix: honor the capabilities protocols filter on every transport
numarasSigmaSoftware Jul 15, 2026
69fe3e7
fix: default assets to {} in the A2A sync_creatives handler
numarasSigmaSoftware Jul 15, 2026
21266ae
feat: durable idempotency reservation infrastructure (schema + primit…
numarasSigmaSoftware Jul 15, 2026
cc5eb33
feat: atomic first-insert-wins idempotency reservation for sync_accou…
numarasSigmaSoftware Jul 15, 2026
90bd405
fix: enforce single-transaction integrity for media-buy revision lock…
numarasSigmaSoftware Jul 15, 2026
ee8a0f0
test: prove sync_accounts idempotency reservation under real concurre…
numarasSigmaSoftware Jul 15, 2026
4bd88b5
fix: wire the sync_creatives idempotency_key to the reservation (#1546)
numarasSigmaSoftware Jul 15, 2026
bb23dbc
test: mock idempotency reservation in sync_creatives A2A transport-ga…
numarasSigmaSoftware Jul 15, 2026
3ed696f
Merge remote-tracking branch 'upstream/main' into pr/adcp-version-neg…
numarasSigmaSoftware Jul 15, 2026
201a738
revert: move idempotency/revision/delivery subsystem out of #1546
numarasSigmaSoftware Jul 16, 2026
fba9bde
Merge remote-tracking branch 'upstream/main' into pr/adcp-version-neg…
numarasSigmaSoftware Jul 16, 2026
26c0778
fix: address #1546 re-review — honest charter behavior on envelope fi…
numarasSigmaSoftware Jul 17, 2026
5627b4a
Merge remote-tracking branch 'upstream/main' into pr/adcp-version-neg…
numarasSigmaSoftware Jul 17, 2026
ed970b5
chore: bump mcp 1.27.2 -> 1.28.1 (CVE-2026-59950 / GHSA-vj7q-gjh5-988w)
numarasSigmaSoftware Jul 17, 2026
9f6f3c7
fix: align AdCP 3.1.1 protocol semantics
numarasSigmaSoftware Jul 19, 2026
b385179
Merge remote-tracking branch 'upstream/main' into pr/adcp-version-neg…
numarasSigmaSoftware Jul 20, 2026
767834a
refactor: delete descoped idempotency policy/canonical modules; FIXME…
numarasSigmaSoftware Jul 20, 2026
4a70eac
fix: address #1546 re-review should-fixes 2-4 and nits (#1546)
numarasSigmaSoftware Jul 20, 2026
d7b6e4f
test: grade the missing-token principal-less guard on the real wire p…
numarasSigmaSoftware Jul 20, 2026
bb2f53d
chore: formatting, import order, and traceability entry for the missi…
numarasSigmaSoftware Jul 20, 2026
397d12e
Revert "refactor: delete descoped idempotency policy/canonical module…
numarasSigmaSoftware Jul 21, 2026
df94257
feat: restore create_media_buy idempotency replay; the replay scenari…
numarasSigmaSoftware Jul 21, 2026
a759d5f
fix: land the multi-agent review findings — versions constant, scrub …
numarasSigmaSoftware Jul 21, 2026
14338ef
test: restore the submitted-replay and key-persistence expectations w…
numarasSigmaSoftware Jul 21, 2026
f741c8d
test: keep the guard-safe UoW form when restoring the key-persistence…
numarasSigmaSoftware Jul 21, 2026
c408cc7
fix: address #1546 f741c8ddf re-review — revision wire-code convergen…
numarasSigmaSoftware Jul 21, 2026
8602a53
refactor: address #1546 KM re-review — unify duplicated predicates, v…
numarasSigmaSoftware Jul 22, 2026
0e5807b
Merge remote-tracking branch 'upstream/main' into pr/adcp-version-neg…
numarasSigmaSoftware Jul 22, 2026
714ca2d
fix: converge the A2A idempotency hash input; stop the REST raw-body …
numarasSigmaSoftware Jul 22, 2026
c7d7cb4
fix: close the log-scrub guard's three blind spots and scrub 15 live …
numarasSigmaSoftware Jul 22, 2026
a5003b8
fix: make the error-wire oracles grade the claims they back (#1546)
numarasSigmaSoftware Jul 22, 2026
a7aba82
docs: reconcile the declared idempotency posture with the shipped one…
numarasSigmaSoftware Jul 22, 2026
232c84b
refactor: land the review's NIT set — honest docstrings, one payload …
numarasSigmaSoftware Jul 22, 2026
69b4b52
fix: bound context serialization depth and stop build_version from su…
numarasSigmaSoftware Jul 24, 2026
8c12670
fix: require authentication for list_accounts on every transport
numarasSigmaSoftware Jul 24, 2026
746293a
fix: scrub buyer-controlled values reaching webhook/scheduler/a2a logs
numarasSigmaSoftware Jul 24, 2026
86225b0
fix: stop narrowing supported_protocols to the requested filter; revi…
numarasSigmaSoftware Jul 24, 2026
523487e
fix: echo negotiated context on REST pin conflicts; match AdCP Bearer…
numarasSigmaSoftware Jul 24, 2026
e9871cd
test: strengthen weak assertions and consolidate duplicated wire-fall…
numarasSigmaSoftware Jul 24, 2026
4535206
fix: detach context iteratively so accepted context is never silently…
numarasSigmaSoftware Jul 24, 2026
853706e
fix: idempotency.supported=false is the honest agent-wide declaration
numarasSigmaSoftware Jul 24, 2026
1718d24
refactor: centralize context narrowing in the error/context boundary
numarasSigmaSoftware Jul 24, 2026
1ddf9ec
fix: unify the dropped-envelope-field audit label across MCP and A2A
numarasSigmaSoftware Jul 24, 2026
e6ef31c
fix: BDD wire-error assertion must fail loud, not fall back, on a rea…
numarasSigmaSoftware Jul 24, 2026
0ed08a9
test: use transport-neutral scenario prose in the cross-transport ver…
numarasSigmaSoftware Jul 24, 2026
403a424
fix: fix deep-context handling on success responses without breaking …
numarasSigmaSoftware Jul 24, 2026
e3d8325
docs: acknowledge the idempotency capability contradiction is open, n…
numarasSigmaSoftware Jul 24, 2026
deca66b
fix: close the keyword-argument bypass in the canonical-label guard
numarasSigmaSoftware Jul 24, 2026
1dc3feb
fix: wire_error_envelope() must not misclassify A2A's direct-raw disp…
numarasSigmaSoftware Jul 24, 2026
eb2723f
docs: drop two more decorative issue-number citations
numarasSigmaSoftware Jul 24, 2026
96c9696
fix: complete AdCP idempotency guarantees
numarasSigmaSoftware Jul 25, 2026
7c5f58f
fix: complete durable idempotency guarantees
numarasSigmaSoftware Jul 25, 2026
b3a0385
fix: close durable idempotency review gaps
numarasSigmaSoftware Jul 25, 2026
461358d
Merge remote-tracking branch 'upstream/main' into pr/adcp-version-neg…
numarasSigmaSoftware Jul 25, 2026
d99e6b2
revert: split the durable idempotency subsystem out to #1689
numarasSigmaSoftware Jul 30, 2026
768a97c
Merge remote-tracking branch 'upstream/main' into pr/adcp-version-neg…
numarasSigmaSoftware Jul 30, 2026
949ab2f
fix: reconcile the #1697 SSRF merge with the branch's pinned-delivery…
numarasSigmaSoftware Jul 31, 2026
05205d5
refactor: classify webhook POST attempts via a named outcome enum
numarasSigmaSoftware Jul 31, 2026
50b2a5a
test: stub the send-time SSRF gate in the signing byte-pinning module
numarasSigmaSoftware Jul 31, 2026
882baab
test: pin the enveloped A2A push-config rejection message
numarasSigmaSoftware Jul 31, 2026
ab37156
fix: scrub the shared SSRF-rejection log helper's buyer-controlled args
numarasSigmaSoftware Jul 31, 2026
fec6130
test: share one send-time-SSRF-gate stub for delivery-mechanics modules
numarasSigmaSoftware Jul 31, 2026
2c9c308
style: sort the signing-module imports after the fixture refactor
numarasSigmaSoftware Jul 31, 2026
04d878f
fix: complete AdCP idempotency guarantees
numarasSigmaSoftware Jul 25, 2026
ea56fcf
fix: complete durable idempotency guarantees
numarasSigmaSoftware Jul 25, 2026
32f55e1
fix: close durable idempotency review gaps
numarasSigmaSoftware Jul 25, 2026
780291e
fix: reconcile the Layer B port with the trimmed #1546 base
numarasSigmaSoftware Jul 31, 2026
e9bc28c
chore: regenerate ratchet baselines for the ported tree
numarasSigmaSoftware Jul 31, 2026
7e3bb7d
test: assert the signed default-profile POST on the public-URL path
numarasSigmaSoftware Jul 31, 2026
cf366f2
test: rebase the two SSRF delivery tests onto the durable-claims harness
numarasSigmaSoftware Jul 31, 2026
6fe4488
Merge upstream/main into pr/adcp-version-negotiation
numarasSigmaSoftware Aug 11, 2026
aaeccfd
fix: keep the error envelope emittable when context holds a non-JSON …
numarasSigmaSoftware Aug 12, 2026
ebc2c04
test: grade AUTH-before-VERSION on the real MCP wire, not against a mock
numarasSigmaSoftware Aug 12, 2026
069ba8c
test: wake two dormant UC-010 scenarios and stop the harness hiding t…
numarasSigmaSoftware Aug 12, 2026
4829393
style: drop the stray blank line ruff format flagged in the bdd conftest
numarasSigmaSoftware Aug 12, 2026
8e4026b
fix(security): stop the buyer's full callback URL reaching the logs
numarasSigmaSoftware Aug 12, 2026
37dfe95
fix(security): single-source the private-target opt-in, and grade the…
numarasSigmaSoftware Aug 12, 2026
da6d4a9
fix: make the delivery deadline cover the retry budget, and record A2…
numarasSigmaSoftware Aug 12, 2026
e21e675
fix: split the agent card's schema path from its advertised negotiati…
numarasSigmaSoftware Aug 12, 2026
a7d011a
fix: correct the inverted idempotency posture, and grade three unguar…
numarasSigmaSoftware Aug 12, 2026
65387cb
docs(test): make the log-scrub matcher's docstring describe the match…
numarasSigmaSoftware Aug 12, 2026
cdb42d8
Merge PR #1546 head (65387cb21) into feat/adcp-idempotency-concurrency
numarasSigmaSoftware Aug 12, 2026
a05ff23
fix: land the grounding-note correction, and stop fitting its guard t…
numarasSigmaSoftware Aug 12, 2026
d9f464b
test(bdd): derive the anti-dormancy union, and close the tag-deletion…
numarasSigmaSoftware Aug 12, 2026
7c70fca
fix: repair the two seams the re-stack's new schema pin exposed
numarasSigmaSoftware Aug 12, 2026
e1385d6
test: derive the log-scrub scan set from buyer-URL handling, not from…
numarasSigmaSoftware Aug 12, 2026
11fdc0f
fix: give the retry delay one home, and make the deadline cover admis…
numarasSigmaSoftware Aug 12, 2026
69070f5
fix: derive the UC-004 product's reporting capabilities from the mode…
numarasSigmaSoftware Aug 12, 2026
42b0390
fix: three defects the previous round's fixes introduced
numarasSigmaSoftware Aug 12, 2026
e664e15
fix(test): deliver UC-004 webhooks through the seams production actua…
numarasSigmaSoftware Aug 12, 2026
f9da2b6
fix: A2A records a durable row, unrecognised auth schemes are loud, p…
numarasSigmaSoftware Aug 12, 2026
75ba0e5
fix: restore the task-not-found payload, and the request REST was dro…
numarasSigmaSoftware Aug 13, 2026
c90564d
refactor: type _best_effort_a2a_identity instead of Any/Any [order NI…
numarasSigmaSoftware Aug 13, 2026
b068b9a
docs: correct the log-scrub guard comment's #1953 coverage claim [ord…
numarasSigmaSoftware Aug 13, 2026
214ecef
docs(bdd): mirror the dormancy annotation onto the sibling @revision …
numarasSigmaSoftware Aug 13, 2026
9f56464
test: strengthen the A2A durable-row assertion and fix a dangling com…
numarasSigmaSoftware Aug 13, 2026
be2c815
fix: split the unrecognised-scheme warning from the missing-token cas…
numarasSigmaSoftware Aug 13, 2026
a02abb5
test(bdd): close the two undisclosed UC-003 anti-dormancy gaps [order…
numarasSigmaSoftware Aug 13, 2026
ab745bc
fix: guard the envelope tier-3 fallback against its own failure [orde…
numarasSigmaSoftware Aug 13, 2026
317cceb
docs(test): name the method the task-not-found tests actually route t…
numarasSigmaSoftware Aug 13, 2026
b71b66f
test: pin the hoisted UC-003 sets in the shrink-only baseline [order …
numarasSigmaSoftware Aug 13, 2026
efdba9f
Merge #1546 head (b71b66f62), and scrub the log sites that left the g…
numarasSigmaSoftware Aug 17, 2026
6e43e7e
fix: refuse an idempotency_key the caller has no scope to replay within
numarasSigmaSoftware Aug 17, 2026
fb6d8eb
Merge upstream/main (eccc45a76) to restore CI, and keep escaping over…
numarasSigmaSoftware Aug 17, 2026
a87a833
chore: re-trigger CI after the GitHub archive-download outage
numarasSigmaSoftware Aug 17, 2026
5696a96
chore: re-run the two checks stranded by the GitHub outage
numarasSigmaSoftware Aug 18, 2026
44e18f9
chore: re-run CI to settle the in-network 500 signature
numarasSigmaSoftware Aug 18, 2026
5618cc5
ci: capture server logs on in-network BDD failure
numarasSigmaSoftware Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
383 changes: 383 additions & 0 deletions .claude/notes/pr1546-adcp-3.1.1-grounding.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions .duplication-baseline
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"src": 35,
"tests": 73,
"src": 33,
"tests": 72,
"scripts": 0
}
25 changes: 25 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,31 @@
# production: Lenient - ignores unknown fields for forward compatibility
# ENVIRONMENT=production

# Read and write idempotency admission limits can be tuned independently.
# If unset, each class falls back to the legacy IDEMPOTENCY_* setting, then
# to the built-in defaults (1000 active rows; 300 inserts per 10 seconds).
# IDEMPOTENCY_READ_MAX_ACTIVE_ATTEMPTS_PER_SCOPE=1000
# IDEMPOTENCY_READ_MAX_INSERTS_PER_WINDOW=300
# IDEMPOTENCY_READ_INSERT_RATE_WINDOW_SECONDS=10
# IDEMPOTENCY_WRITE_MAX_ACTIVE_ATTEMPTS_PER_SCOPE=1000
# IDEMPOTENCY_WRITE_MAX_INSERTS_PER_WINDOW=300
# IDEMPOTENCY_WRITE_INSERT_RATE_WINDOW_SECONDS=10

# When DATABASE_URL uses PgBouncer transaction pooling, provider-operation
# advisory locks require a second URL that connects directly to PostgreSQL.
# COORDINATION_DATABASE_URL=postgresql://user:password@postgres:5432/database

# Required when accepting push_notification_config without an explicit legacy
# Bearer/HMAC authentication block. JSON-encode a private Ed25519 or ES256 JWK
# whose public half is published at the agent's jwks_uri and whose
# adcp_use is "webhook-signing".
# ADCP_WEBHOOK_SIGNING_JWK={"kty":"OKP","crv":"Ed25519","alg":"EdDSA","kid":"...","adcp_use":"webhook-signing","x":"...","d":"..."}
# ADCP_WEBHOOK_SIGNING_JWKS_URI=https://seller.example/.well-known/jwks.json
# ADCP_BRAND_JSON_URL=https://seller.example/.well-known/brand.json
# Optional deployment assertion; the advertised/wire algorithm is derived from
# the JWK and startup validation rejects a mismatch.
# ADCP_WEBHOOK_SIGNING_ALGORITHM=ed25519

# Demo tenant creation (default: false)
# When true, creates a "Default Publisher" tenant with mock adapter on first startup
# Set to true for local development/evaluation, leave false for production deployments
Expand Down
31 changes: 30 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,14 @@ jobs:
docker container prune -f 2>/dev/null || true
docker network prune -f 2>/dev/null || true
docker volume prune -f 2>/dev/null || true
# The e2e stack exposes its narrowly scoped setup controls only when both
# ADCP_TESTING=true and this per-run secret are present. run_all_tests.sh
# generates it for local runs; this job invokes pytest directly, so it has
# to generate its own — the compose stack (docker-compose.e2e.yml) and the
# tests both read it from the environment and must see the same value.
- name: Generate the per-run e2e control token
run: |
echo "ADCP_TEST_CONTROL_TOKEN=$(python3 -c 'import secrets; print(secrets.token_hex(32))')" >> "$GITHUB_ENV"
# No coverage: the server under test runs inside Docker, so host-side
# coverage would measure the client harness, not src/.
- uses: ./.github/actions/_pytest
Expand Down Expand Up @@ -365,9 +373,30 @@ jobs:
# No host uvx in this job (no _setup-env); the dedicated Security
# Audit check owns the uv-secure scan.
RUN_ALL_SKIP_AUDIT: 1
# Fixed rather than the default adcp-innet-$$ (PID-derived, unknown
# to any later step) so the log-capture step below can reach the
# same compose project after run_all_tests.sh exits.
COMPOSE_PROJECT_NAME: ci-bdd-e2e-rest
# Diagnostic only, no behavior change: this job has intermittently failed
# with unrelated scenarios reporting INTERNAL_ERROR / HTTP 500 from
# account resolution and create_media_buy, on three separate runs with
# three disjoint failing-test sets and 0/428 failures on an identical
# local run each time (see PR #1689 review-triage notes). The live
# server here runs the delivery-webhook and media-buy-status background
# schedulers concurrently with foreground requests (not disabled in this
# compose file), and any OperationalError/DisconnectionError anywhere
# trips a process-wide 10s "unhealthy" circuit breaker
# (src/core/database/database_session.py) that fails every OTHER request
# in that window — a plausible single root cause for disjoint collateral
# failures. Captures the server's own traceback so the next occurrence
# is diagnosed from evidence instead of another blind re-run.
- name: Dump server logs on failure
if: failure()
run: |
docker compose -f docker-compose.e2e.yml -p ci-bdd-e2e-rest logs --no-color --timestamps adcp-server postgres 2>&1 | tail -2000 || true
- name: Cleanup Docker services
if: always()
run: docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
run: docker compose -f docker-compose.e2e.yml -p ci-bdd-e2e-rest down -v 2>/dev/null || true

admin-ui-tests:
name: Admin UI Tests
Expand Down
2 changes: 1 addition & 1 deletion .mypy-untyped-defs-baseline
Original file line number Diff line number Diff line change
@@ -1 +1 @@
227
216
4 changes: 2 additions & 2 deletions .ruff-complexity-baseline
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"C901": 183,
"PLR0912": 134,
"C901": 180,
"PLR0912": 133,
"PLR0915": 108
}
2 changes: 1 addition & 1 deletion .type-ignore-baseline
Original file line number Diff line number Diff line change
@@ -1 +1 @@
63
60
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ AST-scanning tests enforce architecture invariants on every `make quality` run.

## AdCP Spec Version

This project targets AdCP spec **3.1.0-beta.3** via the `adcp==5.7.0` Python SDK. See
This project targets AdCP spec **3.1.1** via the `adcp==6.6.0` Python SDK. See
[docs/adcp-spec-version.md](docs/adcp-spec-version.md) for the version mapping
and bump procedure. The CI guard at `tests/unit/test_adcp_spec_version.py`
fails on pin drift.
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ An open-source implementation of the [Ad Context Protocol (AdCP)](https://adcont
sales-agent role, maintained under Prebid.org. It lets AI agents discover and buy advertising
inventory through standardized MCP and A2A interfaces.

> **Status: alpha.** A pre-1.0 implementation of a pre-release protocol
> (AdCP 3.1.0-beta.3), under active development. APIs still change between
> **Status: alpha.** A pre-1.0 implementation of AdCP 3.1.1,
> under active development. APIs still change between
> releases — `v2.0.0` introduced breaking changes — so pin a version and expect
> to adapt on upgrade. It is one of several AdCP sales-agent implementations,
> not a sole or canonical reference. The codebase is substantial and functional
Expand All @@ -22,9 +22,8 @@ The Prebid Sales Agent is a server that:

## AdCP Compatibility

This implementation targets **AdCP spec version 3.1.0-beta.3** via the `adcp==5.7.0`
Python SDK. That spec version is a **beta** — request/response shapes are not yet
frozen, and SDK bumps can change them. See
This implementation targets **AdCP spec version 3.1.1** via the `adcp==6.6.0`
Python SDK. SDK bumps can change request/response shapes. See
[docs/adcp-spec-version.md](docs/adcp-spec-version.md) for the SDK-to-spec mapping
and bump procedure. The pin is enforced by a CI guard
(`tests/unit/test_adcp_spec_version.py`), which fails on drift.
Expand Down Expand Up @@ -75,8 +74,8 @@ Publishers deploy their own sales agent. Choose based on your needs:

**Docker is the fastest** - it bundles PostgreSQL and just works. Cloud platforms require separate database setup.

Because this is alpha software tracking a beta protocol, pin the version you deploy
and re-test after every upgrade — minor releases can carry breaking changes.
Because this implementation is alpha software, pin the version you deploy and
re-test after every upgrade — releases can carry breaking changes.

### After Deployment

Expand Down Expand Up @@ -284,8 +283,10 @@ async with client:
products = await client.tools.get_products(brief="video ads for sports content")

# 2. Book a media buy. Each package references a product and one of its
# pricing options. `idempotency_key` is REQUIRED (16-255 chars) — reusing
# the same key makes retries safe (returns the original buy, no duplicate).
# pricing options. `idempotency_key` is REQUIRED and shape-validated
# (16-255 chars). Retry an ambiguous request with the SAME key: completed
# requests replay for the advertised 24-hour window, while active work
# returns IDEMPOTENCY_IN_FLIGHT. Never mint a fresh key for a retry.
result = await client.tools.create_media_buy(
brand="acme.com", # domain shorthand for a BrandReference
start_time="2026-08-01T00:00:00Z", # ISO 8601, or "asap"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""complete idempotency read and downstream reconciliation guarantees

Revision ID: a4d7e8c91f20
Revises: f3a1c92b47de
Create Date: 2026-07-24 00:00:00.000000
"""

from collections.abc import Sequence

import sqlalchemy as sa

from alembic import op

revision: str = "a4d7e8c91f20"
down_revision: str | Sequence[str] | None = "f3a1c92b47de"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def upgrade() -> None:
"""Enable anonymous read scopes, split admission classes, and add claims."""
op.alter_column("idempotency_attempts", "principal_id", existing_type=sa.String(length=50), nullable=True)
op.add_column(
"idempotency_attempts",
sa.Column("operation_class", sa.String(length=8), nullable=False, server_default="write"),
)
op.create_check_constraint(
"ck_idempotency_attempt_operation_class",
"idempotency_attempts",
"operation_class IN ('read', 'write')",
)
op.create_table(
"downstream_mutation_claims",
sa.Column("claim_id", sa.String(length=50), nullable=False),
sa.Column("tenant_id", sa.String(length=50), nullable=False),
sa.Column("principal_id", sa.String(length=50), nullable=False),
sa.Column("account_id", sa.String(length=100), nullable=True),
sa.Column("idempotency_key", sa.String(length=255), nullable=False),
sa.Column("provider", sa.String(length=50), nullable=False),
sa.Column("operation_key", sa.String(length=255), nullable=False),
sa.Column("downstream_request_id", sa.String(length=64), nullable=False),
sa.Column("request_hash", sa.String(length=64), nullable=False),
sa.Column("status", sa.String(length=16), nullable=False, server_default="planned"),
sa.Column("result_metadata", sa.JSON(), nullable=True),
sa.Column("expires_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
sa.ForeignKeyConstraint(
["tenant_id"],
["tenants.tenant_id"],
name="fk_downstream_mutation_claims_tenant",
ondelete="CASCADE",
),
sa.ForeignKeyConstraint(
["tenant_id", "principal_id"],
["principals.tenant_id", "principals.principal_id"],
name="fk_downstream_mutation_claims_principal",
ondelete="CASCADE",
),
sa.CheckConstraint(
"status IN ('planned', 'invoked', 'applied', 'unknown')",
name="ck_downstream_mutation_claim_status",
),
sa.PrimaryKeyConstraint("claim_id"),
)
op.create_index(
"idx_downstream_mutation_claims_scope",
"downstream_mutation_claims",
["tenant_id", "principal_id", "account_id", "idempotency_key", "provider", "operation_key"],
unique=True,
postgresql_nulls_not_distinct=True,
)
op.create_index(
"idx_downstream_mutation_claims_expires_at",
"downstream_mutation_claims",
["expires_at"],
unique=False,
)


def downgrade() -> None:
"""Remove reconciliation claims and restore authenticated-only attempts."""
op.drop_index("idx_downstream_mutation_claims_expires_at", table_name="downstream_mutation_claims")
op.drop_index("idx_downstream_mutation_claims_scope", table_name="downstream_mutation_claims")
op.drop_table("downstream_mutation_claims")
op.execute("DELETE FROM idempotency_attempts WHERE principal_id IS NULL")
op.drop_constraint(
"ck_idempotency_attempt_operation_class",
"idempotency_attempts",
type_="check",
)
op.drop_column("idempotency_attempts", "operation_class")
op.alter_column("idempotency_attempts", "principal_id", existing_type=sa.String(length=50), nullable=False)
Loading
Loading