Skip to content

feat(decisioning): time_budget deadline wrapper + incomplete projection on get_products#501

Merged
bokelley merged 2 commits into
mainfrom
claude/issue-495-time-budget-wrapper
May 3, 2026
Merged

feat(decisioning): time_budget deadline wrapper + incomplete projection on get_products#501
bokelley merged 2 commits into
mainfrom
claude/issue-495-time-budget-wrapper

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 3, 2026

Closes #495

Summary

Honors GetProductsRequest.time_budget in MediaBuyHandler.get_products. Previously the SDK ignored the field entirely; the platform ran unbounded. Now:

  • resolve_time_budget({interval, unit}) converts the wire Duration to a float seconds deadline; unit='campaign' returns None (no SDK-managed deadline — seller has the full campaign flight; value still reaches the adopter via params).
  • The get_products shim wraps _invoke_platform_method in asyncio.wait_for(coro, timeout=deadline) (only when deadline is not None).
  • On asyncio.TimeoutError: logs a WARNING with budget info + returns {"products": [], "incomplete": [{"scope": "products", "description": "…", "estimated_wait": null}]}.
  • IncrementalGetProducts Protocol + ProductsCheckpoint declared and exported from adcp.decisioning; dispatch routing ships in a follow-up (documented in AGENTS.md and the Protocol docstring).

Behavioral change for existing adopters: Any adopter whose get_products latency exceeds a buyer's time_budget will now see their coroutine cancelled and buyers will receive incomplete[]. Previously the field was silently ignored. A WARNING log fires on each timeout — monitor for regressions after upgrade. The thread-pool slot leak for sync adopters (thread continues post-cancel) is documented in the time_budget.py module header.

Nits not fixed (noted for follow-up):

  • Protocol expert: enumerating only scope='products' on full timeout — pricing/forecast scope descriptions in the spec presuppose products existing, so products: [] makes them inapplicable; proposals scope enumeration deferred to the incremental dispatch PR.
  • Protocol expert: strip_credentials_from_wire_result not called on the SDK-generated timeout dict — the dict is known-safe (no adopter data), but worth adding defensively in the follow-up.

What was tested

  • pytest tests/test_time_budget.py — 20 new tests, all pass: unit conversion for all 4 time units + campaign + None; project_incomplete_response shape; 1s budget vs 10s adapter → incomplete[]; within-budget passthrough; absent budget → no deadline; campaign unit → no deadline; timeout → WARNING log; Protocol importability.
  • pytest tests/test_decisioning_handler.py tests/test_decisioning_handler_shims.py tests/test_decisioning_dispatch.py — 109 existing tests, no regressions.
  • Full unit suite (pytest tests/ -x -q --ignore=tests/integration --ignore=tests/conformance) — 3210 passed, 0 failures.
  • ruff check src/adcp/decisioning/time_budget.py src/adcp/decisioning/handler.py — clean.

Pre-PR review

  • code-reviewer: approved — blockers resolved (IncrementalGetProducts dispatch status documented in AGENTS.md + Protocol docstring; description field present in incomplete dict; type annotations retained on shim)
  • ad-tech-protocol-expert: approved with nits — scope='products' is spec-correct for products: [] case (other scopes presuppose products); campaign→no-deadline documented in module header + Protocol docstring; estimated_wait: None is schema-valid

Triage-managed PR. This bot does not currently iterate on
review comments or PR conversation threads (only on the source
issue). To unblock:

  • Push fixup commits directly: gh pr checkout <num>
    fix → push.
  • Or re-trigger: comment /triage execute on the source
    issue.

See adcp#3121
for context.

Session: https://claude.ai/code/session_01FMKCUEQWdb8my9rXtFVQ9x


Generated by Claude Code

@bokelley bokelley force-pushed the claude/issue-495-time-budget-wrapper branch 2 times, most recently from 7cca60b to 5a7b64c Compare May 3, 2026 23:10
…on on get_products (#495)

Wraps `MediaBuyHandler.get_products` in `asyncio.wait_for` when the buyer
sets `time_budget`, then projects a wire-compliant `incomplete[]` response
on exhaustion. Previously the SDK ignored `time_budget` entirely.

https://claude.ai/code/session_01FMKCUEQWdb8my9rXtFVQ9x
@bokelley bokelley force-pushed the claude/issue-495-time-budget-wrapper branch from 5a7b64c to 80bd2ec Compare May 3, 2026 23:15
@bokelley bokelley marked this pull request as ready for review May 3, 2026 23:29
@bokelley bokelley merged commit bc6ad13 into main May 3, 2026
15 checks passed
@bokelley bokelley deleted the claude/issue-495-time-budget-wrapper branch May 3, 2026 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(decisioning): time_budget deadline wrapper + incomplete projection on get_products

2 participants