Repository navigation
Description
What kind of change?
Add a new field
Which section?
Whole document / more than one section
Current text
§ 2, Object shape:
Proposal → the merchandising pitch, identity, lifecycle state and assent
Line Items[] → fully self-contained buyable units, each in exactly one channel
§ 2.1, Summary Representation:
plus, from the parent proposal: proposal_id · seller_name · description · valid_from · valid_until.
§ 2.1, Full Representation:
every field, with all catalog_ref values resolved. A full fetch MUST return it.
§ 4.1: no row names the array that holds line items. Appendix A lists 71 fields and does not include it.
§ 5.1 Identity:
line_item_id · String · immutable · Identity within the proposal
status · Enum · derived · ... Independent of the proposal's status
channel · Enum · immutable · ...
§ 7:
A buying agent can hand a single line item to a downstream system without resolving proposal-level defaults or cross-channel ambiguity.Proposed text
§ 4.1, new row:
line_items[] · Array[Object] · seller-set · The line items beneath this proposal, each conforming to § 5. MUST contain at least one entry while status is published or later. Order carries no meaning.
§ 5.1, two new rows:
proposal_id · String · immutable · The containing proposal's proposal_id. MUST equal it. Present in every representation of the line item, including the Full Representation returned by a full fetch and any line item transported on its own.
seller_id · String · immutable · The containing proposal's seller_id. MUST equal it. Present in every representation of the line item.
§ 2, add after the no-inheritance rule:
proposal_id and seller_id on a line item are the sole exception to the no-inheritance rule. They are not inherited: they are stated on the line item and MUST be validated equal to the parent. They exist so that a line item fetched, signalled or handed off on its own still identifies its proposal and its seller.
§ 2.1, Full Representation, replace with:
every field of the line item, including proposal_id and seller_id, with all catalog_ref values resolved. A full fetch MUST return it.
§ 2, replace "Line Items[]" in the shape diagram with "line_items[]".
Appendix A: add line_items[] (4.1, Array[Object], seller-set), proposal_id (5.1, String, immutable), seller_id (5.1, String, immutable). Appendix B: seller-set 29 → 30; immutable 6 → 8.
examples/: publish one complete nested document, examples/proposal-full.yaml, showing the omni proposal with its three line items under line_items[], each carrying proposal_id and seller_id. Keep the four existing files as excerpts of it.Why
Field(s) involved
line_items[] (new, § 4.1), proposal_id and seller_id on the line item (new, § 5.1), § 2 object shape, § 2.1 Full Representation, requires (#23), source_ref (#28), commitment.proposal_id (#24)
Two things hold the two-level tree together and neither is written down: the key that contains the children, and the fields that let a child identify its parent.
The container has no name. "Line Items[]" appears once, in the § 2 shape diagram, and nowhere else. No field table defines the key, its cardinality, or whether an empty array is valid. Appendix A, which claims to index every field, omits it. Two implementers serialising a proposal will name the array differently and their documents will not interoperate. Three change requests filed this week assume the container exists without naming it: #23's requires references sibling line items by id, #28's source_ref points at a line item in another seller's proposal, and #24's commitment.proposal_id names the proposal a line item belongs to. All three reference members of an array that has no key.
A line item on its own does not know its seller. Containment is the only link between a line item and seller_id, and the draft defines three situations where a line item leaves its container:
- § 2.1 says "a full fetch MUST return" the Full Representation of a line item. That is one line item returned alone, defined as "every field" of the line item.
seller_idis not a field of the line item. - § 5.1 says line item
statusis "independent of the proposal's status, so one line item may sell out while the rest of the shelf stays live." A status that changes alone is signalled alone. - § 7 says a buying agent "can hand a single line item to a downstream system." The receiving system has no seller to attach it to.
The Summary Representation already concedes the point: § 2.1 requires it to carry proposal_id and seller_name "from the parent proposal." The Full Representation, the one handed downstream, is not required to. The thinner form carries parent identity and the richer form does not.
#24 proposes proposal_id and version inside commitment. That solves the stale-version problem and we support it. It does not solve this one: commitment is "absent on a published standard proposal" (§ 5.6.1), so a line item fetched before composition, or signalled as sold_out, still carries nothing. And commitment is the buyer's object; seller_id is the seller's identity and belongs on the seller's side of the line item.
#28 makes the gap visible from another angle. An aggregator's line item will carry source_ref.seller_id, the original publisher, while the line item itself still has no seller_id of its own. A downstream system receiving that line item alone sees who originally sold it and not who is selling it now.
The example files never show the join. A proposal and its line items are one document; § 2's shape diagram, § 2.1's "from the parent proposal", and #29's manifest all assume it. The four files in examples/ are excerpts of that one document, split for readability under § 6.1 to § 6.4. The split itself is fine. What is missing is any example of the nesting: the proposal excerpt has no line_items key, so the container's name never appears, and the line-item excerpts carry no proposal_id, so an implementer working from the examples, which the README says are "the best reference for shape" until schemas exist, has to guess both the key and the back-reference. One nested example alongside the excerpts closes that.
This is not inheritance. The no-inheritance rule forbids a line item taking a field's value from the parent. Stating proposal_id and seller_id explicitly on the line item and validating them equal to the parent is a reference, the same way requires in #23 references sibling ids. The proposed text names it as the sole, deliberate exception so nobody reads it as a loophole.
Concrete scenario
A buyer agent screens the summary of examples/line-item-ctv.yaml and fetches its Full Representation. Under § 2.1 the response is every field of the line item with catalog refs resolved:
line_item_id: "weather-app-ctv"
status: published
channel: ctv
...
correlation_id: "ep-q4-2026-commitment/weather-app-ctv"The agent holds a fully priced, fully specified line item and cannot say which seller is offering it, which proposal it belongs to, or which offer window applies. If it hands the line item to its DSP as a private-auction deal per § 1.1, the DSP has no seller to key the deal to. If the line item later signals status: sold_out, the signal carries no proposal_id for the agent to update.
With the proposed text the same fetch returns:
proposal_id: "examplepub-us-q4-2026-omni"
seller_id: "reg:examplepub-digital"
line_item_id: "weather-app-ctv"
status: published
channel: ctv
...and the published proposal reads:
proposal_id: "examplepub-us-q4-2026-omni"
seller_id: "reg:examplepub-digital"
...
line_items:
- proposal_id: "examplepub-us-q4-2026-omni"
seller_id: "reg:examplepub-digital"
line_item_id: "business-daily-display"
...
- proposal_id: "examplepub-us-q4-2026-omni"
seller_id: "reg:examplepub-digital"
line_item_id: "weather-app-ctv"
...Every representation of the line item now identifies its proposal and seller, a validator can check the two ids against the parent, and #23's requires, #24's commitment.proposal_id and #28's source_ref all have a named container to index.
Knock-on effects
- § 2. Shape diagram uses the real key. No-inheritance rule gains its one stated exception.
- § 2.1. Full Representation now carries the parent ids the Summary already does.
seller_namecould be added to the full form for symmetry; not proposed here becauseseller_idis the registry key andseller_nameis derivable. - § 4.1. One new row.
line_items[]isseller-set: the seller may add or withdraw line items whiledraftorpublished, consistent with per-line-itemstatus. - § 5.1. Two new immutable rows. Validation rule: equal to the parent, or the document is invalid.
- [Change] § 2, § 4.1, § 5.6 — Line-item dependencies and bonus lines (linked_execution, requires) #23
requires.all_of[]andany_of[]reference ids withinline_items[]of the same proposal and version; the container they index now has a name. - [Change] § 4.1, § 5.6.1 — Commitments must carry proposal_id and version; stale versions are rejected #24. Complementary.
commitment.proposal_idnames the proposal at composition time;line_item.proposal_idnames it at every time. The two MUST agree, which is one more deterministic check for the seller. - [Change] § 2.1, § 5.10 — Cross-publisher composition: searchable summary metadata and source_ref #28
source_ref. An aggregated line item carries both its ownseller_id(the aggregator) andsource_ref.seller_id(the origin). The rule "the aggregator is the seller_id on its own proposal" becomes checkable on the line item alone. - [Change] § 2.3 — Static manifest at a well-known URL for standard proposals #29 manifest.
proposals[].line_items[]in the manifest example already uses the key proposed here. - OQ-5 (OQ-5 — Is a
min_line_itemsfield needed? #6) / [Change] § 2, § 4.1, § 5.6 — Line-item dependencies and bonus lines (linked_execution, requires) #23. Any bundle rule is a constraint online_items[]; it needs the container named. - § 6. One nested example added. Existing files unchanged except for two added lines each.
- Appendix A / B. Three rows; counts above.
- Downstream standards (§ 1.1). A line item handed to OpenDirect or Deals API now carries the seller to attach it to. No change to those standards.
- § 3 round-trip claim. Unaffected.
Would this break an implementation already built to this draft?
Yes — breaking
Alternatives you considered and rejected
- State that a line item is never valid outside its proposal. Clean, but contradicts § 2.1's standalone full fetch, § 5.1's independent status, and § 7's handoff claim. Removing those three would be a larger change than adding two ids.
- Put the ids only in
commitment, as [Change] § 4.1, § 5.6.1 — Commitments must carry proposal_id and version; stale versions are rejected #24 does. Covers composed line items only; a published or sold-out line item still carries nothing. We support [Change] § 4.1, § 5.6.1 — Commitments must carry proposal_id and version; stale versions are rejected #24 as the version-pinning half and propose this as the identity half. - Carry
seller_nameinstead ofseller_id.seller_idis the registry key;seller_nameis display text. The Summary carriesseller_namefor screening; the Full Representation should carry the key a downstream system can resolve. - Leave the container key to the schema. The schema cannot name what the prose does not, and the body is normative.
- Use
correlation_idprefixes as the link. They are informal, buyer-assigned at commitment time, and absent on a published line item.
You are commenting as
AdTech Provider
Before you submit
- I have read CONTRIBUTING.md and agree my contribution may be published under this repository's licence.
- This contains no confidential or commercially sensitive information.
Activity
Sirajmx commentedon Oct 21, 2026
Thank you for filing this. It has been triaged and labelled for the working group, and will be considered at adjudication after the comment period closes on 22 October 2026. Related: #23, #24, #28.
EvgenyAndroid commentedon Oct 27, 2026
Supporting this, with implementation evidence. I couldn't parse a proposal and its line items as one document without inventing a wrapper, because no field table names the container.
line_items[]plusproposal_id/seller_idon each line item is the shape I ended up needing. One addition: a line item handed off on its own should also say which proposalversionit was taken from, which #24 asks for on the commitment.KonstantinMirin commentedon Nov 4, 2026
Agreed on the version, @EvgenyAndroid. Our shared types PR names
line_items[]in § 4.1 and adds aVersionReftype:proposal_id,version, and an optionalline_item_id. A system that holds a line item outside its proposal can name it with aVersionRefand keeps the proposal'sseller.domainbeside it. Aproposal_idis unique only within that domain (our parties PR, from #61).Neither PR copies these keys onto the line item. Without
versionon the line item, a revision to another line item leaves asks and holds on this one current (our lifecycle PR). Afteragreed, downstream execution names the agreed line item by the sameVersionRef(our post-agreement PR, from #68).Analyzed and generated with the help of Claude, fully reviewed by me.