Repository navigation
RFC: advertiser identity model (brand claims, account connections, buy binding) #7782
Copy link
Copy link
Open
Labels
brandIssue concerns the brand protocol domainIssue concerns the brand protocol domainclaude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.Issue has been triaged by the Claude Code triage routine. Remove to re-triage.media-buyIssue concerns the media-buy protocol domainIssue concerns the media-buy protocol domainneeds-wg-reviewBlocked on a working-group decision — surface in WG meeting agendasBlocked on a working-group decision — surface in WG meeting agendasrfcProtocol change — auto-adds to roadmap boardProtocol change — auto-adds to roadmap boardspec / protocol
Milestone
Description
Problem
Many social and retail products run as one of the advertiser's identities: a Facebook Page, an Instagram or TikTok profile, a YouTube channel, a LinkedIn organization. AdCP has no clear home for that identity today:
downstream-connection-requirementmodels it asconnection_type: "publisher_identity", with the platform ID inresource_ref.identity_id/handle/profile_url.published_post.identity_refnames the identity that owns a boosted post, as a hint only.- Nothing lists which identities an account has, and nothing on
create_media_buylets the buyer choose which one a buy runs as. Adapters work around this with seller extensions (for exampleext.scope3_execution_readinesson Meta, TikTok and Snap), and the seller applies an undocumented default.
This came up while shipping #7774 (static execution requirements, experimental in 3.2) and scoping per-account readiness in #7763. It is one of the pieces left for 3.3.
Proposed direction: split identity across three layers
| Layer | Holds | Why |
|---|---|---|
Brand (brand.json) |
A public claim that the brand owns these handles or profile URLs, optionally scoped by brand.countries |
Public, verifiable and the same for every seller. It fits the "Brand FR / Brand BE" case from #7763 |
| Account | Platform connections and authorizations: the platform's identity ID and status (connected / missing / expired / revoked) |
Authorization is granted per platform account, and one brand can have different Pages on Meta and TikTok. The existing downstream-connection shape is already scoped to the account |
| Buy / package | Which identity this buy runs as | A choice made per buy. This is the typed binding field from #7763 |
What each layer should not do:
- Not on the brand alone. That mixes public claims with private platform IDs and grant state.
- Not in the account's identifying key. A Page must not become part of the natural account key (
brand + operator + …). Otherwise, adding or rotating a Page would look like a different advertiser and trigger re-keying orACCOUNT_MOVEDlogic.
Questions for the working group
- Brand claims. Does
brand.jsonget asocial_identities[](or similar) block, and how do sellers verify it against platform state?published_post.identity_refalready says buyer claims are hints, not proof. - Account discovery. Should there be a discovery read, similar to the read-only mode of
sync_event_sources/sync_catalogs, that lists the identities an account has connected on a seller? Or should this come only through the per-account readiness candidates in RFC: per-account execution readiness for products (required resources, candidates, and binding) #7763? - Buy binding. The RFC: per-account execution readiness for products (required resources, candidates, and binding) #7763 triage decision was typed fields, not a generic
resource_bindingslist. Where doesadvertiser_identity_idgo: package, media buy, or creative? Meta's rule "if you usepage_id, the creative must promote thatpage_id" suggests validating the package against the creative. - Proposals. Buys created from
proposal_idcarry no package fields, so how does a proposal carry the identity binding? This is the same open question as event source and catalog bindings for proposal buys (feat(schema): add experimental product execution requirements #7774 left them out of the rejection rule in 3.2). - Candidates. Only identities that are connected are listed as candidates (RFC: per-account execution readiness for products (required resources, candidates, and binding) #7763 triage decision 2). An identity that isn't connected is reported as
setup_required, with the connection'sauthorization_url.
Related
- RFC: per-account execution readiness for products (required resources, candidates, and binding) #7763 — per-account execution readiness (3.3)
- feat(schema): add experimental product execution requirements #7774 — static
Product.execution_requirements(3.2, experimental) - Rename publisher_identity to advertiser_identity (4.0; optional 3.3 alias) #7783 — 4.0 rename of
publisher_identity
Metadata
Metadata
Assignees
Labels
brandIssue concerns the brand protocol domainIssue concerns the brand protocol domainclaude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.Issue has been triaged by the Claude Code triage routine. Remove to re-triage.media-buyIssue concerns the media-buy protocol domainIssue concerns the media-buy protocol domainneeds-wg-reviewBlocked on a working-group decision — surface in WG meeting agendasBlocked on a working-group decision — surface in WG meeting agendasrfcProtocol change — auto-adds to roadmap boardProtocol change — auto-adds to roadmap boardspec / protocol
Type
Projects
- StatusShow more project fieldsNo status
Activity
bokelley commentedon Sep 30, 2026
Triage
Classification: RFC
Bucket(s): spec / protocol, registry / discovery, media-buy, brand
Status: ready-for-human
Milestone: 3.3.0
What the experts said:
$refmust be defined before 3.3 ships; Q4 puntable with spec note; Q5 must be MUST not an open questionbrand.properties[]+property-type.json, not add asocial_identities[]block; Q2 warrants a new standalonesync_publisher_identitiestask that composes with RFC: per-account execution readiness for products (required resources, candidates, and binding) #7763 via the existingdownstream-connection-requirementshape; Q4 explicitly exclude with prose guard; Q5 show all identities with statussetup_requiredgate before deferral is safe for PG/upfront flows; Q5 surfaces two new operator gaps: agency-vs-advertiser Page access grant, and multi-country Page hierarchy (Global Pages)My take: Expert consensus covers the decomposition, the Q1 brand.json approach, and Q4 deferral prose. Three decisions genuinely split and need WG resolution before implementation can begin.
Consensus — these can be implemented as written:
downstream-connection-requirement(existing) already implements the account layer.social_identities[]block. Extendproperty-type.jsonwith social platform values (social_page,social_channel, etc.) and express handles asbrand.properties[]entries withrelationship: owned. This keeps public claims in the existing property-claim model and avoids a second enumeration that must stay in sync.KNOWN_LIMITATIONnote in 3.3 release notes consistent with feat(schema): add experimental product execution requirements #7774's treatment.statusandauthorization_url), not only connected ones — filtering to connected is a footgun when tokens expire mid-flight.@bokelley, your call — Q2: Identity discovery shape
Option A — New standalone
sync_publisher_identitiestask (both protocol experts):Separate caching profile from readiness; composable with #7763 candidates via
connection_idreference. Two surfaces, one primitive shape.Option B — Embedded in
execution_readinesscandidates (product expert):No new task surface; buyers see identities only in buy-planning context. Simplifies implementation but couples identity visibility to product plan requests.
@bokelley, your call — Q3: Binding placement
Option A — Package level (both protocol experts):
// packages array on create_media_buy { "packages": [{ "package_id": "pkg_1", + "identity_binding": { "connection_id": "conn_abc" }, "other_fields": "..." }] }Consistent with how
execution_readinessresources bind in #7763;published-post-asset.identity_refat creative level is the validation counterpart for Meta's page_id rule.Option B — Media buy level (product expert):
// create_media_buy top level { "media_buy_id": "buy_1", + "identity_binding": { "connection_id": "conn_abc" }, "packages": [...] }Matches TTD/DV360 line-item model; avoids multi-brand package ambiguity where one package spans buys for different regional Pages.
@bokelley, your call — Q4 guard level
Protocol experts say fully defer with a spec note. Product expert says proposal buys without identity binding risk silent policy violations at trafficking time (especially for upfront/PG flows where identity matters most) — a minimal
setup_requiredgate is needed even in 3.3.Is a minimum guard ("proposal buy with no identity binding, on a product that requires identity, enters
setup_requiredbefore trafficking") feasible for 3.3, or is full deferral safe given your PG use cases?Q5 — two operator gaps needing explicit deferral call or spec text:
connected≠ agency holds the brand's Page permission grant. The account layer reflects the DSP token; the brand-to-agency Page grant lives in the platform's Business Manager. Does the RFC need to address this path, or declare itsetup_requiredterritory?geo_scopefield on the social identity entry, or explicit guidance that geo-Page selection is outside protocol scope for 3.3?Triaged by Claude Code. Session: https://claude.ai/code/session_01Gp4CfsU2bUmHL8edovVbqJ
Generated by Claude Code
bokelley commentedon Nov 2, 2026
Proposed 3.4 design: account identity discovery, diagnostics, and binding
Class: Normative recommendation. Status: Proposed; not a recorded or
ratified decision. Target: 3.4, experimental. Date: 2026-11-02.
References: #7782; Meta Page evidence on #7763;
#7934; #7783; #8655.
Decision proposed
list_account_identitiestask, independent of products.It can optionally evaluate those same identities against one declared
product requirement. Use one canonical seller-assigned identity reference
on discovery, readiness, and binding.
blocked identities. Separate downstream authorization from product
eligibility. Use candidate
readiness_statusand distinguishnot_authorized,not_eligible, and an inability to check.packages[].identity_bindings[], keyed by the echoed identityrequirement. Bind at package level and validate the chosen identity against
every creative assigned to that package. This is an identity-specific field,
not a generic resource binding list.
publisher_identityas third-party-only or reinterpretadvertiser_accountto mean an advertiser-owned Page. Resolve any ownership distinction and
renaming through Rename publisher_identity to advertiser_identity (4.0; optional 3.3 alias) #7783, with a compatibility plan.
These are recommendations for WG review. Publication of this memo does not
settle #7782 Q2/Q3 or reverse the earlier connected-only call by itself.
Rationale and alternatives
Connected-only filtering hides identities a buyer expects to find and makes
different repair paths indistinguishable. A missing grant needs authorization;
an authorized identity lacking a product feature needs setup on that identity.
Neither is evidence that the identity does not exist.
A dedicated read gives identity enumeration its own pagination, refresh, and
failure handling. An opt-in nested list on
list_accountsis viable but addsper-account pagination inside account pagination. Product-only discovery cannot
answer which identities an account can use before choosing a product.
Package binding supports different regional identities within one buy. A
buy-level binding forces an unnecessary common choice; creative-only binding
does not resolve package-level execution requirements. Creative identity hints
remain useful validation inputs, not authorization proof.
The earlier #7782 discussion contains different recommendations on discovery
and binding placement. This memo recommends the dedicated read and package
placement; it does not claim consensus or a new expert review.
Implications and scope
The proposed feature ids are
account.identitiesandmedia_buy.identity_readiness, advertised inexperimental_features.The latter covers identity-aware readiness and typed package bindings;
it requires the former plus
media_buy.execution_requirementsandmedia_buy.execution_readiness. Exact ids remain part of the review.The new read has no write/provisioning mode. It does not create accounts,
connect identities, mutate grants, or change the account's natural key or
account_id. Brand public claims remain separate; this memo adds no secondidentity directory to
brand.json.Identity-aware product readiness requires a new explicit
include_identity_readiness: trueonget_products, together withinclude_execution_readiness: trueandaccount. Without that opt-in,the shipped 3.3 map, connection-state derivation, and seller-default behavior
remain unchanged. Consumers must discover support before using the new fields;
3.4 implementations that understand this contract reject unsupported opt-ins with
UNSUPPORTED_FEATURE, rather than silently claiming identity-aware results.Legacy servers may ignore unknown fields, so buyers must verify both the
advertised capability and the presence of the requested identity-aware result.
All wire shapes below are proposed API designs, not examples of existing
AdCP schemas. The implementation must add schemas before these shapes appear
in normative reference documentation. Existing versioned artifacts stay
immutable. No runtime or schema implementation is included in this memo.
Canonical identity record
identity_id(seller agent, account_id), not reassigned to another identity. Directly accepted by the typed binding even before authorization exists.providernameresource_refauthorization_statusauthorized,not_authorized, orunknown.authorizedmeans the account currently has the grants required to use this identity for advertising, not just a valid platform login.connection_stateunknown_reasonauthorization_statusisunknown:permission_denied,platform_unavailable, ornot_evaluated.evaluationextIdentity references do not contain credentials. A connection can expire or be
recreated without changing
identity_id. Raw platform ids can be disclosedonly when safe, and cannot substitute for the canonical id unless the seller
explicitly uses that same string in its own identity namespace.
list_account_identitiesRequired request field:
account, using the existing AccountRef. Optional:pagination: existing request shape,max_results1–100, default 50.product_idandrequirement: either both absent, or both present.requirementmust deep-equal a currentexecution_requirements[]entry ofthat product, with
kind: downstream_connectionandconnection.scope: identity. Account-wide discovery needs neither field.An incomplete product/requirement pair returns
INVALID_REQUEST; aninaccessible or unknown product uses
PRODUCT_NOT_FOUND, and a requirementthat no longer matches the product uses
VALIDATION_ERRORatrequirement.Proposed account-only request:
{ "account": { "account_id": "acc_nova" }, "pagination": { "max_results": 50 } }Successful responses require
identities,version,evaluated_at,enumeration_complete, and existing responsepagination. The identity listcontains only records visible to this caller under this account. A page is
bounded to 100 records.
versionis opaque;evaluated_atis a UTC timestamp.The existing transport/protocol envelope remains unchanged.
{ "identities": [ { "identity_id": "ident_nova_fr", "provider": "social.example", "name": "Nova France", "authorization_status": "authorized", "resource_ref": { "identity_id": "platform_page_17" } }, { "identity_id": "ident_nova_be", "provider": "social.example", "name": "Nova Belgium", "authorization_status": "not_authorized", "connection_state": { "provider": "social.example", "connection_type": "publisher_identity", "scope": "identity", "status": "missing", "authorization_url": "https://seller.example/connections/identity" } } ], "version": "identities_42", "evaluated_at": "2026-11-02T12:00:00Z", "enumeration_complete": true, "pagination": { "has_more": false } }enumeration_completemeans all relevant caller-visible identities wereenumerated into the snapshot, not that this page contains them all.
pagination.has_moreidentifies further pages of that snapshot. Sellers mustnot label a truncated or permission-limited upstream enumeration complete.
An incomplete read returns known records with
enumeration_complete: falseand required
unknown_reason, using the same three values above. A failedenumeration with no known records is an incomplete result, never an empty
complete result. Expired account authorization remains an account access error,
not a diagnostic identity listing.
Product-context candidates
With
product_idandrequirement, the same read returns only visibleidentities relevant to that requirement and adds
evaluationto each record:{ "identity_id": "ident_nova_fr", "provider": "social.example", "authorization_status": "authorized", "evaluation": { "readiness_status": "setup_required", "reason": "not_eligible", "issues": [ { "severity": "error", "message": "Create a shop for this identity before using this product." } ] } }Candidate
readiness_statususes a subset of the existing vocabulary:ready: authorization and product eligibility were both verified.setup_required: an established blocker. Requiredreasonisnot_authorizedornot_eligible; required nonemptyissuesuses theexisting diagnostic issue shape with actionable remediation. Authorization
recovery belongs in the candidate's
connection_state.unknown: no established blocker, but a required check could not complete.Required
unknown_reasonispermission_denied,platform_unavailable, ornot_evaluated. It must not be reported as ineligibility.selection_requiredandinput_requireddescribe a requirement-level buyeraction, not the readiness of one named candidate, so neither appears here.
For an established authorization failure, use
not_authorizedbefore testingeligibility; do not claim an eligibility result a missing grant prevented the
seller from checking. An authorization URL is not remediation for
not_eligible.reasonis absent onreadyandunknown.The response also echoes
product_idandrequirement, and requiresevaluation_complete,known_ready_count, and aggregatereadiness_status.Whenever evaluation is incomplete, require a summary
unknown_reason, evenif enough verified ready identities already establish
selection_required.Counts and completeness describe the entire snapshot, not this page.
evaluation_completerequires complete enumeration and an establishedready/blocked result for every relevant identity. Provider-agnostic
requirements may enumerate multiple providers; provider-specific requirements
filter accordingly. A visible identity must not be filtered out merely
because its grant is missing or its product eligibility fails.
selection_required, including when other checks are incompleteready; requireresolved_identity_idsetup_required; require actionable summaryissues, including when the list is emptyunknown; requireunknown_reasonAn unknown alternative prevents a uniqueness claim, but does not prevent the
buyer explicitly binding an independently verified ready identity. Blocked
alternatives do not downgrade a known-ready identity. Candidate selection has
no seller-default exception in this opt-in contract.
Proposed product-context request:
{ "account": { "account_id": "acc_nova" }, "product_id": "social_shop", "requirement": { "kind": "downstream_connection", "connection": { "provider": "social.example", "connection_type": "publisher_identity", "scope": "identity" } }, "pagination": { "max_results": 50 } }Its complete response can contain one usable identity and a blocked alternative:
{ "product_id": "social_shop", "requirement": { "kind": "downstream_connection", "connection": { "provider": "social.example", "connection_type": "publisher_identity", "scope": "identity" } }, "identities": [ { "identity_id": "ident_nova_fr", "provider": "social.example", "authorization_status": "authorized", "evaluation": { "readiness_status": "ready" } }, { "identity_id": "ident_nova_be", "provider": "social.example", "authorization_status": "authorized", "evaluation": { "readiness_status": "setup_required", "reason": "not_eligible", "issues": [ { "severity": "error", "message": "Create a shop for this identity before using this product." } ] } } ], "version": "shop_identities_43", "evaluated_at": "2026-11-02T12:05:00Z", "enumeration_complete": true, "evaluation_complete": true, "known_ready_count": 1, "readiness_status": "ready", "resolved_identity_id": "ident_nova_fr", "pagination": { "has_more": false } }Integration with
get_productsFor opted-in identity requirements, use the existing echoed
requirementandtop-level
readiness_status, replacing the singleconnection_statewith anidentity_candidatesobject. It contains the product-context read'sidentities,version,evaluated_at,enumeration_complete,pagination,evaluation_complete,known_ready_count, and any required unknown reason orsummary issues. Include at most the first 50 identities; paginate by calling
list_account_identitieswith the same account, product, echoed requirement,and returned cursor. Candidate fields and the wrapper need named schema refs,
not independent copies of the list-task definitions.
The requirement's
readiness_statusfollows the table above. When ready, addresolved: { "resource_id": "ident_nova_fr" }.binding_fieldispackages[].identity_bindings[].identity_id.identity_candidatesand thelegacy requirement-level
connection_stateare mutually exclusive.Non-identity connection requirements retain the existing shape and rules.
This requires an explicit 3.4 schema branch: the current 3.3 conditionals
require one connection state, prohibit identity candidates, and derive
readiness solely from grant status. Simply adding candidate properties would
leave the schema contradictory. Existing
candidates[]for event sources andcatalogs retains its current usable-resource semantics and minimum size.
The existing product rollup priority remains
setup_required,input_required,selection_required,unknown, elseready. Identitydiagnostics remain in the private response-level readiness map and never enter
the public product object, wholesale webhooks, or shared public caches.
Typed binding and mutation validation
Proposed package fragment, with the full requirement echoed to avoid an
unstable positional index:
{ "identity_bindings": [ { "requirement": { "kind": "downstream_connection", "connection": { "provider": "social.example", "connection_type": "publisher_identity", "scope": "identity" } }, "identity_id": "ident_nova_fr" } ] }Each binding requires both fields. The list is nonempty, capped at 100, and
contains at most one entry per deep-equal identity requirement. Distinct
requirements can bind different identities. The seller rejects a requirement
not declared by the package's current product; a buyer refetches stale product
requirements rather than guessing an index. A supplied list must bind every
declared identity requirement; omitting the whole field preserves legacy
seller-default behavior. A buyer requiring identity control must send it.
Use the same field in create package requests,
new_packages, package updates,package response objects, and
get_media_buys. An update replaces the entirebinding list; omission preserves it. Null and empty-list clearing are excluded
from this slice. Returning the effective bindings on reads is required for
feature-enabled packages, including defaults that the seller resolved.
Support is advertised by
media_buy.identity_readiness; implementations mustreject explicit bindings they do not support with
UNSUPPORTED_FEATUREbeforemutation. The account-read feature alone does not advertise binding support.
This rejection requirement applies to 3.4 implementations of this contract;
buyers must not send the new field to legacy servers and assume it took effect.
Identity updates also obey the caller's existing field grants and the
package's advertised action/change-right constraints.
The seller rechecks account visibility, authorization, eligibility, and
creative compatibility before accepting creation, identity changes, or new
creative assignments. It must not substitute a different identity when the
explicit selection fails. Provider-native hints and public brand claims never
replace grant verification. Cross-account/unknown identity ids return the
same
VALIDATION_ERRORwithdetails.reason: not_found, without confirminganother account's identity exists.
Mutation failures use existing error codes with exact indexed
error.fieldand proposed identity-specific details:
product_id, echoedrequirement,and
reason(not_bound,not_found,not_eligible, orcreative_identity_mismatch). Known authorization failures use existingAUTHORIZATION_REQUIREDdetails instead; inability to verify because of atransient platform failure uses
SERVICE_UNAVAILABLEwith no mutation.Unreadable required grants must not be treated as authorized. Any other
indeterminate verification must fail before mutation with a truthful existing
error code and remediation. Validation of a proposed package identity change
and its affected creative assignments occurs before applying either change.
Concrete error example for an existing but ineligible choice:
{ "code": "VALIDATION_ERROR", "message": "The selected identity has no shop for this product.", "field": "packages[0].identity_bindings[0].identity_id", "details": { "product_id": "social_shop", "requirement": { "kind": "downstream_connection", "connection": { "provider": "social.example", "connection_type": "publisher_identity", "scope": "identity" } }, "reason": "not_eligible" } }Published-post identity hints must resolve to the bound identity when the
platform requires it; a seller unable to verify that match must not accept
the assignment as verified. Revocation observed later stops affected serving
through existing lifecycle mechanisms; buyers repair with an authorized
binding. This proposal adds no new webhook type or change-feed coverage claim.
Pagination, freshness, and visibility
“All identities” means the caller-visible set in the resolved account, never
the seller credential's global set. Permission to inspect an identity and the
account's permission to promote it are separate checks.
Versions and cursors bind to seller, resolved account, caller visibility,
product/requirement context when present, and the snapshot. Contextual versions
also bind to the product's eligibility configuration. Equal versions imply
identical records and aggregate evaluations across pages. Changing grants or
eligibility yields a new version, but a cursor never silently jumps snapshots.
Reuse in a different scope returns
INVALID_REQUESTwithout revealing data.An expired/unrecoverable snapshot cursor returns
INVALID_REQUESTwithdetails.reason: cursor_expired; restart from the first page. Do not reuseCURSOR_EXPIREDwithout amending its currently account-change-feed-specificcontract.
Recheck caller visibility on every page; if permissions changed, invalidate
the snapshot instead of serving its now-inaccessible records. Account-wide
versioncovers discovery/authorization facts; contextualversionadditionallycovers product evaluations. Cache keys include these scopes. For readiness,
the existing
execution_readiness.versioncovers the identity results too,without changing
cache_scope, feed versions, or public catalogue dedupe.Missing new fields on legacy responses mean the feature was not requested or
not available, never “no identities.” An empty complete list means none were
found; an incomplete empty list means enumeration could not establish that.
All evaluations are observations at
evaluated_at, not a future authorizationguarantee. Every mutation revalidates. Initial scope uses explicit rereads;
conditional fetch and notifications remain #8655.
Proposal buys and other exclusions
This recommendation covers explicit package purchases and later package
updates only. It adds no binding override beside
proposal_idand does notclaim identity control for proposal execution. Buyers needing an explicit
identity use the direct-package path. A later explicit package update cannot
retroactively guarantee which identity served before the update.
Carry bindings in committed proposal snapshots and require validation before
finalization/execution as a follow-up with #8655; do not invent a defaulting
guarantee in this draft. Existing proposal behavior is unchanged. Also excluded:
new brand property types, lead-form/messaging/app resource kinds, a generic
resource-binding list, geo-selection automation, and a 3.x enum rename.
Acceptance and implementation handoff
eligibility. An authorized identity may be ready for traffic and ineligible
for a shop product in contextual reads.
distinct reasons and relevant remediation; unknown checks never become
invented setup failures.
one ready plus unknown alternatives is unknown but permits explicit binding
of the independently verified ready identity.
does not alter whole-snapshot counts, readiness, or versions.
Scope reuse, permission changes, expired snapshots, and upstream truncation
cannot leak data or silently claim completeness.
native id. Recreating its downstream connection preserves the identity id.
after discovery fails revalidation; the seller never silently substitutes.
mismatch, binding replacement, and adding creatives to an existing package
are checked before mutation. Two packages may use different identities.
their current behavior. Capability checks and response-field verification
prevent an ignored opt-in from being mistaken for identity-aware success.
choice; direct purchases cover the first implementation.
After the decisions above are accepted, implement named identity, candidate,
page, binding, and error-detail schemas; the new account task and its registry
entry; opt-in readiness conditionals and capabilities; package request/update/
read fields; and corresponding normative docs, schema tests, and conformance
storyboards. Annotate cross-task identity ids with
x-entity. Add the protocolminor changeset for that implementation, not for this discussion memo. Run
schema, projection, registry, documentation-example, and storyboard checks.
Review requested: accept or amend the dedicated read (including optional
product context), package-level typed binding, all-visible diagnostic
candidates and rollup, and unchanged 3.x connection terminology. Once those
choices are recorded, this is the implementation brief for the 3.4 slice.