Metadata-Version: 2.4
Name: forward-qpop
Version: 0.1.2
Summary: Tamper-evident, append-only forward pre-registration ledger for auditable, leakage-resistant research (the Forward-QPOP protocol).
Project-URL: Homepage, https://github.com/yixingz3/qpop
Project-URL: Repository, https://github.com/yixingz3/qpop
Project-URL: Paper, https://github.com/yixingz3/qpop/tree/main/research/paper
Author-email: Yixing Zheng <yz11739@stern.nyu.edu>
License: MIT
License-File: LICENSE
Keywords: auditability,forward-validation,hash-chain,look-ahead-bias,pre-registration,reproducibility,research-integrity,trustworthy-ai
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# 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`](#cli).

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.jsonl` exits non-zero on tampering.

## Install

```bash
pip install forward-qpop
```

## Quickstart

```python
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

```bash
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>.
