Parent: #452
Three small mechanical helpers bundled together because they all eliminate the same class of open-coding.
(a) State-machine transition helpers
from adcp.decisioning import (
MEDIA_BUY_TRANSITIONS,
assert_media_buy_transition,
CREATIVE_ASSET_TRANSITIONS,
assert_creative_transition,
)
assert_media_buy_transition(from_state="active", to_state="completed") # ok
assert_media_buy_transition(from_state="completed", to_state="active") # raises INVALID_REQUEST
MEDIA_BUY_TRANSITIONS is a dict[State, set[State]] mapping. Same shape for creatives.
(b) Typed exception classes
Subclasses of AdcpError with the correct error code + recovery semantic baked in:
PermissionDeniedError(action: str, **details)
AuthRequiredError(**details)
ServiceUnavailableError
RateLimitedError
MediaBuyNotFoundError
AccountNotFoundError
BillingNotPermittedForAgentError
ValidationError
Today adopters either raise generic AdcpError(code=...) (verbose) or hand-roll subclasses each time.
(c) ref_account_id helper
Drops 'account_id' in ref ? ref.account_id : None open-coding:
from adcp.decisioning import ref_account_id
acct_id = ref_account_id(ref) # str | None
JS source
.changeset/auto-seed-symmetric-resolution.md and recipe #14 in 6.7 migration guide.
Acceptance criteria
- All three helpers + 8 typed exceptions exported from
adcp.decisioning.
- Tests verify error codes match spec.
- Will hit
examples/v3_reference_seller/src/platform.py lines that hand-roll status maps; refactor demo deferred.
Note on credential-leak strip
Folding credential write-only-strip from the original "issue 11" plan into #4 (AccountStore.sync_governance(ctx)), since the strip happens at the AccountStore emit boundary.
Parent: #452
Three small mechanical helpers bundled together because they all eliminate the same class of open-coding.
(a) State-machine transition helpers
MEDIA_BUY_TRANSITIONSis adict[State, set[State]]mapping. Same shape for creatives.(b) Typed exception classes
Subclasses of
AdcpErrorwith the correct error code + recovery semantic baked in:PermissionDeniedError(action: str, **details)AuthRequiredError(**details)ServiceUnavailableErrorRateLimitedErrorMediaBuyNotFoundErrorAccountNotFoundErrorBillingNotPermittedForAgentErrorValidationErrorToday adopters either raise generic
AdcpError(code=...)(verbose) or hand-roll subclasses each time.(c)
ref_account_idhelperDrops
'account_id' in ref ? ref.account_id : Noneopen-coding:JS source
.changeset/auto-seed-symmetric-resolution.mdand recipe #14 in 6.7 migration guide.Acceptance criteria
adcp.decisioning.examples/v3_reference_seller/src/platform.pylines that hand-roll status maps; refactor demo deferred.Note on credential-leak strip
Folding credential write-only-strip from the original "issue 11" plan into #4 (
AccountStore.sync_governance(ctx)), since the strip happens at the AccountStore emit boundary.