Support routing with hysteresis
A support ticket arrives, calms down, and is closed. Each update is decided under the same key, so the page outcome that the first update entered at 0.8 holds while urgency drifts down through the second, and releases only when it drops below 0.6 on the third. Without hysteresis a value hovering around a single threshold flips the outcome on every update.
The shape step picks the two fields the model should see and leaves the ticket id out of the state; the id is the key instead.
// Support routing with hysteresis: one ticket, three updates, a threshold that does not flap.// The same key across calls lets "page" hold while urgency dips below the entry threshold.
import { choice, huncho, jev, noul } from "huncho";
type Ticket = { id: string; subject: string; body: string };
const route = huncho("support.route", { model: jev() }) .shape((ticket: Ticket) => ({ subject: ticket.subject, body: ticket.body })) .ask({ urgent: noul("Does this need a human within the hour?"), topic: choice("What is it about?", ["billing", "bug", "other"]), }) .when((a) => a.urgent.p, { enter: 0.8, exit: 0.6 }, "page") .when((a) => a.topic.is("billing", 0.7), "billing") .else("triage");
const updates: Ticket[] = [ { id: "T-1041", subject: "Checkout is down", body: "Every customer gets a 500 at payment since 09:00. We are losing orders right now.", }, { id: "T-1041", subject: "Checkout is down", body: "Most customers can pay again. A few still see the error when they retry.", }, { id: "T-1041", subject: "Checkout is down", body: "All clear on our side. Filing this so the incident is on record.", },];
for (const ticket of updates) { const decision = await route.decide(ticket, { key: ticket.id }); console.log( `${ticket.id} urgent=${decision.answers.urgent.p.toFixed(2)} topic=${decision.answers.topic.choice}`, `previous=${decision.previous ?? "none"} -> ${decision.outcome}`, );}Run it
Section titled “Run it”Node 22.18 or later runs the file as it is, after npm i huncho:
TYPESAFE_API_KEY=... node support-route.tsEach line prints the urgency, the topic, what this key decided last time and what it decides now:
T-1041 urgent=0.93 topic=bug previous=none -> pageT-1041 urgent=0.62 topic=bug previous=page -> pageT-1041 urgent=0.51 topic=bug previous=page -> triageThe second update is below enter and above exit, so page holds. The third is below exit, so the hold ends and the clauses are checked afresh: the topic is bug, not billing, so else gives triage.
Read more
Section titled “Read more”Policy has the hysteresis table and the rule for how a held outcome interacts with earlier clauses. Questions and answers covers noul, choice and the typed answers the clauses read.