Action ledger
Agents retry. Processes crash. Without care, that means two Slack posts, two issues or two comments. Every tool call in Azhi passes through the gateway, which keeps an action ledger.
One logical action, many attempts
Section titled “One logical action, many attempts”Each external write is a logical action with a stable id derived from the run, node, tool, the intended operation and an ordinal. A retry of the same operation computes the same id, so it finds the earlier action instead of creating a second one. Changing the operation produces a new id.
States
Section titled “States”planned → dispatched → confirmed ↘ failed (the target definitely did not apply it) ↘ outcome_unknown (we cannot tell)Every transition is recorded and fenced by the attempt generation: a stale attempt can never commit after a newer one took over.
Effect classes decide what recovery may do
Section titled “Effect classes decide what recovery may do”If a previous attempt ended after sending but before recording a receipt, the action becomes outcome_unknown.
What happens next depends on the tool’s declared effect class.
| Effect class | Recovery |
|---|---|
write-idempotent | Resend with the same idempotency key |
write-dedupable | Look the write up on the target by its dedupe key. If it is found, mark it confirmed and reuse the receipt. If not, resend |
write-unsafe | Stop. An operator must reconcile it before any retry. Azhi does not guess |
For Slack, that lookup is how a fresh gateway finds the message a crashed one had already posted.
Proof, not a promise
Section titled “Proof, not a promise”The alpha demo kills the gateway right after Slack accepted the post. A fresh gateway finds the message by its dedupe key. Exactly one message exists, and the ledger line shows how.
npm run demo # check 4: crash mid-postnpx azhi inspect <run> # state, flags, attempts, approvals, usage, context manifests and the ledgerThe test suite injects crashes at named points (after planned, after dispatched, after sent) by killing the process, the same as a power loss.
Related behavior
Section titled “Related behavior”- Loops: each iteration has its own ledger ordinal, so writes inside a loop are deduplicated per iteration.
- Sticky comments: the PR review tool is registered with a sticky marker, so a later run edits the earlier review comment instead of adding another.
- Alerts: writes left in
outcome_unknownraise an alert in Mission Control. - Test runs:
azhi test-nodemocks writes, so nothing reaches the ledger.