Tracker for the dangerous oneOf unions still in scripts/oneof-discriminators.baseline.json after the #3917 audit loop landed (#3922 / #3928 / #3934). These are bucketed by the kind of fix each needs — none are "free hoists" like the first two waves. The WG should route each bucket to a driver.
Already filed separately: #3935 (core/format.json outer assets/items two-tier restructure), #3936 (boolean-discriminator unions).
A. Scalar-vs-object shape decisions (10 rows)
These are unions where a value can be either a scalar or a structured object. Today they validate, but downstream codegen produces string | object types that adopters have to narrow with typeof at every read site. Each needs a single-shape decision (drop the scalar, or wrap the scalar in { value: <scalar> }).
Recommend: pick the object form for every case. Adopters who want the scalar can read a single field; the spec stops carrying the validator-ambiguous shape.
B. Response-union variant collisions (4 rows)
Each union has multiple variants that share required keys, so a payload can match more than one variant. This is a genuine spec defect — adopters can't tell success from partial-success from conditional-success without inspecting non-required fields.
Recommend: add a per-variant status: const discriminator ("ok" | "error" | "partial" | "conditional") wherever the variants are not already disjoint. This is the same pattern the WG should standardize family-wide on response unions for 4.0.
C. Top-level / structural overlap (2 rows)
D. Two-tier discriminator restructure (1 row)
Acceptance for closing this tracker
When the buckets above are resolved, scripts/oneof-discriminators.baseline.json should be empty (or contain only intentional ⚠ entries). At that point we can flip the audit walker from baseline-diff mode to hard-fail mode for both ✗ and ⚠ in CI.
Refs
Tracker for the dangerous
oneOfunions still inscripts/oneof-discriminators.baseline.jsonafter the #3917 audit loop landed (#3922 / #3928 / #3934). These are bucketed by the kind of fix each needs — none are "free hoists" like the first two waves. The WG should route each bucket to a driver.A. Scalar-vs-object shape decisions (10 rows)
These are unions where a value can be either a scalar or a structured object. Today they validate, but downstream codegen produces
string | objecttypes that adopters have to narrow with typeof at every read site. Each needs a single-shape decision (drop the scalar, or wrap the scalar in{ value: <scalar> }).brand.json#/definitions/brand/properties/tone/oneOf— string vs empty-objectbrand.json#/definitions/font_role/oneOf— string vs{ family }brand/get-brand-identity-response.json#/oneOf/0/properties/fonts/definitions/font_role/oneOf— duplicate of the abovecore/format.json#/properties/supported_macros/items/oneOf— enum-ref vs raw stringmedia-buy/get-media-buy-delivery-request.json#/properties/status_filter/oneOf— single-enum vs array-of-enummedia-buy/get-media-buy-delivery-request.json#/properties/reporting_dimensions/properties/geo/properties/system/oneOf— metro-system enum vs postal-system enummedia-buy/get-media-buys-request.json#/properties/status_filter/oneOf— same as delivery-requestmedia-buy/get-products-response.json#/properties/filter_diagnostics/properties/excluded_by/additionalProperties/properties/values/items/oneOf— string vs objectpricing-options/cpv-option.json#/properties/parameters/properties/view_threshold/oneOf— number vs{ duration_seconds }property/property-feature-result.json#/properties/property/oneOf— string id vs{ ...property-id object }sponsored-intelligence/si-capabilities.jsonmodalitiesvoice/video/avatar(3 unions) — boolean vs objectRecommend: pick the object form for every case. Adopters who want the scalar can read a single field; the spec stops carrying the validator-ambiguous shape.
B. Response-union variant collisions (4 rows)
Each union has multiple variants that share required keys, so a payload can match more than one variant. This is a genuine spec defect — adopters can't tell success from partial-success from conditional-success without inspecting non-required fields.
compliance/comply-test-controller-response.json#/oneOf— 7 variants all share[success]; variant 4 requires only[success]and matches every other variant. Variant 4 is unreachable / overlapping.brand/acquire-rights-response.json#/oneOf— success / partial / approved-with-conditions all share[rights_id, status, brand_id]brand/creative-approval-response.json#/oneOf— same shape, success / conditions / errors collide on[status, rights_id]content-standards/get-content-standards-response.json#/oneOf— success has empty required, error has[errors]; success matches every error payload tooRecommend: add a per-variant
status: constdiscriminator ("ok" | "error" | "partial" | "conditional") wherever the variants are not already disjoint. This is the same pattern the WG should standardize family-wide on response unions for 4.0.C. Top-level / structural overlap (2 rows)
brand.json#/oneOf— variant 2 has zero required keys, so it matches every document. The other three variants pick byauthoritative_location/house/(house, brands). Variant 2 looks like an unintended catch-all.a2ui/bound-value.json#/oneOf— variant 4 requires[literalString, path], intersecting variants 0 ([literalString]) and 3 ([path]). Either variant 4 should be removed or akinddiscriminator added.D. Two-tier discriminator restructure (1 row)
core/audience-selector.json#/oneOf— variants already declaretype: const, but 3 of 4 sharetype: "signal"and only differ by a secondaryvalue_type(binary/categorical/numeric); the 4th hastype: "description". Same restructure pattern as schemas: restructure core/format.json outer assets oneOf to be discriminated #3935: split the parent oneOf ontype, then nest a second oneOf onvalue_typeinside the signal branch.Acceptance for closing this tracker
When the buckets above are resolved,
scripts/oneof-discriminators.baseline.jsonshould be empty (or contain only intentional ⚠ entries). At that point we can flip the audit walker from baseline-diff mode to hard-fail mode for both ✗ and ⚠ in CI.Refs