{
  "rulesetId": "rs-eligibility-tourist",
  "name": "Tourist visa eligibility",
  "description": "Passport validity gate, watchlist screening via the Interpol API connector, then eligibility outcome.",
  "contractVersion": "app-contract@v2",
  "strategy": "evaluate-all",
  "nodes": [
    {
      "type": "start",
      "id": "start",
      "inputs": ["applicant.*", "passport.*", "trip.*"],
      "next": "n-passport-validity"
    },
    {
      "type": "condition",
      "id": "n-passport-validity",
      "expr": {
        "op": ">=",
        "left": { "fn": "days_until", "args": [{ "field": "passport.expiryDate" }] },
        "right": { "lit": 180 }
      },
      "onTrue": "n-watchlist",
      "onFalse": "o-passport-validity"
    },
    {
      "type": "outcome",
      "id": "o-passport-validity",
      "outcome": "FAIL",
      "hard": true,
      "reasonCode": "PASSPORT_VALIDITY",
      "reasonText": "Passport expires in {days_until(passport.expiryDate)} days; minimum 180 required.",
      "severity": "critical"
    },
    {
      "type": "connectorCheck",
      "id": "n-watchlist",
      "connectorRef": "conn-interpol-api@v2",
      "operation": "screen",
      "inputMap": {
        "fullName": { "field": "applicant.fullName" },
        "dob": { "field": "applicant.dob" },
        "passportNo": { "field": "passport.number" }
      },
      "outputAs": "watchlist",
      "onSuccess": "n-watchlist-hit",
      "onError": "o-connector-error",
      "onErrorBehavior": "Flag",
      "timeoutMs": 3000
    },
    {
      "type": "outcome",
      "id": "o-connector-error",
      "outcome": "FLAG",
      "reasonCode": "CONNECTOR_ERROR",
      "reasonText": "Watchlist screening unavailable; manual review required.",
      "severity": "warning"
    },
    {
      "type": "condition",
      "id": "n-watchlist-hit",
      "expr": {
        "op": "==",
        "left": { "field": "watchlist.hit" },
        "right": { "lit": true }
      },
      "onTrue": "o-watchlist-hit",
      "onFalse": "o-eligible"
    },
    {
      "type": "outcome",
      "id": "o-watchlist-hit",
      "outcome": "FAIL",
      "hard": true,
      "reasonCode": "WATCHLIST_HIT",
      "reasonText": "Watchlist match (score {watchlist.matchScore}).",
      "severity": "critical"
    },
    {
      "type": "outcome",
      "id": "o-eligible",
      "outcome": "PASS",
      "reasonCode": "ELIGIBLE",
      "reasonText": "Applicant {applicant.fullName} is eligible for a {trip.purpose} visa.",
      "severity": "info"
    }
  ]
}
