Skip to content

fix: bootstrap fixable wallet_not_trusted denials into identity_verification_required#1

Merged
vvillait88 merged 7 commits into
mainfrom
bootstrap-fixable-wallet-denials
Apr 30, 2026
Merged

fix: bootstrap fixable wallet_not_trusted denials into identity_verification_required#1
vvillait88 merged 7 commits into
mainfrom
bootstrap-fixable-wallet-denials

Conversation

@vvillait88
Copy link
Copy Markdown
Contributor

Summary

  • Gate now auto-mints a verification session when /v1/assess denies on a fixable compliance reason (kyc_required, kyc_pending, kyc_failed, jurisdiction_required), giving the agent the same poll-and-retry UX as missing_identity. Unfixable reasons (sanctions / age / jurisdiction_restricted) keep the bare wallet_not_trusted denial.
  • Canonical wallet_not_trusted agent_instructions action changes from claim_wallet_or_switch_identitycontact_support to match the new (unfixable-only) semantics.
  • Implementation lives in src/core.ts (the shared evaluate() factory used by all 5 framework adapters): extracted tryMintSessionDenial helper, wired into both cache-hit and fresh-fetch deny branches when reasons are fixable. New session-bootstrap tests in tests/hono.test.ts.
  • OpenAPI denial-code description documents the re-route + contact_support action.

Test plan

  • bun run test --exclude '**/integration.test.ts' — 539 passed
  • bun run lint clean
  • bun run typecheck clean

🤖 Generated with Claude Code

vvillait88 and others added 7 commits April 29, 2026 18:48
…ication_required

Fixable compliance reasons (kyc_required, kyc_pending, kyc_failed,
jurisdiction_required without explicit restriction) now get the same UX as
missing_identity: the gate auto-mints a verification session, the agent polls
until status=verified, gets a fresh opc_..., and retries with X-Operator-Token.
Unfixable reasons (sanctions_flagged, age_insufficient, jurisdiction_restricted)
keep the bare wallet_not_trusted denial — re-verification won't change the
outcome, so the canonical agent_instructions action is now contact_support.

Implementation lives in core.ts (the shared evaluate() factory used by all 5
framework adapters): extracted tryMintSessionDenial helper, wired it into both
the cache-hit deny branch and the fresh-fetch deny branch when reasons are
fixable. New session-bootstrap test coverage (fixable → identity_verification_
required, unfixable → bare wallet_not_trusted). _response.ts canonical
agent_instructions for wallet_not_trusted updated to contact_support copy.
OpenAPI denial-code description documents the re-route + contact_support action.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The API only emits jurisdiction_restricted AFTER KYC is verified — meaning the
user's KYC'd country is in the merchant's blocked list (or absent from the
allowed list). Re-doing KYC won't change the country, so it's permanent. Same
shape as sanctions_flagged / age_insufficient — should surface contact_support,
not bootstrap a doomed verification session.

Also flips empty/undefined reasons to return false (don't bootstrap on unknown
deny — default to bare denial). Updates the canonical wallet_not_trusted
instructions copy and core.ts adapter comments to spell out the API-side
rationale.

Tests updated: jurisdiction_restricted now in the unfixable bucket alongside
sanctions/age, empty reasons returns false.

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

The gate now re-routes fixable reasons (kyc_required/pending/failed) upstream,
so by the time wallet_not_trusted reaches the merchant's onDenied, reasons
should be unfixable. The isFixableDenial branch in the example becomes a
defensive fallback (only fires if the gate's /v1/sessions mint blipped).
Also clarify jurisdiction_restricted is in the unfixable bucket alongside
sanctions/age.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
\`sanctions_check_pending\` isn't a real API code. The real codes are
kyc_required, kyc_pending, kyc_failed, sanctions_flagged, age_insufficient,
jurisdiction_restricted. Test passes either way (gate passes reasons through
verbatim), but the fake string propagates misinformation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Node 18 EOL'd April 2025; pay already declares >=20. Match the canonical
floor across the publishable npm packages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Picks up the just-published 2.1.0 (optional Subject.address +
credential_prefix, optional CredentialListResponse.account_verification,
invalid_credential DenialCode, engines.node >=20). Tests + lint + typecheck
pass with the new version.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vvillait88 vvillait88 merged commit bbda5db into main Apr 30, 2026
6 checks passed
@vvillait88 vvillait88 deleted the bootstrap-fixable-wallet-denials branch April 30, 2026 04:29
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