Use cases / Operations and compliance

Ticket triage and routing

Category, priority, route. Every ticket, every day, with an incident match.

Unit
One incoming Pylon ticket
Ask
Category? Priority? Engineering? Sensitive data? Then: which open incident?
Get
Every ticket routed. 118 matched to an incident.

Pylon · Files · 2 reducers · 6 questions · updated Sep 21, 2026

The flow

0 tickets source yesterday, snapshot 09:00
0 Triage reducer requests, 4 questions each
0 Needs engineering ≥ 0.70 filter p(needs_engineering)
0 Attach open incidents join 3 open at 09:00
0 Match incidents reducer requests, 2 questions each
0 Outputs output every ticket routed

Cheap pass for every ticket. Incident pass only where engineering is needed. 758 requests for 640 tickets. Routing is a table your code applies.

Follow one record

What comes out

Queue triagerun 8a91f3e0, 640 rows, sorted by priority
TicketCategoryPriorityEngineeringIncidentRoute
T-2291account accessP1 2.020.91INC-0244Identity on-call
T-2288bugP1 1.940.84INC-0231Reports team
T-2294billingP2 1.210.06noneBilling queue
T-2290bugP1 1.880.79nonePlatform on-call
T-2287how toP3 0.310.03noneSupport tier 1
T-2293feature requestP3 0.180.11noneProduct inbox

Synthetic rows in the app's Outputs layout. Priority is the expected position on your P3 to P0 scale; the distribution behind it is one click away.

Details, if you want them

whyWhy this shape

Why this shape

  • Four decisions per ticket, one request. Category, priority, engineering, sensitive data.
  • Your priority scale. P3 to P0, in your words, inside the state.
  • Incidents are a snapshot. Matched against what was open at 09:00. The trace shows which.
  • Routing in your code. Decision traced here, action audited there.
questionsThe 6 questions, verbatim
  • Triage categorycategorical, choice

    Which category best fits `ticket.text`?

    billingbughow tofeature requestaccount accessother
  • Triage priorityordinal, score

    Using the definitions in `policy.priority`, how urgent is `ticket.text`?

    P3 cosmetic or questionP2 workaround existsP1 customer blockedP0 outage or data loss
  • Triage needs_engineeringbinary, noul

    Does resolving `ticket.text` require a code or infrastructure change rather than a support action?

  • Triage sensitive_databinary, noul

    Does `ticket.text` contain a payment card number, a government identity number, or a password?

  • Match incidents incident_matchcategorical, choice

    Is the problem in `ticket.text` the same problem as one of the open incidents in `incidents`?

    INC-0244INC-0231INC-0250none
  • Match incidents blockingbinary, noul

    Is the customer described in `ticket.text` unable to work right now because of this problem?

buildHow each stage is built, as JSON
1 reducer Triage
One request per ticket, four questions. priority is an ordinal over your own P3 to P0 definitions, so the expected value is a position on that scale. sensitive_data is a probability, not a scanner; pair it with a deterministic pattern check in code.

stage definition

{
  "id": "triage",
  "input": "source:tickets",
  "kind": "evaluate",
  "model": "jev",
  "name": "Triage",
  "questions": {
    "category": {
      "criteria": {
        "account_access": "Login, SSO, permissions, lockouts",
        "billing": "Charges, invoices, refunds",
        "bug": "Something that used to work does not",
        "feature_request": "The customer wants a capability that does not exist",
        "how_to": "The customer needs to know how to do something",
        "other": "None of the above"
      },
      "instructions": "Which category best fits `ticket.text`?",
      "type": "choice"
    },
    "needs_engineering": {
      "instructions": "Does resolving `ticket.text` require a code or infrastructure change rather than a support action?",
      "type": "noul"
    },
    "priority": {
      "criteria": [
        "P3 cosmetic or question",
        "P2 workaround exists",
        "P1 customer blocked",
        "P0 outage or data loss"
      ],
      "instructions": "Using the definitions in `policy.priority`, how urgent is `ticket.text`?",
      "type": "score"
    },
    "sensitive_data": {
      "instructions": "Does `ticket.text` contain a payment card number, a government identity number, or a password?",
      "type": "noul"
    }
  },
  "state_path": []
}
2 filter Needs engineering ≥ 0.70
A comparison on the stored probability. Tickets below the line are routed by category alone and skip the incident pass; they keep their triage answers.

stage definition

{
  "comparison": "gte",
  "field": [
    "answers",
    "needs_engineering",
    "probability"
  ],
  "id": "engineering",
  "input": "triage",
  "kind": "filter",
  "name": "Needs engineering ≥ 0.70",
  "value": 0.7
}
3 join Attach open incidents
A join that attaches the current open-incident list to every engineering ticket. The list is a snapshot taken at 09:00; the run pins it, so the trace shows which incidents were open when the match was judged.

