Skip to content

feat(decisioning): TaskHandoff create_media_buy on_complete + on_failure hooks#561

Merged
bokelley merged 1 commit into
mainfrom
claude/issue-550-create-media-buy-handoff-on-complete
May 4, 2026
Merged

feat(decisioning): TaskHandoff create_media_buy on_complete + on_failure hooks#561
bokelley merged 1 commit into
mainfrom
claude/issue-550-create-media-buy-handoff-on-complete

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented May 4, 2026

Summary

Closes the v1.5.1 follow-up flagged in PR #550. Adopters returning `ctx.handoff_to_task(...)` from `create_media_buy(proposal_id=...)` now get the same single-ledger D3 guarantee as the inline path: the proposal transitions `CONSUMING → CONSUMED` via the framework's `on_complete` hook when the bg task lands; failures release the reservation so the buyer can retry.

The previous gap: HITL accept-proposal flows left the proposal in `CONSUMING` until the eviction window passed. No data leak, but the reverse-index lookup couldn't hydrate, and a second `create_media_buy(proposal_id=X)` after a HITL rejection would hit `PROPOSAL_NOT_COMMITTED` until eviction.

Mechanism

  • `_project_handoff`: new `on_failure` kwarg paired with `on_complete`. Inner `_fail()` helper centralizes "invoke on_failure (best effort) → registry.fail" so all four failure branches share one path.
  • `_invoke_platform_method`: forwards both kwargs into `_project_handoff` on the handoff path; fires `on_complete` inline before sync return; fires `on_failure` on every adapter exception path. One API surface for inline + HITL.
  • `handler.py:create_media_buy`: replaces local try/except + post-call `mark_consumed` branches with two closures (finalize + release) passed to `_invoke_platform_method`. Single source of truth.

Test plan

  • `test_create_media_buy_handoff_finalizes_consumption_on_completion` — happy path: Submitted envelope → CONSUMING → bg drain → CONSUMED + reverse-index lookup hydrates
  • `test_create_media_buy_handoff_fn_raises_releases_reservation` — GOVERNANCE_DENIED → reservation released → COMMITTED → registry shows the buyer-facing failure
  • `test_create_media_buy_handoff_buyer_can_retry_after_release` — end-to-end: first attempt fails, second attempt succeeds (proves on_failure release actually unblocks retries)
  • All 5 existing finalize-handoff tests still pass
  • 4014 passed, 33 skipped, 0 regressions
  • ruff + mypy clean on changed files

🤖 Generated with Claude Code

…ure hooks

Closes the v1.5.1 follow-up flagged in PR #550. Adopters returning
ctx.handoff_to_task(...) from create_media_buy(proposal_id=...) now
get the same single-ledger D3 guarantee as the inline path: the
proposal transitions CONSUMING → CONSUMED via the framework's
on_complete hook when the bg task lands; failure paths release the
reservation so the buyer can retry.

Previous gap: HITL accept-proposal flows left the proposal in
CONSUMING until eviction. No data leak, but reverse-index lookup
couldn't hydrate, and a second create_media_buy(proposal_id=X) after
a HITL rejection would hit PROPOSAL_NOT_COMMITTED until eviction.

Mechanism:
* _project_handoff gets on_failure paired with on_complete; inner
  _fail() helper centralizes 'invoke on_failure (best effort) then
  registry.fail' across all four failure branches.
* _invoke_platform_method forwards both kwargs into _project_handoff
  on the handoff path; fires on_complete inline before sync return;
  fires on_failure on every adapter exception path.
* handler.py:create_media_buy replaces the local try/except +
  post-call mark_consumed branches with two closures passed to
  _invoke_platform_method. Single source of truth for inline + HITL.

3 new tests: handoff happy path, handoff failure releases
reservation, end-to-end retry-after-release. All 5 existing
finalize-handoff tests still pass. 4014 passed (was 3966 on main +
concurrent CI work). Zero regressions. ruff + mypy clean.
@bokelley bokelley merged commit 3072c6e into main May 4, 2026
16 checks passed
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