Support desk
A sample app in the repository: apps/support-desk. It consumes jevlish from npm like any other application would.
cd apps/support-desk && npm install
npm start # run the pipeline, print the desk reportnpm run plan # what would be sent, without spending anythingnpm run trace # run, then dump every judgment behind each decisionnpm run measure # test the vocabulary against labeled fixturesThe API key is read from the repo-root .env (TYPESAFE_API_KEY).
What it does
Section titled “What it does”Four stages, each one expression:
- Escalation policy:
given(ticket).when(needsAttention).do(escalate).otherwise(leaveUnchanged).whenUncertain(flagForReview) - Priority queue:
from(tickets).where(isOpen).and(reportsProblem).rankedBy(disruption).take(5) - Incident linking:
given({ ticket }).chooseFrom(incidents).by("the incident that explains…").orNone(…) - Owner suggestion:
given({ ticket }).chooseFrom(onCall).by("whose experience best matches…").orNone(…)
The vocabulary
Section titled “The vocabulary”src/vocabulary.ts is the domain language. The samples on this site use the same one:
import { means, scale } from "jevlish";import type { Ticket } from "./support.js";
// A domain vocabulary. These are meanings, not prompts: named, composable,// inspectable, and graded against fixtures like any other function.
export const blocked = means<Ticket>("the customer is currently unable to complete their task") .including("a product failure prevents them from completing the task") .excluding("they can complete the task despite inconvenience, or they are only asking a question") .named("blocked");
export const saysResolved = means<Ticket>("the customer says the problem has been resolved").named("saysResolved");
export const threatensToLeave = means<Ticket>("the customer says they will cancel or switch providers").named( "threatensToLeave",);
export const isOpen = (t: Ticket) => t.status === "open";
export const needsAttention = blocked.unless(saysResolved).or(threatensToLeave).and(isOpen);
export const reportsProblem = means<Ticket>("the message reports a problem with the product") .including("something in the product is broken, failing, or behaving wrongly") .excluding("a how-to question, a feature request, or praise") .named("reportsProblem");
export const disruption = scale<Ticket>("how much the reported problem disrupts the customer's work") .from("Work continues normally; the problem affects appearance only") .through("The task remains possible through a workaround") .to("The task cannot be completed") .named("disruption");src/fixtures.ts holds labeled examples for blocked; npm run measure reports accuracy, coverage, and abstentions.
Things to notice
Section titled “Things to notice”- Closed tickets never reach the model. The code predicate under
.and()folds the whole expression tofalsebefore anything is sent.npm run planshows them as settled by code. needsAttentionisblocked.unless(saysResolved).or(threatensToLeave).and(isOpen). The shape matters: resolution qualifies only theblockedbranch, so a churn threat escalates regardless.- “Search is slow but works” tends to land between the thresholds. It goes to review, never silently to
otherwise. - Every
chooseFromreturns one of your own objects, ornullfororNone, oruncertain. Three different things, kept apart.