Skip to content

feat: delivery domain completion + media buy test coverage (v3.6)#1081

Open
KonstantinMirin wants to merge 294 commits intoprebid:mainfrom
KonstantinMirin:KonstantinMirin/v3.6-delivery-mb-cleanup
Open

feat: delivery domain completion + media buy test coverage (v3.6)#1081
KonstantinMirin wants to merge 294 commits intoprebid:mainfrom
KonstantinMirin:KonstantinMirin/v3.6-delivery-mb-cleanup

Conversation

@KonstantinMirin
Copy link
Collaborator

@KonstantinMirin KonstantinMirin commented Mar 2, 2026

Summary

Completes the Delivery entity vertical slice and expands Media Buy test coverage
for v3.6. Extracts delivery schemas into a standalone package, creates the
DeliveryRepository, fills 127+ behavioral obligation test stubs, and replaces
dict-based v2 compat with model-level serialization.

Merged with latest main (proper two-parent merge). All test suites pass locally.

Key Changes

Commit Type Description
35166f48 fix CPC clicks enum comparison bugPricingModel.cpc vs string "cpc" never matched; click derivation was silently disabled
35166f48 fix Remove dead top-level pricing_option_id lookup (not in AdCP spec)
22dfdcfa refactor Remove dead get_principal_id_from_context function (replaced by ResolvedIdentity)
1ac07a4e test 4 integration tests for webhook_delivery_service.py edge cases
d7d8c36f fix Restore 110 obligation tests lost in auto-merge, fix get_product_catalog patch target
cee9eb3d chore Proper merge with upstream main (two-parent, resolves GH conflict view)
e0aa992a fix Add missing migration 886966ee9a9d + remove stale get_creative_delivery tool registration

Bug found: CPC clicks derivation was silently broken

Line 386 compared pricing_option.pricing_model (raw string "cpc" from DB) against
PricingModel.cpc (Python enum). Python enums don't compare equal to their .value
so the CPC click derivation formula clicks = floor(spend / cpc_rate) never executed.
Fixed by comparing against PricingModel.cpc.value.

Delivery scoped coverage: 97.9%

media_buy_delivery.py          95.4%  (14 uncovered)
webhook_delivery.py           100.0%  (0 uncovered)
webhook_delivery_service.py    98.7%  (3 uncovered)
delivery.py                   100.0%  (0 uncovered)

Local test results (all passing)

Suite Result
Unit 3398 passed, 3 xfailed
Integration 774 passed, 26 skipped, 4 xfailed
Integration V2 172 passed, 10 skipped, 1 xfailed
E2E 81 passed, 25 skipped
UI OK

Prior Changes

Commit Type Description
c7c2727c feat DeliveryRepository for webhook delivery tables (+701 integration test lines)
479f3ca8 refactor Extract delivery schemas → src/core/schemas/delivery.py package
95d05cf3 refactor Tighten delivery schemas against adcp spec (FIXMEs for naming mismatches)
0deff46f feat 17 UC-004 delivery obligation tests (metrics, webhooks, retry/error)
e9404090 refactor Model-level v2 serialization replacing dict-based add_v2_compat
09798d8f feat 54 UC-003 update media buy obligation tests
a9922818 feat 56 UC-002 create media buy obligation tests
15213478 chore Consolidate obligation allowlist

Delivery domain extraction

  • DeliveryRepository (src/core/database/repositories/delivery.py):
    webhook subscription and delivery log queries with tenant-scoped access
  • Schema package (src/core/schemas/delivery.py): extracted from monolithic
    schemas.py; aligned against adcp spec — added flight_ended/goal_met to
    DeliveryStatus, documented naming mismatches (video_completions vs spec
    completed_views), added FIXMEs for webhook metadata placement
  • Integration tests for DeliveryRepository (701 lines)

Integration test harness + factory_boy

  • Test harness (tests/harness/): IntegrationEnv base with factory session binding;
    domain envs (DeliveryPollEnv, WebhookEnv, CircuitBreakerEnv) mock only external
    services, real DB for everything else
  • Factory_boy factories (tests/factories/): TenantFactory, PrincipalFactory,
    ProductFactory, PricingOptionFactory, MediaBuyFactory, MediaPackageFactory,
    PushNotificationConfigFactory with SubFactory cascading

Obligation test coverage (127+ tests)

  • UC-004 (17 tests): delivery metrics, webhook push reporting, retry/error handling
  • UC-002 (56 tests): create media buy — approval workflows, timing, creatives,
    proposals, extensions, postconditions, upgrade paths
  • UC-003 (54 tests): update media buy — budget ops, creative updates, pause/resume,
    targeting overlay, manual approval, extensions A–O

Related

…tes across 4 files)

Completes transport-agnostic error handling migration for the complex modules:
media_buy_create (23 raise sites + 6 except blocks), products (9 sites),
creatives/listing (5 sites), and media_buy_update (4 sites). Adds re-raise
guard in media_buy_create to prevent AdCPError from being re-wrapped as
MEDIA_BUY_CREATION_ERROR. Updates 8 test files to expect AdCPError subclasses.
Also fixes pre-existing mypy errors in mcp_server_enhanced.py and api_v1.py.
Creates transport-agnostic ResolvedIdentity type in src/core/resolved_identity.py
with resolve_identity() boundary helper that implements 4-strategy tenant detection
(Host/subdomain, x-adcp-tenant, Apx-Incoming-Host, localhost fallback) and token
validation. Consolidates auth.py::get_principal_from_token to delegate to auth_utils
version (gains retry logic + admin token support). Adds 11 unit tests.
…edIdentity

All 14 _impl functions now accept identity: ResolvedIdentity | None
instead of ctx: Context | ToolContext | None. Transport-specific auth
extraction moves to MCP wrappers via resolve_identity_from_context().

New module: src/core/transport_helpers.py bridges transport types to
the transport-agnostic ResolvedIdentity at MCP/A2A/REST boundaries.

Updated 19 source files and 20 test files (131 test fixes).
2429 unit tests pass.
Replace broken REST tenant heuristic (split principal on underscores)
with shared resolve_identity() used by all three transports. Remove
MinimalContext workaround and cast(ToolContext) patterns from A2A
server, simplify _verify_principal to accept ResolvedIdentity only.
Move get_http_headers extraction from _impl to MCP transport wrappers
in media_buy_create and media_buy_update. Replace rich console.print
with logger.info in performance _impl. Add AST-based regression tests
ensuring _impl functions never import from fastmcp/a2a/starlette/fastapi
and never use console.print.
…vedIdentity

Replace ctx= parameter with identity= across 10 test files after
prebid#1050 handler migration changed _impl function signatures. Mechanical
change: ToolContext/MagicMock/MockContext → ResolvedIdentity with
AdCPTestContext. Test intent preserved, only context type changes.
Remove patches targeting get_principal_from_context and
get_principal_id_from_context on modules that no longer import them
after prebid#1050 migration. Pass identity=ResolvedIdentity directly to
_impl functions instead. 8 failures across 3 files fixed.
…ests

Replace Mock(spec=['meta']) with ResolvedIdentity in
test_get_products_format_id_filter.py. Mock lacked tenant and
testing_context attributes now required by _get_products_impl.
After prebid#1050 migration, auth failures raise AdCPAuthenticationError
instead of ToolError. Update 6 auth tests across 4 files to accept
both exception types, preserving security test intent.
- Add AdCPValidationError to expected exceptions in error_paths tests
- Add AdCPAuthenticationError to creative_lifecycle auth tests
- Fix A2A get_products to include message/success protocol fields
- Fix A2A error response tenant context for artifact extraction
- Fix delivery webhook scheduler to patch ResolvedIdentity not ToolContext
- File separate issues for pre-existing webhook/UI failures (salesagent-3etp, q6cb)
…ext()

