Skip to content

MIGRATION_v9_to_v10: say that comparing or normalizing models by their dumps changes when one side set a default #1785

Closed

Description

@bokelley

MIGRATION_v9_to_v10's row "model_dump() leaves an unset schema default off" covers a default that must reach a peer ("set it explicitly"). It does not cover code that compares two models, or normalizes one, through model_dump(). On SDK 9 two models with the same values dumped identically. On SDK 10 they differ when one was built with an explicit default and the other wasn't, so equality checks, set membership, dedupe and digests can change silently. Nothing fails at import or type check.

Repro

import adcp
from adcp.types import Product  # noqa: F401  (import order, see #1784)
from adcp.types.domains.formats import Slot

declared = Slot.model_validate({"asset_group_id": "impression_tracker", "asset_type": "pixel_tracker", "event": "impression", "required": False})
received = Slot.model_validate({"asset_group_id": "impression_tracker", "asset_type": "pixel_tracker", "event": "impression"})

print(declared.required == received.required)                                   # True on 9.3.0 and 10.0.0b4
print(declared.model_dump(mode="json", exclude_none=True)
      == received.model_dump(mode="json", exclude_none=True))                   # 9.3.0: True, 10.0.0b4: False

Where this bit a seller

Porting the Scope3 embedded sales agent to 10.0.0b1–b4 needed four fixes:

  • GAM tracker slots: ESA strips the tracker slots it adds at read time by comparing normalized slot dumps. A client that sent a slot back with required omitted or null no longer matched, so the tracker slot would have been stored on the product. Fixed by dumping with exclude_unset=False (src/adapters/gam/format_option_projection.py).
  • Account change feed: repair.available defaults to true and was omitted from the wire (src/core/account_changes.py).
  • Signal options: activation_status: "ready" and default_selected: false on product signal targeting options dropped from discovery and the wholesale API (src/core/property_signals.py).
  • trusted_match: the provider's context_match / identity_match defaults dropped on read-back (src/core/product_conversion.py).

Suggested fix

Add to that row:

  • Comparing, hashing or deduplicating dumps: dump both sides with exclude_unset=False, or compare the models' attributes rather than their dumps.
  • A short checklist: grep for model_dump( used in ==, in, set() or as a digest input.

Activity

  1. bokelley commented on Nov 2, 2026

    @bokelley
    ContributorAuthor

    Documented on next by #1793 (squash c376f50e): MIGRATION_v9_to_v10.md now explains that comparing or normalizing models through their dumps changes when one side set a default, and what to compare instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions