Rensei docs

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.

SurfaceAnswersScope
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/decisions

Filterable 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:

FieldWhat it carries
candidatesConsideredEvery candidate the resolver looked at
exclusionsPer candidate: poolId, stage, ruleId, and an optional detail - the stage and named rule that excluded it
orderedSurvivingSetThe candidates that made it through permission and viability, in ranked order - this is the fallback chain
chosenTarget, orderingPolicy, scoreVector, selectionWeightsWhat stage 4 (ranking) did, in the terms it did it
rulesetRev, snapshotAgeMs, degradedThe ruleset snapshot this decision was evaluated against, and whether it was stale when evaluated - see Staleness and the degraded flag
propensity, explorationFlagLearning-loop bookkeeping, written at decision time
obligationsAny conditions a permit carried (for example, "audit tier must be full"), and whether they were discharged
receiptsThe admission and claim receipt ids this decision produced
stagesThe 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:

AttestationMeaning
ledger_nodeThe parent resolved to an admitted session in this org's own ledger - linked, decision and all.
external_unattestedA 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.
noneNo 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 childrenLookupIndexed flag 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:

AgePosture
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 thresholdLoud, 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.

On this page