feat(media-buy): static OOH channel contract — ooh_metrics, posting records, channel guide - #6241
feat(media-buy): static OOH channel contract — ooh_metrics, posting records, channel guide#6241bokelley wants to merge 2 commits into
Conversation
…ecords, channel guide Static OOH has no play event: the channel's delivery number is a period-level modeled audience estimate and its settlement artifact is the posting record, not an airing log. Adds the option-(c) shape converged on in #6140: - ooh_metrics delivery block: panels[] with typed identifiers (geopath | route_frame | plant_face | other), posting period, contracted share of voice, illuminated hours, estimated_impressions with declared estimation_basis (geopath_dec | route_frame_data | seller_modeled), and postings[] — per-unit posting dates and photo evidence per OAAA proof-of-performance conventions - ooh_metrics in available-metric; delivery doc tables updated - docs/creative/channels/ooh.mdx: canonical custom-format contract for printed units (physical live-copy dimensions at effective print resolution), posting-period flighting, billing_measurement-based reconciliation Design narrative in specs/static-ooh.md (#6146). Closes the schema half of #6140; grounded in OAAA model-contract conventions. Refs #6140, #6138 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated paths touched without required approval.
This PR modifies static/schemas/source/** (delivery-metrics.json and available-metric.json), which are under a hard, deterministic approval gate (gated_paths: true). The current review_decision is REVIEW_REQUIRED, not APPROVED, so the gate is not satisfied. Per decision rule row 2, the outcome must be escalate — human/CODEOWNERS review is required before merge.
The change itself is additive and clean: a new ooh_metrics delivery block wired alongside dooh_metrics (additionalProperties:true per DR-0009), a matching available-metric enum value, and a coherent channel guide doc. Schema↔docs coherence checks out, no oneOf regression, correct minor changeset, fictional companies (Acme Outdoor) used. This appears to be a non-breaking Normative-class change.
Medium findings
- static/schemas/source/core/delivery-metrics.json:336 —
estimated_impressionsprovenance MUST stated in description prose but not enforced byrequired[].
Escalation reasons
- Gated files
static/schemas/source/core/delivery-metrics.jsonandstatic/schemas/source/enums/available-metric.jsonrequire human/CODEOWNERS approval;review_decisionisREVIEW_REQUIRED.
Medium findings
- static/schemas/source/core/delivery-metrics.json:336 — estimation_basis provenance MUST not enforced by required[]
Why human review
- Gated paths under static/schemas/source/** modified (delivery-metrics.json, available-metric.json) and review_decision is REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS review required.
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/delivery-metrics.json (modified) matches
static/schemas/source/**; static/schemas/source/enums/available-metric.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.
…resent The ooh_metrics description asserts provenance MUST be declared, but nothing enforced it — a seller could emit the settlement number with no basis and stay schema-valid. Adds the draft-07 property dependency (same pattern as the existing reach → reach_unit rule) so the MUST is schema-enforced rather than description prose. Addresses PR #6241 review (DR-0001). 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 approval.
This PR adds a static OOH channel contract: ooh_metrics gains a draft-07 dependencies block making estimation_basis required whenever estimated_impressions is present. The reviewer confirms this resolves the prior Medium (provenance MUST is now validator-enforced, not prose-only), that draft-07 is the correct draft for the property-dependency form, and that the pattern matches 11 other source schemas. Schema↔docs coherence looks consistent and there are no current findings.
Why this cannot auto-approve: the diff modifies files under static/schemas/source/** (core/delivery-metrics.json, enums/available-metric.json), which are under a hard, deterministic approval gate. review_decision is not APPROVED (unknown/none), so the gate is not satisfied (decision row 2). This is a hard gate enforced in code regardless of how clean the diff is.
The prior escalation was resolved on the merits (the prior Medium is gone), but the gate persists purely because a human/CODEOWNERS approval has not yet been recorded.
Escalation reasons
- Gated paths under
static/schemas/source/**modified (core/delivery-metrics.json,enums/available-metric.json) andreview_decisionis notAPPROVED— human/CODEOWNERS review required.
Why human review
- Gated paths under static/schemas/source/** modified (core/delivery-metrics.json, enums/available-metric.json) and review_decision is not APPROVED — human/CODEOWNERS review required.
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/delivery-metrics.json (modified) matches
static/schemas/source/**; static/schemas/source/enums/available-metric.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.
HOLD — approve-direction only; ballot after amendmentsPer the WG packet on #6140 (and charter ballot requirements — normative change, five-person/two-org two-thirds): this PR stays in draft and must not merge on green CI. Amendments to land before ballot:
Holding amendments until AdQuick's real posting artifacts are in hand, so |
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated schema paths modified and no APPROVED review on record.
This PR adds a static-OOH delivery contract: an ooh_metrics block in delivery-metrics.json, the ooh_metrics enum value in available-metric.json, matching doc rows, a new channels/ooh.mdx guide, and a correct minor changeset. The change is purely additive (new optional fields + enum value), Normative (non-breaking), schema↔docs coherent, with a correct changeset scope/type. No critical/high findings.
However, static/schemas/source/core/delivery-metrics.json and static/schemas/source/enums/available-metric.json fall under the static/schemas/source/** hard approval gate (gated_paths: true), and review_decision is not APPROVED (unknown/none). Per decision-table row 2, this is a non-overridable gate that requires a recorded human/CODEOWNERS approval before auto-approval is possible. The gate lifts the moment review_decision becomes APPROVED.
Medium findings
- None.
Escalation reasons
- Gated paths under
static/schemas/source/**modified (core/delivery-metrics.json,enums/available-metric.json) andreview_decisionis not APPROVED — human/CODEOWNERS review required.
One non-blocking coherence follow-up (low): estimation_basis enum at delivery-metrics.json:164 is narrower than the currencies named in ooh.mdx — worth a look but not a blocker.
Why human review
- Gated paths under static/schemas/source/** modified (core/delivery-metrics.json, enums/available-metric.json) and review_decision is not APPROVED — human/CODEOWNERS review required.
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/delivery-metrics.json (modified) matches
static/schemas/source/**; static/schemas/source/enums/available-metric.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.
|
Pressure-testing Amendment 1: concede, and for a stronger reason than the Geopath/Ipsos churn. ooh.mdx names Geopath, Route, MOVE and COMMB. The enum I specified covers the first two. So an AU MOVE 2.0 or CA COMMB number is currency-measured, has no honest value available, and has to declare Two corrections before the packets are read Monday. The #6140 comment says "the The #6138 status map says to decide the layering "when #6239/#6241 are reviewed together." #6239 is a closed certification PR; the intended reference is #6139. The evidence SLA landed in the wrong file. The ask on #6140 was a note in ooh.mdx. It went into the Flight end has no normative force. On amendment 2 I don't have a settled view. Per-panel Not approving this review — the HOLD says it must not merge on green CI, and an approve would satisfy the gate. |
|
On Amendment 1: The structural argument is correct and the stronger rationale. Factual corrections confirmed:
Evidence SLA placement: Agreed — the three rules (bulletin five-day photo, per-rotation photo, one-photo-per-creative-variation for showings) belong in Flight end normative force: Real gap — "customarily" has no enforcement weight. Two options for the amendment list: Option A — normative language on computation (no new field):
Option B — explicit carrier field in { "average_posted_date": "2026-03-05" }Option A is lower surface; Option B makes buyer reconciliation deterministic without client-side arithmetic and eliminates the "hope the seller did the same math" risk. Either is non-breaking. WG call — flagging for the amendment list. Amendment 2 (identifiers[] / id_type): Moving to Generated by Claude Code |
|
Took the three open items. §3.5 is settled from the source documents, and it turned up a condition nobody has priced in. OAAA §3.5 — five calendar days, and the two clauses are unrelatedFrom the Bulletin Net Contract, §3.5 Proof of Performance, verbatim:
Plain days, not business days. The schema's "five days" is right and the "5 business days" reading was wrong. The five-business-day figure comes from §2.2, which is a different obligation entirely:
So the two five-day facts already in ooh.mdx aren't just confusable with the evidence SLA, they're a genuinely separate clause about posting completion. Labelling them by clause number when the doc fix lands would keep them apart. The Poster Net Contract §3.5 reads:
No deadline attached, which means the schema description is already correct in not asserting one for showings. Only refinement is "close-up" — the contract specifies it and the schema doesn't. Flight end — Option B, and §2.2 changes what it has to carryOption B, for the reason this thread has landed on repeatedly: flight end decides when money stops, and under Option A the buyer and seller each compute it from the same array, so any divergence in rounding, timezone, or which postings count surfaces as a billing dispute with no artifact to point at. Publishing the date the seller actually used makes it the declared value and But the source clause is conditional, and that isn't in either option as written. §2.2 of the bulletin contract:
The poster contract §3.2(a) carries the identical carve-out. So the average-posting-date rule is not unconditional even in the contract we're encoding: it lapses when the buyer missed its own materials deadline. A bare Two ways to handle that, and I'd take the first:
Either way the field should be documented as derivable from Amendment 2 — wire example{
"panels": [
{
"identifiers": [
{ "id_type": "plant_face", "id": "LAM-4471-N" },
{ "id_type": "geopath", "id": "30412345" }
],
"estimated_impressions": 184200,
"estimation_basis": "currency_measured"
},
{
"identifiers": [
{ "id_type": "plant_face", "id": "OUT-8891-S" }
],
"estimated_impressions": 96400,
"estimation_basis": "seller_modeled"
}
]
}The first panel is the case Amendment 2 exists for: an operator face ID and a Geopath ID on the same physical unit, neither derivable from the other, both needed — the operator's to reconcile against the line item, Geopath's to join to the currency. The second shows the shape degrading cleanly for an unmeasured face, one identifier and a modeled basis, with no null-padding.
|
|
§3.5 — settled; doc fix still waits for HOLD item 5. Flight end — Option B, but the materials-timeliness condition is a new element. Sub-option B-1 — declared date + materials-timeliness flag: { "average_posted_date": "2026-03-05", "materials_timely": true }Machine-readable; buyer sees both the date and whether the §2.2 rule applied. Sub-option B-2 — declared date alone, carve-out in { "average_posted_date": "2026-03-05" }Simpler wire surface; §2.2 condition documented in prose only. Either way: document the field as derivable from Amendment 2 — wire example settles the mechanics. Generated by Claude Code |
|
On B-1 vs B-2: B-1. A prose-only carve-out on the rule that decides when billing stops is the shape this thread has already rejected twice — the number is true and unverifiable at the same time, and the party who benefits from ambiguity is the one who computed it. The asymmetry is what decides it. Under B-2 a buyer seeing One drafting note if B-1 lands: |
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated schema paths modified and review not yet APPROVED.
This PR is an additive static-OOH contract: a new ooh_metrics delivery block, an ooh_metrics available-metric enum value, an OOH channel guide, and delivery-doc updates, with a correct minor changeset. The reviewer found the schema↔docs coherent, no oneOf regression, additionalProperties: true preserved (DR-0009), and the estimated_impressions→estimation_basis provenance MUST correctly encoded. No Critical/High/Medium findings.
However, this PR modifies files under static/schemas/source/** — core/delivery-metrics.json and enums/available-metric.json — which are under a hard, deterministic approval gate (gated_paths: true), and the current review_decision is REVIEW_REQUIRED, not APPROVED. Per decision rule row 2, the outcome must be escalate regardless of how clean the diff is. Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded.
Escalation reasons
- Gated paths under
static/schemas/source/**modified (core/delivery-metrics.json,enums/available-metric.json) andreview_decisionisREVIEW_REQUIRED, notAPPROVED— human/CODEOWNERS review required.
Why human review
- Gated paths under static/schemas/source/** modified (core/delivery-metrics.json, enums/available-metric.json) and review_decision is REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS review required.
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/delivery-metrics.json (modified) matches
static/schemas/source/**; static/schemas/source/enums/available-metric.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.
Summary
Draft for Media Buy WG review — the acceptance shape #6140 states for its option (c), which review on that issue converged on. Design narrative and standards grounding in #6146 (
specs/static-ooh.md); mergeable prerequisites (reporting cadence, print nav) in #6240.Static OOH (printed bulletins, posters, transit, street furniture) has no play event: nothing fires when a vinyl face is displayed. The channel's delivery number is a period-level modeled audience estimate from a measurement currency (Geopath US / Route UK / MOVE AU / COMMB CA), and its settlement artifact is the posting record — proof the posting period ran, not an airing log.
ooh_metrics(delivery-metrics + available-metric)Sibling to
dooh_metrics, carrying what a seller natively knows plus provenance-declared estimates:panels[]— typed unit identifiers:id_type ∈ geopath | route_frame | plant_face | other(the enum proposed by @EvgenyAndroid on Static OOH: measurement shape + proof-of-posting (the no-play-event channel) #6140;plant_faceis the operator's own panel number that OAAA model contracts key line items on, alongside the Geopath/TAB panel number)posting_period_start/end— the in-charge period the row coversshare_of_voice_contracted,illuminated_hours— rotary and illumination contract facts (illumination determines the 12/18/24-hour measured basis and the credit remedy)estimated_impressions+estimation_basis ∈ geopath_dec | route_frame_data | seller_modeled— the delivery number with declared provenance;seller_modeledis the honest fallback for markets without a currencypostings[]— per-unit posting records:posted_at,verified_at,evidence_url,event_type ∈ posted | rotated | repaired | removed. Codifies the OAAA model-contract proof-of-performance conventions (photo per unit within five days of posting for bulletins and after each rotary rotation; representative photo per creative for poster showings; display terms run from average posting date).Billing rides the existing broadcast pattern unchanged: vendor declared in
measurement_terms.billing_measurement, maturation viais_final/measurement_window.docs/creative/channels/ooh.mdxCanonical-style channel guide:
customformat kind like print (production PDF/TIFF at effective print resolution — 18–25 ppi roadside per OAAA production specs), physical constraints on the product declaration, posting-period flighting, andooh_metrics-based reconciliation. Nav entry deferred until merge timing is known (snapshot redirects require the page to exist in the pinned docs snapshot).Open questions for WG (also in #6146)
oohproperty type + adagents.json authorization pathpostings[].evidence_urlshould carry the artifact-URL security contract from Normative security contract for response/artifact URL handoffs (SSRF + principal-binding) #5669by_propertydimension patternRefs #6140, #6138, #6146. Related: #5684 (tearsheet/proof-of-insertion is the same evidence-artifact class), #5537 (audience multiplier placement), #5538 (structured geo).
🤖 Generated with Claude Code