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: sum(debits) == sum(credits)

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” memo

  • tables.md — journal + trial balance + statements as Markdown

  • checks.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 name

  • eventlog.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_id and line_no for 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 entry

    • checks.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.csv

  • balance_sheet.csv (includes a Check row)

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 entry

  • assumptions.md — scope + what is intentionally not modeled yet

  • tables.md — key tables rendered as Markdown for fast inspection

  • root_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.