Skip to content

feat(decisioning): property_list resolver + intersection helper for get_products#500

Merged
bokelley merged 2 commits into
mainfrom
claude/issue-494-property-list-resolver
May 3, 2026
Merged

feat(decisioning): property_list resolver + intersection helper for get_products#500
bokelley merged 2 commits into
mainfrom
claude/issue-494-property-list-resolver

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 3, 2026

Summary

Implements the capability-gated property_list filter for get_products requested in #494. Mirrors the webhook_emit post-adapter side-effect pattern already established in the framework.

  • PropertyListFetcher Protocol — adopter-supplied, no hidden HTTP client; distinct from ResourceResolver (which resolves wire references, not list contents)
  • resolve_property_list — fetches buyer's allowed property IDs from ref.agent_url; wraps fetch failures as AdcpError("SERVICE_UNAVAILABLE", recovery="transient"); auth_token never appears in the wire error message (logged server-side only)
  • filter_products_by_property_list — three-mode filter: 'all' → always include; 'by_id' → strict (all IDs must be ⊆ allowed) or permissive (any intersection); 'by_tag' → always exclude (can't match IDs)
  • maybe_apply_property_list_filter — post-adapter gate called by get_products shim; no-op when capability not declared or params.property_list is absent; uses model_copy not in-place mutation
  • validate_property_list_config — boot-time fail-fast when property_list_filtering=True declared but no fetcher wired
  • property_list_capability_enabled(platform) — shared helper eliminating the duplicate getattr chain in handler.py and serve.py

Public API additions to adcp.decisioning:

  • PropertyListFetcher, filter_products_by_property_list, resolve_property_list (for self-managed adopters)
  • validate_property_list_config, property_list_capability_enabled (for boot-time wiring)

Wire create_adcp_server_from_platform(platform, property_list_fetcher=MyFetcher(client)).

Expert consultation

Protocol expert — confirmed by_tag selection cannot be matched against property ID lists; confirmed property_targeting_allowed strict/permissive semantics. Flagged using model_copy to avoid aliasing the platform's return value.

DX expert — confirmed PropertyListFetcher is NOT redundant with ResourceResolver (different abstraction levels). Flagged that validate_property_list_config must be exported publicly for self-managed adopters who push filtering into their own get_products implementation.

Code reviewer — flagged {exc} interpolation into the wire error message as a credential-leak risk (upstream HTTP exception repr may carry auth_token). Fixed: exception logged server-side via logger.warning, not included in the AdcpError message or details. Also flagged duplicate getattr chain (fixed via property_list_capability_enabled helper).

Test plan

  • 30 unit tests in tests/test_decisioning_property_list.py — all passing
    • TestFilterProductsByPropertyList: 'all' always-include, 'by_id' strict/permissive, 'by_tag' always-exclude, mixed entries, empty products/allowed, property_targeting_allowed=None, empty property_ids list (strict + permissive)
    • TestResolvePropertyList: returns set; auth_token threaded; failure → AdcpError(transient); auth_token absent from error details
    • TestValidatePropertyListConfig: enabled/fetcher combinations; no fetcher + enabled → terminal error
    • TestMaybeApplyPropertyListFilter: no-op cases; filter applied + flag set; no fetcher warning; fetch failure propagation; model_copy used not in-place mutation
  • ruff check src/ — clean
  • mypy src/adcp/decisioning/property_list.py handler.py serve.py __init__.py — no new errors

Closes #494


Triage-managed PR — opened automatically by the triage agent for issue #494 (classification: Execute). Experts consulted: ad-tech-protocol-expert, adtech-product-expert, code-reviewer, dx-expert. All blocker findings addressed before PR creation. Assign a human reviewer before merging.


Generated by Claude Code

claude added 2 commits May 3, 2026 19:09
…et_products

Closes #494

Adds capability-gated framework support for buyer-side property_list
filtering in get_products, mirroring the webhook_emit post-adapter
pattern. When Features(property_list_filtering=True) is declared and a
PropertyListFetcher is wired, the framework fetches the buyer's
authorized property IDs and filters the platform's product list
post-adapter, setting response.property_list_applied=True.

https://claude.ai/code/session_01GqPV3vkvFdC6DesCzwKFNQ
- Remove {exc} interpolation from wire error message to prevent
  auth_token / credential leakage through upstream exception reprs
- Extract property_list_capability_enabled() helper to eliminate
  duplicate getattr chains in handler.py and serve.py
- Export validate_property_list_config and property_list_capability_enabled
  from adcp.decisioning public __all__ so self-managed adopters can
  call the boot guard themselves
- Add two tests for by_id with empty property_ids (strict + permissive)
  confirming the vacuous-subset guard works

https://claude.ai/code/session_01GqPV3vkvFdC6DesCzwKFNQ
@bokelley bokelley force-pushed the claude/issue-494-property-list-resolver branch from 9d9ccf0 to 8edec6c Compare May 3, 2026 23:09
@bokelley bokelley marked this pull request as ready for review May 3, 2026 23:14
@bokelley bokelley merged commit 1dfdcd5 into main May 3, 2026
14 of 15 checks passed
@bokelley bokelley deleted the claude/issue-494-property-list-resolver branch May 3, 2026 23:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(decisioning): property_list resolver + intersection helper for get_products

2 participants