Bump to 2.6.0: surface the pairwise operator handle through the Gate - #111
Merged
Conversation
Python half of the same capability as the node library, kept level with it. Merchants keying durable state on identity (prepaid balances first) need a value that outlives a credential: an opc_ lives 24h and rotates silently off a 90-day refresh, so state keyed on the token instance is stranded daily and revoking a leaked token would forfeit the balance it held. It rides the /v1/assess response the gate already fetches, so reading it is a synchronous read like get_signer_verdict, costs no second round trip on a merchant's hot path, and meters nothing extra. project_operator_handle in identity/core.py is the single derivation both the six adapters and Checkout call. Written twice it would drift, and the failure is silent: one spelling's handle misses the other's rows and a buyer quietly grows a second balance. Stashed BEFORE the allow/deny branch so it is available on denials too: this is identity rather than a verdict, and a merchant recording a denial against the buyer needs it where its handler never runs.
Minor rather than patch: this adds exported surface (get_operator_handle on every adapter, ctx.operator_handle on Checkout, project_operator_handle) rather than only changing behavior.
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.
Summary
Adds
get_operator_handle(request)across all six identity adapters, plusctx.operator_handleinsideCheckouthooks. It returns the stable pairwiseoph_...handle for the account behind the request's operator token. This is the Python half of the same capability landing in the node library, kept level with it.What it is for. Merchants keying durable state on identity, prepaid balances above all, need a value that outlives a credential. An
opc_lives 24h and rotates silently off a 90-day refresh, so state keyed on the token instance is stranded daily, and revoking a leaked token would forfeit the balance it was holding. The handle derives from the account, so rotation, expiry and revocation are free, and it is pairwise per merchant so handles never correlate across stores.It costs nothing extra. The handle rides the
/v1/assessresponse the gate already fetches, so the accessor is a synchronous read exactly likeget_signer_verdict: no second round trip on a merchant's hot path, and nothing additional metered against their quota.One derivation, not six.
project_operator_handle(raw)inidentity/core.pyis what every adapter andCheckoutcalls. Written twice it would drift, and that failure is silent: one spelling's handle simply misses the other's rows, so a buyer quietly grows a second balance while every uniqueness check still passes.Stashed before the allow/deny branch, so it is available on denials too. This is identity rather than a verdict, and a merchant recording a denial against the buyer needs it on exactly the path where its handler never runs.
Anything that is not a well-formed
oph_string reads as absent rather than being passed through, so a half-configured API can never hand a merchant a value it would write balance rows against.Type of change
Public API
Additive only; no migration for existing callers.
get_operator_handle(request)exported fromidentity.{fastapi,middleware,django,aiohttp,sanic}; the Flask variant takes no argument and readsg, matching its sibling accessors.ctx.operator_handleonCheckoutContext.project_operator_handleexported fromidentity.core.Requires an API that returns
operator_handleon/v1/assess. Against an older API the field is simply absent and every accessor returnsNone, so upgrading this package alone changes no behavior.Test plan
New
tests/test_operator_handle.py(17 cases): the projection accepts a well-formed handle and refuses every non-usable shape (absent, empty, wrong prefix, non-string,None, wallet-path response), and each of the six adapters is exercised on its own state mechanism, including Flask both inside and outside an application context.Full local gates:
ruff checkandruff format --checkclean,ty check agentscore_commerce/clean,pytest1861 passed / 4 skipped with coverage 95.35% against the 95% floor.Checklist