Gates and invariants
Understand what fails during configuration and what is checked during a turn.
Admit
Check the supplied envelope and root-thread ownership before execution.
invalid envelope → "envelope-invalid"Fit
Check the opening assembly and estimated input before each model request.
input + reserve > window → "fit-overflow"Steps
Bound the ordinary model loop. Check time and cancellation at execution boundaries.
grant exhausted → "budget-cut"Close
Require a clean finish, content, and any declared deliverable.
missing required part → "deliverable-missing"
An invariant is a rule execution must preserve. Keel checks declarations before work starts, then checks the request and operations as the turn runs. This makes a wiring mistake distinguishable from a failure handling a request.
Check configuration first
| Declaration error | Where it fails |
|---|---|
| Missing provider model ID | Provider constructor or factory |
| Context allocation exceeds a fallback's limits | defineChain or defineAgent |
| Agent declares tools but a tier lacks tool support | defineAgent |
| Read names an undeclared reader | defineAgent |
| Tool feeds a block outside the agent's writes | defineAgent |
| Delegation forwards a part outside either agent's emits | asTool or defineAgent |
These are KeelError exceptions when definitions execute. Some are also
TypeScript errors when enough information is known statically. Numeric
capacity comparisons and dynamic configuration still require runtime checks.
Follow the four gates
Admit validates the ambient context — the core userId and threadId
fields, and the app-declared scope against its defineAmbient schema; a
scope that does not parse settles the turn with ambient-invalid. Three
further checks are root-only: claiming the thread's single root-turn slot
(via the store's claimThread, so a shared durable store serializes root
turns across runtime instances), parsing the request envelope, and checking
its size in UTF-8 bytes against transport.envelopeBytes. Child turns
inherit the already-validated ambient and emit an admit pass with
detail.root set to false.
Fit checks the opening assembly and estimates each subsequent model
request, including growing tool results and schemas. A refusal returns
fit-overflow before a provider call. This is approximate token accounting.
Steps bounds the ordinary model loop. Time and cancellation are checked at execution boundaries; recovery rounds have their own explicit limits.
Close checks the finish, marker completeness, content, and any required deliverable. Partial output does not establish successful completion.
Handle a refused request
const outcome = await runtime.runTurn({
agent: assistant,
ambient,
envelope,
budget: { steps: 6 },
});
if (outcome.kind === "failed") {
console.error(outcome.cause.code, outcome.cause.message);
}An oversized model request produces a failed outcome with fit-overflow.
It is different from constructing an impossible context allocation, which
throws before a turn starts. See Budgets and fit.
Validate operations inside the loop
Tool access is checked when assembling the available tools and again when executing a requested call. The executor checks the tool name and parses its input schema before running application code. A corrected call must pass those same checks.
A defect event does not necessarily terminate the turn: invalid tool input can return feedback and recover. Read the final outcome to determine whether the turn completed.
Inspect in Studio
A configuration error appears before a run can start. For a turn failure, select the turn and locate its refused gate, defect, and final outcome. Compare an invalid allocation at boot with a request that grows beyond its allocation during execution.
Next: Budgets and cancellation.
