Explain a routing decision
Find the rule and inputs behind one routing decision, then diagnose why it behaved differently from what you expected.
On this page
Use policy explain when a request matches an unexpected rule or takes an unexpected route. The
command shows the policy revision, the result, and the inputs used to reach it. You do not have to
read the config and guess what happened.
The command reads an existing decision from the log. It does not send a request, change the policy, or affect traffic.
What you need
Gather these three things before you run the command.
| You need | Where to find it |
|---|---|
| one completed request | any request already written to the decision ledger |
| its request id | the x-tt-request-id response header; trace a request by id shows the other lookup paths |
| read access to the log | the default is ~/.tokentriage/decisions.jsonl |
You do not need the dashboard, a provider connection, or routing enforcement. policy explain
works on a copied log and on rows recorded in ledger, shadow, or route mode.
Explain the request
Replace tt_simple below with your request id. The default log path is used when you omit
--log.
$ tokentriage policy explain --req-id tt_simple
req_id: tt_simple
policy_rev: 184bdaf
decision: rule=simple-to-cheap tier=— upstream=—
path: root → rules[simple-to-cheap]
trace:
• rules[simple-to-cheap] matched
read signal.complexity = 0.21
read $simple_cut = 0.35
This request matched simple-to-cheap because its complexity value, 0.21, was below the
configured cutoff, 0.35. This example was recorded in ledger mode, so the policy match was
recorded but no routing tier was applied. That is why tier and upstream are dashes.
If your log lives somewhere else, add its path:
$ tokentriage policy explain --req-id tt_simple --log ./decisions.jsonl
Read the result in four steps
Reading the fields in this order usually finds the surprise quickly.
- Confirm
policy_rev. This short fingerprint identifies the policy that decided the row. If it does not match the config you are inspecting, you are looking at two different policy versions.tokentriage policy migrate --config ./tokentriage.yamlprints the config revision in its first comment. - Read
decision. This is the result: the rule, tier, upstream, and any model rewrite. Aledgerrow may show a rule with no tier because ledger mode observes without applying a routing target. - Follow
path. It lists the policy nodes that were visited, in order. The final entry is the node that produced the result. - Inspect
traceandread.tracesays what each visited node did. Itsreadlines show the values that were present when a condition evaluated them.
The complete output for selectors, traffic splits, and cascades is in Routing explanation fields.
Diagnose a rule that did not match
A condition stops evaluating as soon as its result is known. For example, once the first part of
an and expression is false, later parts are not read. Fewer read lines than conditions can
therefore be expected.
A rule shown as skipped with why=when=false was evaluated and did not match. A rule missing
from path and trace was not reached. Use
How a routing decision is made to check the evaluation order.
Fix common problems
Start from the symptom in the output, then take the corresponding next step.
| Symptom | Likely cause | What to do |
|---|---|---|
| request id is not found | wrong log, incomplete request, or a damaged log line | confirm the log path and request id; use Trace a request by id to locate the row |
tier=— on a matched rule |
the row was recorded in ledger mode |
use the rule and reads to debug the policy; use shadow when you also need the proposed tier |
(no explanation block …) |
an older or non-policy router wrote the row | inspect the raw ledger row; newer policy-language decisions include an explanation |
| an expected rule is missing | an earlier node ended the walk | follow path from left to right and check the decision order |
an expected read line is missing |
an earlier condition settled the result, or the value was absent | check the preceding reads, then verify the signal, header, or state source |
policy_rev is unfamiliar |
the request used a different config revision | print the revision of the config you are inspecting and compare it with the row |
explain skips a torn JSON line without printing a warning. If the request id and path are
correct but the row is still not found, run report, eval, or calibrate against the log; those
commands count damaged lines on standard error.
Use the same explanation elsewhere
The CLI renders the explanation stored on the decision row. The dashboard record drawer and
GET /tt/api/v1/requests/{req_id} read that stored block too, although each surface presents it
differently. There is no separate explain endpoint.
Use Preview a routing policy change when the next question is not why one request behaved that way, but how a draft would affect a whole log.