Replace 7 scattered try/except blocks in _impl functions with a single
ensure_tenant_context(identity) helper. This fixes the remaining 40
"Principal not found" integration test failures caused by the prebid#1050
migration losing the set_current_tenant() side effect.

The helper handles: string→dict conversion, ContextVar mismatch
detection, DB-backed tenant loading, and identity-based fallback.

23 files changed (8 source, 15 tests).
…ansport boundary

- Add TenantContext Pydantic model with typed fields and dict-like backward compat
- Update _load_full_tenant() to return TenantContext instead of raw dict
- Update resolve_identity() to wrap detected tenant dicts in TenantContext
- Remove ensure_tenant_context() calls from all 7 _impl modules
- _impl functions now use identity.tenant directly (resolved at transport boundary)
- Update regression tests to verify TenantContext type and new patterns
- All 2,448 unit tests pass

Core invariant: Tenant context is resolved ONCE at the transport boundary
and passed through ResolvedIdentity as a TenantContext model — business
logic never resolves, loads, or validates tenant itself.
Introduces LazyTenantContext that holds tenant_id immediately and defers
the full DB query until a non-tenant_id field is first accessed. This
avoids hitting the database for requests that only need tenant_id (the
common case) or that fail auth before reaching tenant-dependent logic.

- LazyTenantContext supports same dict-like and attribute access as TenantContext
- tenant_id, __contains__, __bool__ all work without triggering DB load
- DB result is cached after first resolution (single query per request)
- set_current_tenant() called lazily when full tenant is resolved
- Tests verify no-DB-for-tenant_id-only and single-query-on-field-access
…ions

Thread tenant through identity.tenant instead of reading from
get_current_tenant()/set_current_tenant() ContextVars. Added tenant
parameter to get_adapter() for explicit dependency passing. All 12
_impl modules now use identity.tenant exclusively; only transport-
adjacent handlers retain legitimate ContextVar usage.
…igration

Thread tenant_id through auth functions instead of reading from ContextVar.
Use self.tenant_id in MockAdServer instead of get_current_tenant(). Serialize
FormatId before JSONB storage in workflow steps. Update integration tests to
use ResolvedIdentity directly and accept AdCPAdapterError from _impl calls.
- Refactor TestAuthOptionalEndpoints to use live_server and test_auth_token
  fixtures instead of reading TEST_AUTH_TOKEN env var (3 tests no longer skip)
- Rename test_page → _check_page to prevent pytest miscollection as standalone
  test (1 error fixed)
- Replace sys.exit(1) with pytest.fail()/pytest.skip() in test_all_admin_pages
  and handle ConnectionError gracefully (1 failure fixed)
- Import integration_db fixture into tests/ui/conftest.py and fix
  test_environment to preserve DATABASE_URL for @pytest.mark.requires_db tests
  regardless of directory (4 errors fixed)
Merge run_full_tests_json.sh into run_all_tests.sh. Both quick and ci
modes now produce JSON reports in timestamped test-results/<ddmmyy_HHmm>/
folders. Added set -o pipefail to fix exit code masking through tee pipes.
Added test-results/ to .gitignore. Updated CLAUDE.md, docs, and quality
gates documentation.
E2E (3 tests): Fix MCP endpoint URL in TestAuthOptionalEndpoints — change
POST target from /mcp/tools/call (non-existent) to /mcp/ (the actual
Streamable HTTP endpoint), add required JSON-RPC 2.0 fields (jsonrpc, id).

UI (3 tests): Add marker filter (-m "not requires_server and not slow") to
UI suite in run_all_tests.sh to skip test_all_admin_pages which requires a
live server. Add missing property_mode and selected_property_tags form fields
to test_add_product_json_encoding. Rename formats= to format_ids= kwarg in
test_list_products_json_parsing.
…at_ids

