> ## Documentation Index
> Fetch the complete documentation index at: https://levers9.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Decide playground

> Pick a use case on Lab → Decide and play with the deployed decision model: a call's outcome, message triage, plain-words search, a QA scorecard, and the causality labs

**Lab → Decide** opens on a grid of use cases. Each card asks the [Decision API](https://levers9.mintlify.site/lab/decision-api.md) something
different, with the model that is deployed right now, and shows the answers in its own small animation. Pick a
card and the view below it changes; the address keeps the pick as a hash, so a use case can be shared as a link:

| card | link | what it asks |
| - | - | - |
| Sales call coach | `/lab/decide#sales-call` (also no hash) | where a live call is heading, as you type it ([Decision API](https://levers9.mintlify.site/lab/decision-api.md#the-decide-page)) |
| Post-call outcome | `/lab/decide#post-call` | how a finished call ended, and how the customer felt |
| Message triage | `/lab/decide#message-triage` | a WhatsApp message's or an email's lane, urgency and mood |
| Search the call logs | `/lab/decide#call-search` | a request in plain words, turned into filters |
| QA scorecard | `/lab/decide#qa-scorecard` | five checks a QA lead would make, each as a score question |
| Causality | `/lab/decide#causality-sales-lead`, `#causality-visit-no-show`, `#causality-rent-collection`, `#causality-support-escalation` | what would change if you acted, one lab per pack |

Back and forward move between the cards you picked. Older links to `/lab/decide` and the guided tour still land
on the sales call coach.

## The live cards

Every live card works the same way:

* **Try one**: a few synthetic inputs, in English and Saudi Arabic. Picking one asks the model at once.
* **The input** is editable. **Ask the model** (or Ctrl+Enter) sends it again.
* **What is asked** shows the request in two parts. **Your words** is the only part that changes: what you typed
  or picked, sent as the state (a call as a list of `agent:` and `customer:` turns, a message or a request as its
  text). **The questions the card always asks the model** are fixed for the card: each question in its own words
  and its options as a list. **Raw request** shows both together, the exact JSON body sent to
  `POST /api/rl/decide` (`{state, questions}`).
* **Under the hood** shows the request and the endpoint's raw answer, as on the sales call coach.

When no Decision API is running, the cards still play their input and say what would be asked; **Deploy** on the
card's bar starts one.

| card | questions | the animation |
| - | - | - |
| Post-call outcome | `outcome` (`booked_unit`, `booked_visit`, `requested_info`, `call_back`, `other`) and `sentiment` (`POSITIVE`, `NEUTRAL`, `NEGATIVE`) | the call's turns stream in, then every outcome races as a bar to its probability and the winner pops |
| Message triage | `intent` (six lanes), `urgency` (today, this week, no reply) and `sentiment` | the message flies into the lane the model picked while every lane's bar fills |
| Search the call logs | `period` (today, yesterday, this week, last week, this month, last month), `time_of_day`, `outcome` (including no answer and wrong number) and `topic` (including availability and complaints), each with an `any` option | each filter lights the words that asked for it and its chip flies out of them; below, the filters as a search would take them |
| QA scorecard | `greeting`, `discovery`, `clarity`, `courtesy` and `close`, scores with three levels | a gauge per check fills and its needle sweeps; the overall mark counts up |

The search card shows the filters only; it does not search the calls itself. The model only picks categories:
exact dates and numbers ("on the 3rd", "two days ago", "longer than 5 minutes") are a job for a value parser beside
it, not for these questions. A chip none of your words point to is marked "not in your words". The QA gauge is the expected level
(each level weighted by its probability) on a scale of 0 to 100.

## Trained or read from text

Every live card carries a pill, and so does every question under **What is asked**:

* **trained**: the served model was trained on this question, with these option keys, on a call. With a Fin
  release that is `outcome` and `sentiment` with the keys above; with a Lev it is the `sentiment` question when
  the model reports it. A base model is trained on none of them.
* **read from text (weak)**: anything else. The model reads the question and the options from their words, which
  it does far less well than a question it was trained on. Treat these answers as a demo of the shape, not as
  numbers to act on, until you have measured them on your own data under
  [Decision models](https://levers9.mintlify.site/lab/decision-models.md).

A weak card says so loudly: a banner under its title ("never learned these labels; expect confident mistakes"),
and its top answer is shown as a **guess** with a **not trained** marker instead of a verdict. A high percentage
on a weak card is not a sign the answer is right: outside what it was trained on, a model can be confidently wrong
(a viewing request read as a payment question, at 78%, is a real example). The animations still play; only the
verdict is toned down.

A question asked of a message or a search request is always read from text, even when its id matches a trained
one: a trained question was trained to read calls.

When the model's own "none of these" option beats every answer, the answer is marked as unsure on the card.

## Causality

### Causality builder

The first **Causality** card, `/lab/decide#causality-builder`, replays one lead's journey in time order through the
live model, as the system really meets a customer. There is no simulator and nothing is mocked: every label and
every decision on the screen is the served model's own answer.

A journey starts at the first call. Before the first call's transcript nothing is known about a customer, so no
story has an event from before it. From then on things arrive one at a time: a call's transcript, a note or a stage from the CRM, a booking, a
payment, a message. Pick a story (from the first call to the brochure, a visit and a deposit, retries then a
booking and after the visit, a price objection in the call, or empty) and the page draws it as a graph on a canvas: drag a node to move
it, drag the background to pan, and use the buttons in the corner to zoom or fit. The graph is always drawn at
reading size and no text in it is cut: a long journey scrolls sideways, it is never shrunk to fit.

The three rows are the order of cause, named at the left: **1 Read**, **2 Understand**, **3 Decide**, and then round
again, because what was decided is written into the history and read at the next arrival.

* **Arrival nodes** (Read). One per thing that came from outside, left to right in time and joined by the timeline,
  with its day counted from the first call. A call shows the customer's last two lines and how many turns it has.
* **Labels nodes** (Understand). Under an arrival, what our model labelled or read at that moment, from everything known by
  then. The first one lists every answer (the stage now, the interest and its trend, the open objection, what the
  sales note says, whether an offer would succeed); a later one lists only the answers that changed, with what
  they were.
* **Decision nodes** (Decide). The move, with two bars: the chance with it and the chance if we do nothing. The gap is what
  the move adds. When the best move is to do nothing there is one bar.

**A node appears only where something changed.** A labels node is drawn where the model's reading changed, a
decision node where the decision or its chances changed; the last step always shows the decision that stands. An
arrival that changed nothing is a small node marked "changed nothing". So a decision that never moves is drawn
once, not once per arrival, and every arrow means a change: down from an arrival to the labels it changed and to
the decision, across from one reading to the next, and a coloured arrow from a decision into the next reading,
marked "written into the history, read next". The arrows of the process move in their direction.

**Each arrival is one real request**, cut at that arrival's time, so nothing that arrived later is in it. They are
asked in order, because each step reads what the one before left in the journey: the model's label of what the
customer said, written as a label event by our own model (source `Lev`, "Lev: interest Warm, confidence 0.91");
the decision as the engine's action, the ledger event the decision step returns; and one minute after it a row for
what the system then did (the offer as our own WhatsApp message, "Follow-up call placed", "Lead handed to a sales
rep for a callback"; nothing when the decision was to do nothing). All three are written the way the served
model's training journeys write them. The sentence above the graph says how the decision went.

**One line, or the complete decision.** Above the graph, "A decision is written into the journey as" has two
choices. **One line** writes "Engine chose X (considered: ...)", the name of what was done, and after it the moves
a rule shut, "; not allowed: call\_again (quiet\_hours)". **The complete
decision** writes that line and then everything behind it: what the model read, every option's chance, what the
rules shut, how the pick was made and whether it was flagged. The next step's request carries whichever was
written, as the text of the engine's event; open a node and "written down for the next step" shows it word for
word. With the complete decision chosen the journey is also replayed with the one line, and a sentence beside the
switch says how many steps answered differently and what changed at the end, or that nothing did. That is the test
of whether a fuller history helps the model you serve: a model reads the complete decision only as well as it was
trained on journeys written that way, and one trained on the one line can change its answers just because the
longer text repeats its own earlier readings. The page writes the complete decision itself when the endpoint was
deployed before the decision step returned it, so the switch works with any deployment.

**A call nobody answered is not a decision.** The system retries such a call by itself, on its own schedule: no
sales rep redials it and nothing is chosen. So an unanswered call (a call with no transcript whose line says no
answer) is drawn on the timeline as a small dashed node, "Call attempt", and the model is not asked about it:
there is nothing to read and nothing to decide. It stays in the history, so the next real arrival is read knowing
the calls were tried. A move that calls the customer again is another thing: a new follow-up call, which is
decided like any other move.

**An outcome does not end the journey.** A visit that happened, a deposit that was paid: each arrives like
anything else, is read, and a decision follows it. The story with the retries ends with the customer's visit, and
the last node is what is decided after it.

**Click any node to see its cause.** A modal opens on the step that node belongs to, in the order of cause:

* **Known before it**: everything the model was given at that moment, oldest first. Rows of ours are marked, our
  label and our decision, and the newest decision among them is marked as the previous decision read at this step.
* **1 Read**: what arrived, whole. A call shows every turn.
* **2 Understand**: every answer with every option's probability, and which answers changed with this arrival.
* **3 Decide**: every move with its chance, what a rule shut out, the chance if we do nothing, and why the pick.
* **Written into the history**: what this step wrote down, word for word, and a link to the arrival that reads it
  next.

The part you clicked is marked and the modal opens on it. **The step before** and **The step after** walk the chain
without closing it. Nothing is asked again: the modal shows what the replay already got from the model. The round
button at a node's corner opens the same modal from the keyboard.

**Replay the journey** plays the graph again one node at a time, in the order it happened: the arrival, what the
model read, what was decided, then the next arrival, with each arrow appearing when both of its ends are there.
The line beside the button says which step is playing; **Show it all** stops it. It costs no request, and with
reduced motion on it still steps, without the movement.

**Take an arrival away for the counterfactual**: the button inside it, "what if this had not arrived?", or the
same button in its modal. The journey is replayed without it: its node is struck out, the chain joins around it,
and the nodes show what the model would have read and decided instead, with what it is in the real journey. The
sentence above says what that changed at the end. **Put it back** returns. Steps before the arrival are not asked
again.

On a narrow screen the same steps are a stacked list without the canvas; each card opens the same modal.

Requests are sent with no exploration, so the same journey always gives the same answers. At most the 12 newest
arrivals are steps; older events are history every step reads.

**Why there is no percentage on most answers.** On these questions the served model answers at about 99% nearly
always: it was trained on simulated journeys whose answers follow rules, with label smoothing 0.02, so 98 to 99% is
the most it can say, and it says it. That figure means "this is my answer", not how often it is right on a real
lead, so the graph prints a percentage only on an answer the model is unsure of (under 90%). Every probability is
still under Inspect, Output. A later labels node folds the answers that did not change; click the fold to see
them all. When the served model was trained on the action
outcomes, each move's chance is the model's own (`success_if_<action>`), tagged "right on average, not per lead
yet": on held-out simulated journeys those chances are right on average and rank who an action helps only weakly.

Two closed sections hold the rest:

* **Change the story.** The timeline, one editable event per row (time or days before the decision, source, type,
  text, provenance) with a checkbox to switch an event off without deleting it, and a decision time (events at or
  after it are greyed and never sent); the new call, in English or Arabic; the questions (the fields the served
  model was trained on, each switchable, plus custom ones marked not trained); and the decision block: candidates
  with value and cost in SAR, where each chance comes from, the rules (quiet hours, max count, exclude), the
  objective, exploration and the review margin. Every edit is replayed. A story's earlier call keeps the transcript
  it came with; its line in the timeline can be edited, its turns cannot yet.
* **Every ask, with the raw requests.** While it is open, each edit asks the whole journey at once and adds a card:
  what changed since the previous ask, every answer with all its probabilities and how each moved, the ranked
  candidates with chance, uplift, score and the rule that excluded one, the propensity, the review and the why. Pin
  a card to compare every later ask with it.

### Wired journey

The second **Causality** card, `/lab/decide#causality-wired`, is the one lab where what happens next follows the
decision. The other labs replay a customer whose side was written beforehand: whatever is decided, the same messages
come. Here a simulated lead is run forward from its first AI voice call, and every time something arrives that
needs a decision (a call that connected, a WhatsApp message from the customer, a sales rep's note, a visit kept or
missed) the world stops and the served model is asked. The world then carries out the move it picked: the offer
goes out and gets an answer or silence, the follow-up call is placed and picked up or not, a sales rep calls back
and writes a note, the customer reserves or goes quiet. The next decision reads all of it, our own earlier
decisions included.

* **The journey** is a timeline: each row says when, whose it is and what it says, with the row's source and type
  as the model reads them. A call opens to its transcript. Our own rows are marked: **our label** (the model's
  reading of what the customer just said), **our decision**, and **what we did** (the row the world writes when it
  carries a decision out).
* **A decision** is a card in the timeline: what set it off, what the model read in the journey (its seven answers,
  one to a line), the chance of a reservation it gave each move, the move that was made, and the line written into
  the journey for the next decision to read. A move the client's house rules shut is greyed and says why in plain
  words; the line names it with the client's own reason, "; not allowed: send\_offer (no\_discount\_before\_visit)".
  **Inspect** on the card shows the request word for word and its check against the served model's schema.
* **This client's house rules** are listed above the journey: who hands a lead to a rep, how WhatsApp reaches us,
  whether an offer can go out before a visit, how many offers and contacts a week. They differ from lead to lead.
* **Show what only the simulator knows** puts the truth beside each decision: the lead's hidden kind, what really
  held them back at that point, the true chance of every move for this lead as a second bar, which open move was
  best, what this client's old rule would have done, and under each of the model's seven answers whether it is
  what the journey really shows. At the end it counts how many picks were the best open move and how many readings
  were right. None of it is ever part of a request to the model.
* **What if we had made another move here?** Under a decision, once the journey has ended, each other open move
  is a button. It plays the same lead again with that move at that decision: everything before it is the same, and
  what follows is the world's answer to the move you made, decided on by the model from there. The card says the
  move was yours and what the model had picked.
* **Next lead**, **Previous**, **Play it again** and the language (English, Arabic or both in one journey) choose
  the lead. A lead is a number: the same number gives the same lead.

What is real here is every label and every decision: they are the live model's own, asked in its own words. The
world is simulated, with known causes; it is the world the served model's training journeys came from, written in
wordings that no training journey has, so the model is reading text it has not seen. A reservation is a roll of the
dice against the lead's true chance, so one journey proves nothing by itself: play several. The world runs on the
dashboard API, which keeps nothing between calls:

```bash theme={null}
curl -s -X POST $DASH/api/rl/wired/next -H 'content-type: application/json' -d '{"seed": 18, "lang": "en", "steps": []}'
```

returns the journey so far (`rows`), and `pending`: the decision the world waits for, with the `state` to ask the
model on, the moves that are open and the ones a house rule shut. Ask `POST /api/rl/recommend` with that state, then
call again with the answer added to `steps` (`{"move", "interest", "objection", "ledger"}`: the move, the model's
label of what the customer said, and the decision's ledger event). `decided` holds the truth of each decision made,
and `ended` is `reserved`, `quiet` or `limit` when the journey is over.

### Inspect

Every card under "Every ask" in the builder, the decisions of the wired journey, and the labelling, WhatsApp
labelling, journey and decision rows in the labs, have an **Inspect** button. It opens a drawer with three tabs:

* **Graph**: for a read or a decision in a lab's journey, the whole process it belongs to on the same canvas: the
  journey replayed from the first call, read, understand, decide and round again. For a card of the builder's log,
  that ask's own journey. For the labelling of a single call, the labelling itself: on the left the call that was
  labelled, every turn of it, and on the right one node per label with the answer and every option's probability
  as a bar.
* **Input**: what the model actually read, line by line in order, with each event's id, the windows drawn as
  brackets, and what it dropped and why, from the model's own
  [explain block](https://levers9.mintlify.site/lab/decision-api.md#explain-what-the-model-read). Below it, each exact request body.
* **Output**: every answer with all its probabilities, then the recommendation: the ranked candidates with chance,
  uplift, score and any rule that excluded them, the propensity, the review and the ledger event.

### Causality labs

The other **Causality** cards open one lab per pack: a sales lead, a visit no-show, rent collection and a support
escalation. They ask what would happen if you acted (send an offer, call, hand to a person, or wait) rather than
what is true about a call.

Each lab works the same way:

* **Pick a journey.** A list of 24 prepared customers per pack, with filters for language, outcome and stage, or
  Randomize. Moving to the next customer is always your click; nothing rolls on by itself.
* **Read the call.** The first call is shown in full, right to left when it is in Arabic. Switch the same customer's
  call between English and Arabic, or write your own conversation and run the journey with it.
* **Your own conversation.** Two parts can be yours: the first call (a voice call) and the WhatsApp messages after it,
  each one line per turn, `agent: ...` or `customer: ...`, in English or Arabic. Write them in the picker's box, or
  from the timeline: the call's row has a **Conversation** button and every WhatsApp row an **Edit the
  conversation** button, which open the conversation to read, with **Edit**. **Run with this conversation** rebuilds
  the journey, and the timeline, the model's reading, the decision and the decision graph are asked again by
  themselves; **Back to the simulated conversation** undoes it. Only what you changed is marked as yours. A typed
  call is told to the model as what it is: its line says what our own model labelled it, never a summary the page
  guessed, and the simulated labeller's and reviewer's rows are left out, since they never read it. A typed WhatsApp
  line moves no stage and does not count toward the contact cap.
* **The WhatsApp conversation, labelled again.** After the call, a journey's WhatsApp conversation is a real
  exchange: the customer's message, our reply, the customer again (in every prepared journey of the sales lead lab,
  and wherever the customer wrote in the other three). The served model labels it as it labelled the call: the
  customer's sentiment in it, their interest now and an open objection, read as one conversation with the call's
  label already in its history. The timeline has a row for it, **Model labelled the WhatsApp conversation**, with
  the answers and the label that was written into the journey after the last message. Its **Inspect** opens the
  conversation that was labelled, every message of it, beside one node per label with every option's probability;
  the exact request (the conversation, and the history it was read with) is under Input. In the decision graph a
  customer's message is labelled again the same way and its label is read by the next step.
* **Whose chances decide, and where.** In the sales lead lab the served model has chances of its own for the
  lab's moves, so the decision is made on them, for a simulated journey as for a conversation you typed: the
  request gives no chance. The journey is decided step by step, as production decides it: after the first call,
  after the customer's last message in a WhatsApp conversation, at a sales note or a booking, and at the lab's own
  decision time. A stage move, a label, a call nobody answered and a line of our own are read and not decided on,
  and neither is a message the customer follows with another one a few minutes later: nobody decides in the middle
  of a conversation. Each earlier decision has its row in the timeline (**Next-best action, at this point**: the
  move, its chance, the chance with nothing done, what quiet hours shut, the label written from what the customer
  just said, and what the system then did), and it is written into the journey whole with the row for what was
  done after it, so the last request holds every earlier decision in full and the last decision reads them: an
  offer already sent makes another one worth less. The earlier steps do not explore. The simulated customer
  never saw them: what they do next was scripted without them, and the lab still scores the last decision only. The journey is sent as production sends it. The first call is the new input, the model labels it, and the
  label is written into the journey; the WhatsApp conversation is labelled again the same way; every WhatsApp
  message is a row of the source `WhatsApp` whose text opens with the speaker (`customer: ...`, `agent: ...`), the
  way the model's training journeys write one; and the moves go under the names the model knows (the lab's "Wait 3
  days" is sent as `no_action`, the baseline). The decision is asked with the model's own seven questions about
  the journey in the same request (the customer's interest now, an open objection, how engaged they have been,
  what the latest sales note says, our last step, how many offers we sent in 14 days, how much we contacted them
  in 7), when the served model was trained on all seven; the decision row lists what it read above what it
  decided, and under each answer the row of the journey it read most ("read most from our own earlier decision,
  4 May 03:55 (98%)"); the call's row shows the label that was written into the journey for it. In the decision
  graph and the Causality builder, where a journey is asked step by step, the same seven are asked at every
  step, and a decision of ours goes into every later request as the row it is: its one line of text, which the
  model reads, with the move, the decision's id, its odds and the complete decision as data beside it (what was
  read, every option with its chance, what a rule shut, the pick, the review). The decision row and the
  action card say whose chances decided, and **Inspect** on the decision shows each request word for word. The chances the lab learned on its own simulated
  logs are then not used for the decision. The model's chances follow its own labels, the interest above all. On 5
  October 2026, on the model served then, for one typed call followed by three different WhatsApp conversations: a
  customer who asked for the offer was labelled hot, and the offer got 55% against 38% with nothing done; one who
  wrote not to be contacted again was labelled cold, 22% against 7%; an angry one was labelled cold, 21% against 7%.
  Without the label of the WhatsApp conversation, only the call's label was in the journey and the offer stayed at
  23 to 25% whatever was written. The pick was the offer in all three: the step weighs a sale's value against a
  small cost and has no do-not-contact rule. A served model that was not trained on the lab's moves, and the other
  three labs, where no model has chances of its own for the moves, still decide on the chances learned on the
  simulated customer; a typed conversation then changes only the labels, and the lab says so.
* **The model's own schema, and a check of every request.** A journey model answers well when it is asked in the
  words it was trained on, about a journey written the way its training journeys were. The page keeps one copy of
  the served model's schema, written from the release's own files: every question with its option keys in the
  model's order and each option's description, the moves the model has a chance of its own for, and the kinds of
  row its training journeys hold. Everything the labs and the builder ask is built from that copy: the seven
  questions of a decision, the five journey questions, the two that label what a customer said, and for the first
  call the model's own question for a call that just ended (`outcome_now`) with its sentiment question. **Inspect**
  checks each request against it and says so under the request, "Checked against fin2-v7's own schema. 7 questions,
  4 moves, 15 journey rows: all as fin2-v7 was trained", or lists what is not: a question asked in other words, an
  option the model has that was not offered, a move it has no chance for, a row its journeys never hold, a
  WhatsApp line that does not say whose words it is, a decision with no row after it for what was done. A row the
  model knows from its older journey set only (a reviewer's confirmation, a deposit) is listed as outside the
  decision journeys, not as wrong. The builder's palette adds rows in that same form, and its source suggestions
  are the sources of those journeys (`CRM`, `AI call`, `WhatsApp`, `Lev`, `reviewer`, `engine`). When the served
  model is another release than the copy's, Inspect says the request was not checked.
* **Simulate this decision.** Under the action card: 100, 500 or 2,000 imagined leads with this same journey, each
  rolled once and drawn as a dot: succeeds anyway, succeeds because of the move, lost by the move, or does not
  succeed either way. One roll serves both worlds (the engine deciding, and nothing done), so nobody is counted
  twice; the engine's own dice are rolled too, so a few leads get another move, as exploration does. **Again** rolls
  new dice and adds the runs up. It follows the decision above, whatever journey or conversation made it, and it
  plays out that decision's chances: it shows what they mean and adds no evidence.
* **The timeline.** Every event of the journey in order. What the model answered is a short list, the question on
  the left and the answer on the right, one to a line: the call's labels, the journey's reading, and the decision.
  On an answer a percentage is printed only where the model is unsure (under 90%); every probability is under
  Inspect. The decision is all percentages: the chance of success with the move, the chance if we do nothing, what
  the move adds in points, how it was chosen, and how often the move is taken. That last line is the propensity,
  the logged chance of the move taken, said as a share of the time ("95% of the time, the other 5% another move is
  tried, to keep learning") and never as a bare 0.950. The timeline ends at the outcome
  because a lab scores one decision against its outcome. A real journey does not end there: the outcome arrives
  like anything else and the next decision reads it, which the builder's story with the visit shows.
* **Decision graph.** The second step of every lab is the same graph as the builder's: this customer's journey
  replayed from the first call through the live model, a node per thing that arrived, a labels node where the
  model's reading changed and a decision node where the decision changed, every node opening its cause, an arrival
  to take away, and the replay. A call the lab's customer did not pick up is an automatic retry there too. Only what arrived from outside
  is given (the calls, the customer's messages, what the CRM returned); the lab's simulated labels and actions are
  left out, and the model's own label and the decision are asked for at every step and fed forward. The moves and
  their chances are the lab's own, given with the request and the same at every step, so what moves a decision here
  is the rules: quiet hours, and caps that count what was already done. The served model was trained on sales-lead
  journeys, so its reading of the other three worlds is weak, and the step says so.
* **What the model did.** The served model, the exact request, the latency (in the browser, through the dashboard and
  on the model) and every probability, marked live or simulated, and how each label changed the engine's inputs. The
  model only labels what happened in the call; the causal part is the engine choosing an action from chances learned
  on logged decisions that carry their propensities.
* **Why the old rule fooled us.** Replays 500 past customers the old rule handled: what the logs say, what really
  happens, and what can be recovered when the logs include exploration. Those panels are simulated from end to end.
* **Our model, asked live** (in that step, sales lead lab). The one panel there that is not simulated: 100, 250 or
  500 leads of the same kind are sent to the served model, one whole journey each. Per lead it is asked as
  production asks: it labels the call, that label is written into the journey, it labels the WhatsApp conversation
  after it the same way, and then it decides, with its own chance for every move (the request gives none). A dot waits at the top until the model has answered and lands in
  the bucket of the move it picked. Under the dots, on the same leads: what doing nothing, the old rule, our model,
  the lab's own engine and the ceiling earn per lead; which move the model made for each hidden kind of lead; and
  the chance it gave its picks against the chance they really have. The leads stay simulated, because only a
  simulated world knows what the same lead would have done under another move; every decision is the model's. It
  is three requests a lead (100 leads take about 55 seconds); the leads are the same every time and an answer is
  kept for the session. On one run of 100 leads on 5 October 2026 the served model earned SAR 29,744 a lead,
  against SAR 24,376 for the old rule, SAR 31,666 for the lab's engine (which learned its chances on that world's
  own logs) and SAR 34,272 at the ceiling. It sent the offer to 88 of the 100, and it was too hopeful: it gave its
  picks 45% where that world gives 28%. Before the WhatsApp conversation was labelled, the same 100 leads gave
  SAR 30,435 a lead with a more even mix of moves. The other three labs do not offer the run, since the served model has chances of its
  own for the sales-lead moves only.

Everything except the model's labels is a simulated world with known causes; the page marks every simulated number.
What a lab sends to the model starts at the customer's first call: the lead's creation in the CRM is never sent, and
a call's label is written as our own model's (source `Lev`).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.