Skip to content

feat(schemas): filter_diagnostics on get_products response#3508

Merged
bokelley merged 2 commits into
mainfrom
bokelley/filter-diagnostics
Apr 29, 2026
Merged

feat(schemas): filter_diagnostics on get_products response#3508
bokelley merged 2 commits into
mainfrom
bokelley/filter-diagnostics

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Adds optional filter_diagnostics block to get-products-response.json — non-fatal observability for the filter-not-fail empty-result UX gap that both pre-build expert reviewers independently flagged on PR #3472.

The gap

Every required_* filter in AdCP is silent-exclude semantics (matches OpenRTB / SSP capability discovery patterns). When the result list is empty or unexpectedly small, the buyer can't distinguish:

  • "No inventory matches the brief"
  • "required_metrics excluded everything"
  • "required_geo_targeting excluded everything"
  • "budget_range had no overlap"

Today the buyer must blindly relax filters one at a time to find the unsatisfiable one.

Shape

{
  "products": [],
  "filter_diagnostics": {
    "total_candidates": 47,
    "excluded_by": {
      "required_metrics": { "count": 31, "values": ["completed_views"] },
      "required_geo_targeting": { "count": 9 },
      "budget_range": { "count": 7 }
    }
  }
}
  • total_candidates: baseline before filters applied. May be sampled at large catalogs.
  • excluded_by: keyed by filter property name; each value carries count (required), optional values (the filter values that contributed to exclusions), and optional notes.

Design decisions

  • Counts only — never product names. Listing excluded products would leak competitive intel about adjacent campaigns. Counts plus filter values (inputs that did the excluding, not the products that got excluded) is enough for triage without that leakage.
  • Counting semantics intentionally loose. Sellers vary on ANY-filter vs ONLY-this-filter counting. Spec documents counts as approximate triage signal, not exact accounting. Tightening would force every seller to implement the same filter-evaluation ordering — internal architecture AdCP shouldn't mandate.
  • Observability, not error reporting. Sellers still silently exclude unmatched products per the filter-not-fail convention. This block is purely additive triage signal.

Backwards compatibility

Optional and additive. Sellers that don't populate the field, and buyers that don't consume it, see no change. Sellers without per-filter instrumentation adopt incrementally; absence is conformant.

Test plan

  • npm run build:schemas — clean
  • npm run test:schemas — 7/7
  • npm run test:examples — 34/34
  • npm run test:json-schema — 255/255
  • npm run test:composed — 32/32
  • npm run typecheck — clean
  • precommit + pre-push — green

Closes #3482.

🤖 Generated with Claude Code

bokelley and others added 2 commits April 29, 2026 06:47
Adds optional filter_diagnostics block to get-products-response.json —
non-fatal observability for the filter-not-fail empty-result UX gap that
both pre-build expert reviewers independently flagged on PR #3472.

Shape: { total_candidates, excluded_by: { <filter_name>: { count, values?,
notes? } } }. Optional, additive, counts only (never product names) to
avoid competitive-intel leakage on adjacent campaigns or seller inventory.

Disambiguates "no inventory matches the brief" from "your required_metrics
filter excluded everything" without breaking the filter-not-fail
convention. Sellers still silently exclude unmatched products; this block
is observability, not error reporting.

Counting semantics intentionally loose — sellers vary on ANY-filter vs
ONLY-this-filter counting. Spec documents counts as approximate triage
signal, not exact accounting. Tightening would force every seller to
implement the same filter-evaluation ordering, which AdCP shouldn't
mandate.

Closes #3482.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ed envelope, typed values

Three review fixes per expert feedback on #3508:

1. Add semantics: "only" | "any" | "approximate" enum on the parent
   filter_diagnostics block. Both reviewers flagged that "approximate"
   alone isn't RFC-2119 enough — buyers writing automated triage will
   file tickets when arithmetic doesn't add up. Sellers self-declare
   how their counts are computed; buyers inspect before doing math.
   Recommended value is "only" (counts products that would have been
   included if not for this filter alone — the value that's actually
   useful for "which filter killed my result set").

2. Close the per-filter detail envelope (additionalProperties: false).
   Sellers wanting to extend should use the response-level ext field;
   the inner shape stays sealed.

3. Constrain values to oneOf: [{type:string},{type:object}] instead of
   the open items: {}. Each filter's value type is known; buyers can
   at least know they're dealing with strings or objects without a
   per-filter switch statement.

Plus: document that total_candidates and excluded_by are independently
optional — sellers whose baseline catalog size is sensitive MAY emit
excluded_by without total_candidates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley
bokelley merged commit 0276746 into main Apr 29, 2026
18 checks passed
@bokelley
bokelley deleted the bokelley/filter-diagnostics branch April 29, 2026 12:25
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.

filter_diagnostics on get_products response — non-fatal observability for filter-not-fail

1 participant