An agent proposes. Something else decides.
Every action passes through the same four stages, in the same order, with no way around them. This is each one, including what happens when it fails.
The four stages
The order is the design. An agent that could execute would be an agent whose judgement is the last word.
Propose
An agent reads the situation and names an abstract action and its parameters. Nothing more.
Cannot call a connector, hold a credential, or reach anything outside OnDuty.
Evaluate
The decision engine checks the proposal against your workspace's guardrails and answers permit, hold or deny, with a reason and a trace id.
Never executes. Holds no connector credentials.
Execute
Only a clean permit reaches the executor, which calls the connector once, keyed so a retry cannot send the same message twice.
A hold goes to a person. A deny stops. Nothing else executes.
Record
The verdict, its reason, its trace and its outcome are written to a hash-chained trail, whether permitted, held or refused.
Refusals are recorded as carefully as executions.
Nothing acts on your customers because a model was confident. It acts because a rule you wrote said it could, and the record of that rule saying so is kept.
The two contracts
Fixed shapes. There is no free-text channel through which an agent can talk the executor into something.
What an agent proposes
- action
- The abstract operation, for example messaging.send. Never a vendor name.
- parameters
- What the operation needs. Structured, validated, readable by a person reviewing a hold.
- agent_id
- Which agent proposed it, so a pattern of bad proposals is traceable.
- tenant_id
- Whose workspace this belongs to. Every rule and every key resolves per tenant.
- context
- What the agent was looking at. This is what a reviewer reads.
- idempotency_key
- The identity of the intent. Two deliveries execute once.
What comes back
- decision
- permit, hold, or deny. No fourth answer, no partial one.
- reason
- Why, in terms of the rule that applied.
- trace_id
- Ties this verdict to its audit entry and to what followed.
- latency_ms
- How long the decision took.
- active_campaigns
- Which standing rules were in force, including a kill switch if one was on.
- shadow
- Whether the workspace is still proving its rules rather than enforcing them.
When something goes wrong
Fail-closed is not a setting. It lives in the executor, the one place that can actually stop an action.
| Situation | What happens | Why |
|---|---|---|
| The engine times out | Nothing executes. | A slow answer is not a permit. |
| The engine returns an error | Nothing executes. | An error means the rules were not applied. |
| The engine is unreachable | Nothing executes. | Availability of governance is never traded for getting the action out. |
| The verdict is hold | It waits in the review queue for a person. | Auto-resolving a hold would delete the only thing it was for. |
| The verdict is deny | It stops, and is recorded as denied. | The useful question is often what the system was stopped from doing. |
| The connector fails after a permit | Retries, then a dead-letter queue. | The decision was clean; only delivery failed, and the key stops duplicates. |
Around the seam
Three mechanisms that make a hold a person rather than a timer, and a verdict a record rather than a log line.
The review queue
hold
A held action carries the proposal, the context, the rule that stopped it and what approval does. Approving releases this one action, not the agent.
Holds resolve asynchronously
webhook and poll
OnDuty subscribes to the engine's webhooks and also polls, so a webhook that fails every retry cannot leave an action waiting with nobody aware.
An unanswered hold becomes a signal, and then a row on Today.
Shadow mode
prove it
Every rule runs and every verdict is recorded without anything executing. Real proposals, real verdicts, a week to read before one is real.
Shadow is a setting on the engine's side. OnDuty honours it and never claims production governance while it is on.
The decision trail
hash chain
Append-only, hash-chained, with a verify endpoint that answers about the whole record rather than one row.
Where this stands today
The seam
tests
Agents cannot construct an executor, no route names a gate implementation, and the executor refuses to act without a clean permit. Enforced by the test suite, not by convention.
Two gates behind one interface
production and local
The production path calls the decision engine. The local path permits everything and says so loudly: a workspace on it is shown as ungoverned.
Your action vocabulary and guardrails
with you
The concrete actions an agent may propose in your operation, and the rules over them, are written with you. Until then the shipped rules propose only work a person does.
Read the rules before you trust them.
Connect one system, run it in shadow, and read a week of decisions that did not happen.
On duty ·