Commit 68682ff
feat(decisioning): ProposalManager v1.5 core primitives — store, recipe, capabilities, lifecycle helpers (#538-impl)
Lands the foundation for v1.5 proposal lifecycle work per
docs/proposals/proposal-manager-v15-design.md (PR #538):
- proposal_store.py: ProposalStore Protocol + InMemoryProposalStore
reference impl with state-machine guards (DRAFT → COMMITTED → CONSUMED).
Includes get_by_media_buy_id reverse-index for the single-ledger
recipe-persistence design (D1, D3). create_dev_proposal_store()
factory warns on construction. is_durable=False for the in-memory
ref mirrors TaskRegistry posture.
- recipe.py: CapabilityOverlap dataclass with four typed axes
(pricing_models, targeting_dimensions, delivery_types, signal_types).
No extras dict per § D4 — adopters subclass. Recipe gains optional
capability_overlap field.
- proposal_manager.py: ProposalCapabilities adds finalize +
expires_at_grace_seconds. ProposalManager Protocol adds
finalize_proposal returning union (FinalizeProposalSuccess |
TaskHandoff[FinalizeProposalSuccess]) — single method per § D2,
matching the create_media_buy precedent. Adds typed
FinalizeProposalRequest / FinalizeProposalSuccess dataclasses.
multi_decisioning retained for v1 source-compat but framework no
longer reads it (Resolutions §6).
- proposal_lifecycle.py: framework intercept seam parallel to
refine.py / webhook_emit.py. Carries enforce_proposal_expiry (D7),
validate_capability_overlap (D4 buyer-request gate),
validate_overlap_subset_of_wire (D4 round-4 drift check),
detect_finalize_action helper, and structured log emit helpers
per § Observability (proposal.draft_persisted, proposal.finalized,
proposal.expired, proposal.consumed).
- platform_router.py: adds proposal_stores= constructor kwarg with
orphan-key validation + cross-store consistency check (D5). Hard
error at construction when finalize=True without a wired store —
the error message names the exact kwarg to add.
- context.py: RequestContext.recipes field typed as Mapping[str, Recipe]
per § D3.
- tests/test_error_code_conformance.py: PROPOSAL_NOT_FOUND added to
KNOWN_NON_SPEC_CODES (PROPOSAL_EXPIRED + PROPOSAL_NOT_COMMITTED
already in canonical 3.0 enum). Cites adcp#4043 spec issue.
Out of this commit (follow-up): full dispatch wiring for finalize
interception in get_products and create_media_buy(proposal_id=...);
unit tests; the v1.5 hello example.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent b16d18f commit 68682ff
8 files changed
Lines changed: 1430 additions & 22 deletions
File tree
- src/adcp/decisioning
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
123 | 123 | | |
124 | 124 | | |
125 | 125 | | |
| 126 | + | |
| 127 | + | |
126 | 128 | | |
127 | 129 | | |
128 | 130 | | |
129 | 131 | | |
130 | 132 | | |
131 | | - | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
132 | 141 | | |
133 | 142 | | |
134 | 143 | | |
| |||
331 | 340 | | |
332 | 341 | | |
333 | 342 | | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
334 | 351 | | |
335 | 352 | | |
336 | 353 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| 31 | + | |
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| |||
534 | 535 | | |
535 | 536 | | |
536 | 537 | | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
537 | 544 | | |
538 | 545 | | |
539 | 546 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
| 124 | + | |
124 | 125 | | |
125 | 126 | | |
126 | 127 | | |
| |||
325 | 326 | | |
326 | 327 | | |
327 | 328 | | |
| 329 | + | |
328 | 330 | | |
329 | 331 | | |
330 | 332 | | |
| |||
351 | 353 | | |
352 | 354 | | |
353 | 355 | | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
354 | 386 | | |
355 | 387 | | |
356 | 388 | | |
| |||
585 | 617 | | |
586 | 618 | | |
587 | 619 | | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
588 | 632 | | |
589 | 633 | | |
0 commit comments