Skip to content

fix(server): preserve valid creatives in lenient legacy batches - #1841

Merged
bokelley merged 3 commits into
mainfrom
claude/issue-1839-mixed-creative-projection
Nov 3, 2026
Merged

bokelley merged 3 commits into
mainfrom
claude/issue-1839-mixed-creative-projection

Conversation

@bokelley

@bokelley bokelley commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

An unprojectable legacy format_id currently rejects an entire sync_creatives request before the seller runs, including requests explicitly selecting validation_mode: "lenient". A lenient batch containing one unsupported format and two supported display creatives now returns one action: "failed" row and two accepted rows, in submitted order. Strict and omitted validation modes retain atomic rejection.

The dispatcher validates the original envelope before isolating projection failures, preserves the original input positions for configured converters, and keeps rejected input content in SDK idempotency hashing. That original-input hint is consumed at the first matching boundary so nested operations retain their own key and payload; pre-validation hooks supplying keys still preserve replay and conflict behavior. SDK-owned failures are materialized before dispatch, preserving schema-valid assignment error keys and the adopter's accepted typed row objects. Assignments to rejected submissions are refused. Managed PlatformHandler task completion overlays the same failures before persistence and webhook publication.

The SDK does not own adopter library reconciliation or external task completion. Partial batches therefore fail before dispatch when delete_missing, creative_ids filtering, unrelated assignments with no projectable uploads, or detectable custom async surfaces would make a reduced request unsafe. Explicit assignment operations continue to require strict mode; partial managed workflow handoffs are refused before enqueue. The migration guide documents these boundaries and retries.

Validation:

  • Focused batch, legacy round-trip, canonical projection, and idempotency suites: 110 passed.
  • Final batch-only AdCP 3.0/3.1, strict request/response validation, nested idempotency, and typed assignment matrix: 35 passed (including independently reviewed dotted/empty package IDs through inline and managed task completion).
  • Source/adopter mypy, pyright, lint, and normal pre-commit hooks passed.
  • Wheel and sdist build and Twine checks passed. A fresh environment installed the wheel and verified strict rejection plus lenient two accepted / one failed behavior.
  • Full make test: 18,863 passed, 2,819 skipped, 10 deselected, one existing expected failure; 92.16% coverage (80% required). The mixed-batch suite has no expected failures.
  • All 46 GitHub checks passed on final head aaa2d0bcb7cb4ea943b062b5d62cdd25ed569ca2, including the Python 3.10–3.13 matrix and PostgreSQL/installed-package gates.

Fixes #1839.

@bokelley
bokelley marked this pull request as ready for review November 3, 2026 12:32

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve.

Reviewer found no blocking or Medium findings. This PR implements the spec'd validation_mode: "lenient" partial-admission flow for legacy sync_creatives: supported creatives dispatch while unprojectable ones return as action: "failed"/UNSUPPORTED_FEATURE in submitted order.

What I checked against the decision table:

  • No critical/high findings → row 1 does not fire.
  • gated_paths: false → row 2 does not fire (despite review_decision: REVIEW_REQUIRED).
  • high_risk: true, but reasons are (modified)/(added) only — no (deleted) → row 3 does not fire. New-file scaffolding (_creative_batch.py) is normal.
  • No Medium findings at all → rows 4, 5, and 8 do not fire.
  • No prior decision → row 6 does not fire.
  • No no-auto-approve team match → row 7 does not fire.
  • Falls through to row 9 → approve.

Note on high-risk modified files (handler.py, idempotency/store.py, mcp_tools.py): the reviewer confirmed the merge is idempotent by construction, the double overlay converges, assignment refusal fails closed pre-dispatch, idempotency hash uses untouched raw_params (no input mutation), and the DR-0032/DR-0033 version-pathway is handled (refused/typed-unsupported) with a regression test. Semver signal fix(server): is correct — no public-surface removal or wire-shape change, no type-system/forward-compat files touched. The required reviewer backstop remains in effect via branch protection.

@bokelley
bokelley merged commit 2bd7751 into main Nov 3, 2026
50 checks passed
@bokelley
bokelley deleted the claude/issue-1839-mixed-creative-projection branch November 3, 2026 12:42
@aao-ipr-bot aao-ipr-bot Bot mentioned this pull request Nov 3, 2026
bokelley added a commit that referenced this pull request Nov 3, 2026
Forward-port the stable server fix from #1841 to the SDK 10 beta line. Explicit lenient legacy creative uploads retain supported siblings and ordered per-creative projection failures while preserving strict atomic validation, original idempotency identity, rejected-assignment safety, and SDK-managed completion results.

Keep next's pre-dispatch mutation-outcome tracking and account-change response projection. Give the existing JOSE factory and thumbprint boundaries explicit result types so the Python 3.10 static contract works with joserfc 1.8 without changing runtime cryptography or dependency ranges.

Validated the exact reviewed source with 21,645 passing tests, 92.04% coverage, all static and generated-code gates, all GitHub CI jobs, and independent protocol review.

(cherry picked from commit 2bd7751)

Refs #1839.
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.

Reject unsupported legacy creative formats per item without losing valid siblings

1 participant