From 8409f289419dff635687405f9cd5fbf08a4c6158 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Sat, 24 Oct 2026 01:46:17 +0000 Subject: [PATCH 1/2] fix(types): refuse a JSON null on reporting_webhook.operation_id too #1555 added NoExplicitNull to every generated optional, non-nullable property, including push_notification_config.operation_id. The 3.2 forward-compat reporting_webhook.operation_id from #1552 is pinned to that field's constraints and kept the old metadata, so the parity test failed on main. Add the marker to the extension, compare the published schema against the push field, and cover the explicit-null refusal. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/adcp/types/_forward_compat.py | 5 ++++- tests/test_reporting_webhook_operation_id.py | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/adcp/types/_forward_compat.py b/src/adcp/types/_forward_compat.py index a3e8a4f92..42851a573 100644 --- a/src/adcp/types/_forward_compat.py +++ b/src/adcp/types/_forward_compat.py @@ -58,7 +58,7 @@ from pydantic_core import CoreSchema, InitErrorDetails, PydanticCustomError, core_schema from pydantic_core.core_schema import ErrorType -from adcp.types.base import AdCPBaseModel +from adcp.types.base import AdCPBaseModel, NoExplicitNull from adcp.types.canonical_creative import CreateMediaBuyRequest as _PublicCreateMediaBuyRequest from adcp.types.canonical_creative import PackageRequest as PublicPackageRequest from adcp.types.canonical_creative import PackageUpdate as PublicPackageUpdate @@ -156,6 +156,9 @@ "identical wire shape, so confirm seller support before relying on it." ), ), + # Optional, not nullable: a JSON null is refused, as on the generated + # push_notification_config.operation_id this field mirrors (#1547). + NoExplicitNull, ] if TYPE_CHECKING: diff --git a/tests/test_reporting_webhook_operation_id.py b/tests/test_reporting_webhook_operation_id.py index 6727ce410..07747ae79 100644 --- a/tests/test_reporting_webhook_operation_id.py +++ b/tests/test_reporting_webhook_operation_id.py @@ -220,7 +220,12 @@ def test_reporting_operation_id_constraints_match_push_notification_config() -> assert reporting.annotation == push.annotation assert [repr(item) for item in reporting.metadata] == [repr(item) for item in push.metadata] schema = ReportingWebhook.model_json_schema()["properties"]["operation_id"] - assert schema["anyOf"][0] == { + push_schema = PushNotificationConfig.model_json_schema()["properties"]["operation_id"] + wire_keys = ("type", "minLength", "maxLength", "pattern", "anyOf") + assert {key: schema.get(key) for key in wire_keys} == { + key: push_schema.get(key) for key in wire_keys + } + assert {key: schema[key] for key in ("type", "minLength", "maxLength", "pattern")} == { "type": "string", "minLength": 1, "maxLength": 255, @@ -229,6 +234,18 @@ def test_reporting_operation_id_constraints_match_push_notification_config() -> assert "MUST NOT emit `media_buy_delivery`" in schema["description"] +def test_reporting_operation_id_refuses_an_explicit_json_null() -> None: + """Omitted is fine; a JSON null is not, matching the generated push field.""" + base = { + "url": "https://buyer.example/reporting", + "authentication": _AUTHENTICATION, + "reporting_frequency": "daily", + } + assert ReportingWebhook.model_validate_json(json.dumps(base)).operation_id is None + with pytest.raises(ValidationError): + ReportingWebhook.model_validate_json(json.dumps({**base, "operation_id": None})) + + @pytest.mark.asyncio async def test_stored_registration_correlates_delivery_and_retry(tmp_path: Path) -> None: registration = ReportingWebhook( From 470846d18fa0061d478bbf0f19f10f3283c3ab7e Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Sat, 24 Oct 2026 02:26:54 +0000 Subject: [PATCH 2/2] ci: give the Postgres core lane headroom over its runtime The core conformance lane runs 25-29 minutes on main against a 30 minute limit, so slower runners cancel it. Raise the limit to 45 minutes. Co-Authored-By: Claude Opus 5.5 (1M context) --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ccf64763..689855d76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -199,8 +199,9 @@ jobs: matrix: include: # Core also builds and installs the reporting wheel/sdist fixtures. + # It runs 25-29 minutes on main, so 30 cancelled it on slow runners. - lane: core - timeout_minutes: 30 + timeout_minutes: 45 - lane: process timeout_minutes: 15 services: