Core concepts
Obligations
An obligation is a commitment a transaction created and a later transaction must satisfy. Commitments are first-class, queryable state — the trial balance for authority, kept alongside the trial balance for money.
Why commitments are separate objects
A purchase that requires a receipt is not one fact. It is two: the purchase, which happened, and the receipt, which is owed. They are separated in time, they are performed by potentially different actors, and the second one can fail to happen at all.
A system that records only the first fact cannot tell you what is outstanding. Axorum records the second as a real object with a lifecycle, a due date, and an identity — so that at any moment you can ask what is outstanding, who owes it, and when it falls due, and get an exact answer from the ledger rather than an estimate from a report.
The lifecycle
Finance reading
Every commitment is in exactly one of six states.
| State | Meaning |
|---|---|
| Not yet in force | Recorded, but the condition that activates it has not occurred. |
| Outstanding | Live and owed. The clock is running against its due date. |
| Satisfied | Done. The action that settles it was performed. Final. |
| Exception | The due date passed unmet, or a prohibited act occurred. Final. |
| Waived | An authorised approver released it. Final. |
| Lapsed | Its window closed before it ever came into force. Final. |
The last four are final. A commitment that became an exception does not quietly return to good standing — an exception that could be undone would not be an exception. It stays on the record, and the resolution is a separate, recorded act.
What is outstanding — everything not yet in force or currently owed — is the working set. It is what you look at to know your exposure, and it is exact, not reconstructed.
Engineering reading
Every obligation instance moves through a finite state machine.
| State | Meaning |
|---|---|
Dormant | Instantiated, but its guard has not yet become true. |
Active | Live and owed. The deadline is anchored and running. |
Discharged | The discharging action committed. Terminal. |
Violated | The deadline passed unfulfilled, or a prohibited act committed. Terminal. |
Waived | An authorized waiver released it. Terminal. |
Expired | Its applicability window closed before it ever triggered. Terminal. |
The four terminal states are terminal. There is no path back out of Violated for the instance itself — a breach that could be undone would not be a breach.
The transition function is pure and total. Any state-event pair that is not a legal transition is a typed error, never a panic and never a silent no-op.
The open set — every instance in Dormant or Active — is the queryable working set, served by GET /api/v1/obligations. It is what an agent reads to learn what it currently owes, and what the deadline sweep walks to find what has fallen due.
Due dates and the clock
Finance reading
Time advances in the ledger as an explicit, forward-only count, not by reading the wall clock. Each advance sweeps everything outstanding for commitments whose due dates have passed, and marks them as exceptions.
A due date is set in one of two ways: a fixed period after the commitment came into force ("within two business days of the purchase"), or an absolute date ("by the end of the fiscal period").
The second form admits a case worth naming. A commitment can be created already overdue, if the date it names has already passed at the moment it comes into force. Axorum does not quietly absorb this. The commitment is recorded as an exception immediately, whatever the policy requires in response is opened at the same moment, and the audit trail carries both. A commitment incurred past its own due date is an exception, and pretending otherwise would be the system tidying away a fact you need.
Engineering reading
Time enters the ledger as an explicit, monotone tick, never as a clock read. A heartbeat advances it, and each advance sweeps the open set for duties whose deadlines have passed. Those breach, in the log, at a known tick.
Deadlines anchor in one of two ways. A duty declared within(d) anchors at the tick it triggered and breaches when the tick exceeds trigger + d. A duty declared by(t) breaches when the tick exceeds t, regardless of trigger tick.
That second form admits a strange and important case: a duty can be born already breached, when the resolved deadline is strictly before its anchor tick at instantiation. Axorum does not swallow it. The instance is created directly in Violated, its reparation is instantiated at the same tick, and the audit trail carries the born-violation as a fact. Expire-at-birth would silently swallow a norm, and validation-time rejection is impossible because the anchor is a runtime tick.
When a commitment is not met
Finance reading
The interesting question about a commitment is not what happens when it is met. It is what happens when it is not.
Policy can say what is owed because something was missed. When the exception is recorded, the remedy opens automatically, at the same moment, against the same party. The missed receipt opens a duty to file an exception report. The late payment opens the penalty.
This is the part an approval workflow cannot express, and it is the part your actual policy is made of. The real rule is never "receipts must be filed." It is "receipts must be filed, and when one is not, here is what must then happen." Both halves are policy. Both halves are enforced, and both halves are on the record.
A remedy does not itself cascade into a further remedy. That bound is deliberate: a control system whose exceptions breed exceptions cannot be reasoned about, and cannot be signed off.
Engineering reading
The interesting question about a duty is not what happens when it is met. It is what happens when it is not.
A rule may declare a contrary-to-duty reparation: what is owed because the primary duty was breached. On breach, the reparation is instantiated automatically, at the violation tick, bound to the same actor. The missed receipt opens a duty to file an exception report. The late payment opens the penalty.
This is contrary-to-duty reasoning, and it is what separates a deontic model from an authorization check. The organization's real policy is not "receipts must be filed." It is "receipts must be filed, and when one is not, here is what must then happen." Both halves are policy. Both halves are enforced.
Reparation is single-level: it does not cascade a further reparation. A deliberate bound, not an oversight — unbounded chains of repair duties are how a control system becomes unanalyzable.
Identity and idempotence
Finance reading
Each commitment's identity is derived from the facts that gave rise to it: what policy, which rule, which party, and when. Two copies of the ledger derive the same identity for the same commitment. Nothing about it is arbitrary or assigned at random.
The consequence you can rely on: a duplicate request does not create a duplicate commitment. An agent that retries does not end up owing the same receipt twice, and a remedy is never confused with the commitment that caused it.
Engineering reading
An instance's id is derived by UUIDv5 over a frozen namespace from its provenance-determining facts: origin, policy, source rule, actor, and anchor tick. Two replicas evaluating the same commit derive bit-identical ids. Nothing is minted from a clock or a random source — time-seeded id minting inside evaluation is forbidden.
A consequence worth relying on: re-incurring a duty whose derived id is already open is a no-op. The evaluator skips creation rather than minting a duplicate. An agent that retries does not owe the receipt twice.
The origin discriminator is part of the identity and separates a triggered duty from a reparation of a specific violated instance, so the two cannot collide even when rule, actor, and tick coincide.