Finite state machine specifications for every autonomous agent — with HITL pause nodes, rollback paths, audit annotations, and shared checkpoint architecture.
| State | Type | Entry Action | Exit Condition | Next State(s) | Audit Event | HITL Required |
|---|---|---|---|---|---|---|
| IDLE | Start | Await Pub/Sub trigger or ad-hoc query | Trigger received | EXTRACTING | state_init | — |
| EXTRACTING | Process | Pull IC entries from all 6 SAP entities via OData; parallelised by entity | All entities returned, or timeout after 30min | MATCHING · ERROR | extract_start · extract_complete | — |
| MATCHING | Process | ML anomaly detector scores each IC pair; confidence interval computed per mismatch | All pairs scored; ≥1 mismatch flagged | HITL-IC-01 · COMPLETE (clean) | match_complete · mismatch_flagged | — |
| HITL-IC-01 | ⏸ PAUSE | Dispatch review task to Group Controller UI; 4h SLA timer starts; escalation to CFO at 3h50m | Approve or Reject decision received with hitl_id token | POSTING (approved) · IDLE (rejected) · SLA_BREACH (4h elapsed) | hitl_dispatched · hitl_resolved | ALWAYS |
| SLA_BREACH | ⚠ Timeout | Escalate to CFO + Finance Director; set compliance_flag=true; block POSTING until decision received | Human decision received OR manual override by CFO | POSTING (decision=APPROVE) · IDLE (decision=REJECT) | sla_breached · escalation_fired · compliance_flagged | CFO override |
| POSTING | Write | SAP BAPI journal correction write; hitl_id embedded in SAP document header. Idempotent: run_id deduplication guard — duplicate run_id returns existing confirmation, no re-write | BAPI returns success or fault | COMPLETE · ERROR | sap_bapi_write · post_confirmed | hitl_id required |
| COMPLETE | Terminal | Close period reconciliation record; notify month-end dashboard | — | — | recon_complete | — |
| ERROR | Stable | Log structured error; page on-call engineer; if BAPI partial write → transition to ROLLBACK. No auto-retry from ERROR — requires explicit RETRYING transition with backoff. | On-call engineer initiates retry or rollback | ROLLBACK (if partial write) · RETRYING (transient fault, max 3) | error_logged · escalation_fired | — |
| RETRYING | Recovery | Exponential backoff retry (×3 max); re-attempt BAPI write with same run_id (idempotent) | BAPI success OR max retries exceeded | COMPLETE (success) · ERROR (max retries) | retry_attempt · retry_exhausted | — |
| ROLLBACK | Recovery | Execute SAP compensating journal entry; revert all partial writes from this run | Compensating entries confirmed | IDLE | rollback_exec · rollback_confirmed | — |
// S — State set S = { IDLE, EXTRACTING, MATCHING, HITL-IC-01, SLA_BREACH, POSTING, RETRYING, COMPLETE, ERROR, ROLLBACK } // s₀ — Initial state · F — Terminal states s₀ = IDLE F = { COMPLETE } Dead-ends = { ERROR (unrecovered) } // δ(current_state, event [guard]) → next_state δ(IDLE, pubsub_trigger) → EXTRACTING δ(EXTRACTING, all_entities_returned) → MATCHING δ(EXTRACTING, timeout_30m OR odata_fault) → ERROR δ(MATCHING, [mismatches = 0]) → COMPLETE δ(MATCHING, [mismatches ≥ 1]) → HITL-IC-01 δ(HITL-IC-01, decision=APPROVE, hitl_id_present) → POSTING δ(HITL-IC-01, decision=REJECT) → IDLE δ(HITL-IC-01, [sla_elapsed ≥ 3h50m] AND no_decision) → SLA_BREACH // escalation fires δ(SLA_BREACH, decision=APPROVE, hitl_id_present) → POSTING δ(SLA_BREACH, decision=REJECT) → IDLE δ(POSTING, bapi_success, [run_id not duplicate]) → COMPLETE // idempotent δ(POSTING, bapi_fault, [no partial write]) → ERROR δ(POSTING, bapi_fault, [partial write confirmed]) → ROLLBACK δ(ERROR, engineer_initiates_retry, [retry_count < 3]) → RETRYING δ(RETRYING, bapi_success) → COMPLETE δ(RETRYING, bapi_fault, [retry_count = 3]) → ERROR // dead-letter δ(ROLLBACK, compensating_entry_confirmed) → IDLE
| State | Type | Entry Action | Exit Condition | Next State(s) | Audit Event | HITL Required |
|---|---|---|---|---|---|---|
| IDLE | Start | Await daily scheduler or FX alert | Scheduled trigger fires | FETCHING | state_init | — |
| FETCHING | Process | Pull positions from bank APIs (SWIFT gpi, open banking); pull live FX rates | All feeds returned or timeout 15min | FORECASTING · ERROR | fetch_start · fetch_complete | — |
| FORECASTING | Process | Run 7-day cash model; compute confidence interval; generate FX hedge recommendation | Model returns forecast + confidence | ANOMALY_CHECK | forecast_run · forecast_complete | — |
| ANOMALY_CHECK | Decision | Evaluate: confidence < 0.75 OR proposed hedge > €500K? | Decision threshold evaluated | HITL-TR-01 (triggered) · COMPLETE (auto-report) | anomaly_check_result | Conditional |
| HITL-TR-01 | ⏸ PAUSE | Dispatch forecast + hedge recommendation to Treasury Manager; 2h SLA; escalation to CFO at 1h45m. Compound trigger: if both conf < 0.75 AND notional > €500K fire simultaneously, a single consolidated HITL task is created with both conditions surfaced in the review UI — one hitl_id issued. | Approve or Reject with hitl_id | EXECUTING (approved) · IDLE (rejected) · SLA_BREACH (2h elapsed) | hitl_dispatched · hitl_resolved | ALWAYS (when triggered) |
| SLA_BREACH | ⚠ Timeout | Escalate to CFO + Head of Treasury; set compliance_flag=true; hedge execution blocked until decision received | Human decision received or CFO override | EXECUTING (decision=APPROVE) · IDLE (decision=REJECT) | sla_breached · escalation_fired · compliance_flagged | CFO override |
| EXECUTING | Write | Send hedge instruction to bank API; hitl_id attached to instruction record. Idempotent: instruction_id deduplication — duplicate instruction_id returns existing confirmation without re-executing | Bank API confirms or faults | COMPLETE · ERROR | hedge_instruction · exec_confirmed | hitl_id required |
| COMPLETE | Terminal | Publish daily treasury dashboard; archive forecast record | — | — | treasury_complete | — |
| ERROR | Stable | Log structured error; alert Treasury Operations. No auto-retry — requires explicit operator-initiated RETRYING transition with exponential backoff (max 3 attempts). | Operator initiates retry | RETRYING (transient, max 3) · IDLE (unrecoverable, manual reset) | error_logged · escalation_fired | — |
| RETRYING | Recovery | Exponential backoff retry (×3 max); re-attempt bank API call with same instruction_id (idempotent) | API success OR max retries exceeded | COMPLETE (success) · ERROR (max retries) | retry_attempt · retry_exhausted | — |
S = { IDLE, FETCHING, FORECASTING, ANOMALY_CHECK, HITL-TR-01, SLA_BREACH, EXECUTING, RETRYING, COMPLETE, ERROR }
s₀ = IDLE F = { COMPLETE }
// δ(current_state, event [guard]) → next_state
δ(IDLE, daily_scheduler_trigger) → FETCHING
δ(FETCHING, all_feeds_returned) → FORECASTING
δ(FETCHING, timeout_15m OR api_fault) → ERROR
δ(FORECASTING, model_complete) → ANOMALY_CHECK
δ(ANOMALY_CHECK, [conf ≥ 0.75 AND notional ≤ €500K]) → COMPLETE // auto-report
δ(ANOMALY_CHECK, [conf < 0.75 OR notional > €500K]) → HITL-TR-01
δ(HITL-TR-01, decision=APPROVE, hitl_id_present) → EXECUTING
δ(HITL-TR-01, decision=REJECT) → IDLE
δ(HITL-TR-01, [sla_elapsed ≥ 1h45m] AND no_decision) → SLA_BREACH
δ(SLA_BREACH, decision=APPROVE, hitl_id_present) → EXECUTING
δ(SLA_BREACH, decision=REJECT) → IDLE
δ(EXECUTING, bank_api_success, [instruction_id not duplicate]) → COMPLETE // idempotent
δ(EXECUTING, bank_api_fault) → ERROR
δ(ERROR, operator_retry, [retry_count < 3]) → RETRYING
δ(RETRYING, bank_api_success) → COMPLETE
δ(RETRYING, bank_api_fault, [retry_count = 3]) → ERROR // dead-letter
| State | Type | Entry Action | Exit Condition | Next State(s) | Audit Event | HITL Required |
|---|---|---|---|---|---|---|
| IDLE | Start | Await invoice ingest feed event | Invoice batch arrives | INGESTING | state_init | — |
| INGESTING | Process | Document AI OCR parse; extract PO number, line items, vendor, amount, cost centre | Parse complete or Document AI fault | CLASSIFYING · ERROR | ingest_start · ingest_complete | — |
| CLASSIFYING | Process | Exception classifier assigns label: PO_MISMATCH · PRICE_VARIANCE · MISSING_CC · DUPLICATE · APPROVED. APPROVED label requires classifier confidence ≥ 0.92 AND no rule-based exception flags — below threshold routes to HITL regardless of label. | Label assigned with confidence score | HITL-AP-01 (exceptions or conf < 0.92) · POSTING-AUTO (APPROVED, conf ≥ 0.92) | classification · routing_decision | — |
| HITL-AP-01 | ⏸ PAUSE | Route to AP Lead review queue with exception label, original invoice, PO diff, and confidence score; 8h SLA timer starts; escalation to Finance Controller at 7h | Approve or Reject with hitl_id | POSTING (approved) · IDLE (rejected) · SLA_BREACH (8h elapsed) | hitl_dispatched · hitl_resolved | ALWAYS (exceptions) |
| SLA_BREACH | ⚠ Timeout | Escalate to Finance Controller + AP Manager; set compliance_flag=true; invoice remains parked; supplier notified of hold extension | Human decision received | POSTING (decision=APPROVE) · IDLE (decision=REJECT) | sla_breached · escalation_fired · supplier_notified | Controller override |
| POSTING | Write | ERP write; hitl_id attached for exception invoices; null hitl_id for APPROVED-auto path. Idempotent: invoice_id deduplication guard — duplicate invoice_id returns existing ERP confirmation, no re-write | ERP confirms or faults | COMPLETE · ERROR | erp_write · post_confirmed | hitl_id for exceptions |
| COMPLETE | Terminal | Mark invoice processed; update AP dashboard; trigger payment run eligibility | — | — | invoice_complete | — |
| ERROR | Stable | Log structured error; flag invoice for manual triage; alert AP Operations. No auto-retry — explicit operator-initiated RETRYING transition required. | Operator initiates retry or manual triage | RETRYING (transient, max 3) · IDLE (unrecoverable) | error_logged · invoice_flagged | — |
| RETRYING | Recovery | Exponential backoff retry (×3 max); re-attempt ERP write with same invoice_id (idempotent) | ERP success OR max retries exceeded | COMPLETE (success) · ERROR (max retries) | retry_attempt · retry_exhausted | — |
S = { IDLE, INGESTING, CLASSIFYING, HITL-AP-01, SLA_BREACH, POSTING, RETRYING, COMPLETE, ERROR }
s₀ = IDLE F = { COMPLETE }
// δ(current_state, event [guard]) → next_state
δ(IDLE, invoice_batch_arrives) → INGESTING
δ(INGESTING, parse_complete) → CLASSIFYING
δ(INGESTING, document_ai_fault) → ERROR
δ(CLASSIFYING, [label ∈ {PO_MISMATCH,PRICE_VARIANCE,MISSING_CC,DUPLICATE}]) → HITL-AP-01
δ(CLASSIFYING, [label = APPROVED AND conf < 0.92]) → HITL-AP-01 // conf gate
δ(CLASSIFYING, [label = APPROVED AND conf ≥ 0.92]) → POSTING // auto-path
δ(HITL-AP-01, decision=APPROVE, hitl_id_present) → POSTING
δ(HITL-AP-01, decision=REJECT) → IDLE // invoice parked
δ(HITL-AP-01, [sla_elapsed ≥ 7h] AND no_decision) → SLA_BREACH
δ(SLA_BREACH, decision=APPROVE, hitl_id_present) → POSTING
δ(SLA_BREACH, decision=REJECT) → IDLE
δ(POSTING, erp_success, [invoice_id not duplicate]) → COMPLETE // idempotent
δ(POSTING, erp_fault) → ERROR
δ(ERROR, operator_retry, [retry_count < 3]) → RETRYING
δ(RETRYING, erp_success) → COMPLETE
δ(RETRYING, erp_fault, [retry_count = 3]) → ERROR // dead-letter
| Checkpoint ID | Agent | Trigger Condition | SLA | SLA Breach → State | Approver | Escalation | Escalation At | hitl_id Required | Rejection Route |
|---|---|---|---|---|---|---|---|---|---|
| HITL-IC-01 | IC Recon Agent | Any flagged IC mismatch detected by ML anomaly detector. No confidence threshold — HITL is unconditional for all corrections. | 4 Hours | SLA_BREACH · compliance_flag=true · CFO notified · POSTING blocked | Group Controller | CFO + Finance Director | 3h 50m | SAP BAPI header (required for all ERP writes in this agent) | IDLE · no journal posted · reason logged |
| HITL-TR-01 | Treasury Agent | Forecast confidence < 0.75 OR proposed FX hedge notional > €500,000. Either condition independently triggers. Both conditions simultaneously → single consolidated HITL task, one hitl_id. Below threshold + below notional → autonomous daily report only. | 2 Hours | SLA_BREACH · compliance_flag=true · hedge blocked · CFO + Head of Treasury notified | Treasury Manager | CFO + Head of Treasury | 1h 45m | Bank API hedge instruction record (required for all execution writes) | IDLE · no hedge executed · forecast published as advisory only |
| HITL-AP-01 | AP Exception Agent | Exception classifier assigns label PO_MISMATCH, PRICE_VARIANCE, MISSING_CC, or DUPLICATE — OR — any APPROVED-labelled invoice where confidence < 0.92. APPROVED label with confidence ≥ 0.92 bypasses HITL and posts autonomously with null hitl_id. | 8 Hours | SLA_BREACH · compliance_flag=true · invoice parked · Finance Controller + AP Manager notified | AP Lead | Finance Controller + AP Manager | 7h 00m | ERP write record (required for exception class invoices; null for APPROVED class ≥ 0.92) | IDLE · invoice parked · supplier notified of hold |