Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/catalog-optional-collection-features.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"adcontextprotocol": patch
---

Collection selection and owner-sold carriage checks now discover native wholesale sandbox products instead of seeding invented collection support. Six independent scopes cover selectable subsets, one-pair default materialization, fixed bundles (omitted or false targeting flag), and structural cross-publisher selection. A completed valid catalog with no qualifying product reports `not_applicable` (`fixture_unsatisfied`): the feature was not tested, not passed or proven unsupported. Discovery and selected-path errors still fail.

Selectors, negative witnesses and commercial terms come from the same selected product/price graph. Exact echoes compare domain-qualified sets without assuming illustrative collection names, domains or prices. Two separately tool-gated companions retain persisted subset/default checks without making optional `get_media_buys` support a prerequisite for creation. Only non-guaranteed, fixed-price CPM catalog products are covered; seeded-only and brief-only support remains outside this evidence.

Completes #8254 with the reporting correction in #8336. No seller capability field, controller seed-refusal rule or released artifact is changed.
4 changes: 3 additions & 1 deletion docs/reference/known-limitations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ Request-specific configured offers are not a gap: canonical products carry no `i
- **Catalog reporting selection.** `advanced_delivery_reporting` selects a native wholesale sandbox product advertising format breakdown; `time_based_delivery_reporting` independently selects one listing `time_based_views`.
- **Catalog reporting not applicable.** `fixture_unsatisfied`, projected `not_applicable`, means no qualifying product was found. The feature was not tested; this is neither a pass nor proof of nonsupport. Fixture discovery errors and broken selected support still fail.
- **Catalog reporting pass scope.** A pass covers required response fields, metric narrowing and legal returned measurements. A pass does not attest that the seller returns time-based views; the controller cannot inject view observations. A one-row format request cannot prove multi-row ordering or selection from an unobserved population. Mature metric value-or-disclosure accountability remains [#8412](https://github.com/adcontextprotocol/adcp/issues/8412).
- **Catalog reporting untested paths.** Only non-guaranteed, fixed-price CPM products are graded. Guaranteed, auction-priced, brief-only and seeded-only support is not tested, and the card cannot yet distinguish this from nonsupport ([#8416](https://github.com/adcontextprotocol/adcp/issues/8416)). Collection scenarios are unchanged. The scheduled heartbeat runs bare `comply()` without selected-price budget or authored-brand derivation until [SDK #3381](https://github.com/adcontextprotocol/adcp-client/issues/3381) and [SDK #3382](https://github.com/adcontextprotocol/adcp-client/issues/3382) are addressed. Adapter and SDK details are in the [qualification memo](https://github.com/adcontextprotocol/adcp/blob/main/specs/optional-product-reporting-qualification.md).
- **Catalog reporting untested paths.** Only non-guaranteed, fixed-price CPM products are graded. Guaranteed, auction-priced, brief-only and seeded-only support is not tested, and the card cannot yet distinguish this from nonsupport ([#8416](https://github.com/adcontextprotocol/adcp/issues/8416)). The scheduled heartbeat runs bare `comply()` without selected-price budget or authored-brand derivation until [SDK #3381](https://github.com/adcontextprotocol/adcp-client/issues/3381) and [SDK #3382](https://github.com/adcontextprotocol/adcp-client/issues/3382) are addressed. Adapter and SDK details are in the [qualification memo](https://github.com/adcontextprotocol/adcp/blob/main/specs/optional-product-reporting-qualification.md).
- **Catalog collection coverage.** Six independently qualified scopes grade selectable subsets, one-pair default materialization, fixed-bundle enforcement and restatement, and structural cross-publisher carriage/full or subset selection. Fixed bundles accept literal false or an omitted flag. A valid complete no-match is `fixture_unsatisfied`: feature not tested, never a support pass or proof of nonsupport. Fixture catalog discovery errors and broken selected paths still fail. Two separately reader-gated companions check persisted subset/default echoes; absence of `get_media_buys` does not suppress core creation checks.
- **Collection coverage boundaries.** Only native wholesale sandbox, non-guaranteed fixed-price CPM products are exercised; seeded-only, brief-only, guaranteed and auction-priced support remains outside this catalog slice. Cross-publisher means a collection namespace outside every property namespace on the same product; it does not verify ownership, host authorization or a supply path. Standalone reads require trusted successful-create buy/package/product/committed-selector context and requalify the current catalog; no durable cross-request grant or catalog-retirement immunity is claimed. SDK 16.3 can falsely fail an active read after a legal create response omitting optional `media_buy_status` ([SDK #3395](https://github.com/adcontextprotocol/adcp-client/issues/3395)); that variation remains unqualified, and the optional field is not made mandatory. Widened sets still fail, but failed comparison evidence omits the authored context key ([SDK #3396](https://github.com/adcontextprotocol/adcp-client/issues/3396)). Wholesale feed changes between pages are not yet restarted by the buyer ([SDK #3397](https://github.com/adcontextprotocol/adcp-client/issues/3397)); mutable-feed qualification remains incomplete, not seller nonconformance. The scheduled bare `comply()` path still lacks the repository caller's commercial/selector projection until [SDK #3382](https://github.com/adcontextprotocol/adcp-client/issues/3382). Details and historical #8259 reconciliation are in the [collection qualification memo](https://github.com/adcontextprotocol/adcp/blob/main/specs/optional-product-collection-qualification.md).
- **OOH/audio journey coverage remains bounded.** Expanded OOH/audio measurement identities still have schema and focused contract-test coverage rather than one complete discovery → purchase → delivery journey per metric family.
- **Audience activation coverage.** Audience activation has capability-gated storyboards for both discovery and execution: `audience_activation_discovery` covers the catalog-wide union, per-product declarations, vendor matching, OR/wildcard filter semantics, and exclusion of undeclared products; `external_audience_source_binding` binds a declared shared dataset, preserves its source and match state through discovery, uses the resulting audience in a media buy, and rejects undeclared rails or cross-transport updates. The reference seller deliberately advertises `dataset_query`, not `platform_distribution`, so the latter remains not applicable rather than simulated as a capability it does not provide.
- **No automated enforcement of platform agnosticism** beyond the `check:platform-agnostic` lint scanning property names. A richer check covering schema semantics is future work.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
"test:tenant-routing": "vitest run --config server/vitest.config.ts server/src/training-agent/tenants/tenant-smoke.test.ts",
"test:format-identity-boundaries": "node --test --test-force-exit --test-timeout=30000 tests/lint-format-identity-boundaries.test.cjs",
"test:sdk-runner-capability-gates": "node --test --test-force-exit --test-timeout=30000 tests/sdk-runner-capability-gates.test.cjs && node --test --test-timeout=30000 tests/evaluator-allowlist-runner.test.cjs && node --test --test-timeout=30000 tests/optional-product-reporting.test.cjs tests/optional-product-reporting-public.test.cjs tests/catalog-reporting-preflight.test.cjs tests/catalog-reporting-preflight-security.test.cjs",
"test:optional-product-collections": "node --test --test-concurrency=1 --test-timeout=30000 tests/optional-product-collection-discovery.test.cjs tests/optional-product-collection-echo.test.cjs tests/optional-product-collection-price.test.cjs tests/optional-product-collection-rejections.test.cjs tests/optional-product-collection-scope.test.cjs tests/optional-product-collections.test.cjs tests/optional-product-collection-history.test.cjs",
"test:callapi-state-change": "node --test --test-force-exit --test-timeout=30000 tests/lint-callapi-state-change.test.cjs",
"test:cdn-artifacts-worker": "node --test --test-force-exit --test-timeout=30000 tests/artifact-cdn-worker.test.cjs",
"test:sign-protocol-tarball": "node --test --test-force-exit --test-timeout=30000 tests/sign-protocol-tarball.test.cjs",
Expand Down Expand Up @@ -171,7 +172,7 @@
"audit:verification-profile-shadow": "tsx server/src/scripts/audit-verification-profile-shadow.ts",
"test:schema-utf8": "node scripts/normalize-schema-utf8.mjs --check",
"fix:schema-utf8": "node scripts/normalize-schema-utf8.mjs",
"test": "npm run test:sdk-response-conformance && npm run test:docs-nav && npm run test:owned-links && npm run test:release-docs-nav && npm run test:rewrite-dist-redirect-links && npm run test:rewrite-dist-links-idempotency && npm run test:docs-error-handling-copy && npm run test:schemas && npm run test:security-profiles && npm run test:performance-feedback && npm run test:mcp-schema-projection && npm run test:dist-schema-version-ids && npm run test:examples && npm run test:extensions && npm run test:extension-schemas && npm run test:error-handling && npm run test:json-schema && npm run test:audio-radio && npm run test:adagents-catalog-only && npm run test:fast-channel-collections && npm run test:collection-selection-echo && npm run test:canonical-reference-resolver && npm run test:composed && npm run test:rejection-arm-mutex && npm run test:migrations && npm run test:hmac-vectors && npm run test:hmac-signer-conformance && npm run test:webhook-signing-vectors && npm run test:functional-request-signing-contract && npm run test:webhook-receiver-envelope && npm run test:webhook-emission-storyboard && npm run test:oauth-setup-vectors && npm run test:a2a-profile-extension && npm run test:transport-errors && npm run test:wba-profile-vectors && npm run test:targeting-overlay-vectors && npm run test:targeting-null-clear && npm run test:targeting-aware-discovery && npm run test:demographic-targeting && npm run test:audience-activation-methods && npm run test:principal && npm run test:audience-external-sources && npm run test:language-targeting && npm run test:availability-horizon && npm run test:attestation-vectors && npm run test:rights-attestations && npm run test:governance-runtime-attestations && npm run test:governance-conditions-storyboard && npm run test:signal-governance-storyboard && npm run test:governance-task-slices && npm run test:audience-evidence && npm run test:status-as-of-vectors && npm run test:storyboard-scoping && npm run test:storyboard-branch-sets && npm run test:storyboard-provides-state-for && npm run test:storyboard-fixture-resolution && npm run test:storyboard-contradictions && npm run test:storyboard-context-entity && npm run test:storyboard-auth-shape && npm run test:storyboard-test-kits && npm run test:storyboard-buyer-fixture-lifecycle-tools && npm run test:storyboard-buyer-fixture-products && npm run test:compliance-packaged-refs && npm run test:compliance-source-authority && npm run test:storyboard-sample-request-schema && npm run test:storyboard-response-schema && npm run test:storyboard-context-output-paths && npm run test:storyboard-context-resolution && npm run test:storyboard-validations-paths && npm run test:storyboard-check-enum && npm run test:update-media-buy-affected-packages && npm run test:storyboard-advisory-expiry && npm run test:storyboard-controller-requires && npm run test:storyboard-raw-mode-required && npm run test:storyboard-upstream-traffic-paths && npm run test:storyboard-generate-phase-scope && npm run test:refine-finalize-validation-ids && npm run test:run-storyboards-schema-root && npm run test:storyboard-doc-parity && npm run test:v4-compatibility-registry && npm run test:pagination-invariant && npm run test:version-envelope && npm run test:test-dynamic-imports && npm run test:sdk-shims && npm run test:format-identity-boundaries && npm run test:sdk-runner-capability-gates && npm run test:callapi-state-change && npm run test:sign-protocol-tarball && npm run test:chat-streaming-code-fences && npm run test:certification-demo-formatting && npm run test:build-schemas-hoist-enums && npm run test:build-schemas-hoist-marked && npm run test:build-schemas-hoist-by-identity && npm run test:build-schemas-async-response-refs && npm run test:build-schemas-vendor-metric-uniqueness && npm run test:release-workflow && npm run test:immutable-release-artifacts && npm run test:patch-3-0-compat-bundle && npm run test:error-codes && npm run test:compliance-snippets && npm run test:doc-compliance-drift && npm run test:substitution-vector-names && npm run test:platform-agnostic && npm run test:oneof-discriminators && npm run test:schema-utf8 && npm run test:unit && npm run test:server-unit && npm run test:openapi && npm run typecheck && npm run test:contracts && npm run test:example-validation && npm run test:test-coverage",
"test": "npm run test:sdk-response-conformance && npm run test:docs-nav && npm run test:owned-links && npm run test:release-docs-nav && npm run test:rewrite-dist-redirect-links && npm run test:rewrite-dist-links-idempotency && npm run test:docs-error-handling-copy && npm run test:schemas && npm run test:security-profiles && npm run test:performance-feedback && npm run test:mcp-schema-projection && npm run test:dist-schema-version-ids && npm run test:examples && npm run test:extensions && npm run test:extension-schemas && npm run test:error-handling && npm run test:json-schema && npm run test:audio-radio && npm run test:adagents-catalog-only && npm run test:fast-channel-collections && npm run test:collection-selection-echo && npm run test:canonical-reference-resolver && npm run test:composed && npm run test:rejection-arm-mutex && npm run test:migrations && npm run test:hmac-vectors && npm run test:hmac-signer-conformance && npm run test:webhook-signing-vectors && npm run test:functional-request-signing-contract && npm run test:webhook-receiver-envelope && npm run test:webhook-emission-storyboard && npm run test:oauth-setup-vectors && npm run test:a2a-profile-extension && npm run test:transport-errors && npm run test:wba-profile-vectors && npm run test:targeting-overlay-vectors && npm run test:targeting-null-clear && npm run test:targeting-aware-discovery && npm run test:demographic-targeting && npm run test:audience-activation-methods && npm run test:principal && npm run test:audience-external-sources && npm run test:language-targeting && npm run test:availability-horizon && npm run test:attestation-vectors && npm run test:rights-attestations && npm run test:governance-runtime-attestations && npm run test:governance-conditions-storyboard && npm run test:signal-governance-storyboard && npm run test:governance-task-slices && npm run test:audience-evidence && npm run test:status-as-of-vectors && npm run test:storyboard-scoping && npm run test:storyboard-branch-sets && npm run test:storyboard-provides-state-for && npm run test:storyboard-fixture-resolution && npm run test:storyboard-contradictions && npm run test:storyboard-context-entity && npm run test:storyboard-auth-shape && npm run test:storyboard-test-kits && npm run test:storyboard-buyer-fixture-lifecycle-tools && npm run test:storyboard-buyer-fixture-products && npm run test:compliance-packaged-refs && npm run test:compliance-source-authority && npm run test:storyboard-sample-request-schema && npm run test:storyboard-response-schema && npm run test:storyboard-context-output-paths && npm run test:storyboard-context-resolution && npm run test:storyboard-validations-paths && npm run test:storyboard-check-enum && npm run test:update-media-buy-affected-packages && npm run test:storyboard-advisory-expiry && npm run test:storyboard-controller-requires && npm run test:storyboard-raw-mode-required && npm run test:storyboard-upstream-traffic-paths && npm run test:storyboard-generate-phase-scope && npm run test:refine-finalize-validation-ids && npm run test:run-storyboards-schema-root && npm run test:storyboard-doc-parity && npm run test:v4-compatibility-registry && npm run test:pagination-invariant && npm run test:version-envelope && npm run test:test-dynamic-imports && npm run test:sdk-shims && npm run test:format-identity-boundaries && npm run test:sdk-runner-capability-gates && npm run test:optional-product-collections && npm run test:callapi-state-change && npm run test:sign-protocol-tarball && npm run test:chat-streaming-code-fences && npm run test:certification-demo-formatting && npm run test:build-schemas-hoist-enums && npm run test:build-schemas-hoist-marked && npm run test:build-schemas-hoist-by-identity && npm run test:build-schemas-async-response-refs && npm run test:build-schemas-vendor-metric-uniqueness && npm run test:release-workflow && npm run test:immutable-release-artifacts && npm run test:patch-3-0-compat-bundle && npm run test:error-codes && npm run test:compliance-snippets && npm run test:doc-compliance-drift && npm run test:substitution-vector-names && npm run test:platform-agnostic && npm run test:oneof-discriminators && npm run test:schema-utf8 && npm run test:unit && npm run test:server-unit && npm run test:openapi && npm run typecheck && npm run test:contracts && npm run test:example-validation && npm run test:test-coverage",
"test:all": "npm run test:schemas && npm run test:examples && npm run test:extensions && npm run test:error-handling && npm run test:snippets && npm run typecheck",
"precommit:server-unit": "node scripts/precommit-server-unit.cjs",
"precommit": "bash scripts/with-timeout.sh 300 npm run test:unit && npm run test:test-dynamic-imports && npm run test:format-identity-boundaries && npm run test:callapi-state-change && bash scripts/with-timeout.sh 1500 npm run precommit:server-unit && npm run typecheck",
Expand Down
14 changes: 13 additions & 1 deletion scripts/lint-storyboard-fixture-resolution.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const OPERATORS = new Set([
'contains_all',
'any_match',
'canonical_format_satisfies',
'collection_selection_satisfies',
]);
const ID_FIELDS = {
accounts: 'account_id',
Expand Down Expand Up @@ -63,7 +64,7 @@ function lintMatchClause(clause, at, violations) {
violations.push({ rule: 'match_clause_shape', at });
return;
}
if (typeof clause.path !== 'string' || !clause.path.startsWith('/')) {
if (typeof clause.path !== 'string' || (clause.path !== '' && !clause.path.startsWith('/'))) {
violations.push({ rule: 'match_path', at });
}
if (!OPERATORS.has(clause.operator)) {
Expand All @@ -87,6 +88,17 @@ function lintMatchClause(clause, at, violations) {
if (!Object.hasOwn(clause, 'value') || Object.hasOwn(clause, 'where')) {
violations.push({ rule: 'match_value_shape', at });
}
// Keep the merged #8376 operand closed, including provisional spellings.
if (clause.operator === 'collection_selection_satisfies') {
const value = clause.value;
if (!value || typeof value !== 'object' || Array.isArray(value) ||
Object.keys(value).some(key => !['collection_targeting', 'minimum_pairs', 'require_cross_publisher_collection'].includes(key)) ||
!['fixed', 'selectable', 'any'].includes(value.collection_targeting) ||
(value.minimum_pairs !== undefined && ![1, 2].includes(value.minimum_pairs)) ||
(value.require_cross_publisher_collection !== undefined && typeof value.require_cross_publisher_collection !== 'boolean')) {
violations.push({ rule: 'collection_selection_value', at });
}
}
if (clause.operator === 'contains_all' && !Array.isArray(clause.value)) {
violations.push({ rule: 'contains_all_value', at });
}
Expand Down
Loading
Loading