Tamper-evident, append-only forward pre-registration ledger for auditable, leakage-resistant research (the Forward-QPOP protocol).
Project description
forward-qpop
A tamper-evident, append-only forward pre-registration ledger for auditable, leakage-resistant research.
Register a hypothesis — a claim, dated evidence, measurable exit triggers, and a prior —
before the evaluation window opens. Each entry is content-hashed over its frozen fields and
chained to its predecessor, so the record proves what was predicted and that no entry was later
edited, inserted, deleted, or reordered. Proving it was registered before an outcome (wall-clock
time) needs an external anchor — see anchor / verify-anchor.
This is the domain-agnostic core of the Forward-QPOP protocol from the methods paper Forward-Registered, Auditable LLM-Assisted Research — useful for any pre-registered work (ML experiments, forecasts, studies), not only the finance testbed in the paper.
- Zero dependencies — pure Python standard library.
entry_hash = sha256(content_hash ‖ prev_hash)— a real hash chain, not just a set of independently-hashed rows.- CI-friendly —
forward-qpop verify ledger.jsonlexits non-zero on tampering.
Install
pip install forward-qpop
Quickstart
from forward_qpop import Ledger
led = Ledger("ledger.jsonl")
# 1. Pre-register BEFORE the evaluation window:
led.register(
"H-AI-01",
claim="Method X reduces silent production-ML failures vs the ungated baseline.",
mechanism="Deterministic gates reject low-evidence candidates before the expensive step.",
prior=0.5,
evidence=[{"summary": "pilot result", "tier": "primary", "date": "2026-06-24"}],
exit_triggers=[{"id": "no_effect", "metric": "failure-rate delta", "op": "~0",
"data_source": {"tier": "primary"}}],
fields={"domain": "ai-reliability"}, # any domain-specific payload, also hashed
)
# 2. Record belief updates as evidence arrives (tertiary-only is blocked by default):
led.update("H-AI-01", evidence=[{"summary": "replication", "tier": "secondary",
"date": "2026-09-01"}])
# 3. Close with a pre-committed outcome (supported / weakened / falsified):
led.close("H-AI-01", "supported", observed={"failure_rate_delta": -0.31})
# 4. Verify integrity at any time:
res = led.verify()
print(res.ok, res.n_entries, res.problems)
CLI
forward-qpop verify ledger.jsonl # exit 0 if intact, 1 (with details) if tampered
forward-qpop show ledger.jsonl # list entries
forward-qpop anchor ledger.jsonl # manifest committing to the head (bind to a public commit / OpenTimestamps)
forward-qpop verify-anchor ledger.jsonl # detect any drift since anchoring
Why forward, and why a chain
A backward test of an LLM-scored process is structurally invalid (the outcomes are
already in the model's training data). Pre-registration replaces "fit the past" with
"commit, then observe the future"; the hash chain makes that commitment auditable — an external
reviewer can confirm that no past entry was silently changed. Proving the entry existed before its
outcome additionally requires binding the ledger head to an external, publicly-dated record (a pushed
Git commit, or OpenTimestamps) via anchor / verify-anchor.
License
MIT. Part of https://github.com/yixingz3/qpop.
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 forward_qpop-0.1.2.tar.gz.
File metadata
- Download URL: forward_qpop-0.1.2.tar.gz
- Upload date:
- Size: 23.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fbabdc882e1c8ca6031d05e6217c878ea399363ae3c3c9deb66fae8233bea853
|
|
| MD5 |
2e46e1393c4f2a990626fc28c7973bc4
|
|
| BLAKE2b-256 |
8634c033dcca8ed444273ec0ad5d1eccdd6110b7aed9fbf5f2259324aa737b2c
|
Provenance
The following attestation bundles were made for forward_qpop-0.1.2.tar.gz:
Publisher:
publish.yml on yixingz3/qpop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
forward_qpop-0.1.2.tar.gz -
Subject digest:
fbabdc882e1c8ca6031d05e6217c878ea399363ae3c3c9deb66fae8233bea853 - Sigstore transparency entry: 1999156796
- Sigstore integration time:
-
Permalink:
yixingz3/qpop@b87210a95a9a5a7c15572702a4cdab1587f92e2f -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/yixingz3
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b87210a95a9a5a7c15572702a4cdab1587f92e2f -
Trigger Event:
release
-
Statement type:
File details
Details for the file forward_qpop-0.1.2-py3-none-any.whl.
File metadata
- Download URL: forward_qpop-0.1.2-py3-none-any.whl
- Upload date:
- Size: 15.0 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 |
d1d0dc6a02b804349287593f2998f5526f692789573d48bce32e7ec7fad861a4
|
|
| MD5 |
133dfd48a0c4247984148cad8338b0e0
|
|
| BLAKE2b-256 |
4762ae2e20aa0660bae875bf62d3c341f93dcafdb0815b6bfb54a736fe1d72aa
|
Provenance
The following attestation bundles were made for forward_qpop-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on yixingz3/qpop
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
forward_qpop-0.1.2-py3-none-any.whl -
Subject digest:
d1d0dc6a02b804349287593f2998f5526f692789573d48bce32e7ec7fad861a4 - Sigstore transparency entry: 1999156882
- Sigstore integration time:
-
Permalink:
yixingz3/qpop@b87210a95a9a5a7c15572702a4cdab1587f92e2f -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/yixingz3
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b87210a95a9a5a7c15572702a4cdab1587f92e2f -
Trigger Event:
release
-
Statement type: