Skip to content

feat: collapsed verifyWalletSignerMatch + per-adapter quota/fail-open helpers - #4

Merged
vvillait88 merged 10 commits into
mainfrom
monetization-branding-rollout
May 2, 2026
Merged

vvillait88 merged 10 commits into
mainfrom
monetization-branding-rollout

Conversation

@vvillait88

@vvillait88 vvillait88 commented May 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Identity / signer matching:

  • verifyWalletSignerMatch collapses the prior 3-call gate fan-out into a single /v1/assess call carrying resolve_signer; API emits a signer_match verdict in the same response
  • Per-(claimed, signer) cache so repeat lookups skip the API
  • Fallback to the legacy 2-resolve path when the API response omits signer_match (canary rollout safety)

Fail-open + quota helpers across the 5 framework adapters (hono, express, fastify, nextjs, web):

  • failOpen: true flag on agentscoreGate(...); 429 / 5xx / network-timeout pass through to the handler with degraded=true + infraReason on gate state. Compliance denials still deny.
  • getGateDegradedState(request) reads {degraded, infraReason?} from framework state
  • getGateQuotaInfo(request) returns the assess quota envelope captured during evaluate
  • Dedicated 429 path with quota-specific recovery instructions

Brand + disclosure scrubs:

  • "AgentScore Commerce" brand applied to examples README
  • Disclosure cleanup on quota / 429 paths

Deps:

  • @agent-score/sdk 2.1.1 → 2.2.0 (typed assess errors + X-Quota capture + ResolveSigner/SignerMatch types just published)
  • mppx 0.6.8 → 0.6.13 (5 wevm security patches: Stripe proxy header strip, SSE multiline forge prevention, Tempo escrow override hardening, challenge-bound memo validation, post-force-close charge blocking)

Version: 1.0.3 → 1.1.0 (additive minor).

,.

Test plan

  • Unit tests pass locally (bun run test — 562 passing, 4 skipped, 47 files)
  • Coverage above tier-A thresholds (95/90/95/95)
  • Lefthook pre-push (typecheck) passes
  • CI green on this PR

🤖 Generated with Claude Code

vvillait88 and others added 10 commits May 1, 2026 03:58
Per `core/internal_docs/branding.md` — first-mention bare "commerce SDK" /
"Commerce" replaced with "AgentScore Commerce" in the examples README intro.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…en paths

Closes TEC-265 (Node side). Opt-in (failOpen=false default) is preserved.

When failOpen=true and AgentScore-side infra fails (429/5xx/network timeout):
- Gate result carries `degraded: true` + `infraReason: 'quota_exceeded' |
  'api_error' | 'network_timeout'` so merchants can log/alert without parsing
  console output. Compliance denials (sanctions, age, jurisdiction, etc.) are
  unaffected — those still deny regardless of failOpen.

### Core (`src/core.ts`)
- `EvaluateOutcome.allow` extended with optional `degraded` + `infraReason`
- New 429 branch on `/v1/assess` response (was collapsed into generic api_error)
- Catch path distinguishes `TimeoutError` / `AbortError` (`network_timeout`) from
  generic `Error` (`api_error`)
- All 3 short-circuit sites updated; non-infra short-circuits (empty identity,
  402) keep the existing plain-allow shape — degraded only marks real infra
  failures

### Adapters (5)
- hono.ts, express.ts, fastify.ts: extend GateState with degraded + infraReason;
  new `getGateDegradedState()` helper for merchants to read the flag
- web.ts: degraded + infraReason added to `GuardResult.allowed=true` variant
- nextjs.ts: passes degraded + infraReason through to handler's gate object

### Agent-facing copy (`_response.ts`)
- New `API_ERROR_INSTRUCTIONS` block with `action: 'retry_with_backoff'` + steps
  + user_message so agents distinguish transient AgentScore-side issues from
  compliance denials. Also added to `DEFAULT_AGENT_INSTRUCTIONS` map. Existing
  `next_steps: { action: 'retry', retry_after_seconds: 5 }` shape preserved for
  back-compat.

All 540 existing tests pass; typecheck + lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nfraReason

End-to-end audit found that `withAgentScoreGate` (in web.ts, used by Cloudflare
Workers / Bun / Deno consumers) destructured the GuardResult into
`{data, captureWallet, verifyWalletSignerMatch}` and dropped `degraded` /
`infraReason` — vendors using `withAgentScoreGate` could not read the degraded
flag from the handler. The Next.js-flavored variant (`nextjs.ts`) was correct.

Fix: spread `degraded` + `infraReason` into the gate object passed to the
handler when present. Type updated to declare the optional fields on the
handler's gate parameter.

Pairs with the Python audit-follow-up (TEC-265 closeout commit in
python-commerce: get_gate_degraded_state helper added to all 6 Python
adapters, dead AssessResult fields dropped, QuotaExceededError subclasses
RuntimeError for back-compat).

540 tests pass; typecheck + lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…losure cleanup

- 429 fail-closed denials now carry contact_merchant agent_instructions (do-not-retry guidance) instead of generic retry_with_backoff that would loop forever
- 429 fail-open path returns degraded=true + infraReason='quota_exceeded' on EvaluateOutcome.allow
- Per-adapter regression tests for degraded propagation across hono/express/fastify/nextjs/web (+8 tests)
- README adds Fail-open behavior section with the get/with helpers
- Strip metering/pricing language from agent_instructions, source comments, and HTTP response bodies (public-package strict surface)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e description

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
verifyWalletSignerMatch now makes a single sdk.assess call with
resolveSigner set; the API's signer_match block is projected onto the
gate's VerifyWalletSignerResult. Repeat (claimed, signer) lookups read a
per-cache-entry signerMatchBySigner sub-map and skip both the round trip
and SDK-side telemetry post (the API records the verdict server-side).
Falls back to the legacy 2-resolve path when the response has no
signer_match. Bumps sdk peer dep to ^2.2.0.

getGateQuotaInfo(c) per-adapter helper exposes the SDK's quota field
(captured from X-Quota-* response headers on the success path) on every
framework adapter — express, hono, fastify, nextjs, web. EvaluateOutcome
gains an optional quota field; gate state stashes it during the allow
path. Mirrors the existing getGateDegradedState pattern.

Bumps to 1.1.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pulls in just-published @agent-score/sdk@2.2.0 (typed assess errors +
X-Quota header capture + ResolveSigner/SignerMatch types) so commerce
gates can consume the new typed error surface and signer_match response
field.

Picks up 5 mppx security patches (Stripe proxy header strip, SSE
multiline forge prevention, Tempo escrow override hardening,
challenge-bound memo validation, post-force-close charge blocking).
Coverage delta vs main:
- Statements: 95.07% → 96.54% (above 95% threshold)
- Functions:  94.73% → 98.02% (above 95% threshold)
- Lines:      95.82% → 97.52% (above 95% threshold)
- Branches:   88.72% → 89.36% (still 0.64% short of 90%)

Added focused tests for read helpers (getAgentScoreData,
getGateQuotaInfo) across express + fastify + hono. Mirror the
test_gate_quota_info.py pattern from python-commerce: prime the
framework state container directly, verify the helper returns it.

Also added passthrough-import tests for src/payment/signer.ts and
src/api/index.ts re-export shims (previously 0% coverage because
nothing imported through them).

v8 ignore'd peer-dep absence guards in:
- src/stripe-multichain/mppx_stripe.ts
- src/payment/mppx_server.ts (mppx + tempo.charge + tempo.session guards)
- src/payment/x402_server.ts (@x402/core + @coinbase/x402 + @x402/evm
  exact + @x402/evm upto + @x402/svm + @x402/extensions guards)

Per the coverage-standards rule, peer-dep factory bodies that can't run
without a peer dep the test env doesn't bundle are excluded with
focused inline comments + a reason.
… ignore

Adds the missing branch-coverage path on buildWellKnownMpp by exercising
every optional field at once (description, openapi, catalog,
required_fields, optional_fields, purchase.extra, purchase.identity,
shipping). Pushes branch coverage from 89.36% → 90.01% — barely above
the tier-A 90% threshold but enough to clear CI.

Also v8-ignore'd the @x402/extensions/bazaar peer-dep-absence guards
in src/discovery/bazaar.ts (mirrors the same treatment applied to
mppx_server / mppx_stripe / x402_server in the prior commit).
@vvillait88
vvillait88 merged commit 797a39e into main May 2, 2026
6 checks passed
@vvillait88
vvillait88 deleted the monetization-branding-rollout branch May 2, 2026 05:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant