{
  "rule.greeting": {
    "type": "regex",
    "value": "^(hi|hello|hey|thanks|thank you|thx|ok|okay|cool|great|got it|good (morning|afternoon|evening))(\\s+\\w+)?[\\s!.,?]*$",
    "flags": "i",
    "description": "A message that is only a greeting or thanks (goes to the model, no special handling)."
  },
  "rule.sqlSyntax": {
    "type": "regex",
    "value": "\\bselect\\s|\\bgroup\\s+by\\b|\\bsum\\s*\\(|\\bjoin\\s",
    "flags": "i",
    "description": "The person is writing SQL themselves. Sent to the model untouched."
  },
  "rule.marketData": {
    "type": "regexList",
    "value": [
      "\\b(stock|share|market|current|live|latest|today'?s|closing|opening)\\s+(price|quote)s?\\b",
      "\\bprices?\\s+(of|for)\\b",
      "\\b(exchange|fx|currency)\\s+rates?\\b",
      "\\b(s&p|s and p|nasdaq|dow jones|ftse|nikkei|nifty|sensex|dax|hang seng|vix)\\b",
      "\\b(news|headlines|market (outlook|forecast|update|today)|earnings (report|date))\\b",
      "\\b(bitcoin|btc|ethereum|dogecoin)\\b"
    ],
    "flags": "i",
    "description": "Questions that need live market data (prices, indices, FX, news, crypto). Any pattern matching sends the fixed 'no market data' reply, unless the question is about their own holdings."
  },
  "rule.portfolioReference": {
    "type": "regex",
    "value": "\\b(my|our|mine|portfolio|holdings?|positions?)\\b",
    "flags": "i",
    "description": "Words showing the question is about the client's own holdings, so 'price' is not a request for a quote."
  },
  "rule.rankWord": {
    "type": "regex",
    "value": "(?:top|bottom|biggest|largest|smallest|highest|lowest|best|worst)",
    "flags": "i",
    "description": "Words meaning 'the most/least', as a pattern fragment. Used to build the ranking and count patterns."
  },
  "rule.rankNoun": {
    "type": "regex",
    "value": "(?:positions?|holdings?|stocks?|assets?|investments?|securities|gainers?|losers?|performers?|performing|bonds?|funds?|equit(?:y|ies)|deposits?)",
    "flags": "i",
    "description": "Things that can be ranked (positions, bonds, gainers, ...), as a pattern fragment. Used to build the ranking and count patterns."
  },
  "rule.rankingWords": {
    "type": "regex",
    "value": "\\b(gainers|losers|winners|underperformers|outperformers)\\b",
    "flags": "i",
    "description": "Words that on their own mean a ranking ('gainers', 'losers')."
  },
  "rule.pnl": {
    "type": "regex",
    "value": "\\b(gains?|gainers?|loss|losses|losers?|winners?|p\\s*&\\s*l|pnl|profits?|performers?|performing|returns?|unreali[sz]ed)\\b",
    "flags": "i",
    "description": "A ranking by profit and loss."
  },
  "rule.cost": {
    "type": "regex",
    "value": "\\b(cost|purchase|invested)\\b",
    "flags": "i",
    "description": "A ranking by cost."
  },
  "rule.ascending": {
    "type": "regex",
    "value": "\\b(worst|losers?|losses?|lowest|smallest|least|bottom|underperform\\w*)\\b",
    "flags": "i",
    "description": "A ranking from the bottom (worst, smallest) rather than the top."
  },
  "rule.byBank": {
    "type": "regex",
    "value": "\\b(?:by|per|across|each|every)\\s+(?:the\\s+)?banks?\\b|\\bbank[\\s-]?(?:wise|breakdown|split)\\b|\\bwhich bank\\b",
    "flags": "i",
    "description": "A request to split values by bank."
  },
  "rule.listNoun": {
    "type": "regex",
    "value": "\\b(positions?|holdings?|investments?|assets?|securities)\\b",
    "flags": "i",
    "description": "Nouns meaning 'the positions themselves'."
  },
  "rule.listVerb": {
    "type": "regex",
    "value": "\\b(show|list|display|give|which|what|see)\\b",
    "flags": "i",
    "description": "Verbs asking to see a list."
  },
  "rule.howMuch": {
    "type": "regex",
    "value": "\\b(how much|total|worth|value)\\b",
    "flags": "i",
    "description": "'How much / total / worth' questions."
  },
  "rule.chart": {
    "type": "regex",
    "value": "\\b(sankey|diagram|chart|graph|plot|visuali[sz]e|visual|flow)\\b",
    "flags": "i",
    "description": "A request for a chart or diagram."
  },
  "rule.advice": {
    "type": "regex",
    "value": "\\b(advice|advise|recommend\\w*|should i|risks?|risky|diversif\\w*|rebalanc\\w*|how am i doing|strategy|opinion)\\b",
    "flags": "i",
    "description": "A request for an opinion on risk, diversification or strategy."
  },
  "rule.overview": {
    "type": "regex",
    "value": "\\b(total|net worth|worth|how much|portfolio value|allocation|breakdown|broken down|asset (?:class|type)e?s?|exposure|concentration|summary|overview|snapshot)\\b",
    "flags": "i",
    "description": "A request for allocation or a general summary."
  },
  "rule.total": {
    "type": "regex",
    "value": "\\b(aum|assets under management|net worth|portfolio (?:value|worth)|total (?:portfolio )?(?:value|worth|assets|holdings)|(?:worth|value) of (?:my|the|our) (?:whole |entire )?portfolio|portfolio is worth)\\b",
    "flags": "i",
    "description": "The headline figure: AUM, net worth, total portfolio value."
  },
  "rule.countLeadWord": {
    "type": "regex",
    "value": "(?:top|bottom|first|best|worst|biggest|largest|smallest|highest|lowest)",
    "flags": "i",
    "description": "Words that precede a number of rows ('top 5'), as a pattern fragment."
  },
  "rule.numberWords": {
    "type": "numberMap",
    "value": {
      "one": 1,
      "two": 2,
      "three": 3,
      "four": 4,
      "five": 5,
      "six": 6,
      "seven": 7,
      "eight": 8,
      "nine": 9,
      "ten": 10,
      "fifteen": 15,
      "twenty": 20,
      "fifty": 50
    },
    "description": "Number words understood as a row count ('top five')."
  },
  "rule.customerWord": {
    "type": "regex",
    "value": "\\b(customer|client|account|switch|change)\\b",
    "flags": "i",
    "description": "Words showing the person means to choose or change the customer. Once a customer is chosen, a name in a question only switches customers when one of these is present."
  },
  "rule.customerFiller": {
    "type": "regex",
    "value": "^(customer|client|account|cust|id|number|no|nr|show|me|my|the|portfolio|of|for|switch|change|to|use|look|at|about|please|hi|hello|hey|thanks|thank|you|view|open|select|pick|choose|name|is|it|s|its|his|her|their|i|want|would|like|see|can|could|let|us|lets|now|and|a|an|check|get)$",
    "flags": "i",
    "description": "Single words that add nothing to a customer's name. A message left with only these once the name is removed is just choosing a customer (so the answer is their headline figures), not asking a question."
  }
}
