feat(decisioning): state-machine helpers, typed exceptions, ref_account_id#467
Closed
feat(decisioning): state-machine helpers, typed exceptions, ref_account_id#467
Conversation
…nt_id (#455) Adds three sets of additive helpers to adcp.decisioning (issue #455, Batch 1 item 3 of the JS 6.7 parity epic #452): (a) State-machine transition helpers - MEDIA_BUY_TRANSITIONS: dict[MediaBuyStatus, set[MediaBuyStatus]] — valid from→{to} moves per spec; terminals map to empty set - validate_media_buy_transition(from, to): raises AdcpError(INVALID_REQUEST) on disallowed transitions or unknown status strings - CREATIVE_TRANSITIONS / validate_creative_transition — same shape; includes the rejected→processing resubmit path and archived→approved unarchive path from creative-status.json (would have been missing in the original proposal) (b) Typed AdcpError subclasses in adcp.decisioning.exceptions PermissionDeniedError, AuthRequiredError, ServiceUnavailableError, RateLimitedError, MediaBuyNotFoundError, AccountNotFoundError, BillingNotPermittedForAgentError, RequestValidationError — each hard-codes the correct wire code and recovery semantics so adopters don't open-code AdcpError(code=..., recovery=...) inline. Named RequestValidationError (not ValidationError) to avoid shadowing pydantic.ValidationError. (c) ref_account_id(ref) -> str | None Extracts account_id from a wire account reference dict; eliminates the ref.get('account_id') if ref else None inline pattern. 56 new tests, all passing. Ruff and mypy clean on new files. https://claude.ai/code/session_01XbBfrSnfmVy6fQtoebgxWJ
…NOT_PERMITTED_FOR_AGENT Both codes classify as 'correctable' in the spec's enumMetadata block (schemas/cache/enums/error-code.json), not 'terminal'. The initial implementation used the wrong recovery value; this aligns with the authoritative enumMetadata classification that SDKs MUST consume. https://claude.ai/code/session_01XbBfrSnfmVy6fQtoebgxWJ
Contributor
Author
|
Superseded by #465 (feat(decisioning): state-machine helpers + typed exception classes + ref_account_id), already merged to main. Same three-part scope: transitions, typed exceptions, ref_account_id. |
Contributor
Author
|
Acknowledged — noted that this was superseded by #465 (already merged to main). No further action needed on this PR. Generated by Claude Code Generated by Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #455
Adds three sets of additive helpers to
adcp.decisioning(Batch 1 item 3 of the JS 6.7 parity epic #452). All additions are non-breaking new exports; no public symbols changed or removed.(a) State-machine transition helpers (
src/adcp/decisioning/transitions.py) —MEDIA_BUY_TRANSITIONSandCREATIVE_TRANSITIONSaredict[Status, set[Status]]maps derived from the spec state-machine diagrams.validate_media_buy_transition/validate_creative_transitionaccept raw strings or enum members and raiseAdcpError(INVALID_REQUEST, recovery="correctable")on disallowed moves or unknown status strings — eliminating the bareValueErrorleak that open-coded enum lookups produce. Creativerejected→processing(resubmit) andarchived→approved(unarchive) paths are included percreative-status.jsonenumDescriptions.(b) Typed
AdcpErrorsubclasses (src/adcp/decisioning/exceptions.py) —PermissionDeniedError,AuthRequiredError,ServiceUnavailableError,RateLimitedError,MediaBuyNotFoundError,AccountNotFoundError,BillingNotPermittedForAgentError,RequestValidationError. Each hard-codes the correct wire code and recovery value fromenumMetadatainschemas/cache/enums/error-code.json(the spec's authoritative SDK classification block). NamedRequestValidationError(notValidationError) to avoid shadowingpydantic.ValidationErrorand the existingadcp.validation.legacy.ValidationError.(c)
ref_account_id(ref) -> str | None— extractsaccount_idfrom a wire account reference dict, returningNonefor missing/non-string values. Eliminatesref.get("account_id") if ref else Noneinline patterns in multi-tenant platform methods.What was tested
pytest tests/test_decisioning_transitions_exceptions.py— 56 new tests, all passingpytest tests/test_decisioning_types.py tests/test_decisioning_dispatch.py— 57 existing tests, all passing, no regressionsruff check— clean on all new filesmypy src/adcp/decisioning/exceptions.py src/adcp/decisioning/types.py— no issuesmypy src/adcp/decisioning/transitions.py src/adcp/decisioning/types.py— no issuesPre-PR review
PERMISSION_DENIEDandBILLING_NOT_PERMITTED_FOR_AGENTarecorrectableper specenumMetadata(notterminal); both corrected before PR open.__init__.pydoesn't mention the new helpers (omitted per nit policy); (2)ref_account_idplacement intransitions.pyrather than a dedicatedhelpers.py(placement is acceptable given the module docstring already covers "account-reference utilities"; follow-up issue if it creates agent-tax); (3)validate_*functions raiseAdcpError(INVALID_REQUEST)notRequestValidationError— correct and intentional, but worth a doc note on the validate functions.Session: https://claude.ai/code/session_01XbBfrSnfmVy6fQtoebgxWJ
Generated by Claude Code