Skip to content

types: 66 silent name collisions in adcp.types / _generated.py — adopters can't safely migrate off generated_poc #911

@KonstantinMirin

Description

@KonstantinMirin

Problem

consolidate_exports.py flattens all generated_poc/ types into _generated.py using first-seen-wins (alphabetical sort). When multiple JSON schemas define a type with the same name, one class silently shadows the others. adcp.types.__init__.py re-exports from _generated, so from adcp.types import X may resolve to a different class than the adopter had from their deep generated_poc import.

The migration guide and codemod tell adopters "import from adcp.types (stable public API) instead", but for 66 type names this advice silently swaps the underlying class.

Concrete examples (verified with is identity checks)

Symbol adcp.types resolves to Adopter likely needs Fields match?
Brand protocol.get_adcp_capabilities_response brand (identity, with names, logos, etc.) No — 5 vs 80+ fields
Creative creative.get_creative_delivery_response creative.list_creatives_response No — 6 vs 17+ fields
Account core.account account.sync_accounts_request OR sync_accounts_response (3 different classes) No
Sort core.tasks_list_request creative.list_creatives_request No
Authentication account.sync_governance_request core.push_notification_config No
Unit enums.dimension_unit core.duration No
MediaBuy core.media_buy protocol.get_adcp_capabilities_response (for capabilities) No
GovernanceAgent account.sync_governance_request core.account No
CreditLimit account.sync_accounts_response core.account No

Full list: 66 different-shape collisions across non-bundled modules. Only 5 names are in known_collisions; the remaining 61 are unhandled.

Impact on adopters

salesagent (first production adopter) has 85 generated_poc deep imports across 43 files. Following the migration guide, we attempted to rewrite all of them to adcp.types. The result:

  • ~40% were safe (class identity preserved)
  • ~60% silently swapped classes — code compiled, ruff passed, but mypy caught type mismatches for some; others would have been silent runtime bugs
  • The GENERATED_POC_SYMBOL_MAP in the codemod only covers 8 symbols; lines 148-157 of v3_to_v4.py acknowledge the problem for CreditLimit/Setup/GovernanceAgent but defer resolution

The current workaround (documented in extending-types.md via #644) is to import from the specific submodule. This is correct but means the migration guide's primary advice is unsafe for most types.

Proposed fix

Step 1: Make collisions loud (safety net)

In consolidate_exports.py, add a collision_resolution dict. Any collision not in known_collisions or collision_resolution fails the build. This prevents new silent collisions and forces explicit decisions on existing ones. ~50 lines.

Step 2: Add semantic aliases to aliases.py

For the 66 colliding names, add disambiguated aliases that adopters can import safely:

# aliases.py additions
from adcp.types.generated_poc.brand import Brand as BrandIdentity
from adcp.types.generated_poc.creative.list_creatives_response import Creative as ListingCreative
from adcp.types.generated_poc.creative.get_creative_delivery_response import Creative as DeliveryCreative
from adcp.types.generated_poc.creative.sync_creatives_response import Creative as SyncCreativeResult
# ... etc.

This can be done incrementally — start with the high-impact names above and expand as adopters report needs.

Step 3: Expand the codemod's per-symbol map

Add source-module metadata to GENERATED_POC_SYMBOL_MAP so --auto-apply can generate precise rewrites instead of the generic "import from adcp.types" hint.

Relation to existing work

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions