Support open-ended hosted duration ranges#5323
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
There was a problem hiding this comment.
Schema mechanism is the right shape. Changeset categorization is wrong and ships a wire change without a version signal.
Block is on one line of frontmatter. Everything else is a non-blocking nit or a scoped follow-up.
Must fix
-
.changeset/5322-open-ended-hosted-duration.md:1-2— empty frontmatter on a protocol-spec change. Per.agents/playbook.md:289-304,327-332:--emptyis for non-protocol changes (UI, docs, infra, migrations, tooling).patch/minor/majoris for schema/task-def/API-ref. Wideningduration_ms_range.itemsto acceptnullin two published canonical formats is the JSON Schema analog of "add new enum value" — textbookminor. Prior art:.changeset/2260-creative-retention-outlasts-campaigns.md:1-3and.changeset/2911-mcp-envelope-flat-serialization.md:1-3both declare"adcontextprotocol": minorfor schema-surface changes. Fix:--- "adcontextprotocol": minor ---
Things I checked
- Schema mechanism:
items.anyOf: [{integer,minimum:0},{null}]+ array-levelcontains: {type: integer, minimum: 0}+minItems/maxItems: 2correctly enforces "each endpoint is integer≥0 or null, at least one must be integer." Rejects[null, null]; accepts[X, null],[null, Y],[X, Y]. - Walker: new
anyOfis insideitems, not top-level.scripts/audit-oneof.mjsonly walksoneOfarrays — no baseline ratchet needed. - Schema-vs-docs coherence: new "Duration constraint precedence" section in
docs/creative/canonical-formats.mdx:990-1010matches bothaudio_hosted.json:22andvideo_hosted.json:42description prose, and the updatedcore/product-format-declaration.jsondescription prose. MUST/SHOULD verbs are used consistently. - Backward compat: producers emitting closed
[5000, 30000]continue to validate. Existing closed-range examples indocs/creative/canonical-formats-migration.mdx:81,376unaffected. Additive widening, not breaking —minoris correct. - Test coverage: three new cases in
tests/composed-schema-validation.test.cjscover the three relevant branches (open-bounded accepted, exact+range coexistence,[null,null]rejection). - Precedence wording on
audio_hosted.jsonupgraded to the long-form version thatvideo_hosted.jsonalready carried — buyers MUST validate against the exact value when both ship. Right call, brings the two hosted formats into alignment.
Follow-ups (non-blocking — file as issues)
- VAST/DAAST coverage asymmetry.
static/schemas/source/formats/canonical/video_vast.json:40-46andaudio_daast.json:19-25still carry the un-wideneditems: {integer, minimum 0}and the older terse precedence wording. A VAST product wanting to say "any creative up to 30s" now has no way to express it without the very_min_only/_max_onlyfields this PR rejects. Was the hosted-only scope deliberate, or worth folding tag-served formats in? docs/media-buy/product-discovery/media-products.mdx:266still references "a duration range" without acknowledging one-sided ranges. Worth updating to keep the canonical-formats.mdx ↔ media-products.mdx pair in sync.min ≤ maxnot enforced.[5000, 3000]validates. Pre-existing behavior — not a regression — but worth a backlog issue if endpoint ordering is meant to be a wire-shape invariant.
Minor nits (non-blocking)
video_hosted.json:42description dropped the advisory-metadata clause. Previous wording included "The range is treated as advisory metadata in that case (e.g., for UI display showing the broader product family)." The PR removes it for symmetry withaudio_hosted. Not load-bearing — the canonical-formats.mdx section preserves the same intent — but if the symmetry was incidental, the clause was useful for implementers.- Negative-case test gap. No assertion that
[3000, 60000.5]or[3000, "60000"]is rejected. Theitems.anyOfhandles it correctly today; a one-line case would lock that in.
Fix the frontmatter and this is ready. The --empty reaches for the docs carve-out in the playbook, but the changes that aren't docs are exactly the changes the playbook reserves minor for.
733b774 to
1fd59ef
Compare
There was a problem hiding this comment.
LGTM. The right shape — null-endpoint widening with contains: integer forecloses [null, null] at validation time, and keeping the vocabulary to two modes (duration_ms_exact, duration_ms_range) avoids minting a third min/max scalar pair as #5322 set out to avoid.
Things I checked
- JSON Schema mechanics on
static/schemas/source/formats/canonical/audio_hosted.json:16-28andvideo_hosted.json:34-47:items.anyOf[{integer ≥0},{null}]+minItems/maxItems: 2+contains: {integer ≥0}correctly admits[int,int],[int,null],[null,int]and rejects[null,null]. Draft-07containsis AJV-native and already used elsewhere in the source schemas (signal-definition.json,sync-plans-request.json). - Existing
[int,int]data continues to validate. Producer-side this is additive, sominoris defensible — and matches the precedent in.changeset/5076-5078-contract-clarifications.mdfor a similar nullable widening. - Schema-vs-docs coherence: new "Duration constraint precedence" section in
docs/creative/canonical-formats.mdx:990-1002matches the per-canonical descriptions on both hosted files. The two-mode framing explicitly forecloses the "why notduration_ms_min/duration_ms_max" question.product-format-declaration.json:5description reads cleanly after the inline edits. - Tests at
tests/composed-schema-validation.test.cjs:200-340cover both one-sided shapes on both hosted canonicals, exact+range coexistence, and[null, null]rejection. - Changeset present, correctly typed
minor, body names the wire change.
Follow-ups (non-blocking — file as issues)
- Containment math with null is unspecified.
docs/creative/canonical-formats.mdx:999says a request satisfies a product when every permitted value falls inside the accepted range. With null endpoints, the natural reading is "null = unbounded on that side," but the spec doesn't say so. Two conformant SDKs could disagree on whether[null, 30000]satisfies[5000, 60000]. One sentence in the new precedence section closes the gap. - Narrowing rule at
docs/creative/canonical-formats.mdx:1134is arithmetic. "v2's lower bound ≥ v1's lower bound AND v2's upper bound ≤ v1's upper bound" doesn't say what happens when either side is null. Same fix. - VAST/DAAST asymmetry.
static/schemas/source/formats/canonical/video_vast.jsonandaudio_daast.jsoncarry the sameduration_ms_rangefield with the integer-only shape. PR scopes itself to hosted, but the rationale isn't written down. Either extend the change or add a sentence in the new precedence section explaining that hosted is the only kind that admits open-ended ranges. Silent divergence on a shared field name is a spec smell. - Examples table at
docs/creative/canonical-formats.mdx:1003-1008has no one-sided row. Integrators get the rule in prose but no worked containment example. One row covering[null, 30000](accept) and one covering[15000, null]against an out-of-range exact (reject) would do more for clarity than the prose section alone.
Minor nits (non-blocking)
- Range/interval term inconsistency at
docs/creative/canonical-formats.mdx:999. "...falls within the product's accepted range; overlap alone is insufficient. An exact value... satisfies a range when that exact value falls inside the accepted interval." Two terms for the same thing in adjacent sentences. "Range" reads better —duration_ms_rangeliterally has the word in its name. items.anyOf[0].minimum: 0onaudio_hosted.json:19andvideo_hosted.json:36admits[0, null]and[0, 0];duration_ms_exactusesminimum: 1. A 0-ms hosted spot is meaningless. Pre-existing inconsistency but the PR is touching exactly this keyword, so it's a clean place to align.
Approving.
Summary
video_hostedandaudio_hostedduration_ms_rangeendpoints to benullfor one-sided ranges.[null, null]by requiring at least one bounded endpoint.duration_ms_exactfor fixed durations andduration_ms_rangefor bounded or one-sided ranges.[null, null]rejection.Why
Issue #5322 raised that bare min/max fields would create a third duration vocabulary. One-sided ranges express “up to X” and “at least Y” without adding separate min-only or max-only fields.
Issue #5321 was withdrawn and is intentionally not included.
Validation
npm run build:schemasnpm run test:schemasnpm run test:composednpm run test:canonical-conventionsnpm run test:schema-utf8npm run test:json-schema -- --file docs/creative/canonical-formats.mdxgit diff --checkExpert Review