Chapter 01 — Journal vs Event Log
This chapter gives you the core LedgerLoom mental model in one runnable, inspectable demo:
Store immutable facts (an append-only event log)
Derive views + reports (journal, ledger view, trial balance, statements)
Enforce invariants (double-entry, accounting equation check)
Ship reproducible artifacts (manifests, checksums, memos)
If you already think in terms of databases, data pipelines, and tests, this will feel familiar.
What you will build
Running the Chapter 01 CLI produces a tiny, deterministic dataset and writes a small “artifact bundle”
under outputs/ledgerloom/ch01/:
an event log (JSONL)
a journal view (CSV)
a ledger view (CSV with running balances)
a trial balance (CSV check/view)
an income statement and balance sheet (CSV)
a handful of WOW artifacts for explainability and reproducibility:
tables.md(tables rendered as Markdown)checks.md(human-readable invariant results)lineage.mmd(a pipeline diagram)manifest.json(what was produced + hashes)
Developer mapping
LedgerLoom intentionally translates accounting terms into developer mental models:
Accounting concept |
Developer mental model |
|---|---|
Journal entry |
Event (immutable fact) |
Journal (table) |
One possible view of the events |
General ledger |
Derived view / projection / materialized view |
Double-entry |
Invariant: |
Trial balance |
Automated check over account totals |
Financial statements |
Deterministic report views over a validated TB |
Audit trail |
Reproducible artifacts + lineage + diffs |
Run it
From the repo root (editable install):
python -m ledgerloom.chapters.ch01_journal_vs_eventlog --outdir outputs/ledgerloom --seed 123
Or use the Makefile target:
make ll-ch01
You should see something like:
Wrote LedgerLoom Chapter 01 artifacts -> outputs/ledgerloom/ch01
Open the outputs
Go to outputs/ledgerloom/ch01/ and scan these first:
summary.md— the “what happened” memotables.md— journal + trial balance + statements as Markdownchecks.md— invariant results (so you can eyeball PASS/FAIL)manifest.json— artifact list + hashes (reproducibility)
Core artifacts
The chapter produces multiple representations of the same underlying facts.
1) Event log (canonical truth)
The event log is the canonical record:
ledger.jsonl— canonical nameeventlog.jsonl— friendly alias used in the README/VISION
Each line is a JSON object representing one balanced entry. Here’s what a single line conceptually looks like (formatting added for readability):
{
"dt": "2026-01-02",
"meta": {"doc": "INV-0001"},
"narration": "Invoice client for services",
"postings": [
{"account": "Assets:AccountsReceivable", "credit": "0", "debit": "1000.00"},
{"account": "Revenue:Services", "credit": "1000.00", "debit": "0"}
]
}
Why JSONL?
append-only
diff-friendly
easy to stream
easy to validate
The journal table, ledger view, and reports are derived from this immutable log.
2) Journal view (classic accounting table)
journal.csv is a traditional journal view:
one row per posting
explicit debit/credit columns
includes
entry_idandline_nofor readability
This is a view of the event log, not a separate source of truth.
3) Ledger view (developer-friendly derived view)
ledger_view.csv is a derived projection that feels like a “database view”:
per-posting deltas
per-account running balances
type-aware sign convention (assets/expenses increase on debits; income/liab/equity increase on credits)
This is where developers often “get it”: the general ledger is a materialized view.
Checks and reports
Double-entry invariant
LedgerLoom treats double-entry as a testable constraint:
each entry must satisfy:
sum(debits) == sum(credits)
You can see this in two places:
code-level validation (entries validate as balanced)
artifact-level proof:
entry_balancing.csv— debits/credits totals per entrychecks.md— PASS/FAIL summary
Trial balance
trial_balance.csv is an automated check/view:
it aggregates balances by account
it only exists because the event log is valid
Statements
The statements are deterministic summaries over the trial balance:
income_statement.csvbalance_sheet.csv(includes aCheckrow)
In this early chapter, the balance sheet includes a computed close effect:
Net income is treated as an increase to equity (
EquityAfterClose)The accounting equation check is:
Assets - (Liabilities + EquityAfterClose) == 0
If the Check row is non-zero, something is wrong.
“Wow” factor artifacts
LedgerLoom isn’t just about computing numbers — it’s about engineering trust. So Chapter 01 produces several “audit-friendly” artifacts:
entry_explanations.md— narrative explanation of each entryassumptions.md— scope + what is intentionally not modeled yettables.md— key tables rendered as Markdown for fast inspectionroot_bar_chart.md— a tiny visual sanity check (bars by account root)lineage.mmd— a diagram of the pipeline (events → views → reports)run_meta.json— hashes + sizes of artifacts (reproducible builds)manifest.json— human-friendly artifact catalog
These are designed to be useful in:
PR reviews
debugging
teaching
“audit trail” conversations
Try a modification (exercise)
Open the chapter runner:
src/ledgerloom/chapters/ch01_journal_vs_eventlog.py
Add a new entry to the demo, for example:
owner invests cash (Assets:Cash up; Equity:OwnerCapital up)
Then re-run:
make lint
pytest -q
make ll-ch01
If you added an unbalanced entry, one of the checks will fail (and that’s the point).
Next
Chapter 02 explains the “debit/credit” system as an encoding choice and introduces a modern signed representation.