stage definition

{
  "id": "with_incidents",
  "input": "engineering",
  "kind": "join",
  "left_key": [
    "tenant"
  ],
  "name": "Attach open incidents",
  "right": "source:incidents",
  "right_key": [
    "tenant"
  ]
}
4 reducer Match incidents
The second reducer needs the incident list, which is new context, so it is a second stage. incident_match chooses among the open incidents or none; blocking asks whether the customer is stopped right now. The options here are the incidents open at 09:00. When incidents open or close, a script saves a new version of this stage.

stage definition

{
  "id": "match",
  "input": "with_incidents",
  "kind": "evaluate",
  "model": "jev",
  "name": "Match incidents",
  "questions": {
    "blocking": {
      "instructions": "Is the customer described in `ticket.text` unable to work right now because of this problem?",
      "type": "noul"
    },
    "incident_match": {
      "criteria": {
        "INC-0231": "Report exports slower than 60 seconds",
        "INC-0244": "SSO certificate rotation failed for EU tenants",
        "INC-0250": "Mobile push notifications delayed",
        "none": "Not one of the open incidents"
      },
      "instructions": "Is the problem in `ticket.text` the same problem as one of the open incidents in `incidents`?",
      "type": "choice"
    }
  },
  "state_path": []
}
5 definition The whole workflow, as saved

Saving publishes an immutable version. A run freezes this definition, the snapshot IDs and each reducer’s model profile, so the trace can always show exactly what ran. Edits create a new version; old runs keep theirs.

jevreduce.local.v1

{
  "name": "Ticket triage and routing",
  "schema_version": "jevreduce.local.v1",
  "sources": {
    "incidents": "snap_incidents_2026-09-20T09",
    "routing": "snap_routing_v7",
    "tickets": "snap_pylon_queue_2026-09-20T09"
  },
  "stages": [
    {
      "id": "triage",
      "input": "source:tickets",
      "kind": "evaluate",
      "model": "jev",
      "name": "Triage",
      "questions": {
        "category": {
          "criteria": {
            "account_access": "Login, SSO, permissions, lockouts",
            "billing": "Charges, invoices, refunds",
            "bug": "Something that used to work does not",
            "feature_request": "The customer wants a capability that does not exist",
            "how_to": "The customer needs to know how to do something",
            "other": "None of the above"
          },
          "instructions": "Which category best fits `ticket.text`?",
          "type": "choice"
        },
        "needs_engineering": {
          "instructions": "Does resolving `ticket.text` require a code or infrastructure change rather than a support action?",
          "type": "noul"
        },
        "priority": {
          "criteria": [
            "P3 cosmetic or question",
            "P2 workaround exists",
            "P1 customer blocked",
            "P0 outage or data loss"
          ],
          "instructions": "Using the definitions in `policy.priority`, how urgent is `ticket.text`?",
          "type": "score"
        },
        "sensitive_data": {
          "instructions": "Does `ticket.text` contain a payment card number, a government identity number, or a password?",
          "type": "noul"
        }
      },
      "state_path": []
    },
    {
      "comparison": "gte",
      "field": [
        "answers",
        "needs_engineering",
        "probability"
      ],
      "id": "engineering",
      "input": "triage",
      "kind": "filter",
      "name": "Needs engineering ≥ 0.70",
      "value": 0.7
    },
    {
      "id": "with_incidents",
      "input": "engineering",
      "kind": "join",
      "left_key": [
        "tenant"
      ],
      "name": "Attach open incidents",
      "right": "source:incidents",
      "right_key": [
        "tenant"
      ]
    },
    {
      "id": "match",
      "input": "with_incidents",
      "kind": "evaluate",
      "model": "jev",
      "name": "Match incidents",
      "questions": {
        "blocking": {
          "instructions": "Is the customer described in `ticket.text` unable to work right now because of this problem?",
          "type": "noul"
        },
        "incident_match": {
          "criteria": {
            "INC-0231": "Report exports slower than 60 seconds",
            "INC-0244": "SSO certificate rotation failed for EU tenants",
            "INC-0250": "Mobile push notifications delayed",
            "none": "Not one of the open incidents"
          },
          "instructions": "Is the problem in `ticket.text` the same problem as one of the open incidents in `incidents`?",
          "type": "choice"
        }
      },
      "state_path": []
    }
  ]
}
limitsWhat it will not do
  • Outputs, not actions. Your code assigns in Pylon.
  • Incident options are a saved version. New incident, new version.
  • Sensitive-data is a probability. Pair it with a pattern check.
  • No deduplication. Same outage, two tickets.
  • Text only. Screenshots need extraction.
relatedRelated pages

Try it on your own records.