spec(tmp): add required seller_agent_url to context_match_request#5410
Conversation
The context-match request now carries seller_agent_url, mirroring the identity-match request (#3687). The provider uses it to resolve the active package set it has synced for the asking seller; when package_ids is omitted, evaluation runs against that seller's full set, and an unknown seller_agent_url MUST return empty rather than fall back to another seller's set. This reverses PR #3063's decision to keep seller identity off the context request. A provider serves many sellers and needs the asking seller on the wire to scope its active-set resolution without a deployment-pinned constant — the same role seller_agent_url plays on the identity path. It does not weaken package-set decorrelation: seller_agent_url is a single stable value per placement, carries no user identity, and adds no per-user signal; the decorrelation guarantee constrains per-user-varying data (package_ids), which is unchanged. The package-side seller_agent binding remains attribution-only. Required, consistent with identity-match; context_match_request is x-status experimental so the added required field is permitted pre-stable. Normative seller-attribution prose, request examples across the trusted-match docs, and the example-validation fixtures are updated to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
One required field is missing from the one place that's normative for it. The schema now requires seller_agent_url on context_match_request, but the ContextMatchRequest field table — the human-normative listing an adopter builds the request from — never got the row. That's schema-vs-docs drift on the exact field this PR adds.
The block
docs/trusted-match/specification.mdx:55 — required field missing from the ContextMatchRequest field table. The PR makes seller_agent_url required in static/schemas/source/tmp/context-match-request.json:228 (added to required, schema is additionalProperties: false). The field table at L49-60 is exhaustive — it lists all twelve schema fields through package_ids. seller_agent_url is not among them. The IdentityMatchRequest table at L184 carries its row; the Context table does not.
What breaks for adopters: anyone who constructs context_match_request from the normative field table omits seller_agent_url, and the request fails schema validation (required + additionalProperties: false). The PR updated the §Seller Attribution prose and nine example JSON blocks but skipped the field table that actually defines the contract.
Fix is one row after placement_id (L55): | \seller_agent_url` | string (URI) | Yes | ... |, mirroring the IdentityMatchRequest row at L184. ad-tech-protocol-expert` flagged this as the primary spec-drift defect.
Things I checked
- Schema edit is well-formed.
seller_agent_urlproperty block (string,format: uri) and therequiredarray addition at context-match-request.json:56,228 are valid JSON;additionalProperties: falsepreserved. - Field parity with identity-match. Shape matches
identity-match-request.json:39(string/uri). The description correctly diverges on actor — provider resolves its synced set vs. buyer resolves its registered set — which is right per TMP roles, not a copy-paste error. - Decorrelation argument holds.
seller_agent_urlis a single per-placement constant, identical across users, carries no user identity. The Package-set decorrelation guarantee constrains per-user-varying data (package_ids), which is unchanged. The rewritten "Not a per-user filter" framing at the §Seller Attribution block is internally consistent. - Test fixtures pass. Both context-match fixtures (
tests/example-validation-simple.test.cjs:760,826) validate against the source schema via AJV withrequiredenforcement; both now carryseller_agent_url.code-reviewer: clean, 42/42 will pass. - Example JSON well-formed. All nine
.mdxinsertions sit after a comma-terminatedplacement_idline; no trailing-comma breakage. - No residual contradicting prose. Checked the surface guides and context-and-identity/buyer-guide — none still assert context_match_request omits seller identity. The old claim lived only in §Seller Attribution, which this PR rewrites.
Follow-ups (non-blocking — file after the table fix)
- The surface examples using
property_idinstead of the schema's requiredproperty_rid/request_id(surfaces/{web,mobile,ctv,ai-assistants,retail-media}.mdx,context-and-identity.mdx) are illustrative, not schema-validated — pre-existing, out of scope here, but worth a sweep. - Changeset prose says "mirroring" / "same role" — the buyer-vs-provider resolution boundary is a real, intended difference, not a mirror. Wording only.
Minor nits (non-blocking)
minorchangeset is the right call.context-match-request.json:7isx-status: experimental; adding a required field pre-stable is permitted andminoris correct. No change needed — noting it so the next reviewer doesn't re-litigate.
Add the table row and I'll flip to approve — everything else is clean. Required field, normative table, no entry: fail-closed for adopters who read the spec.
…table The schema change made seller_agent_url required, but the normative ContextMatchRequest field table in specification.mdx — what adopters build the request from — was missing the row, so a request built from the table would fail validation. Add the row after placement_id, mirroring the IdentityMatchRequest table. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…or-specific The buyer-vs-provider resolution boundary is an intended difference, not a mirror: the field shape/placement matches identity-match, but the context path resolves the provider's synced set while the identity path resolves the buyer agent's registered set. Wording only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Approving. Symmetric placement of seller_agent_url across both match requests is the right shape — the provider needs the asking seller on the wire to scope active-set resolution, exactly as the buyer agent already does on the identity path.
Things I checked
- Versioning is correct.
context-match-request.json:7isx-status: experimental, so adding a required field is permitted pre-stable.minoris the right changeset —majorwould be wrong here precisely because the stability contract doesn't yet apply.ad-tech-protocol-expert: sound. - Mirror holds. New field at
context-match-request.json:56-60(string,format: uri, canonicalization clause) matchesidentity-match-request.json:39-42near-verbatim, and therequiredparity is real (both listseller_agent_url). Aligns withseller_agent.agent_url/agent_urlconventions.additionalProperties: falsepreserved; nooneOf/discriminator interaction. - All examples updated. All 11
context_match_requestexamples acrossdocs/trusted-match/**carry the now-required field (4 top-level + 7 surface examples), plus both fixtures intests/example-validation-simple.test.cjs— which compile the schema through ajv andexit(1)on error, so they'd fail CI if the field were absent.code-reviewer: Approve. - Decorrelation argument holds.
seller_agent_urlis invariant per placement and carries no per-user signal, so it adds nothing to package-set correlation (which constrains per-user-varyingpackage_ids). The rewritten §Seller Attribution (specification.mdx:349, Router row:378, "What This Is Not":383) is internally consistent with the schema.
Follow-ups (non-blocking — file as issues)
- Signed-fields gap. The Context Match signed-fields table (
specification.mdx:456-463) was not updated.seller_agent_urlis now a required, "static per placement" field that scopes which seller's active set the provider resolves, yet it sits outside the signed envelope — meaning the seller scope can be swapped without invalidating the cached per-epoch signature. Identity Match doesn't sign it either, so there's precedent, but a field that selects which seller's set gets evaluated is load-bearing enough to either add to the signed set or to explicitly document as intentionally-unsigned. State the intent.
Minor nits (non-blocking)
- Harmonize the decorrelation bullet.
specification.mdx:400still reads "No per-request package list is sent, so the publisher cannot accidentally leak identity through package filtering." Literally still true —package_idsisn't on Context Match — but it reads as residual from the pre-reversal model now that seller scoping lives on the path. A one-clause tie-back to the new §Seller Attribution would keep the two sections in step.
The reversal of #3063 is well-argued in both the PR body and the rewritten prose — the change documents its own parent, which is the right way to relitigate a deliberate prior decision.
LGTM. Follow-ups noted below.
There was a problem hiding this comment.
Approving. Mirrors the identity-match seller_agent_url field exactly, and a multi-seller provider needs the asking seller on the wire to scope active-set resolution — deriving it from a deployment-pinned constant forces one provider per seller, which is the wrong shape.
Things I checked
- Field shape matches identity-match.
string/format: uri/required, same URL-canonicalization comparison rule, same empty-set-on-unsynced-seller MUST.context-match-request.json:56-60againstidentity-match-request.json:39-42. Right shape, and symmetric. - Changeset is the correct type.
minoris right:context-match-request.json:7isx-status: experimental, so a required field is permitted pre-stable..changeset/tmp-context-match-seller-agent-url.mdpresent and named well.ad-tech-protocol-expert: sound-with-caveats — no major bump needed. - No oneOf/discriminator impact. Both schemas use a
consttype discriminator withadditionalProperties: false, untouched.audit-oneofgate unaffected. - Schema-vs-docs coherence. The rewritten §Seller Attribution prose (Placement rationale, Router row, "What This Is Not") is internally consistent with the new field. The rewrite also drops a latent false claim — the old "What This Is Not" bullet asserted
package_ids"is not present on Context Match," butcontext-match-request.json:207has had an optionalpackage_idsall along. Good cleanup. - Required-field fallout is covered. Both inline context-match fixtures got the field (
tests/example-validation-simple.test.cjs:763,:837) — those are the only two in the repo. All 9 doc files with acontext_match_requestblock were updated, including all three blocks insurfaces/mobile.mdx. Doc JSON blocks carry no$schemaso they aren't strict-validated; no hidden CI failure.code-reviewer: no blockers.
Follow-ups (non-blocking — file as issues)
- Paired examples disagree with the PR's own thesis. The thesis is that
seller_agent_urlis "a single stable value identifying the asking seller" — but the same actor shows two different URLs across its paired Context/Identity examples:surfaces/retail-media.mdx:25(https://grocery-retailer.example) vs:81(https://retailer.example), andcontext-and-identity.mdx:35(https://retailer.example) vs:127(https://publisher.example). Examples aren't schema-validated so nothing breaks, but a reader pairing the two requests will trip on it. Make each scenario's context value match its identity value.
Minor nits (non-blocking)
syncedvsregistereddrift in the prose. The schema description and changeset deliberately say the provider resolves the set it has synced (context-match-request.json:60), but the rewritten "Placement rationale" inspecification.mdxsays the provider resolves the set it has registered. The actor-specific split (provider syncs, buyer registers) is the whole point of the rewrite — use "synced" for the provider path so the distinction survives.
LGTM. Follow-ups noted below.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
) Backport of #5410 to the 3.0.x line. Adds seller_agent_url to context_match_request so a provider can resolve the active package set it synced for the asking seller, mirroring the identity-match request's field shape (resolution actor differs: provider-side here, buyer-side on identity). Required; context_match_request is x-status experimental, so the additive required field is patch-eligible per the release playbook. Changeset is patch (3.0.x is not in pre mode; minor would cut 3.1.0). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The seller a context-match request is for now arrives per-request as `seller_agent_url` (adcontextprotocol/adcp#5410), so a single deployment serves every seller it has synced packages for. The engine passes req.SellerAgentURL into Storage.ActivePackages instead of a fixed deployment-wide value. - tmproto: add SellerAgentURL to ContextMatchRequest and require it in ValidateContextRequest. The types_gen.go field is a manual ahead-of-bundle stub for adcp#5410; it regenerates identically once adcp/schemas/VERSION pins a bundle containing the field. - targeting: drop SellerAgentURL from ContextEngine/ContextEngineConfig and resolve the active set against req.SellerAgentURL per request. - contextagent: remove the SELLER_AGENT_URL env var, its config field, and validation; update the standalone example env. - tmpclient: the Activate flow populates seller_agent_url on the context request (it already carried it on the identity request). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What
Adds a required
seller_agent_url(string,uri) tocontext_match_request, mirroring the identity-match request (#3687). The provider uses it as the key to resolve the active package set it has synced for the asking seller. Whenpackage_idsis omitted, evaluation runs against that seller's full active set; aseller_agent_urlthe provider has not synced packages for MUST return an empty offer set rather than fall back to another seller's set.Why this reverses a prior decision
PR #3063's seller-attribution section deliberately kept seller identity off
context_match_request, on two grounds: (a) the provider already holds the sync-timeseller_agentbinding, so a request field is redundant; (b) putting seller on the context path opens a request-time filtering vector that could threaten package-set decorrelation.In practice that doesn't hold up for a multi-seller provider:
seller_agent_urlon the wire for exactly this reason on the identity path; the context path needs the same. Deriving seller fromproperty_ridvia the sync-time binding is possible but couples active-set resolution to an implicit lookup the request should make explicit — and is asymmetric with identity-match for no real gain.package_ids, whose composition MUST be independent of placement.seller_agent_urlis a single stable value identifying the asking seller: identical for every user on a placement, carrying no user identity, adding no per-user signal that context and identity requests could be correlated on. It is receiver-side active-set scoping, not a per-user filter. The package-sideseller_agentbinding remains attribution-only.The normative §Seller Attribution prose (placement rationale, the Router participant row, the "What This Is Not" bullet) is rewritten so the spec is internally consistent with the new field.
Required, and why that's safe here
Required, consistent with identity-match.
context_match_requestisx-status: experimental, so adding a required field is permitted before the endpoint stabilizes.Changes
static/schemas/source/tmp/context-match-request.json—seller_agent_urladded topropertiesandrequired.docs/trusted-match/specification.mdx— §Seller Attribution rewritten (placement rationale, Router row, "What This Is Not").docs/trusted-match/{index,buyer-guide,context-and-identity,ai-mediation}.mdx+docs/trusted-match/surfaces/{web,mobile,ctv,ai-assistants,retail-media}.mdx— request examples updated.tests/example-validation-simple.test.cjs— both context-match request fixtures updated.Tests
test:schemas(15/15),test:json-schema(282/282), andexample-validation-simple(42/42) all pass.🤖 Generated with Claude Code