Concepts · 05
Event-driven autonomy
The difference between an agent that responds and an agent that operates is who decides when to run. The first waits for a person. The second wakes up when the world changes. This page is how we make the second safe.
The thesis: agents react, they don't poll
Every state change in every Ergon service emits a typed event with a stable schema: {service}.{entity}.{action}. A workflow item moved to a new phase. A document finished processing. A row was updated. A message arrived on a channel. A conversation was created. A grant was revoked. These events are durable, ordered per-aggregate, and published to the platform event bus.
Automations is the platform's reaction substrate. It subscribes to every service's event stream. When an event arrives that matches a rule the organization has configured, Automations dispatches a step graph — a small directed graph of conditions, switches, actions, and waits — that decides what should happen next.
An agent does not poll for things to do. An agent does not need to know about the rest of the company. The agent is the action at the end of a step graph triggered by an event the agent never had to subscribe to. The platform routes the reaction.
The shape of a reaction
Every automation is the same shape:
trigger: workflows.items.routed (an event from the workflows service)
condition: item.to_phase == "needs-research" (a guard so the rule fires only when relevant)
action: agents.invoke (call the Researcher agent)
input:
prompt: "Surface authorities for matter {item.id}"
wait: conversations.message.received (until the Researcher posts back)
action: workflows.items.transition (move the item to "research-ready")The same shape covers a hundred patterns. When a ticket comes in, classify it and assign. When a contract uploads, extract the parties and stakeholders into a worksheet. When a row in the AR worksheet stays unpaid for 14 days, draft a chasing email and queue it for review. All three are triggers, conditions, actions, waits.
The step graph
An automation's body is a graph of nodes connected by edges. Five node kinds cover everything we have needed:
trigger
An event from any service the automations engine subscribes to. The starting point. Every graph has at least one trigger node.
condition
A boolean expression over the trigger payload (and previous step outputs). Single edge out — runs the next step only if true.
switch
Multi-way branching. Edges are labeled by case; one branch runs based on the value of an expression.
action
A call out to a service: invoke an agent, send a channel message, transition a workflow item, write a worksheet row, upload a document. Actions are typed; the platform knows their inputs and outputs.
wait
Pause until a downstream event arrives or a timeout fires. The run holds in a 'waiting' state, durable across restarts. Resume happens when the matching event comes in or the timeout sweeper fires.
Why a graph and not a script
A script encourages embedding business logic in code that only one engineer understands. A graph forces the rule to be visualized, validated, and edited by anyone who understands the business. The graph is the artifact you show to the head of operations. The script is the artifact you show to the engineer who wrote it.
Guards against runaway loops
The first thing anyone building an event-driven autonomy layer worries about is loops. Agent A reacts to event X by doing thing Y; thing Y emits event Z; an automation reacts to event Z by invoking agent A again. Without guards, this is how an organization wakes up to a five-figure invoice.
Three structural guards stop this:
Self-loop firewall
Lifecycle events emitted by Automations itself (automations.*) are filtered out by the subscription worker before rule matching. An automation cannot trigger itself by changing its own configuration. This is set at the substrate, not configurable per rule.
Per-automation depth limit
Every automation declares a max_trigger_depth (default 3, max 10). When an automation invocation cascades into another automation invocation through events, the depth counter increments. When it hits the limit, the run is marked depth_limited and stops. The cascade can be visualized in the activity log.
Idempotency keys
Action calls (agent invokes, workflow transitions, channel sends) accept an Idempotency-Key header. When set, repeated calls with the same key return the original result instead of running again. Step graphs use this to make retries safe.
Time as a trigger
Not every reaction is to a state change. Some are to a calendar. Automations supports schedule triggers — cron-style or interval-based — that emit a synthetic automations.schedule.fired event at the configured time. From the rest of the engine's point of view, this is just another event with a payload, so the same conditions, switches, actions, and waits apply.
This is how an organization expresses “every Monday at 8am, run the weekly compliance check and post a summary to the partners' channel.” The agent does not need a clock.
Depth limits and trigger budgets
The autonomy boundary of an organization can be tuned by two knobs without touching the agents or the rules:
- max_trigger_depth on the automation. Lower means a tighter blast radius — an agent action that produces an event will not be allowed to cascade as deeply. Set this aggressively low when first deploying an automation.
- agent IAM grants. The invoked agent's grants define which tools it can use when the rule runs.
Both are configuration. Neither requires redeploying code. That is what we mean by autonomy expressed as policy.
Worked example — the deadline watcher
A mid-sized firm runs a “deadline watcher” automation against its matters. The rule is conceptually simple but chains many primitives:
- Trigger. Every morning at 6am, an
automations.schedule.firedevent arrives. - Action. Query the matters worksheet for all rows where
filing_deadlineis within 7 days andfiledis false. - Switch. For each matching row, branch on
days_until_deadline: 7 days → notify lead partner only; 3 days → notify partner and managing partner; 1 day → notify all partners and automatically open a “rush filing” workflow item. - Action — invoke Researcher. For 3-and 1-day branches, invoke the Researcher agent on the matter to surface any new authorities since the last research memo. The agent writes the result to the matter's draft folder.
- Action — channel send. Send a templated email to the relevant partners with a link to the matter and the new research memo if any.
- Wait. If the partner does not transition the matter to “in progress” within 24 hours, escalate.
What this replaces
A spreadsheet with deadlines, a recurring calendar reminder a paralegal has to act on, an email template someone has to remember to send, and a research task that gets delayed because everyone is busy. The automation runs every morning. The Researcher agent acts under the firm's grants. The partners are in the loop on every escalation. The audit log records all of it. No engineer was involved.
Notice that the agents in this example never knew about the schedule, never knew about the worksheet structure, never had to be told “today is Tuesday, check deadlines”. The automation routed the reaction. The agents did the part of the work that benefits from judgment.
Now go deeper
The concept pages describe the worldview. The rest of the documentation describes how to do these things in practice.
- Automations API reference — every endpoint, every event slug, every step type.
- Conversations API reference — the substrate that carries agent-to-agent and human-to-agent dialogue.
- Agent Hub API reference — provider keys, LLM configs, invoke, grants.
- Webhooks & Events guide — the event envelope, the catalog, how to subscribe.