Background
Surfaced during expert review of #679. Pre-existing gap, not introduced by that PR.
The v2.5 brand-manifest-ref schema (schemas/cache/2.5/core/brand-manifest-ref.json) defines brand_manifest as:
oneOf:
- BrandManifest object # inline object
- URL string # reference URL
Both branches are spec-compliant v2.5 input.
Impact
The current adapter only handles the string branch:
src/adcp/compat/legacy/v2_5/get_products.py:130:
brand_manifest = out.pop(\"brand_manifest\", None)
if isinstance(brand_manifest, str) and brand_manifest and \"brand\" not in out:
out[\"brand\"] = {\"domain\": extract_brand_domain(brand_manifest)}
And the matching helper at src/adcp/compat/legacy/v2_5/_media_buy_helpers.py (used by create_media_buy) has the same guard.
When a v2.5 buyer sends an inline brand_manifest object, the field is popped (so it won't survive into v3), but brand is never populated either — the request goes through with no brand at all, which then fails v3 validation if brand is required, or silently advertises no brand if it isn't.
Suggested fix
Handle the dict branch — extract the hostname from the inline object's URL field (whatever the v2.5 BrandManifest schema names it), or if the inline object has no derivable hostname, raise a typed translation error so the buyer gets a clear INVALID_REQUEST[brand_manifest] rather than a confusing downstream regex failure or silent drop.
Both call sites need the fix:
src/adcp/compat/legacy/v2_5/get_products.py:adapt_request
src/adcp/compat/legacy/v2_5/_media_buy_helpers.py:adapt_brand_manifest_to_brand (used by create_media_buy)
Background
Surfaced during expert review of #679. Pre-existing gap, not introduced by that PR.
The v2.5
brand-manifest-refschema (schemas/cache/2.5/core/brand-manifest-ref.json) definesbrand_manifestas:Both branches are spec-compliant v2.5 input.
Impact
The current adapter only handles the string branch:
src/adcp/compat/legacy/v2_5/get_products.py:130:And the matching helper at
src/adcp/compat/legacy/v2_5/_media_buy_helpers.py(used bycreate_media_buy) has the same guard.When a v2.5 buyer sends an inline
brand_manifestobject, the field is popped (so it won't survive into v3), butbrandis never populated either — the request goes through with no brand at all, which then fails v3 validation ifbrandis required, or silently advertises no brand if it isn't.Suggested fix
Handle the dict branch — extract the hostname from the inline object's URL field (whatever the v2.5
BrandManifestschema names it), or if the inline object has no derivable hostname, raise a typed translation error so the buyer gets a clearINVALID_REQUEST[brand_manifest]rather than a confusing downstream regex failure or silent drop.Both call sites need the fix:
src/adcp/compat/legacy/v2_5/get_products.py:adapt_requestsrc/adcp/compat/legacy/v2_5/_media_buy_helpers.py:adapt_brand_manifest_to_brand(used bycreate_media_buy)