Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: agentscore/python-commerce
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.5.18
Choose a base ref
...
head repository: agentscore/python-commerce
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.6.0
Choose a head ref
  • 1 commit
  • 12 files changed
  • 1 contributor

Commits on Aug 12, 2026

  1. Bump to 2.6.0: surface the pairwise operator handle through the Gate (#…

    …111)
    
    ## Summary
    
    Adds `get_operator_handle(request)` across all six identity adapters,
    plus `ctx.operator_handle` inside `Checkout` hooks. It returns the
    stable pairwise `oph_...` 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/assess` response
    the gate already fetches, so the accessor is a synchronous read exactly
    like `get_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)` in
    `identity/core.py` is what every adapter and `Checkout` calls. 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
    
    - [ ] Bug fix (no breaking change)
    - [x] New feature (no breaking change)
    - [ ] Breaking change (existing callers must update)
    - [ ] Docs, tests, or internal maintenance only
    
    ## Public API
    
    Additive only; no migration for existing callers.
    
    - `get_operator_handle(request)` exported from
    `identity.{fastapi,middleware,django,aiohttp,sanic}`; the Flask variant
    takes no argument and reads `g`, matching its sibling accessors.
    - `ctx.operator_handle` on `CheckoutContext`.
    - `project_operator_handle` exported from `identity.core`.
    
    Requires an API that returns `operator_handle` on `/v1/assess`. Against
    an older API the field is simply absent and every accessor returns
    `None`, 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 check` and `ruff format --check` clean, `ty
    check agentscore_commerce/` clean, `pytest` 1861 passed / 4 skipped with
    coverage 95.35% against the 95% floor.
    
    ## Checklist
    
    - [x] Tests cover the new behavior, and the suite passes locally
    - [x] Lint, format, and type checks pass
    - [x] Docs and README examples updated if the public surface changed
    - [x] No secrets, credentials, or personal data in the diff or the tests
    vvillait88 authored Aug 12, 2026
    Configuration menu
    Copy the full SHA
    de9811a View commit details
    Browse the repository at this point in the history
Loading