fix(types): resolve capability sub-models via field annotation, not numbered names#745
Merged
Merged
Conversation
…umbered names ``capabilities.py`` imported ``Features2 as SignalsFeatures`` and ``Idempotency3 as IdempotencyUnsupported`` from the bundled ``get_adcp_capabilities_response`` module. Those numbered class names are an internal codegen detail: ``datamodel-code-generator`` 0.56.1 assigns numbered suffixes (``Features1``, ``Features2``…) to inline schemas from a global counter whose traversal order shifts with both upstream schema layout and filesystem-iteration order (APFS-on-macOS vs ext4-on-Linux). The committed bundled file was generated when the counter produced ``Features2`` and ``Idempotency3``; today's regen (on the same pinned generator) produces ``Features1`` and ``Idempotency1``, breaking ``capabilities.py`` at import time and taking the whole ``adcp`` package down with it — which is what ``Validate schemas are up-to-date`` CI was reporting. Reach the classes via their parents' field annotation instead. ``Signals.features`` and ``Adcp.idempotency`` are stable wire-spec class names; their field annotations carry the union arms directly, independent of whatever number the codegen assigned this regen. Pin both lookups with explicit assertions so future schema changes that break the shape fail loudly at boot rather than silently mis-resolving. Also commits the cosmetic regen drift the same run produced: - ``SCHEMA_DELTAS.md``: previously reported ``extension_meta`` delta is already merged upstream; deltas list is now empty. - ``_ergonomic.py``: ``list[...]`` → ``Sequence[...]`` in three ``BeforeValidator`` annotations (regen normalization). - enum + media_buy bundled files: duplicate-import cleanup. Test plan: - pytest tests/ -x → 4731 passed, 34 skipped - mypy + ruff clean - Reproduced the CI failure in a Linux 3.11-slim Docker container against schemas 3.0.7; verified the fix resolves SignalsFeatures → Features1 and IdempotencyUnsupported → Idempotency1 cleanly after fresh regen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…offsets
The AdCP storyboard runner (``@adcp/sdk`` on npm) validates response
shapes with ``zod``. ``zod.string().datetime()`` rejects the ``+00:00``
offset by default — only the Zulu form (``...Z``) passes. Python's
``datetime.isoformat()`` produces ``+00:00`` for tz-aware UTC values,
so every response builder that auto-stamped a timestamp (``confirmed_at``,
``canceled_at``, ``expires_at``, ``reporting_period.start/end``,
``created_date`` / ``updated_date`` on listed creatives) emitted output
that failed schema validation in both the ``examples/seller_agent.py``
and ``sales-proposal-mode (proposal_finalize)`` storyboard jobs:
Schema validation: /confirmed_at: Invalid ISO datetime
Reproduced locally:
$ node -e 'const {z}=require("zod"); z.string().datetime().parse("2026-05-19T21:56:22.349222+00:00")'
ZodError: [{"code":"invalid_string","validation":"datetime",...}]
$ node -e 'const {z}=require("zod"); z.string().datetime().parse("2026-05-19T21:56:22.349222Z")'
# succeeds
Add ``_rfc3339_now()`` in ``server/responses.py`` and route the three
auto-stamp sites inside that file through it; inline the equivalent
``.isoformat().replace("+00:00", "Z")`` shim in the four other touch
points (``server/helpers.py`` canceled_at, ``server/proposal.py``
expires_at, ``sales_proposal_mode_seller`` confirmed_at and expires_at).
ajv-formats and python-rfc3339 both still accept the new form, and
``test_server_helpers.py:373`` was already tolerant of either suffix —
no test changes needed.
Test plan:
- pytest tests/ -x → 4731 passed, 34 skipped, 1 xfailed
- Verified ``_rfc3339_now()`` output ends in ``Z`` and parses cleanly
in zod ``string().datetime()`` (against ``@adcp/sdk`` 's pinned zod).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Validate schemas are up-to-dateonmain(failing since49c4b5ab, May 14).capabilities.pywas importingFeatures2 as SignalsFeaturesandIdempotency3 as IdempotencyUnsupported— both are codegen-internal numbered names that drift across regens.Signals.features/Adcp.idempotency) instead, so the imports survive future renumbering.Root cause
datamodel-code-generator0.56.1 numbers inline-schema classes from a global counter whose traversal order depends on filesystem iteration. Reproduced in a Linux 3.11-slim Docker container against schemas 3.0.7:Features+Features2,Idempotency+Idempotency3Features+Features1,Idempotency+Idempotency1The committed bundled file was generated when the counter produced
Features2/Idempotency3. Today's regen (same pinned generator) producesFeatures1/Idempotency1, socapabilities.py:117blows up withImportError: cannot import name 'Features2'during theGenerate modelsstep, taking the wholeadcppackage import down with it.The fix
Reach the classes via the parent's field annotation —
SignalsandAdcpare stable wire-spec class names; the union arm types live directly on the field annotations:Both lookups raise loudly if the annotation shape ever changes — silent mis-resolution beats import-time bombs, but a clear
RuntimeErrorat boot beats both.Also in this PR (regen drift)
The same
python scripts/generate_types.pyproduced minor cosmetic drift the previous regen never committed:SCHEMA_DELTAS.md— previously reportedextension_metadelta is already merged upstream._ergonomic.py—list[...]→Sequence[...]in threeBeforeValidatorannotations.Test plan
pytest tests/ -x→ 4731 passed, 34 skipped, 1 xfailedmypy src/adcp/types/capabilities.py src/adcp/decisioning/capabilities.py→ cleanruff check src/adcp/types/capabilities.py→ cleanSignalsFeatures → Features1andIdempotencyUnsupported → Idempotency1cleanly after fresh regen.🤖 Generated with Claude Code