- E2E: Add required Accept: application/json, text/event-stream header
  for MCP Streamable HTTP protocol (6 tests in TestAuthOptionalEndpoints)
- E2E: Extract MCP_HEADERS class constant to avoid 7x duplication
- UI: Use property_mode=none to bypass tag validation in JSON encoding test
- UI: Fix format_ids to use AdCP structure (id + agent_url fields)
…ssertion

E2E (5 tests): Rewrite TestAuthOptionalEndpoints to use fastmcp Client
with StreamableHttpTransport instead of raw requests.post(). MCP Streamable
HTTP requires session initialization handshake. Without-auth tests use
Host header for domain-based tenant resolution (no xfail).

UI test_add_product_json_encoding: Use indexed pricing form fields
(pricing_model_0, floor_0) matching what the HTML form actually sends —
parse_pricing_options_from_form() expects this indexed format.

UI test_list_products_json_parsing: Remove fragile b'500' assertion that
matched base template JS setTimeout(..., 500). status_code == 200 and
product name presence already verify correct rendering.
…cks, xfails)

- salesagent-3laa: Fix unawaited coroutine in ActivityFeed — extract sync
  _store_activity() method, check for running event loop before creating
  broadcast coroutine to prevent RuntimeWarning leak
- salesagent-5shl: Fix unawaited coroutine in naming.py — replace
  asyncio.run() with run_async_in_sync_context() for nested event loop safety
- salesagent-r5xr: Rename TestScenario → ScenarioSpec to avoid
  PytestCollectionWarning (pytest collects classes prefixed with "Test")
- salesagent-nehy: Replace return with assert in
  test_create_minimal_gam_tenant (PytestReturnNotNoneWarning)
- salesagent-fjxw: Fix 3 xfailed admin domain e2e tests — add admin
  redirect case to _handle_landing_page(), add nginx proxy + env vars to
  docker-compose.e2e.yml
- salesagent-jmx8: Add missing video_standard_30s to mock format registry,
  remove reactive skip from GAM pricing test
…bid#1041)

- Fix pause/resume early-return not completing workflow step (left in_progress forever)
- Fix approval gate storing no request data (empty affected_packages only)
- Store original request_data in workflow step for post-approval execution
- Remove stale xfail from test_admin_ui_network_detection_endpoint
- Fix test session setup: use sess["user"] dict for require_tenant_access
- Mock full GAM OAuth chain in network detection test
…noise

Suppress ~122 warnings/run from a2a-sdk, starlette, httplib2, google-generativeai,
googleads, jsonschema, and zeep. Our own code warnings remain visible via the
default action.
…ion warnings

httplib2 v0.31.2 fixes deprecated pyparsing API usage (setName, leaveWhitespace,
setParseAction, etc.) that produced ~9 warnings per test run.
Replace deprecated jsonschema.RefResolver.from_schema() with the
referencing library's Registry(retrieve=...) pattern in the e2e schema
validator. Eliminates ~19 DeprecationWarning per e2e test run.
Production code already uses pydantic-ai (google-genai) for all AI operations.
Remove the dead google-generativeai dependency, migrate the example file to
the google.genai SDK, and clean up 5 dead test fixtures that patched the old
module.
FastMCP's __init__.py calls simplefilter("default", DeprecationWarning)
at import time, which prepends a catch-all filter that shadows pytest.ini's
ignore entries. Set FASTMCP_DEPRECATION_WARNINGS=false via pytest_configure()
so the a2a HTTP_413 and starlette WSGI filters work as intended.
- Close event loops in test_task_management_tools.py (3 helpers)
- Close TestClient in test_a2a_transport_contract.py fixture teardown
- Close subprocess stdout/stderr pipes in mcp_server fixture
- Use Flask test_client() context manager in test_health_route_migration.py

