AiDR

Pattern 01 · Facts

Every change to a case reaches AIOL, in order, exactly once

AIOL has to know what ADR knows: who the parties are, which documents were filed, where the procedure stands. Not by polling, and not by a hook on every route — by observing the database itself.

  1. 01 · What you see
  2. 02 · How it is used
  3. 03 · Technically
  4. 04 · The explainer

01What you see

ONE TRANSACTIONa document is filedits event is writtenOUTBOXseq 1 · dispute.createdseq 2 · document.filedseq 3 · …ONE WORKERsigns · sends in orderretries with backoffpauses on a defectAIOLdedups on event_idA change that rolls back leaves noevent. An event exists exactlywhen the fact does.
The pattern in one picture: the fact and its announcement share a transaction.
# the operator's view of the same thing
$ flask aiol status
integration: ON -> https://aiol.local
LIT-304  seq=2  active  pending=2

Live output from a demonstration instance. One line per case: where its sequence stands, whether it is active, paused or closed, and how much is still in flight.

02How it is used

Nobody uses it. That is the point: there is no “send to AIOL” button, no export, no nightly job a clerk has to remember. A party files a document, the Secretariat admits a case, an arbitrator signs an award — and AIOL knows.

What a human does touch is the operator surface, when something is wrong:

  1. flask aiol status — where every case stands.
  2. flask aiol retry — put a paused case back in the queue once the cause is fixed.
  3. flask aiol resync <case> — start that case over from a fresh snapshot.
  4. flask aiol drain — send what is pending now instead of waiting for the next tick.

03Technically

  • Observed at the flush.The outbox listens to SQLAlchemy's session flush, so a case change is noticed wherever it happens. There are no per-route hooks to forget.
  • Ordered per case. Sequence numbers are allocated under a row lock; a later event can never overtake an earlier one.
  • Signed and fresh. Every request carries a timestamp, a nonce and an HMAC over the exact bytes sent. AIOL tolerates ±5 minutes and never accepts a nonce twice.
  • Failure is not silence. 5xx, 429 and network errors retry with exponential backoff; a 409 gap re-sends from where AIOL is; a 404 starts over; a 400 or 401 is a defect and pauses the case for an operator instead of hammering it.
  • It can never break the case. If the listener itself fails, the change still commits — the event is recoverable with resync.

the signature, both directions

X-ADR-Timestamp: 1760441700
X-ADR-Nonce:     "9f3c…"  (16–128 chars, single use)
X-ADR-Signature: HMAC-SHA256(
                   secret,
                   "<timestamp>.<nonce>." + body
                 )

what travels — and what never does

sentstable ids (LIT-…, usr-…, doc-…), parties, the merits of the case, PDF/DOCX only
never sentidentity papers, powers of attorney, payment receipts, CVs, pictures, phone numbers, addresses

A single worker is not a tuning choice: the scheduler runs in-process, so a second worker would send everything twice.