Foreledger — a durable archive of recurring forecast runs with horizon-keyed accuracy evaluation, cross-model/version comparison, and origin-scoped as-of queries.
Project description
Foreledger
A durable ledger for your forecasts — so you can finally answer "how accurate are we, really?"
The problem
Every team that forecasts — demand, revenue, staffing, inventory — produces predictions on a schedule, acts on them, and then quietly throws them away. Next week brings a new forecast, and the old one is overwritten in a spreadsheet, buried in a model artifact, or simply gone.
That makes the questions leadership actually asks surprisingly hard to answer honestly:
- "How far out can we trust the forecast?" Nobody stored what was predicted 14 days ahead vs. 2 days ahead, so nobody knows where accuracy falls off.
- "Is the new model actually better?" The data scientist's backtest says yes, but there's no like-for-like record of what each model predicted in production.
- "Why did we order so much stock in March?" The forecast that drove the decision no longer exists — only the embarrassing outcome does.
And there's a subtler trap: the truth itself changes. Actuals get restated — a late data feed, a returns adjustment, a finance correction. If you score yesterday's forecast against today's revised numbers without keeping track, your accuracy reports quietly stop meaning anything.
What Foreledger does
Foreledger treats forecasts the way accounting treats money: every entry is recorded, nothing is erased, and the books always reconcile.
You push each forecast run into the ledger as it happens (one line of code in your existing pipeline), register actuals as they arrive — including revisions and an optional "official" designation for the numbers finance signed off on — and Foreledger answers, instantly and reproducibly:
- Accuracy by horizon — a curve showing exactly how error grows the further out you predict, for any model, series, or time window.
- Model vs. model — side-by-side comparisons across models and versions over a common scope, with deltas against your designated champion. Every row reports its own sample count and coverage, and each value provably equals the corresponding single-model query — no hidden re-weighting.
- Origin-scoped snapshots — query the current record filtered to runs dated on or before any past day, with no later-dated runs leaking in. One honest caveat: an explicit overwrite revises this view (supersessions are recorded), so it reflects the archive's current state of those runs, not a transaction-time replay.
When the data can't fully support an answer, Foreledger says so out loud: a scope with gaps is flagged partial with the missing count attached, and one where nothing can be scored is an explicit insufficient — never a number that quietly reads as perfect accuracy.
Quick start
pip install foreledger
python examples/quickstart.py # full demo on synthetic data, zero config
from foreledger import ForecastArchive
archive = ForecastArchive("./forecast_ledger")
# after every forecast run:
archive.ingest(predictions_df, model_id="prophet", model_version="2.1")
# as actuals arrive (revisions welcome):
archive.register_actuals(actuals_df, source="warehouse")
# the payoff:
curve = archive.accuracy_curve(metric="MAE", model_id="prophet", model_version="2.1")
print(curve.to_frame())
Read the full quick-start guide →
Key capabilities
- Append-only forecast archive keyed on
(model, version, series, run date, target date)— parallel model versions coexist; ingestion is atomic and idempotent, so crashed or replayed pipelines can't corrupt or duplicate history. - Revisable actuals log — every restatement is kept; accuracy can be scored against the latest numbers or only the official ones, and same-timestamp conflicts between feeds are resolved by your priority rules or flagged loudly, never picked silently.
- Built-in metrics (MAE, RMSE, MAPE, MASE) plus a protocol for registering your own — custom metrics run behind an error/timeout guard, so one that crashes is skipped and one that hangs is skipped and quarantined for the session, rather than corrupting a recompute. (Containment, not a security sandbox: registered code runs in-process.)
- Champion/challenger workflow — designate a champion per model and every comparison reports challenger deltas automatically.
- Drill-down provenance — any headline accuracy number can be exploded into the exact forecast/actual pairs behind it, and they reconcile exactly.
- Crash-safe and tamper-evident — data becomes visible only when its
manifest commits, so a crash mid-write leaves the ledger at its prior
state; every committed file is fingerprinted (size/mtime/sha256), checked
cheaply on every query, and fully hash-audited by
reconcile(). Files deleted or modified outside the library fail loudly with a typed error — never silently absent rows. - Local-first, warehouse-ready — v1 stores Parquet on your disk via DuckDB (your data never leaves your machine); the storage layer is built behind a dialect-aware seam so a Snowflake backend (v1.1) drops in without changing your code.
How it works (the technical bit)
Foreledger is a small, layered Python library (≥ 3.11):
- Raw archive — append-only Parquet, the permanent source of truth, with an on-disk format version gate. Visibility is committed via a run manifest, which is what makes ingestion all-or-nothing.
- Actuals log — model-independent, append-only revisions keyed
(series, target, source, recorded_at), with sticky official designations. - Integrity registry — a fingerprint journal over every committed file, doubling as the commit log: entries are staged before a write becomes visible and confirmed after, so the store can always tell a failed write's leftovers from externally tampered data.
- Accuracy summary — a precomputed, disposable cache rebuilt eagerly on
every write. Queries route to it invisibly and fall back to raw
computation; both paths share one code path and a
reconcile()check asserts they agree exactly. - Backend seam — all storage and query runs through an engine-neutral, dialect-parameterized layer; a CI guard (AST scan) ensures no DuckDB-only code leaks out of the backend module.
Architecture and the decisions behind it are documented in docs/internal/: the tech spec, ADRs 001–007, and the implementation plan. Contributor ground rules live in AGENTS.md.
Development
pip install -e ".[dev]"
pytest # incl. atomicity, reconciliation, replay, no-leakage tests
ruff check . && ruff format --check .
mypy src # strict
python examples/quickstart.py
Status & roadmap
Working MVP (Phases 1–4 of the plan): the full ingestion → actuals → evaluation surface described above, gated by CI and released on PyPI as v0.1 (Phase 5). Next: the Snowflake warehouse backend as v1.1 (Phase 6).
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file foreledger-0.1.0.tar.gz.
File metadata
- Download URL: foreledger-0.1.0.tar.gz
- Upload date:
- Size: 99.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d4a0213d3b3260650a663f1cf353de996834697b8ba8a6efb6fbdd98f982c24c
|
|
| MD5 |
43768698a3a51c39a95f66610ab1f128
|
|
| BLAKE2b-256 |
87de8e33a252996bf3309be5a7f6c2d950e6b54ec6539b1e03cde408078d064b
|
Provenance
The following attestation bundles were made for foreledger-0.1.0.tar.gz:
Publisher:
publish.yml on mbagalman/Foreledger
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
foreledger-0.1.0.tar.gz -
Subject digest:
d4a0213d3b3260650a663f1cf353de996834697b8ba8a6efb6fbdd98f982c24c - Sigstore transparency entry: 2140012403
- Sigstore integration time:
-
Permalink:
mbagalman/Foreledger@27840571e3b935879a12100e909d9420939334cc -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mbagalman
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@27840571e3b935879a12100e909d9420939334cc -
Trigger Event:
release
-
Statement type:
File details
Details for the file foreledger-0.1.0-py3-none-any.whl.
File metadata
- Download URL: foreledger-0.1.0-py3-none-any.whl
- Upload date:
- Size: 74.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
07d51e9746db1b73b859e1a2968268345da8a829a82f03dbe532184087c27263
|
|
| MD5 |
7d4d3b83668d085a5ad08954e6b5a3dc
|
|
| BLAKE2b-256 |
70e5564a7c2f668e5c8da2676d5133240cf06a1a0c6f039f7a98bcdab7e13de1
|
Provenance
The following attestation bundles were made for foreledger-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on mbagalman/Foreledger
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
foreledger-0.1.0-py3-none-any.whl -
Subject digest:
07d51e9746db1b73b859e1a2968268345da8a829a82f03dbe532184087c27263 - Sigstore transparency entry: 2140012484
- Sigstore integration time:
-
Permalink:
mbagalman/Foreledger@27840571e3b935879a12100e909d9420939334cc -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mbagalman
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@27840571e3b935879a12100e909d9420939334cc -
Trigger Event:
release
-
Statement type: