{
  "schema_version": "1.0.0",
  "bot_id": "1.8",
  "bot_name": "ComplianceGate",
  "slug": "compliancegate",
  "layer": "Risk",
  "layer_key": "risk",
  "bot_class": "Guardrail",
  "authority": [
    "Reject",
    "Reshape"
  ],
  "status": "live",
  "readiness": "General live",
  "flagship": false,
  "is_reference": false,
  "public_export": false,
  "identity": {
    "layer": "Risk",
    "bot_class": "Guardrail",
    "authority": "Reject, Reshape",
    "runs_before": "ExecutionPlan emit",
    "runs_after": "Strategy OrderIntent",
    "applies_to": "Every OrderIntent \u2014 checks the originating user wallet and target market for jurisdiction, KYC, sanctions, and market-eligibility policy",
    "default_mode": "general_live",
    "user_visible": "summary-only",
    "developer_owner": "Polytraders core \u2014 Risk pod"
  },
  "purpose": "ComplianceGate enforces Polymarket's terms-of-service access policy on every OrderIntent before it reaches execution. It validates (1) that the originating wallet has completed Polymarket onboarding, (2) that the user's jurisdiction is not on the blocked list, (3) that the wallet address does not appear on OFAC or other configured sanctions lists, and (4) that the target market is eligible for trading (not restricted by category policy). When any check fails the order is rejected outright; for geopolitical markets the bot applies NegRisk-aware category rules before passing. ComplianceGate is fail-closed: any inability to verify policy results in a hard reject.",
  "why_it_matters": [
    {
      "failure": "Sanctioned-address order submitted",
      "consequence": "Allowing a wallet on an OFAC or other sanctions list to trade exposes the operator to regulatory and legal liability, and may result in asset freezing or platform shutdown.",
      "worked_example": {
        "setup": "An OrderIntent is submitted from wallet 0xc1.. for 800 pUSD on a US-listed political market. Compliance lists were last refreshed 22 minutes ago.",
        "without_bot": "The order routes to CTFExchangeV2 and fills. 14 hours later the daily compliance feed update shows 0xc1.. was added to a sanctions list yesterday. The position must now be unwound under regulatory pressure.",
        "with_bot": "ComplianceGate reads the freshest sanctions snapshot, finds 0xc1.. on the list, returns `RiskVote{decision: REJECT, reason_code: COMPLIANCE_SANCTIONED}`. The order never reaches CTFExchangeV2; an audit record is written."
      }
    },
    {
      "failure": "Blocked-jurisdiction user bypasses geo check",
      "consequence": "Trading from a restricted jurisdiction violates Polymarket's terms and applicable law; unchecked this can lead to regulatory action against the platform."
    },
    {
      "failure": "Non-onboarded wallet places order",
      "consequence": "Polymarket's KYC/AML process has not been completed; the user has not accepted terms and the platform has no identity verification for the account."
    },
    {
      "failure": "Market category restriction not enforced",
      "consequence": "Certain market categories (e.g. restricted financial instruments, region-specific political events) may not be available to all users; ignoring category eligibility creates unequal treatment and compliance gaps."
    },
    {
      "failure": "Fail-open on policy-service outage",
      "consequence": "If compliance checks succeed silently during a data outage, ineligible users can trade undetected. ComplianceGate must fail-closed to prevent this."
    }
  ],
  "polymarket_inputs": [
    {
      "input": "Gamma API market metadata \u2014 negRisk flag and category",
      "source": "gamma",
      "required": true,
      "use": "Determine whether the market is a NegRisk category (e.g. geopolitical) and whether category-level trading restrictions apply."
    },
    {
      "input": "Polymarket onboarding status for the linked wallet",
      "source": "internal",
      "required": true,
      "use": "Confirm the wallet has completed Polymarket KYC/AML onboarding before any order is allowed."
    }
  ],
  "internal_inputs": [
    {
      "input": "User jurisdiction metadata (from account profile or IP-geolocation)",
      "source": "internal",
      "required": true,
      "use": "Check the user's detected jurisdiction against the blocked_jurisdictions list."
    },
    {
      "input": "Sanctions list snapshot (OFAC SDN + configured providers)",
      "source": "internal",
      "required": true,
      "use": "Screen the originating wallet address against current sanctions lists; hard-reject on any match."
    },
    {
      "input": "KillSwitch active flag",
      "source": "KillSwitch",
      "required": true,
      "use": "If KillSwitch is active, reject all orders immediately without running policy checks."
    },
    {
      "input": "Market eligibility override map",
      "source": "Admin UI",
      "required": false,
      "use": "Allow compliance team to whitelist or blacklist specific markets outside of automated category rules."
    }
  ],
  "raw_params": [
    "require_polymarket_onboarded \u00b7 bool",
    "blocked_jurisdictions \u00b7 list",
    "sanctions_list_source \u00b7 enum",
    "close_only_on_violation \u00b7 bool"
  ],
  "parameters": [
    {
      "name": "require_polymarket_onboarded",
      "default": true,
      "warning": null,
      "hard": true,
      "controls": "Whether the originating wallet must have completed Polymarket's onboarding flow before any order is permitted.",
      "why_default_matters": "Polymarket's terms require onboarding acceptance before trading. Disabling this check would allow unauthenticated wallets to trade, violating ToS and AML obligations.",
      "threshold_logic": [
        {
          "condition": "onboarded = true",
          "action": "APPROVE (this check)"
        },
        {
          "condition": "onboarded = false",
          "action": "REJECT \u2014 COMPLIANCE_GATE_NOT_ONBOARDED"
        }
      ],
      "dev_check": "if (require_polymarket_onboarded && !user.polymarket_onboarded) return reject('COMPLIANCE_GATE_NOT_ONBOARDED');",
      "user_facing": "Your account must complete Polymarket onboarding before placing orders."
    },
    {
      "name": "blocked_jurisdictions",
      "default": [
        "US",
        "GB",
        "IR",
        "KP",
        "SY",
        "CU"
      ],
      "warning": [
        "US",
        "GB",
        "IR",
        "KP",
        "SY",
        "CU",
        "UA"
      ],
      "hard": [
        "US",
        "GB",
        "IR",
        "KP",
        "SY",
        "CU"
      ],
      "controls": "ISO-3166 country codes that are prohibited from trading on Polymarket per platform policy. Warning threshold: list narrower than the recommended 7-country minimum triggers a WARN annotation.",
      "why_default_matters": "Polymarket does not permit trading from these jurisdictions due to regulatory restrictions. The list is reviewed by the compliance team; hardcoded minimum set cannot be removed via config.",
      "threshold_logic": [
        {
          "condition": "configured list has >= 7 entries",
          "action": "No warning \u2014 list meets minimum recommended coverage"
        },
        {
          "condition": "configured list has 4\u20136 entries",
          "action": "WARN \u2014 COMPLIANCE_GATE_JURISDICTION_LIST_NARROW: list is below recommended minimum"
        },
        {
          "condition": "user jurisdiction not in blocked_jurisdictions",
          "action": "APPROVE (this check)"
        },
        {
          "condition": "user jurisdiction in blocked_jurisdictions",
          "action": "REJECT \u2014 COMPLIANCE_GATE_JURISDICTION_BLOCKED"
        }
      ],
      "dev_check": "if (blocked_jurisdictions.includes(user.country_code)) return reject('COMPLIANCE_GATE_JURISDICTION_BLOCKED');",
      "user_facing": "Trading is not available in your region due to regulatory restrictions."
    },
    {
      "name": "sanctions_list_source",
      "default": "OFAC_SDN",
      "warning": null,
      "hard": "OFAC_SDN",
      "controls": "Which sanctions screening provider(s) to use. Options: OFAC_SDN, CHAINALYSIS, ELLIPTIC, COMBINED.",
      "why_default_matters": "OFAC SDN is the minimum legal requirement for US-linked operators. Additional providers increase coverage.",
      "threshold_logic": [
        {
          "condition": "wallet not on any screened list",
          "action": "APPROVE (this check)"
        },
        {
          "condition": "wallet matches any entry",
          "action": "REJECT \u2014 COMPLIANCE_GATE_SANCTIONS_HIT"
        }
      ],
      "dev_check": "const hit = sanctionsScreener.check(intent.wallet, params.sanctions_list_source); if (hit) return reject('COMPLIANCE_GATE_SANCTIONS_HIT');",
      "user_facing": "This wallet cannot be used for trading on this platform."
    },
    {
      "name": "close_only_on_violation",
      "default": false,
      "warning": null,
      "hard": false,
      "controls": "If true, a user who fails a non-sanctions policy check (e.g. jurisdiction change post-onboarding) may still submit close/reduce-only orders to exit existing positions rather than being fully hard-rejected.",
      "why_default_matters": "Defaulting to false (full reject) is the conservative compliance posture. Enable only with legal review, as it may be required to allow position exits in some jurisdictions.",
      "threshold_logic": [
        {
          "condition": "close_only_on_violation = false AND check fails",
          "action": "REJECT \u2014 COMPLIANCE_GATE_JURISDICTION_BLOCKED"
        },
        {
          "condition": "close_only_on_violation = true AND check fails AND order is close/reduce",
          "action": "RESHAPE \u2014 set constraints.close_only = true"
        }
      ],
      "dev_check": "if (policyFailed && params.close_only_on_violation && isCloseOrder(intent)) return reshape({ close_only: true });",
      "user_facing": "You may only close existing positions in this market."
    }
  ],
  "default_config": {
    "bot_id": "risk.compliance_gate",
    "version": "2.0.0",
    "mode": "hard_guard",
    "defaults": {
      "require_polymarket_onboarded": true,
      "blocked_jurisdictions": [
        "US",
        "GB",
        "IR",
        "KP",
        "SY",
        "CU"
      ],
      "sanctions_list_source": "OFAC_SDN",
      "close_only_on_violation": false
    },
    "locked": {
      "require_polymarket_onboarded": {
        "min": true
      },
      "sanctions_list_source": {
        "allowed": [
          "OFAC_SDN",
          "CHAINALYSIS",
          "ELLIPTIC",
          "COMBINED"
        ]
      }
    }
  },
  "implementation_flow": [
    "Receive OrderIntent from Strategy layer including market_id, wallet address, and user metadata.",
    "Check KillSwitch active flag; if active, return REJECT with KILL_SWITCH_ACTIVE immediately.",
    "Screen the originating wallet address against the configured sanctions list (OFAC SDN + additional providers). On any match, return HARD_REJECT with COMPLIANCE_GATE_SANCTIONS_HIT \u2014 this check cannot be bypassed by close_only_on_violation.",
    "Check user jurisdiction against blocked_jurisdictions. If blocked and close_only_on_violation=false, return HARD_REJECT with COMPLIANCE_GATE_JURISDICTION_BLOCKED. If blocked and close_only_on_violation=true and this is a close/reduce order, return RESHAPE with constraints.close_only=true.",
    "If require_polymarket_onboarded=true, verify the wallet's onboarding status from the internal onboarding cache. If not onboarded, return HARD_REJECT with COMPLIANCE_GATE_NOT_ONBOARDED.",
    "Fetch target market metadata from Gamma API to determine category and negRisk flag. If the market category is restricted for this user (e.g. geopolitical events in certain jurisdictions), return HARD_REJECT with COMPLIANCE_GATE_MARKET_INELIGIBLE.",
    "Check the admin-configurable market eligibility override map. If market is explicitly blacklisted, return HARD_REJECT with COMPLIANCE_GATE_MARKET_INELIGIBLE.",
    "All checks passed \u2014 return APPROVE with inputs_used list and checked_at timestamp."
  ],
  "decision_logic": {
    "approve": "Wallet is not sanctioned, jurisdiction is not blocked, user is onboarded (if required), and the target market is eligible for this user's category and jurisdiction profile.",
    "reshape_required": "User fails a non-sanctions policy check (e.g. jurisdiction change) but close_only_on_violation=true \u2014 emit constraints.close_only=true to allow position exit only.",
    "reject": "Wallet matches a sanctions list entry, user jurisdiction is in blocked_jurisdictions (with close_only disabled), user is not onboarded, market category is restricted, KillSwitch is active, or any compliance data source is unavailable (fail-closed).",
    "warning_only": "Not used \u2014 ComplianceGate has reject authority and is fail-closed. No compliance check result is advisory-only."
  },
  "decision_output_schema": "RiskVote",
  "decision_output_example": {
    "guard_id": "risk.compliance_gate",
    "decision": "HARD_REJECT",
    "severity": "HARD",
    "reason_code": "COMPLIANCE_GATE_JURISDICTION_BLOCKED",
    "message": "Wallet jurisdiction GB is on the blocked list. Order rejected.",
    "constraints": {},
    "inputs_used": [
      "internal.user.jurisdiction",
      "internal.sanctions.OFAC_SDN",
      "internal.onboarding.status",
      "gamma.market.category"
    ],
    "checked_at": "2026-05-09T10:22:00Z"
  },
  "developer_log": {
    "bot_id": "risk.compliance_gate",
    "decision": "HARD_REJECT",
    "reason_code": "COMPLIANCE_GATE_JURISDICTION_BLOCKED",
    "inputs_used": [
      "internal.user.jurisdiction",
      "internal.sanctions.OFAC_SDN"
    ],
    "metrics": {
      "wallet": "0xaabbcc...redacted",
      "jurisdiction": "GB",
      "sanctions_hit": false,
      "onboarded": true,
      "market_category": "politics",
      "negrisk": false
    },
    "checked_at": "2026-05-09T10:22:00Z"
  },
  "user_explanations": [
    {
      "situation": "Order blocked \u2014 jurisdiction restricted",
      "message": "Trading is not available in your region due to regulatory restrictions on this platform."
    },
    {
      "situation": "Order blocked \u2014 account not onboarded",
      "message": "Your account must complete the Polymarket onboarding process before placing orders. Please complete verification and try again."
    },
    {
      "situation": "Order blocked \u2014 wallet flagged",
      "message": "This wallet cannot be used for trading on this platform. Please contact support if you believe this is an error."
    },
    {
      "situation": "Order blocked \u2014 market not available in your region",
      "message": "This market is not available for trading in your region due to local regulations."
    },
    {
      "situation": "Order limited to close-only",
      "message": "You may only close or reduce existing positions in this market. New positions cannot be opened from your account."
    }
  ],
  "failure_modes": {
    "main_failure_mode": "Failing open during a compliance-service outage, allowing ineligible users or sanctioned wallets to trade undetected. ComplianceGate must never approve when its data sources are unavailable.",
    "false_positive_risk": "Incorrectly blocking a legitimate user due to a stale jurisdiction record, a cached sanctions-list false positive, or a market-eligibility misconfiguration in the admin override map.",
    "false_negative_risk": "Approving an order using a stale sanctions-list snapshot that does not reflect a recently added entry, creating a compliance gap.",
    "safe_fallback": "If any compliance data source (sanctions API, onboarding cache, Gamma market metadata) returns an error or stale data, ComplianceGate hard-rejects the order with COMPLIANCE_GATE_DATA_UNAVAILABLE. It never approves on missing policy data.",
    "required_dependencies": [
      "Sanctions screening service (OFAC SDN or configured provider)",
      "Polymarket onboarding status cache",
      "Gamma API \u2014 market category and negRisk metadata",
      "Internal user jurisdiction service",
      "KillSwitch active flag",
      "Admin market eligibility override map"
    ]
  },
  "acceptance_tests": {
    "unit": [
      {
        "test": "Approve when all checks pass",
        "setup": "wallet not sanctioned, jurisdiction=DE, onboarded=true, market category=crypto, negrisk=false",
        "expected": "APPROVE with no constraints"
      },
      {
        "test": "Reject when jurisdiction is blocked",
        "setup": "jurisdiction=US, close_only_on_violation=false",
        "expected": "HARD_REJECT with reason_code=COMPLIANCE_GATE_JURISDICTION_BLOCKED"
      },
      {
        "test": "Reshape to close-only when jurisdiction blocked and close_only_on_violation=true",
        "setup": "jurisdiction=US, close_only_on_violation=true, order_type=REDUCE",
        "expected": "RESHAPE with constraints.close_only=true"
      },
      {
        "test": "Reject when wallet is on OFAC SDN list",
        "setup": "wallet address matches OFAC SDN entry",
        "expected": "HARD_REJECT with reason_code=COMPLIANCE_GATE_SANCTIONS_HIT, regardless of close_only_on_violation"
      },
      {
        "test": "Reject when user not onboarded",
        "setup": "require_polymarket_onboarded=true, onboarded=false",
        "expected": "HARD_REJECT with reason_code=COMPLIANCE_GATE_NOT_ONBOARDED"
      },
      {
        "test": "Reject when market category restricted for user",
        "setup": "market category=geopolitical, user jurisdiction eligibility=restricted",
        "expected": "HARD_REJECT with reason_code=COMPLIANCE_GATE_MARKET_INELIGIBLE"
      },
      {
        "test": "Reject when sanctions service unavailable (fail-closed)",
        "setup": "sanctions service returns 503",
        "expected": "HARD_REJECT with reason_code=COMPLIANCE_GATE_DATA_UNAVAILABLE"
      }
    ],
    "integration": [
      {
        "test": "Sanctions list refresh picked up within TTL window",
        "expected": "HARD_REJECT(COMPLIANCE_GATE_SANCTIONS_HIT) after a wallet is added to the OFAC SDN snapshot and the cache is refreshed"
      },
      {
        "test": "NegRisk geopolitical market category check via Gamma API",
        "expected": "HARD_REJECT(COMPLIANCE_GATE_MARKET_INELIGIBLE) when Gamma returns negRisk=true and category=geopolitical for a restricted user"
      },
      {
        "test": "KillSwitch active bypasses all policy checks",
        "expected": "HARD_REJECT(KILL_SWITCH_ACTIVE) without consulting any compliance data source"
      }
    ],
    "property": [
      {
        "property": "Sanctions-hit wallets are never approved regardless of other checks",
        "required": "Always true \u2014 COMPLIANCE_GATE_SANCTIONS_HIT cannot be overridden by close_only_on_violation"
      },
      {
        "property": "Unavailable policy data never results in APPROVE",
        "required": "Always true \u2014 any data source error produces HARD_REJECT(COMPLIANCE_GATE_DATA_UNAVAILABLE)"
      },
      {
        "property": "Reshape close_only is only emitted for non-sanctions violations with close_only_on_violation=true",
        "required": "Always true \u2014 sanctions hits are always HARD_REJECT"
      }
    ]
  },
  "checklist_overrides": {},
  "legacy_goal": "Enforce geo and KYC parity with Polymarket's terms in the hosted runtime.",
  "legacy_pm_signals": [
    "Polymarket onboarding state on linked wallet"
  ],
  "legacy_external_feeds": [
    "Wallet IP / connection metadata",
    "OFAC / sanctioned-address lists"
  ],
  "reporting_groups": [
    "risk_compliance"
  ],
  "network": [
    "polygon"
  ],
  "api_surface": [
    "gamma",
    "internal"
  ],
  "version": {
    "spec": "2.0.0",
    "implementation": "2.1.0",
    "schema": "2",
    "released": "2026-04-28"
  },
  "migration_history": [
    {
      "date": "2026-04-28",
      "from": "v1",
      "to": "v2",
      "reason": "CLOB V2 cutover",
      "action_taken": "Switched to py-clob-client-v2; all notional amounts now denominated in pUSD. Removed feeRateBps references from compliance-context payloads. Updated Gamma market-metadata fetch to use V2 negRisk and enableNegRisk flags for geopolitical category classification. EIP-712 domain version updated to '2' in downstream intent inspection."
    }
  ],
  "polymarket_v2_compat": {
    "clob_version": "v2",
    "collateral": "pUSD",
    "eip712_domain_version": "2",
    "builder_code_aware": false,
    "negrisk_aware": true,
    "multichain_ready": false,
    "sdk_used": "py-clob-client-v2",
    "settlement_contract": "CTFExchangeV2",
    "notes": "ComplianceGate inspects market metadata from Gamma V2 to apply negRisk-aware category rules; geopolitical markets have zero platform fees and are flagged as negRisk, which affects eligibility checks. The bot does not sign orders or interact with CTFExchangeV2 directly."
  },
  "reference_implementation": {
    "summary": "Runs four sequential policy gates \u2014 KillSwitch, sanctions screening, jurisdiction check, and market eligibility \u2014 before emitting a RiskVote. All gates are fail-closed: any data-source error produces HARD_REJECT.",
    "language_note": "Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output. Translate to TS/Python/Go/Rust.",
    "pseudocode": "FUNCTION evaluateCompliance(intent):\n  // --- 0. KillSwitch gate ---\n  ks = FETCH internal.killswitch.status\n  IF ks.active:\n    EMIT RiskVote(decision=HARD_REJECT, reason=KILL_SWITCH_ACTIVE)\n    RETURN\n\n  // --- 1. Sanctions screening ---\n  sanctionsResult = FETCH internal.sanctions.check(intent.wallet, params.sanctions_list_source)\n  IF sanctionsResult IS NULL:\n    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_DATA_UNAVAILABLE)\n    RETURN\n  IF sanctionsResult.hit:\n    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_SANCTIONS_HIT)\n    RETURN\n\n  // --- 2. Jurisdiction check ---\n  user = FETCH internal.user.profile(intent.user_id)\n  IF user IS NULL:\n    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_DATA_UNAVAILABLE)\n    RETURN\n  IF params.blocked_jurisdictions.contains(user.country_code):\n    IF params.close_only_on_violation AND isCloseOrder(intent):\n      EMIT RiskVote(decision=RESHAPE_REQUIRED,\n                    reason=COMPLIANCE_GATE_JURISDICTION_CLOSE_ONLY,\n                    constraints={close_only: true})\n    ELSE:\n      EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_JURISDICTION_BLOCKED)\n    RETURN\n\n  // --- 3. Onboarding check ---\n  IF params.require_polymarket_onboarded:\n    onboarding = FETCH internal.onboarding.status(intent.wallet)\n    IF onboarding IS NULL:\n      EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_DATA_UNAVAILABLE)\n      RETURN\n    IF NOT onboarding.completed:\n      EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_NOT_ONBOARDED)\n      RETURN\n\n  // --- 4. Market eligibility check ---\n  market = FETCH gamma.getMarketByConditionId(intent.market_id)\n  IF market IS NULL:\n    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_DATA_UNAVAILABLE)\n    RETURN\n  override = FETCH internal.market_eligibility_overrides.get(intent.market_id)\n  IF override == 'BLOCKED':\n    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_MARKET_INELIGIBLE)\n    RETURN\n  IF isCategoryRestrictedForUser(market.category, market.negRisk, user):\n    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_MARKET_INELIGIBLE)\n    RETURN\n\n  // --- 5. All checks passed ---\n  EMIT RiskVote(decision=APPROVE,\n                inputs_used=['internal.sanctions', 'internal.user.profile',\n                             'internal.onboarding', 'gamma.market'],\n                checked_at=now_iso())\n",
    "sdk_calls": [
      "gamma.getMarketByConditionId(market_id)",
      "internal.sanctions.check(wallet, provider)",
      "internal.onboarding.status(wallet)",
      "internal.user.profile(user_id)",
      "internal.market_eligibility_overrides.get(market_id)",
      "internal.killswitch.status()"
    ],
    "complexity": "O(1) \u2014 constant number of policy checks per intent, each backed by in-memory cache with periodic refresh"
  },
  "wire_examples": {
    "input": [
      {
        "label": "OrderIntent from strategy \u2014 user in blocked jurisdiction",
        "source": "internal",
        "payload": {
          "intent_id": "int_c1d2e3f4a5b6c7d8",
          "market_id": "0x7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a",
          "side": "BUY",
          "outcome": "YES",
          "size_usd": 500,
          "price": 0.55,
          "wallet": "0xDeAdBeEf1234567890AbCdEf1234567890AbCdEf",
          "user_id": "usr_9a8b7c6d",
          "generated_at_ms": 1746780000000
        }
      },
      {
        "label": "User profile snapshot",
        "source": "internal",
        "payload": {
          "user_id": "usr_9a8b7c6d",
          "country_code": "GB",
          "polymarket_onboarded": true,
          "profile_fetched_at_ms": 1746779950000
        }
      }
    ],
    "output": [
      {
        "label": "RiskVote \u2014 HARD_REJECT (jurisdiction blocked)",
        "payload": {
          "guard_id": "risk.compliance_gate",
          "decision": "HARD_REJECT",
          "severity": "HARD",
          "reason_code": "COMPLIANCE_GATE_JURISDICTION_BLOCKED",
          "message": "Wallet jurisdiction GB is on the blocked list. Order rejected per platform compliance policy.",
          "constraints": {},
          "inputs_used": [
            "internal.sanctions.OFAC_SDN",
            "internal.user.profile",
            "internal.onboarding.status",
            "gamma.market.category"
          ],
          "checked_at": "2026-05-09T10:22:01Z"
        }
      },
      {
        "label": "RiskVote \u2014 APPROVE (all checks pass)",
        "payload": {
          "guard_id": "risk.compliance_gate",
          "decision": "APPROVE",
          "severity": "INFO",
          "reason_code": "COMPLIANCE_GATE_PASS",
          "message": "All compliance checks passed for wallet 0xAb12...Cd34.",
          "constraints": {},
          "inputs_used": [
            "internal.sanctions.OFAC_SDN",
            "internal.user.profile",
            "internal.onboarding.status",
            "gamma.market.category"
          ],
          "checked_at": "2026-05-09T10:22:01Z"
        }
      }
    ]
  },
  "reason_codes": [
    {
      "code": "KILL_SWITCH_ACTIVE",
      "severity": "HARD_REJECT",
      "meaning": "Global kill switch is active; no orders may proceed.",
      "action": "Immediately return HARD_REJECT without running any compliance check.",
      "user_message": "Trading is currently paused. Please try again later."
    },
    {
      "code": "COMPLIANCE_GATE_SANCTIONS_HIT",
      "severity": "HARD_REJECT",
      "meaning": "The originating wallet address matched an entry in the configured sanctions list (e.g. OFAC SDN).",
      "action": "Return HARD_REJECT; do not expose which list triggered the match in user-facing messaging.",
      "user_message": "This wallet cannot be used for trading on this platform."
    },
    {
      "code": "COMPLIANCE_GATE_JURISDICTION_BLOCKED",
      "severity": "HARD_REJECT",
      "meaning": "The user's detected jurisdiction is on the blocked_jurisdictions list and close_only_on_violation is false.",
      "action": "Return HARD_REJECT; log the country_code for compliance reporting.",
      "user_message": "Trading is not available in your region due to regulatory restrictions."
    },
    {
      "code": "COMPLIANCE_GATE_JURISDICTION_CLOSE_ONLY",
      "severity": "RESHAPE",
      "meaning": "The user's jurisdiction is blocked but close_only_on_violation=true and this is a close/reduce order.",
      "action": "Return RESHAPE_REQUIRED with constraints.close_only=true.",
      "user_message": "You may only close existing positions in this market from your current region."
    },
    {
      "code": "COMPLIANCE_GATE_NOT_ONBOARDED",
      "severity": "HARD_REJECT",
      "meaning": "The wallet has not completed Polymarket's required onboarding/KYC flow.",
      "action": "Return HARD_REJECT; direct user to complete onboarding.",
      "user_message": "Your account must complete Polymarket onboarding before placing orders."
    },
    {
      "code": "COMPLIANCE_GATE_MARKET_INELIGIBLE",
      "severity": "HARD_REJECT",
      "meaning": "The target market is blocked by category policy, admin override, or a NegRisk geopolitical restriction for this user.",
      "action": "Return HARD_REJECT; log market_id and category for audit.",
      "user_message": "This market is not available for trading in your region or account profile."
    },
    {
      "code": "COMPLIANCE_GATE_DATA_UNAVAILABLE",
      "severity": "HARD_REJECT",
      "meaning": "One or more compliance data sources (sanctions API, onboarding cache, Gamma market metadata, user profile) returned an error or were unreachable.",
      "action": "Return HARD_REJECT (fail-closed). Log which data source failed and alert on-call if sustained.",
      "user_message": "We could not verify your eligibility at this time. Please try again shortly."
    },
    {
      "code": "COMPLIANCE_GATE_PASS",
      "severity": "INFO",
      "meaning": "All compliance checks passed for this wallet and market.",
      "action": "Emit APPROVE and continue to next guardrail.",
      "user_message": ""
    }
  ],
  "metrics": {
    "emitted": [
      {
        "name": "polytraders_risk_compliancegate_decisions_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "decision",
          "reason_code"
        ],
        "meaning": "Total RiskVote decisions emitted by ComplianceGate, broken down by decision type and reason code. Monitors block rate per policy dimension."
      },
      {
        "name": "polytraders_risk_compliancegate_sanctions_checks_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "provider",
          "result"
        ],
        "meaning": "Number of sanctions screenings performed, broken down by provider and result (hit / clean / error)."
      },
      {
        "name": "polytraders_risk_compliancegate_jurisdiction_blocks_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "country_code"
        ],
        "meaning": "Number of hard-rejects due to blocked jurisdiction, by country code. Used for compliance reporting."
      },
      {
        "name": "polytraders_risk_compliancegate_data_source_errors_total",
        "type": "counter",
        "unit": "count",
        "labels": [
          "source"
        ],
        "meaning": "Number of fail-closed rejections caused by unavailable compliance data, by source name. Triggers alert when non-zero."
      },
      {
        "name": "polytraders_risk_compliancegate_eval_latency_ms",
        "type": "histogram",
        "unit": "milliseconds",
        "labels": [],
        "meaning": "Wall-clock time from OrderIntent receipt to RiskVote emit. P99 target < 50ms (cache-backed checks)."
      },
      {
        "name": "polytraders_risk_compliancegate_sanctions_cache_age_seconds",
        "type": "gauge",
        "unit": "seconds",
        "labels": [
          "provider"
        ],
        "meaning": "Age of the cached sanctions list snapshot. Alerts if stale beyond configured refresh interval."
      }
    ],
    "alerts": [
      {
        "name": "ComplianceGateDataSourceError",
        "condition": "rate(polytraders_risk_compliancegate_data_source_errors_total[5m]) > 0",
        "severity": "page",
        "runbook": "#runbook-compliancegate-data-source-error"
      },
      {
        "name": "ComplianceGateSanctionsCacheStale",
        "condition": "polytraders_risk_compliancegate_sanctions_cache_age_seconds > 3600",
        "severity": "page",
        "runbook": "#runbook-compliancegate-sanctions-cache"
      },
      {
        "name": "ComplianceGateHighRejectRate",
        "condition": "rate(polytraders_risk_compliancegate_decisions_total{decision='HARD_REJECT'}[5m]) / rate(polytraders_risk_compliancegate_decisions_total[5m]) > 0.2",
        "severity": "warn",
        "runbook": "#runbook-compliancegate-reject-rate"
      },
      {
        "name": "ComplianceGateHighLatency",
        "condition": "histogram_quantile(0.99, rate(polytraders_risk_compliancegate_eval_latency_ms_bucket[5m])) > 100",
        "severity": "warn",
        "runbook": "#runbook-compliancegate-latency"
      }
    ],
    "dashboards": [
      "Grafana \u2014 Risk overview / ComplianceGate",
      "Grafana \u2014 Compliance audit / jurisdiction and sanctions breakdown"
    ],
    "log_level": "info"
  },
  "state": {
    "store": "redis",
    "shape": "Sanctions list snapshot keyed by provider name; user onboarding status keyed by wallet address; market eligibility overrides keyed by market_id; all with TTL-based expiry and background refresh.",
    "ttl": "Sanctions snapshot: 3600s; onboarding status: 300s; market eligibility: 600s",
    "recovery": "On cold start, all caches are empty. First evaluation per wallet/market triggers a blocking fetch. If the fetch fails, the order is rejected fail-closed until the cache is populated.",
    "size_estimate": "~50 KB sanctions snapshot (OFAC SDN); ~1 KB per active wallet (onboarding + jurisdiction); ~200 B per market override entry"
  },
  "concurrency": {
    "execution_model": "single-threaded event loop",
    "max_in_flight": 500,
    "idempotency_key": "intent_id",
    "timeout_ms": 50,
    "backpressure": "drop newest",
    "locking": "per-wallet mutex for onboarding cache writes; sanctions list accessed read-only after refresh"
  },
  "dependencies": {
    "depends_on": [
      {
        "bot_id": "risk.kill_switch",
        "why": "Global brake \u2014 checked first before any compliance data is read.",
        "contract": "RiskVote.HARD_REJECT(KILL_SWITCH_ACTIVE) short-circuits all policy evaluation."
      }
    ],
    "emits_to": [
      {
        "bot_id": "exec.smart_router",
        "why": "Approved RiskVote passes to SmartRouter for ExecutionPlan construction.",
        "contract": "APPROVE passes through; HARD_REJECT causes SmartRouter to discard the intent without retry."
      }
    ],
    "sibling": [
      {
        "bot_id": "risk.portfolio_guard",
        "why": "Sibling guardrail; both must APPROVE or RESHAPE before SmartRouter runs."
      },
      {
        "bot_id": "risk.liquidity_guard",
        "why": "Sibling guardrail."
      }
    ],
    "external": [
      {
        "service": "Gamma API (market metadata)",
        "endpoint": "https://gamma-api.polymarket.com",
        "sla": "99.9% / 300ms p99",
        "fallback": "HARD_REJECT(COMPLIANCE_GATE_DATA_UNAVAILABLE) if Gamma is unreachable."
      },
      {
        "service": "OFAC SDN / Sanctions Screening Service",
        "endpoint": "internal sanctions cache refreshed from upstream provider",
        "sla": "99.99% (in-memory cache); upstream refresh every 1h",
        "fallback": "HARD_REJECT(COMPLIANCE_GATE_DATA_UNAVAILABLE) if cache is expired and refresh fails."
      }
    ]
  },
  "security_surfaces": {
    "signs_orders": false,
    "private_key_access": "none",
    "abuse_vectors": [
      "VPN / proxy use to circumvent jurisdiction detection",
      "Presenting a freshly created wallet not yet on sanctions lists",
      "Timing attack: submitting order during sanctions-list refresh window when cache has just expired"
    ],
    "mitigations": [
      "Jurisdiction check uses platform-side geo signal, not user-declared location",
      "Sanctions list refresh is asynchronous with overlap: old list is held until new list is fully loaded",
      "Fail-closed policy: expired or unavailable cache \u2192 HARD_REJECT, never APPROVE"
    ],
    "contract_calls": []
  },
  "failure_injection": [
    {
      "scenario": "SANCTIONS_SERVICE_DOWN",
      "how_to_inject": "Block outbound calls to sanctions provider; let cache TTL expire",
      "expected_behaviour": "All evaluations return HARD_REJECT(COMPLIANCE_GATE_DATA_UNAVAILABLE) once cache expires",
      "recovery": "Returns to normal within one cache-refresh cycle after connectivity is restored."
    },
    {
      "scenario": "JURISDICTION_BLOCKED_USER",
      "how_to_inject": "Set user.country_code = 'US' in internal user profile mock",
      "expected_behaviour": "HARD_REJECT(COMPLIANCE_GATE_JURISDICTION_BLOCKED) on every intent for that user",
      "recovery": "Immediate on next evaluation once user profile is updated to a non-blocked jurisdiction."
    },
    {
      "scenario": "SANCTIONS_HIT",
      "how_to_inject": "Add test wallet address to OFAC SDN mock list",
      "expected_behaviour": "HARD_REJECT(COMPLIANCE_GATE_SANCTIONS_HIT) regardless of close_only_on_violation setting",
      "recovery": "Returns to APPROVE once wallet is removed from the sanctions list and cache is refreshed."
    },
    {
      "scenario": "GAMMA_API_DOWN",
      "how_to_inject": "Return 503 from Gamma API mock for market metadata endpoint",
      "expected_behaviour": "HARD_REJECT(COMPLIANCE_GATE_DATA_UNAVAILABLE) for any intent requiring market category check",
      "recovery": "Returns to APPROVE once Gamma API is reachable and market metadata is cached."
    },
    {
      "scenario": "NOT_ONBOARDED_WALLET",
      "how_to_inject": "Set onboarding.completed = false in onboarding cache mock for test wallet",
      "expected_behaviour": "HARD_REJECT(COMPLIANCE_GATE_NOT_ONBOARDED)",
      "recovery": "Returns to APPROVE once onboarding.completed = true is set and cache is refreshed."
    },
    {
      "scenario": "KILL_SWITCH_ON",
      "how_to_inject": "Set internal.killswitch.status.active = true",
      "expected_behaviour": "HARD_REJECT(KILL_SWITCH_ACTIVE) on every intent without touching any compliance data source",
      "recovery": "Returns to normal pipeline on manual KillSwitch reset."
    }
  ],
  "runbook": {
    "summary": "ComplianceGate incidents typically involve a data-source outage causing fail-closed mass rejections, or a sanctions-list staleness alert. Any COMPLIANCE_GATE_SANCTIONS_HIT in production must be escalated to the compliance team immediately and never silently cleared.",
    "oncall_actions": [
      {
        "alert": "ComplianceGateDataSourceError",
        "first_action": "Identify which data source is failing via the source label on the error counter. Check network connectivity to the affected service.",
        "escalate_to": "Risk pod lead + compliance team if sustained > 2 minutes."
      },
      {
        "alert": "ComplianceGateSanctionsCacheStale",
        "first_action": "Check the sanctions refresh job logs. Manually trigger a cache refresh via 'polytraders compliance refresh-sanctions'.",
        "escalate_to": "Compliance team if cache age > 7200s."
      },
      {
        "alert": "ComplianceGateHighRejectRate",
        "first_action": "Check reason_code distribution. If dominated by COMPLIANCE_GATE_DATA_UNAVAILABLE, follow data-source runbook. If dominated by jurisdiction blocks, check if a geo-detection change was deployed.",
        "escalate_to": "Risk pod lead + compliance team."
      },
      {
        "alert": "ComplianceGateHighLatency",
        "first_action": "Check whether cache hit rates have dropped (indicating cold-start or cache eviction). Confirm Redis connectivity.",
        "escalate_to": "Infra on-call if latency > 200ms sustained."
      }
    ],
    "manual_overrides": [
      {
        "name": "force_pass",
        "how": "set config.force_verdict = pass for 60s",
        "when": "NEVER \u2014 ComplianceGate is fail-closed and must not be force-passed under any circumstance without written compliance team approval."
      },
      {
        "name": "refresh_sanctions_cache",
        "how": "polytraders compliance refresh-sanctions --provider OFAC_SDN",
        "when": "When ComplianceGateSanctionsCacheStale alert fires and automated refresh has failed."
      }
    ],
    "healthcheck": "GET /internal/health/compliancegate \u2192 200 if all data source caches (sanctions, onboarding, market eligibility) are within TTL and last successful refresh < 3600s ago; red if any cache is expired or last refresh attempt failed, or evaluation latency p99 > 100ms."
  },
  "promotion_gates": {
    "to_shadow": [
      {
        "gate": "All unit tests pass including fail-closed scenarios",
        "how_measured": "CI test run",
        "threshold": "100% pass"
      },
      {
        "gate": "Sanctions screening integration test with mock OFAC SDN list",
        "how_measured": "Integration test suite",
        "threshold": "Pass"
      }
    ],
    "to_limited_live": [
      {
        "gate": "Shadow reject rate matches expected baseline within 5% over 48h",
        "how_measured": "Grafana shadow vs live comparison dashboard",
        "threshold": "< 5% divergence"
      },
      {
        "gate": "p99 evaluation latency < 50ms (cache-backed)",
        "how_measured": "polytraders_risk_compliancegate_eval_latency_ms histogram",
        "threshold": "p99 < 50ms"
      }
    ],
    "to_general_live": [
      {
        "gate": "Zero COMPLIANCE_GATE_DATA_UNAVAILABLE rejections during normal operating hours over 7 days",
        "how_measured": "ComplianceGateDataSourceError alert history",
        "threshold": "0 firings"
      },
      {
        "gate": "Compliance team sign-off on sanctions list integration",
        "how_measured": "Written approval from compliance team",
        "threshold": "Approved"
      },
      {
        "gate": "Fail-closed injection test confirmed in staging",
        "how_measured": "Failure injection test suite",
        "threshold": "Pass"
      }
    ]
  },
  "reporting": {
    "emits_kinds": [
      "RiskVote"
    ],
    "topics": [
      "polytraders.reports.risk"
    ],
    "cadence": "every-event",
    "retention_class": "2y",
    "sampling_rule": "emit-every",
    "bus_failure_action": "fail-closed",
    "user_visible": "summary-only",
    "consumes_kinds": [
      "ObservationReport"
    ]
  },
  "capital_impact": "Critical",
  "mode_support": [
    "quarantine"
  ],
  "v3_status": {
    "phase": 4,
    "phase_name": "Core risk",
    "docs": {
      "done": 27,
      "total": 27,
      "state": "done"
    },
    "impl": {
      "done": 0,
      "total": 15,
      "state": "pending"
    },
    "runtime": {
      "done": 0,
      "total": 8,
      "state": "pending"
    },
    "overall": "pending"
  }
}