feat(media-buy): add committed collection selection to targeting - #7079
Conversation
targeting_overlay.collection_selection mirrors placement_selection: mode selected names the complete required collection set as domain-qualified selectors with explicit collection_ids (partial selection gated by product collection_targeting_allowed; the domain-only bulk-grant selector form stays authorization-only), and mode default accepts the product's full bundle. On package readback sellers MUST echo the committed selection here, materializing the result of any collection_list composition — which makes the existing package collection-echo obligation satisfiable and gives channel buys an audit surface for the axis that makes them distinct. Resolved collection-list rows can now carry the domain-qualified identity (publisher_domain + collection_id) so a row stays matchable when no platform-independent distribution identifier exists. The shared inventory-selection graph adds ~0.7 KiB to each targeting-bearing media-buy request schema. Two measured, commented budget increases carry it: the model-context ceiling moves 400 → 405 KiB and the compact four-tool discovery bound moves 133 → 135 KiB; schema-analysis pins advance accordingly. Closes #7050. Partially addresses #7051 (resolved-row identity; the tuple-carrying exclusion arm remains open). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated schema paths require human/CODEOWNERS review.
This PR is a clean additive Normative change: it adds targeting_overlay.collection_selection (mirroring placement_selection) and adds (publisher_domain, collection_id) identity on resolved collection-list rows. The reviewer verified changeset scope/type (minor, correct), a properly discriminated oneOf (on mode), schema↔docs coherence, immutable artifacts, and confirmed no wire break. Only one non-blocking Low finding was noted inline (collection_selection.collections omits the uniqueItems constraint its placement_refs mirror enforces) — not a blocker.
The diff modifies multiple files under static/schemas/source/**, which the repo declares as gated paths (hard approval gate). gated_paths: true and review_decision: REVIEW_REQUIRED (not APPROVED), so decision rule row 2 fires: the gate is not satisfied and the outcome must be escalate until a human/CODEOWNERS approval lands.
Escalation reasons
- Modifies gated schema source files under
static/schemas/source/**(get-collection-list-response.json, core/collection-selection.json [added], core/package.json, core/product.json, core/targeting.json, media-buy/get-media-buys-response.json) whilereview_decisionisREVIEW_REQUIRED. Human/CODEOWNERS approval required before merge.
No blocking (critical/high) findings exist; the escalation is purely the deterministic gated-paths gate.
Why human review
- Gated paths touched (static/schemas/source/**) with review_decision REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS approval required before merge (decision rule row 2).
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/collection/get-collection-list-response.json (modified) matches
static/schemas/source/**; static/schemas/source/core/collection-selection.json (added) matchesstatic/schemas/source/**; static/schemas/source/core/package.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/product.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/targeting.json (modified) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-media-buys-response.json (modified) matchesstatic/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.
Mirror placement_selection.placement_refs: uniqueItems alongside minItems, so an exact-duplicate selector fails at schema level instead of delegating dedup entirely to the seller. Overlapping (non-identical) selectors remain seller-rejected per the selection contract. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated schema paths touched and no human/CODEOWNERS approval recorded.
This PR modifies/adds files under static/schemas/source/**, which is a hard, deterministic approval gate (gated_paths: true). The current review_decision is unknown/none — not APPROVED — so decision rule row 2 fires: the gate is not satisfied and Ladon cannot auto-approve regardless of how clean the diff is.
The delta since the prior clean pass is additive and correct: it adds uniqueItems: true (plus minItems: 1 parity) to the collections array in the new collection-selection.json and a test asserting exact-duplicate selectors are rejected, bringing it into parity with placement-selection.json. This is on a not-yet-released 3.2 schema with its minor changeset already present. The reviewer found no Critical/High/Medium findings and no schema↔docs drift.
Blocking findings: none.
Medium findings: none.
Escalation reasons:
- Gated paths touched (
static/schemas/source/**) withreview_decisionnotAPPROVED— human/CODEOWNERS approval required before merge (decision rule row 2). Gated files includestatic/schemas/source/core/collection-selection.json(added) andstatic/schemas/source/core/product.json,targeting.json,package.json,get-collection-list-response.json,get-media-buys-response.json(modified).
Once a human/CODEOWNERS approval is recorded, this clean diff can fall through to approve.
Why human review
- Gated paths touched (static/schemas/source/**) with review_decision not APPROVED — human/CODEOWNERS approval required before merge (decision rule row 2). Gated files: static/schemas/source/core/collection-selection.json (added), plus product.json, targeting.json, package.json, get-collection-list-response.json, get-media-buys-response.json (modified).
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/collection/get-collection-list-response.json (modified) matches
static/schemas/source/**; static/schemas/source/core/collection-selection.json (added) matchesstatic/schemas/source/**; static/schemas/source/core/package.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/product.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/targeting.json (modified) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-media-buys-response.json (modified) matchesstatic/schemas/source/**) and the current GitHub review decision is 'unknown', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.
…echo # Conflicts: # package.json # tests/mcp-schema-analysis.test.cjs
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated paths touched without an APPROVED review decision.
This PR modifies/adds files under static/schemas/source/**, which are under a hard, non-overridable approval gate (gated_paths: true). The current review_decision is unknown/none — not APPROVED — so decision rule row 2 forces escalation. A human/CODEOWNERS approval must be recorded before this can merge.
Blocking findings
- None.
Medium findings
- None. The reviewer reports a clean pass: head e231fdb is a merge-only update (parents = prior-reviewed head 57fc1f5 + main tip 877f6a2). The collection_selection schema contribution is unchanged and intact; the delta is pure main catch-up already reviewed on merge.
Escalation reasons
- Gated paths touched (
static/schemas/source/**) withreview_decisionnot APPROVED — human/CODEOWNERS approval required. Gated files:static/schemas/source/core/collection-selection.json(added), plusproduct.json,targeting.json,package.json,collection/get-collection-list-response.json,media-buy/get-media-buys-response.json(modified).
Note: the prior decision was also escalate for the identical gate reason. No fresh critical/high/medium findings exist, but the gate itself is a deterministic path-based hard gate independent of findings — it persists until an APPROVED review is recorded.
Why human review
- Gated paths touched (static/schemas/source/**) with review_decision not APPROVED — human/CODEOWNERS approval required before merge (decision rule row 2). Gated files: static/schemas/source/core/collection-selection.json (added), plus product.json, targeting.json, package.json, get-collection-list-response.json, get-media-buys-response.json (modified).
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/collection/get-collection-list-response.json (modified) matches
static/schemas/source/**; static/schemas/source/core/collection-selection.json (added) matchesstatic/schemas/source/**; static/schemas/source/core/package.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/product.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/targeting.json (modified) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-media-buys-response.json (modified) matchesstatic/schemas/source/**) and the current GitHub review decision is 'unknown', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.
What
Adds
targeting_overlay.collection_selection, mirroringplacement_selection, plus domain-qualified identity on resolved collection-list rows.core/collection-selection.json—mode: "selected"names the complete required collection set as domain-qualified selectors with explicitcollection_ids(partial selection gated by productcollection_targeting_allowed; exact full-bundle restatement is an inherent match; the domain-only bulk-grant selector form stays authorization-only).mode: "default"accepts the product's full bundle.collection_selection, materializing the result of anycollection_listcomposition — making the existingpackage.targeting_overlaycollection-echo obligation satisfiable. For a channel buy this is the audit surface for the axis that makes the product distinct.get-collection-list-response) can carrypublisher_domain+collection_id, so a row whose only host identifier is aplatform_channel_idstays matchable ((publisher_domain, collection_id)tuple identity).Why
Closes #7050 —
package.targeting_overlaysaid sellers MUST echo collection selection, buttargeting.jsonhad no field to write it in; buyers of collection-scoped products (notablykind: "channel"from #6897) could not audit what they bought without re-fetching the product catalog. Partially addresses #7051 (resolved-row identity; the tuple-carrying exclusion arm remains open for WG discussion).Budget note (deliberate)
The shared inventory-selection graph bundles ~0.7 KiB into each targeting-bearing media-buy request schema. Two budget lines move as measured, commented increases (not headroom): model-context ceiling 400 → 405 KiB, compact four-tool discovery bound 133 → 135 KiB. Schema-analysis pins advance accordingly (601→611 instances, 157→159 unique defs, 113→115 repeated).
Testing
tests/collection-selection-echo.test.cjs(5 spec-anchored schema tests: selected/default modes, bulk-grant rejection in selection, empty-set/unknown-mode rejection, resolved-row identity), wired into the main chain.🤖 Generated with Claude Code