Phase 2 — Runtime Engine Scope

Phase 1 (built) renders the 23-model schema as an AI-Safe CRUD register: master data, journals, accounting documents, inbound messages, attempts, errors, acknowledgements, simulation rules and events — browsable, with a dashboard. But the states are static: the seed shows a batch already POSTED, a document already created, an ack already returned. Nothing is validated, posted or simulated by the system.

Phase 2 is the runtime that makes it a live test-double — accepting inbound messages, validating against master data, posting to accounting documents, enforcing idempotency, evaluating simulation rules and delivering outbound events. This app's whole purpose is to be the finance endpoint the Orchestrator and domain apps integrate against, so its Phase 2 is literally the integration engine.

Everything below is drawn from the pack's .rules.yaml, .workflows.yaml, .simulation.yaml, .events.yaml and .orchestrator_mappings.yaml. It is a scope, not a design.


A. Inbound message intake & idempotency

An HTTP endpoint accepts JOURNAL_VALIDATE / JOURNAL_POST / JOURNAL_STATUS messages, writes an InboundMessage (keyed on message_sid), and de-duplicates: a repeat message_id returns the original outcome with acknowledgement type DUPLICATE (recording duplicate_of_message_sid) rather than posting twice. This is the idempotency guarantee a real ERP interface needs.

B. Validation engine

Before posting, validate the mapped JournalBatch against real master data:

  • Balance: total_debit == total_credit per batch; each entry balanced.
  • Master data: every gl_account_code exists, is posting_allowed, active; cost_centre_code / profit_centre_code exist and are valid (the demo's BAD-MINE → COST_CENTRE_INVALID); requires_cost_centre / requires_profit_centre honoured; tax_code valid where tax_relevant.
  • Fiscal period: posting_date falls in an OPEN period for the company (a CLOSED/LOCKED period → rejection).

Each check that fails writes a PostingError (with field_name, rejected_value, retryable), and the message → REJECTED with a VALIDATED-fail or REJECTED acknowledgement.

C. Posting engine

On a valid JOURNAL_POST, allocate a document number from the matching DocumentNumberSequence (advancing next_number), create the AccountingDocument + AccountingDocumentLines, set the batch POSTED, and return a POSTED acknowledgement with the document number and fiscal year. Each try is a PostingAttempt (VALIDATE then POST) with timing and HTTP status.

D. Failure simulation

Before/around real processing, evaluate the enabled SimulationRules in priority order:

  • MATCH (field/operator/value), PROBABILITY, LATENCY, FORCED_RESPONSE.
  • A matching rule can force REJECTED / TEMPORARY_FAILURE / DELAY_ONLY, inject an error_code/latency_ms, and stop_processing. Each firing writes a SimulationRuleHit.

This is what lets an integration test exercise retries, temporary failures and deterministic rejections on demand — the core reason this simulator exists.

E. Acknowledgement & outbound events

Every terminal state produces an Acknowledgement (VALIDATED / POSTED / REJECTED / DUPLICATE / TEMPORARY_FAILURE) with the error count and a payload. A successful post also writes an OutboundEvent (finance.document.posted) delivered to each enabled WebhookSubscription (with signing), tracking delivery_status and retrying on failure — the callback the Orchestrator consumes to close the loop with the originating domain app.


How it would be built

  1. A posting service (the intake/validation/posting engine, A–C) behind an HTTP API — this is the app's product, not an optional add-on.
  2. A simulation evaluator (D) invoked in the pipeline, config-driven from SimulationRule.
  3. An outbox deliverer (E) — reuse the orchestrator client for webhook delivery + retry.
  4. Tests straight from the pack's demo_transactions.yaml (valid posting, invalid cost centre, closed period, duplicate, simulated temporary failure) — these are ready-made acceptance cases.

Suggested sequencing

  1. Intake + idempotency (A) — the endpoint and dedup, the integration contract.
  2. Validation engine (B) — deterministic rejections against master data + fiscal period.
  3. Posting engine (C) — document creation + numbering + POSTED ack.
  4. Failure simulation (D) — the raison d'être: forced failures for test harnesses.
  5. Acknowledgement + outbound delivery (E) — close the loop back to the Orchestrator.

Each slice is independently shippable and demoable. The orchestrator (built and run live this session) is both the client this simulator serves and the working template for the outbox/tests.