Reduces ResourceWarnings from 52 to ~15 across test suites.
Swap starlette.middleware.wsgi import to a2wsgi (starlette-recommended
drop-in replacement). Also clean up filterwarnings entries for packages
no longer installed (httplib2, google-generativeai, jsonschema RefResolver).
Add dedicated e2e test file for AdCP discovery endpoints that exercise
the full MCP transport path against the Docker stack with no mocking.
KonstantinMirin and others added 27 commits March 5, 2026 11:52
AdCP JSON schema for get-products-request.json added a time_budget field
that the adcp library does not yet model. This caused 2 test failures in
test_pydantic_schema_alignment.py. Added to KNOWN_SCHEMA_LIBRARY_MISMATCHES
until the adcp library adds support.
_get_pricing_options() was casting string pricing_option_ids to int and
querying PricingOption.id (auto-increment PK). Non-numeric synthetic IDs
like 'cpm_usd_fixed' were silently discarded, causing pricing context
loss in delivery responses.

The fix reconstructs the synthetic pricing_option_id from the
PricingOption's (pricing_model, currency, is_fixed) columns -- the same
formula used by product_conversion.py -- and matches against requested
string IDs.

- Remove stale PricingOption.id.in_ entry from query type safety
  allowlist
- Fix 4 xfail integration tests (now passing)
- Update unit test mock to match new lookup behavior
- Update test_delivery_v3 roundtrip to use synthetic ID
Add optional conversions (float) and viewability (float, 0.0-1.0) fields
to DeliveryTotals schema. Propagate values from adapter response totals
through to the delivery response. Update integration tests that were
asserting viewability absence (gap G44) to assert presence instead,
and remove xfail markers from the 2 obligation tests.
Add GetMediaBuyDeliveryResponse.webhook_payload() that excludes
aggregated_totals (polling-only) and supports requested_metrics
filtering for per-delivery totals. Remove xfail markers from two
webhook payload tests.
… delivery response

- Auto-set notification_type: 'scheduled' for active campaigns, 'final' for completed
- Compute next_expected_at as now + 24h for non-final deliveries
- Persist auto-incrementing sequence_number via WebhookDeliveryLog
- Fix status filter: return all matching buys when specific IDs provided
- Add is_paused boolean column to MediaBuy model with migration
- _resolve_delivery_status returns 'paused' when is_paused is True
- Status filter 'paused' works in _get_target_media_buys
- deliver_webhook_with_retry rejects delivery for paused media buys
- _get_media_buy_delivery_impl checks circuit breaker state and sets
  'reporting_delayed' status when circuit is OPEN
- Remove 3 xfail markers from now-passing tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Auth failure (401/403) sets persistent auth_blocked_at on
  PushNotificationConfig, blocking further delivery until credentials
  are reconfigured (UC-004-EXT-G-07)
- Adapter failures during delivery polling now write AuditLog records
  for persistent audit trail (NFR-003, UC-004-EXT-F-03)
- Add auth_blocked_at column to PushNotificationConfig model
- Skip auth-blocked endpoints in WebhookDeliveryService
- Remove xfail markers from passing tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ation

- scripts/check_scoped_coverage.py: per-entity coverage checker using
  scripts/run-test.sh for test execution (no manual DATABASE_URL exports)
- tests/coverage_scopes.yaml: scope definitions derived from AdCP schema
  entities (delivery=100% target, pre-existing code=90% minimum)

Usage: scripts/check_scoped_coverage.py delivery --verbose
The legacy fallback converting raw_request.product_ids to packages
format (lines 372-375) is dead code. Investigation confirms:

- CreateMediaBuyRequest requires packages (validation raises if empty)
- AdCPBaseModel.model_dump uses exclude_none=True by default, so
  raw_request always contains "packages" key for valid media buys
- Transport wrappers accept product_ids but never pass it to the
  request constructor
- A2A server explicitly documents legacy format is not supported

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add TestCircuitBreakerReportingDelayed: exercises real _is_circuit_breaker_open()
  code path by injecting an OPEN CircuitBreaker into the global singleton, verifying
  that active media buys get status='reporting_delayed' (covers lines 66-68)
- Add TestPartialFailureTolerance: verifies that when one media buy's processing
  raises an exception, other buys are still returned in the response (covers the
  outer except handler at lines 485-487)
- hq98 diagnosis: adapter failure audit trail (lines 304-319) already covered by
  existing TestAdapterFailureAuditTrail test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ith_backoff

Add 9 integration tests covering:
- Auth-blocked PushNotificationConfig skip (UC-004-EXT-G-07)
- HMAC-SHA256 signing with secret validation (UC-004-EXT-G-06)
- Bearer token authentication (UC-004-ALT-WEBHOOK-PUSH-REPORTING-08)
- Happy path delivery to configured endpoint (UC-004-ALT-WEBHOOK-PUSH-REPORTING-01)
- No-config returns false (UC-004-ALT-WEBHOOK-PUSH-REPORTING-01)
- Successful delivery records success on circuit breaker (UC-004-EXT-G-01)
- 500 retry with backoff and circuit breaker failure (UC-004-EXT-G-01)
- Timeout handling with retries (UC-004-EXT-G-01)

All tests use CircuitBreakerEnv with real PostgreSQL for
PushNotificationConfig queries and mock httpx for HTTP.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ryService

Grepped entire codebase — zero callers in production or test code.
Other methods in the range (reset_sequence, get_circuit_breaker_state,
_shutdown) are live and remain untouched.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover 8 previously uncovered lines in src/core/schemas/delivery.py:
- Line 304: model_dump() override forcing next_expected_at=null
- Lines 311, 314: __str__() for GetMediaBuyDeliveryResponse
- Lines 486-491: __str__() for GetCreativeDeliveryResponse

New test classes:
- TestMediaBuyDeliveryResponseStr: zero/one/many pattern
- TestCreativeDeliveryResponseStr: zero/one/many pattern
- TestNextExpectedAtSerialization: final/scheduled/none cases

Obligation IDs: UC-004-DISPLAY-01, UC-004-SERIAL-01

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 9 integration tests to test_delivery_webhook_behavioral.py:

SSRF validation (salesagent-dk73):
- Invalid/internal URL rejected with validation error
- Metrics recorded on validation failure with tenant context
- Metrics skipped when no tenant_id provided

Retry backoff (salesagent-pggp):
- 5xx retry with eventual success (503->503->200)
- 5xx retry exhaustion (always 500, max retries reached)
- Timeout exception triggers retry
- ConnectionError triggers retry
- RequestException triggers retry
- All retries timeout reports failure

Covers: webhook_delivery.py lines 93-99 (SSRF) and 222-235 (retry exceptions)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The requires_server marker had its skip logic removed but
test_connect_to_local_mcp_server still needs the full Docker stack.
Restore conditional skip with socket probe on localhost:8100.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover webhook_delivery.py lines 355, 357, 383-384: the defensive
catch-all handlers in _update_delivery_record and _set_auth_blocked
that absorb DB errors to avoid disrupting delivery results.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove dead WebhookQueue.size() method (zero callers).
Add 11 unit tests covering: CircuitBreaker HALF_OPEN can_attempt,
record_success while OPEN, WebhookQueue full/empty, reset_sequence,
get_circuit_breaker_state default, generic exception retry break,
and _shutdown handler.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove dead code in media_buy_delivery.py:
- Unreachable else branch (adapter totals always present)
- Unreachable ext lookup (no ext field on adapter response)
- Unused _require_admin function and its import

Add 5 unit tests: missing principal_id, missing tenant, raw string
status filter edge cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Include test_webhook_delivery.py and test_webhook_delivery_service.py
in the delivery coverage scope config so their coverage contribution
is measured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ge cases

- is_adjusted notification type (line 239)
- queue full drops webhook (lines 408-409)
- weak secret omits signature (line 463)
- empty dequeue returns False (line 447)

Brings webhook_delivery_service.py from 96.9% to ~99% scoped coverage.
Only 2 uncovered lines remain (shutdown exception handler lines 545, 547).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Zero production callers — all MCP wrappers use resolve_identity() via
ResolvedIdentity. Removes:
- Function definition from context_helpers.py
- Re-exports from helpers/__init__.py and creatives/__init__.py
- No-op mock patches from test_update_media_buy_creative_assignment.py
- Direct test from test_tenant_context_ordering.py

Updates pre-commit hook (check_tenant_context_order.py) to recognize
identity.principal_id/identity.tenant as valid auth patterns instead
of the removed function.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ption_id code

- Fix PricingModel.cpc enum vs string comparison: DB stores raw strings,
  but code compared against enum member. Use .value for correct comparison.
- Remove top-level pricing_option_id collection and usage from raw_request
  (not in AdCP spec, violates per-package pricing abstraction).
- Add integration test: CPC package clicks = floor(spend / cpc_rate)
  (domain business rule, not spec-mandated).
- Add integration test: start_time preferred over start_date for status
  determination (data migration strategy for legacy NOT NULL column).

Delivery scoped coverage: 97.5% -> 98.1%

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolves conflicts from squash-merge of PR prebid#1071 (AdCP v3.6 upgrade)
into main. Key resolutions:

- Keep our schemas/ package structure (main has flat schemas.py)
- Keep our delivery additions (conversions, viewability, webhook_payload)
- Keep dead code removals (get_principal_id_from_context, top-level pricing_option_id)
- Keep CPC enum comparison fix (PricingModel.cpc.value)
- Apply main's Creative schema fix (name required, dates auto-default)
- Update test mocks for is_paused field and synthetic pricing IDs
- Clean obligation coverage allowlist (remove covered entries)
- Update architecture guard known violations for fixed issues

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The squash-merge of prebid#1071 auto-merged test_create_media_buy_behavioral.py
and test_update_media_buy_behavioral.py to main's shorter versions (38 tests),
silently dropping our branch's 148-test versions (74+74 obligation stubs for
UC-002 and UC-003).

Restored our branch versions and fixed 7 broken tests where
get_product_catalog patch target changed from src.core.main to
src.core.tools.products (main moved the import).

Removed 110 now-covered entries from obligation_coverage_allowlist.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…delivery-mb-cleanup

# Conflicts:
#	docs/test-obligations/UC-004-deliver-media-buy-metrics.md
#	src/core/helpers/context_helpers.py
#	src/core/schemas/__init__.py
#	src/core/tools/creatives/__init__.py
#	src/core/tools/media_buy_delivery.py
#	src/core/version_compat.py
#	tests/unit/obligation_coverage_allowlist.json
#	tests/unit/test_architecture_obligation_coverage.py
#	tests/unit/test_architecture_query_type_safety.py
#	tests/unit/test_architecture_repository_pattern.py
#	tests/unit/test_architecture_schema_inheritance.py
#	tests/unit/test_create_media_buy_behavioral.py
#	tests/unit/test_delivery.py
#	tests/unit/test_delivery_behavioral.py
#	tests/unit/test_media_buy.py
#	tests/unit/test_tenant_context_ordering.py
#	tests/unit/test_update_media_buy_behavioral.py
#	tests/unit/test_version_compat_registry.py
#	tests/unit/test_webhook_delivery.py
#	uv.lock
- Commit untracked migration 886966ee9a9d (auth_blocked_at column) that
  5cf35536db6f (is_paused) depends on — CI couldn't resolve the chain
- Remove get_creative_delivery from expected MCP tools (tool was removed
  with creative_delivery.py but test_tool_registration.py kept the entry)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@KonstantinMirin KonstantinMirin marked this pull request as ready for review March 6, 2026 20:23
@ChrisHuie ChrisHuie requested review from ChrisHuie March 6, 2026 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant