beancount_ex is the behavioral oracle for the native Elixir engine.
It wraps real Beancount behind a stable Elixir API; the native engine
validates against it.
What is an oracle?
In testing, an oracle is a trusted reference that tells you what the correct answer should be. Beancount is a mature, widely used, double-entry accounting engine. By wrapping it behind a stable Elixir API, we get a reference implementation whose behavior the native engine can be compared against.
Why Beancount?
- It is correct and battle-tested across years of real-world ledgers.
- It has a well-defined text format that we can render deterministically.
- It exposes a checker (
bean-check) we can drive programmatically.
Rather than re-deriving accounting semantics from scratch, the native engine can be validated against Beancount's observable behavior.
The plan
v0.6 beancount_ex = Ecto storage + native engine + canned reports
- SQLite (:memory:) and file backends
- Booking, balance assertions, pad resolution
- Ecto.Query for ad-hoc queries
- Golden fixture parity via Compare.compare/3
v0.7 beancount_ex = BQL gap closure + Postgres/Mnesia backends
- Full BQL surface (functions, filters, JOINs)
- Storage backends: PostgreSQL (Ecto), Mnesia
- Plugin semantics, price database
v1.0 beancount_ex = Stable oracle + optional split
- Engine behaviour as the stable seam
- Optional extraction of native engine to separate packageAt every step the public Beancount.* API stays identical.
How equivalence is checked
- Generate valid ledgers with
Beancount.Property(StreamData). - Run the same input through the oracle (
Engine.CLI) and the native engine (Engine.Elixir). - Compare the normalized results from
check/1and canned report queries viaBeancount.Compare.compare/3.
{:ok, :equivalent} =
Beancount.Compare.compare(Beancount.Engine.CLI, Beancount.Engine.Elixir, ledger)On mismatch, Beancount.Property.Diff describes which callback diverged and
the normalized oracle vs native payloads.
Parity contract
Equivalence is asserted for:
- structural
check/1results by normalized error category - canned reports:
balances,balance_sheet,income_statement,holdings - full booking semantics (FIFO, LIFO, STRICT, AVERAGE, NONE), balance assertions, pad resolution, and tolerance inference on the golden fixtures
Golden fixtures exercise booking and validation edge cases against the CLI oracle.
Combined with golden files (deterministic, committed reference output) and property tests (broad, generated coverage), this gives two complementary safety nets for the native engine.
Stability promise
The public API is intended to remain stable forever. New engines are an
implementation detail behind Beancount.Engine; they must never require a
change to Beancount.*.