Repository navigation
feat: schedule status checks from expected_confirmation_at - #1809
Conversation
| "preview_creative_response", | ||
| "products_response", | ||
| "signals_response", | ||
| "submitted_response", |
There was a problem hiding this comment.
Medium: New public response builder submitted_response lands without a matching row in AGENTS.md's Response Builders table (L75-94), where every other *_response builder is documented. Per repo policy a new public export without a handler-table entry is a medium finding — adopters and coding agents driving the seller surface won't discover it. Add | submitted_response(task_id, ...) | Submitted task dict | create/buy/accept/refine |.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — additive AdCP 3.3 expected_confirmation_at support with correct semver signal and version-translation pathway.
What I checked:
- Downgrade keep/drop logic in
versions.py(open-object_OLDER_PEER_SAFE_FIELDSkeep + explicit_LATEST_LINE_ONLY_FIELDSdrop with recordedfield_unavailableloss, gated byminor == 3) — satisfies the DR-0032/0033 pathway-back requirement. - Delay floor/ceiling math, dispatch projection task gating + warning, handler auto-emit gate fix for extra-key projections.
- Per-release schema-validated tests covering the version-translation pathway.
- All changes are additive;
feat:is the correct conventional-commit prefix — no public-surface removals/renames/required-flips, so nofeat!:/migration requirement triggered. - High-risk flag is true but all matching files are
(modified)with only a single Medium finding (docs-only), so no escalation trigger (rows 3–5 not met).
Medium findings (1):
- src/adcp/server/init.py:382 —
submitted_responsemissing from AGENTS.md Response Builders table
Decision table: no critical/high (row 1 n/a); gated_paths false (row 2 n/a); no deleted high-risk file (row 3 n/a); the single medium is docs/scope, not data-loss/schema/infra (row 4 n/a); only one medium finding so row 5 n/a; no prior decision (row 6 n/a); no team gate (row 7 n/a); fewer than three mediums (row 8 n/a) → row 9 approve. Recommend addressing the AGENTS.md table entry as a follow-up nit.
Medium findings
- src/adcp/server/init.py:382 — submitted_response missing from AGENTS.md Response Builders table
fd1b3b3 to
bb11115
Compare
| "preview_creative_response", | ||
| "products_response", | ||
| "signals_response", | ||
| "submitted_response", |
There was a problem hiding this comment.
Medium: submitted_response lands as a new public export from adcp.server / adcp.server.responses but is still absent from the Response Builders table in AGENTS.md:75. Per repo convention a new public export without a matching handler-table entry is a Medium. Add a row — submitted_response(task_id, *, expected_confirmation_at=..., message=...) → Submitted envelope → create_media_buy/buy_products/accept_proposal/refine_proposals. Flagged on the prior pass; unaddressed.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — AdCP 3.3 expected_confirmation_at support added end-to-end, all additive.
What I checked:
- Version-conversion drop rules: downgrade drop fires before
_projectso exactly onefield_unavailableloss is recorded (test asserts single-entry list). create_media_buy/get_media_buys retain the field via_OLDER_PEER_SAFE_FIELDSon open targets. DR-0032/0033 pathway satisfied (droppable-with-recorded-loss for compact tasks, safe-keep for open ones) with tests. next_status_check_delayfloor/ceiling math validated (test_expected_confirmation_at.py:924); naive datetimes refused at the handoff site._is_framework_submitted_projectionguard correctly re-identifies the submitted envelope now carrying a third key.- All changes additive →
feat:is the correct semver signal; no public-surface break. - high_risk is true but all reasons are
(modified)files with no medium-or-higher concern on them; gated_paths false; review_decision APPROVED.
Medium findings (1):
- src/adcp/server/init.py:382 —
submitted_responseexport missing from AGENTS.md Response Builders table.
One medium finding, no blocking issues, no escalation trigger, no team gate → falls through to row 9 (approve). This medium re-surfaced from the prior run; worth addressing but non-blocking.
Medium findings
- src/adcp/server/init.py:382 — submitted_response missing from AGENTS.md Response Builders table
AdCP 3.3 lets a seller estimate when a submitted buy or proposal confirms. Buyers should check status then instead of polling tightly; it is never a failure deadline. Buyer side: - TaskResult.expected_confirmation_at reads the estimate from the response. - adcp.next_status_check_delay schedules the next check from it, floored at the polling interval (also used once the estimate passes) and capped by a ceiling so an early confirmation is still seen. - wait_for_refinement_verified uses it, with a new max_poll_interval. - CompatibilityPurchaseOperation gains expected_confirmation_at and next_refresh_delay for pending legacy purchases. Seller side: - adcp.server.submitted_response(task_id, expected_confirmation_at=...) and ctx.handoff_to_task / handoff_to_workflow(..., expected_confirmation_at=...) put an RFC 3339 UTC estimate on the submitted envelope. Naive datetimes are refused; a task that does not define the field omits it with a warning. - Version conversion drops it below 3.3 on buy_products, accept_proposal and refine_proposals with a recorded loss. The async submitted schemas of the first two let it pass through before. It is now kept on create_media_buy and get_media_buys for 3.0 to 3.2 callers, whose objects are open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bb11115 to
50f83a2
Compare
| "preview_creative_response", | ||
| "products_response", | ||
| "signals_response", | ||
| "submitted_response", |
There was a problem hiding this comment.
Medium: submitted_response lands as a new public export (adcp.server / adcp.server.responses) but is absent from AGENTS.md's Response Builders table (AGENTS.md:75-94). Per repo rule a new public export without a matching handler-table entry is a medium finding — add a row (submitted_response(task_id, *, expected_confirmation_at=..., message=...) → submitted envelope → create_media_buy / buy_products / accept_proposal / refine_proposals). Unchanged from the prior pass.
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — AdCP 3.3 expected_confirmation_at surface lands cleanly with correct version-translation pathways.
What I checked:
- Version-translation pathways (DR-0032/DR-0033):
_LATEST_LINE_ONLY_FIELDScorrectly dropsexpected_confirmation_atbelow 3.3 on buy_products/accept_proposal/refine_proposals with afield_unavailableloss;_OLDER_PEER_SAFE_FIELDSkeeps it on create_media_buy/get_media_buys where the object is open. Conversion direction/open-object logic is correct and reported. - Naive datetimes refused;
_FrameworkSubmittedProjectionisinstance guard correctly re-identifies the envelope with a third key. - 345-line test file exercises downgrade, seller, and buyer-scheduling paths with floor/ceiling bounds verified — pathway test present as required.
- No public-API breakage (additive surface), no credential-in-metadata, no generated-code hand-edits, no import-layering violations.
Decision path: No critical/high findings (row 1 no). gated_paths false (row 2 no). high_risk true but all reasons are (modified); the single finding is a documentation medium, and no (deleted) entries (rows 3,5 no). The one medium is category docs, not data-loss/schema/infra (row 4 no). Prior decision was approve, not escalate (row 6 no). No no-auto-approve team (row 7 no). Only one medium finding, fewer than three (row 8 no). Falls through to row 9 = approve.
Medium findings:
- src/adcp/server/init.py:382 —
submitted_responseis a new public export missing from the AGENTS.md Response Builders table. Standing nit re-surfaced from the prior run; non-blocking.
Medium findings
- src/adcp/server/init.py:382 — submitted_response missing from AGENTS.md Response Builders table
Summary
This PR implements AdCP 3.3
expected_confirmation_atagainst the pinned3.3.0-beta.3bundle. The field is still present in that bundle. It is the seller's estimate of when a submitted task will confirm. Buyers should check status at that time instead of polling tightly. A passed timestamp is never a failure.Buyer side
TaskResult.expected_confirmation_atreturns the estimate from the response as an aware datetime. It reads typed data or wire dicts.adcp.expected_confirmation_at(...)does the same for a model, aget_media_buysbuy or a mapping.adcp.next_status_check_delay(estimate, poll_interval=, max_interval=)returns the wait before the next check:poll_interval(the floor);max_intervalso an early confirmation is still seen.wait_for_refinement_verifiednow schedules its polls this way. A newmax_poll_intervalparameter (default 60s) sets the cap. Callers that don't pass it get the old behavior unless the seller sends an estimate.CompatibilityPurchaseOperationaddsexpected_confirmation_atandnext_refresh_delay(...), so callers drivingrefresh_pending_legacy_purchasecan schedule the next refresh. The continuation does not poll on its own.Seller side
adcp.server.submitted_response(task_id, expected_confirmation_at=...), or passctx.handoff_to_task(fn, expected_confirmation_at=...)/ctx.handoff_to_workflow(...)on a decisioning platform. The value is normalized to RFC 3339 UTC. A naive datetime is refused, and a past one is logged.create_media_buy,buy_products,accept_proposalandrefine_proposals. On any other task it is left off with a warning.compat/versions.py):buy_products,accept_proposalandrefine_proposals, on every arm, with a recordedfield_unavailableloss. The generic projection did not do this before for the first two: their async submitted schemas don't declare the field and are open, so it passed through to 3.2 callers whose main response arms are closed.create_media_buy's submitted envelope and onget_media_buysbuys. Those objects are open and the spec makes the field safe to ignore there. It used to be dropped.Notes
wait_for_taskhelper. Callers pollingget_task_statusthemselves usenext_status_check_delay. Task status polls don't repeat the estimate, so it comes from the submitted response.src/adcp/types/core.py: a read-only property onTaskResult.src/adcp/status_checks.py: a new module.src/adcp/server/responses.py: the helper.context.py/types.py,_submitted_projectionindispatch.py, and one condition inhandler.py.nextafter feat!: wire the list_proposals and list_collections tasks #1803 merged. The MIGRATION and inventory conflicts were resolved by keeping both sides' rows.Test plan
tests/test_expected_confirmation_at.py(new), covering:buy_productscaller and recording the loss;make lint,make typecheck-all, public API snapshot regenerated.-n 3after the rebase: 21314 passed, 0 failed.Closes #1640
🤖 Generated with Claude Code