Skip to content

schemas: tracker for the 20 remaining undiscriminated oneOf unions #3939

Description

@bokelley

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> }).

  • brand.json #/definitions/brand/properties/tone/oneOf — string vs empty-object
  • brand.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 above
  • core/format.json #/properties/supported_macros/items/oneOf — enum-ref vs raw string
  • media-buy/get-media-buy-delivery-request.json #/properties/status_filter/oneOf — single-enum vs array-of-enum
  • media-buy/get-media-buy-delivery-request.json #/properties/reporting_dimensions/properties/geo/properties/system/oneOf — metro-system enum vs postal-system enum
  • media-buy/get-media-buys-request.json #/properties/status_filter/oneOf — same as delivery-request
  • media-buy/get-products-response.json #/properties/filter_diagnostics/properties/excluded_by/additionalProperties/properties/values/items/oneOf — string vs object
  • pricing-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.json modalities voice / video / avatar (3 unions) — boolean vs object

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.

  • 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 too

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)

  • brand.json #/oneOf — variant 2 has zero required keys, so it matches every document. The other three variants pick by authoritative_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 a kind discriminator added.

D. Two-tier discriminator restructure (1 row)

  • core/audience-selector.json #/oneOf — variants already declare type: const, but 3 of 4 share type: "signal" and only differ by a secondary value_type (binary / categorical / numeric); the 4th has type: "description". Same restructure pattern as schemas: restructure core/format.json outer assets oneOf to be discriminated #3935: split the parent oneOf on type, then nest a second oneOf on value_type inside the signal branch.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-triagedIssue has been triaged by the Claude Code triage routine. Remove to re-triage.enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions