Decision Records & Explain
Reading a placement decision after the fact: the hosted decision-trace API, the local `rensei routing explain` / `donmai routing explain` surface, and ruleset-snapshot staleness.
There are two separate explain surfaces, and they answer different
questions. This page covers both, and is explicit about which is which -
they are easy to conflate because one of them is reachable from the rensei
binary even though it never talks to the platform.
| Surface | Answers | Scope |
|---|---|---|
| Hosted decision-trace API | "Why did this specific past dispatch land where it did?" | Org-wide, persisted, every real dispatch |
rensei capacity profile test | "What would the resolver do for this project right now?" | A live dry-run against the hosted resolver, not a historical lookup |
rensei routing explain <session-id> / donmai routing explain <session-id> | "Why did this machine's local daemon route this session's LLM and sandbox provider pairing?" | This machine only, in-process ring buffer, never touches the platform |
Hosted: the decision-trace API
Every placement and selection decision the platform's resolver makes - hosted, successful or not - is persisted as one immutable record, per the six-stage composition law.
List decisions:
GET /api/org/{orgId}/routing/decisionsFilterable by sessionId, instanceId, or stepId, and by outcome
(selected or unsatisfiable); keyset-paginated with a before cursor. Each
row is a summary: id, session/instance/step identifiers, outcome, the chosen
pool and provider, a reason code, and when it was decided.
One decision, in full:
GET /api/org/{orgId}/routing/decisions/{id}Returns the complete six-stage walk plus:
| Field | What it carries |
|---|---|
candidatesConsidered | Every candidate the resolver looked at |
exclusions | Per candidate: poolId, stage, ruleId, and an optional detail - the stage and named rule that excluded it |
orderedSurvivingSet | The candidates that made it through permission and viability, in ranked order - this is the fallback chain |
chosenTarget, orderingPolicy, scoreVector, selectionWeights | What stage 4 (ranking) did, in the terms it did it |
rulesetRev, snapshotAgeMs, degraded | The ruleset snapshot this decision was evaluated against, and whether it was stale when evaluated - see Staleness and the degraded flag |
propensity, explorationFlag | Learning-loop bookkeeping, written at decision time |
obligations | Any conditions a permit carried (for example, "audit tier must be full"), and whether they were discharged |
receipts | The admission and claim receipt ids this decision produced |
stages | The full per-stage walk: intent, permission, viability, preference, ranking, bind |
A decision that cannot be explained from its own record is treated as a defect, not a missing feature - every field above is written at decision time, never reconstructed after the fact.
In the dashboard, an individual decision renders as the decision-trace view, linked from a run's placement details. The run/instance detail view resolves each step's placement (model, harness, pool, host, staleness, and the degraded flag) against the same decision records this page describes and renders it inline per node - there is no separate, hand-maintained copy of "what actually ran where."
Multi-hop A2A stitching
A delegated dispatch (a coordinator handing work to a worker agent) is two
separate placement decisions - one per session - not one. Reading them as a
single chain means resolving the session_ledger_edges row that links the
child session back to the one that delegated to it.
GET /api/org/{orgId}/routing/decisions/{id} includes this linkage
alongside the stage walk: the parent decision (if any) and every child
decision reachable from this one, each carrying an attestation level
rather than an assumed identity:
| Attestation | Meaning |
|---|---|
ledger_node | The parent resolved to an admitted session in this org's own ledger - linked, decision and all. |
external_unattested | A delegation edge exists and names a transport, but the parent session it points at is not one this org's ledger contains (for example, a host-CLI root outside the platform). The edge is still named; there is nothing further to link to. |
none | No delegation edge targets this session at all - it is a root dispatch, or was dispatched by a path that never wrote one. |
This is deliberately attested, not asserted: a chain only links as far as the ledger can prove, and a decision never claims a parent it cannot resolve. The child side of the lookup is a capped, org-scoped scan rather than an indexed point lookup today - large fan-outs are still correct, just not O(1)
- so the API response carries its own
childrenLookupIndexedflag rather than asking a caller to assume otherwise.
The practical effect: a workflow that hops coordinator to worker to a delegated peer, however many times, still reads as one traceable decision chain from the top-level session's decision-trace view, each hop showing its own candidates, exclusions, and chosen target - not a dead end at the first delegation.
Probing the resolver live: capacity profile test
To see what the resolver would do for a project right now, without waiting for a real dispatch, use the CLI:
rensei capacity profile test <project>This renders the same shape the hosted API returns - candidates considered, each exclusion with its stage and rule, the ordered surviving set, and the chosen target - but it is a fresh probe, not a lookup of a past decision. An unsatisfiable outcome still prints the full breakdown (the exclusions are the answer to "why"), and the command still exits non-zero so scripts detect failure the way they always have. See Capacity CLI reference for flags and full example output.
Local: rensei routing explain / donmai routing explain
This command is identical whether invoked as rensei routing explain or
donmai routing explain - the platform CLI embeds the open-source donmai
daemon client as a library, so it is the same code either way.
This never talks to the platform. It queries the local daemon's own HTTP
control API on 127.0.0.1:7734 and explains a decision that daemon made
about its own local inventory. It is not a client for the hosted
decision-trace API above, and it cannot look up a session that ran through
the platform's resolver rather than this machine's daemon.
rensei routing explain <session-id>
# or, running donmai directly:
donmai routing explain <session-id>The daemon's local routing engine is an older, narrower decision than the
six-stage placement law: a 2D scheduler pairing an LLM with a sandbox
provider for one session, walked in four phases - capability-filter (hard
constraints: OS, arch, region, GPU, duration), tenant-policy (Layer 6 hooks
and allow/forbid lists), capacity-filter (providers over their concurrency
ceiling or unhealthy), and score (a Thompson-Sampling composite of cost and
latency). For each phase the trace shows which providers were eliminated and
why; the final step shows the chosen provider and its score. Sessions whose
decisions have aged out of the daemon's in-process ring buffer (default 50
entries) return "not found" - this surface is a recent-history debugger, not
a durable store.
rensei routing show displays the same daemon's current routing
configuration, Thompson-Sampling state, and a table of recent decisions,
without needing a session id.
Staleness and the degraded flag
A daemon can be configured to hold a ruleset snapshot - a compiled,
signed, versioned bundle an embedding control plane publishes so the daemon's
claim path keeps working, within a bounded time-to-live, when that control
plane is unreachable. This is the delivery of
the composition law's staleness requirement:
every decision record, hosted or local, carries a revision, an age, and a
degraded flag rather than failing silently on stale data.
Three ages, one posture:
| Age | Posture |
|---|---|
| Below the degraded threshold (5 minutes by default) | Normal. Fresh. |
| Between the degraded and refuse thresholds (up to 30 minutes by default) | Fail-static. The claim is still permitted from the cached snapshot, but the decision record is marked degraded: true with its age - visibly, never silently. |
| Past the refuse threshold | Loud, typed refusal. The daemon does not guess; it returns a typed expiry error rather than assuming the cached snapshot is still good enough. |
This is deliberately fail-static for placement and preference, but
fail-closed for authorization: a stale-but-bounded policy bundle still
denies by default if it cannot be freshly evaluated, while ordering and
preference keep serving the last known-good snapshot rather than stopping
work outright. Age is always recomputed at read time, never frozen at fetch
time - two callers a minute apart see two different ages for the same cached
snapshot, the same way an HTTP Age header works.
When a daemon has a ruleset-snapshot source configured, routing explain and
routing show print an extra line:
Ruleset snapshot: <revision> age=<duration> (degraded)The (degraded) suffix appears only once the age has crossed the degraded
threshold, and the line is omitted entirely for a daemon that has no snapshot
source configured - self-hosted, single-machine donmai installs behave
exactly as they did before this existed. A daemon with no configured snapshot
source and no live provider fails closed with a typed error rather than
silently reporting "no opinion."
This page covers what a staleness-flagged decision record looks like. For the full per-decision-class picture (why authorization, placement, budget, and the bandit each commit to a different posture, how the ruleset snapshot is signed and verified, and what a live outage rehearsal actually showed), see Routing Durability & On-Prem Posture.
Related pages
- Routing Durability & On-Prem Posture - fail-closed vs fail-static vs conservative-cap vs fail-open, the signed ruleset snapshot, and what a control-plane outage actually does to admitted work
- Placement, Selection & Routing Vocabulary - the six-stage law these records trace
- Capacity profiles - the object stage 3 reads
- Capacity CLI reference -
capacity profile testand the rest of the profile verb set
Capacity Profiles
The capacity-profile contract: ordered pools, all six ordering policies, burst posture, disconnect reconciliation, and the org-authors/project-consumes grant model.
Routing Durability & On-Prem Posture
The four decision-class failure postures (fail-closed, fail-static, conservative-cap, fail-open-deterministic), the signed ruleset snapshot that makes fail-static possible, and what a control-plane outage actually does to admitted work.