Repository navigation
Conversation
A merge in sync_creatives assignment_operations works per package. On platforms where one creative runs in many ads across many packages, a buyer adopting it needs one merge per package, and an assignment the seller makes later stays under the seller's ID. A merge without package_id is now a library-level merge, gated by the new creative.supports_library_merge capability (requires supports_assignment_merge). The seller merges every current assignment, makes every later assignment under the buyer's creative_id, and keeps the seller-assigned creative listed with merged_into_creative_id, the durable record buyers use to join delivery from before the merge. Delivery before a merge is still not restated. - sync-creatives-request: package_id optional on merge. - sync-creatives-response: merges[] binding entry without package_id; approval_status required whenever package_id is present. - creative-merge-rejected: package_id optional; names each blocking package for a library-level merge. - creative-merge-rejection-reason: not_in_account, already_merged. - list-creatives-response: merged_into_creative_id. - Docs, normative spec bullet, error-code text, schema tests. Refs #8897 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A
mergeinsync_creativesassignment_operationsworks one package at a time. On platforms where one creative runs in many ads across many packages, typically self-serve social and search platforms, a buyer that adopts a creative made in the platform's own tools has two problems:A buyer platform building this on Meta hit both while building adoption.
This PR adds a library-level merge: a
mergewith nopackage_id. It declares the seller-assigned creative to be the buyer's creative everywhere in the account. It's gated by the newcreative.supports_library_mergecapability.Wire changes
All changes are additive and capability-gated.
protocol/get-adcp-capabilities-response.jsoncreative.supports_library_merge(boolean, defaultfalse). Requiressupports_assignment_merge: true, enforced withif/then.creative/sync-creatives-request.jsonpackage_idis no longer required onmerge. Leaving it out makes the merge library-level.x-adcp-validationgains a library capability gate, the two new reasons, and the library on-success rules.creative/sync-creatives-response.jsonmerges[]entry may omitpackage_id. That entry records the account-wide binding.approval_statusis required wheneverpackage_idis present.error-details/creative-merge-rejected.jsonpackage_idis optional. For a library-level merge, there is one entry per blocking package.enums/creative-merge-rejection-reason.jsonnot_in_accountandalready_merged, both for library-level merges only.creative/list-creatives-response.jsonmerged_into_creative_idon a seller-assigned creative that a library-level merge bound to a buyer creative.enums/error-code.jsonCREATIVE_MERGE_REJECTEDtext covers library-level merges. The snippet is regenerated.Semantics
These are defined in
sync_creatives.mdx, under Merging across the account. A seller that declaressupports_library_merge:creative_id, and never under the seller's ID;merged_into_creative_idset;The response carries one binding entry without
package_id, then one entry per moved assignment, inpackage_idorder. Repeating the same pair succeeds with only the binding entry. Package merges are unchanged.History after a merge: open, one option recorded here
The #8897 addendum asked whether a merge should be able to ask the seller to restate the creative's earlier delivery under the buyer's ID (for example with
restate_history: true), or whether buyers should join the two IDs themselves.Not decided. The choice is between sellers never restating, where buyers join the two IDs, and an opt-in restatement that a seller declares with a capability. This draft records the first option: it adds
merged_into_creative_id, which makes the join durable, and keeps the 3.3 rule that earlier delivery is not restated. An opt-in restatement could be layered on later. #8908 states the join rules for 3.3 package merges.The case for the recorded option:
reporting_revision_id, immutable control totals) and with webhook windows the buyer has already received.restate_historyis a mutating-request field. An older seller that ignores it would deliver a different result than the buyer expects. Under rule 4 of the versioning rules (docs(versioning): state the rules for what changes between releases #8704), it would need its own capability on top of this one.If buyers later need deduplicated reach over a creative's whole life, a read-side option (report under the current ID) is the shape to consider. It changes no stored history.
Alternatives not picked
merge_libraryoradopt) instead of an optionalpackage_id. The verification, errors and response are the same as for a package merge, so a scoping field is simpler. Dispatch also stays onoperationalone.mergebranches told apart by whetherpackage_idis present. This breaks the "dispatch is literal" principle in RFC: AdCP 4.0 — a fast, cleanup-first major (scope, principles, schedule) #8756.not_on_packagefor library-level failures. Its meaning is package-specific, so a buyer couldn't tell which correction to make.Versioning and process
x-added-inis set to4.0.0provisionally; set it to the release this lands in. Changeset:minor. Don't merge whilemainis in 3.3 pre mode.package_idfromrequiredon the closedmergebranch, and on the closed rejection entry, is not an additive-minor violation. The new properties are on open objects (creative capabilities, thelist_creativesitem,merges[]items). The enum only gains values.npm run test:additive-minorpasses.mergewithoutpackage_id, becausepackage_idis required there.docs/governance/rfc-process.mdxit needs WG review and a decision record before merge. The proposal is get_media_buy_delivery: daily rows per creative, and creative rows for creatives the buyer did not assign #8897 and its addendum.Open questions for the WG
unassign/replaceper package enough?platform_id. After a merge, the buyer'screative_idcan be served by the seller's existing objects. Shouldlist_creativessay anything aboutplatform_idfor it?Test plan
npm run test:composed: seven new cases. They cover a library merge request, the binding plus moved-assignment readback,approval_statusrequired withpackage_id, the new rejection reasons with and without a blocking package, the capability dependency, andmerged_into_creative_id.npm run test:schemas,npm run test:additive-minor,npm run test:oneof-discriminators,npm run test:error-codes,npm run test:error-code-driftnpm run build:compliance -- --check,npm run test:compliance-snippetsnpm run test:mcp-schema-projection(prompt-view budget unchanged),npm run test:docs-nav,node tests/check-platform-agnostic.cjsnode tests/json-schema-validation.test.cjs --fileon every changed pagecheck-changeset-protocol-scopeandchangeset statusRefs #8897
🤖 Generated with Claude Code