Your AI agent will admit any plausible idea. qpop makes it earn each one — gated, pre-registered, and falsifiable.
A Claude Code plugin that turns an over-eager LLM into a disciplined researcher. The headline result is counterintuitive: a well-run agent rejects most of what it surfaces — and that restraint, recorded in a tamper-evident ledger, is the point.
Two audiences. Claude Code users: install the plugin — that's the whole setup. Researchers / citers: jump to the paper in Learn more. One project, three names: the plugin & repo
qpop, the Python packageforward-qpop, and the method, Forward-QPOP.
LLMs are brilliant at sourcing ideas and unreliable as decision engines. Left ungated, an LLM screener over-admits by construction — it is rewarded for finding ideas, not refusing them — and it confabulates, agrees with itself (sycophancy), and, when "backtested," already knows the answer (look-ahead leakage). The missing piece isn't better prompts. It's auditable restraint.
It wraps your agent's research in a discipline and makes every decision auditable:
- Rejects most candidates, for defensible reasons — deterministic gates + a bear case written before the recommendation.
- Pre-registers the survivors to a tamper-evident, hash-chained ledger — the claim, dated evidence, and measurable exit triggers, committed before the outcome is known.
- Validates forward, not by a leaky backtest.
Pilot evidence (the methods paper,
research/paper/): in exploratory system contrasts on one 38-card batch, every weakened-discipline configuration admitted far more than the full pipeline (37–100% vs 0%; a configuration-level observation — no single component's causal contribution is identified); on a documented, seeded sample of n=40 rejections a held-out bull-only LLM auditor agreed with 31/40 (0.775) of the engine's rejections — missing the ≥0.80 criterion later registered for the prospective H5 window, a failure reported as a result (a full-record LLM adjudicator overrode all nine disagreements; model disagreement under asymmetric information, not a human audit or ground truth — rejection quality is unresolved pending a human lane); "no action" is the modal outcome. These are pilot metrics — they validate process discipline, not investment performance.
/plugin marketplace add yixingz3/qpop
/plugin install qpop@qpop
That's it. The auditable-research discipline activates when you screen ideas or act on a finding, and
the /qpop:* commands below are ready. (Other agents: the discipline is portable markdown — see
Learn more.)
After installing, ask Claude Code:
"Screen these 3 research claims with qpop and pre-register only the survivors."
A disciplined run looks like this (illustrative):
3 candidates screened
2 rejected — tertiary-only evidence / fails replace-don't-stack
1 preregistered — H-001
entry_hash: sha256:9f3c… (chained to prev)
verify: OK — 1 entry, chain intact
"2 of 3 rejected" is the feature, not a bug — no action is the correct, modal outcome. No Claude
Code? pip install forward-qpop gives you the same ledger from the command line (see Learn more).
| Command | What it does |
|---|---|
auditable-research |
(auto) the full discipline, applied when you screen ideas or evaluate a finding |
/qpop:preregister |
register a hypothesis to the tamper-evident ledger before evaluating |
/qpop:review |
audit current claims / a diff for stories, leakage, over-admission, missing pre-registration |
/qpop:verify |
verify a ledger's integrity — detect any post-hoc edit, insertion, or reorder |
The ledger is a real hash chain (entry_hash = sha256(content_hash ‖ prev_hash)): edit a past entry,
insert one, delete one from the interior, or reorder them, and verify fails (and exits
non-zero — drop it in CI). Deleting a suffix leaves a valid shorter chain: detecting rollback
needs an anchored head (anchor / --expected-head).
qpop is auditable research infrastructure, not a turnkey trading engine. What ships in this repo:
| Area | Status |
|---|---|
Claude Code plugin discipline (auditable-research + /qpop:*) |
Working — v0.1 |
Hash-chained Python ledger (forward_qpop) + CLI |
Working (98 tests collected: 97 passed, 1 network test skipped by default) |
Sequential trigger test (evalue) — per-hypothesis anytime-valid rule wired to the ledger (forward-qpop evalue); guarantee holds in registered (fixed-membership) mode under stated assumptions (WI-40/44/45/46 hardening: single verified-snapshot replay, lifecycle validation at write and replay, derived-snapshot sidecar, log-space sup-decision latching, alias-safe paths, outcome-independent reporting assumption); no book-wide multiplicity control |
Working — per-hypothesis scope (25 e-process + 45 ledger-integration tests) — e-value / Ville, methods note |
Local anchor manifest (anchor / verify-anchor) |
Working — manifest + drift-detection + git / local OpenTimestamps stamp |
External timestamp anchor (anchor external / verify-external) |
Working — manual/opt-in by design (WI-30, 2026-07-09; not an auto-hook on ledger writes) — submits to OpenTimestamps, sidecar receipt + drift-detection (details) |
| JSON Schemas for cards / entries / runs | Included (schemas/) |
| Synthetic fixtures + worked examples | Included (examples/) |
| Methods paper — theory + pilot evidence | Included (PDF) |
Full SOURCE → GATE → EVALUATE engine (AI-supply-chain) |
Specified — interfaces/contracts in src/; the reference implementation is private and being generalized |
PyPI package (forward-qpop) |
Published — pip install forward-qpop |
| Forward performance results | Pending — not claimed |
Nothing here is finance-specific: the discipline and the ledger are domain-agnostic, and finance is a deliberately adversarial testbed. The same flow fits an agentic literature review or an ML-eval claim — and the metric the ablations measure, the over-admission rate (how often an agent admits plausible-but-weak ideas), is itself a reusable reliability benchmark.
The hash chain is tamper-evidence, not a clock. Be precise about the guarantee:
| Claim | Chain alone? | What closes the gap |
|---|---|---|
| A past entry was edited | ✅ detected | hash verification |
| An entry was inserted / interior-deleted / reordered | ✅ detected | hash-chain verification |
| A suffix was deleted (rollback to a valid prefix) | anchor external / an anchored expected head (--expected-head) |
|
| An entry existed before the outcome | anchor external + OpenTimestamps (or a pushed public commit) |
|
| The LLM's reasoning was correct | ❌ no | human / source review |
| The strategy is profitable | ❌ no | a forward window + the validity checklist |
The "before the outcome" guarantee needs an external anchor — and qpop ships one:
forward-qpop anchor <ledger> writes a local manifest committing to the ledger head, and
verify-anchor detects any drift since. Bind it to time by committing the manifest to a public repo
(the commit date is the anchor) or by submitting it to a public, append-only timestamp service.
Anchoring is manual/opt-in by design, not an auto-hook (decided 2026-07-09, WI-30).
Nothing calls anchor / anchor external automatically from register/update/close —
calendar-server reachability is outside this repo's control, and a failed auto-anchor must
never block a ledger append (the hash chain is the load-bearing, always-offline guarantee;
external anchoring is a strictly weaker, best-effort addition on top of it). Recommended
cadence: anchor at hypothesis registration, at close, and before publishing any
results that cite the ledger. See the module docstring for
the full rationale.
forward-qpop anchor external <ledger> --method ots submits the manifest's head digest to
OpenTimestamps (a public, append-only Bitcoin-backed timestamp
service) and records the outcome — method, service, digest, submission time, status — in a
<ledger>.external-anchor.json sidecar next to the ledger. verify-external re-checks that
sidecar's digest against the current ledger head, so any rewrite after submission is loud, not
silent:
forward-qpop anchor <ledger> # local manifest first (always required)
forward-qpop anchor external <ledger> --method ots # submit the head digest externally
forward-qpop verify-external <ledger> # detect drift since the external submissionRoute chosen: OpenTimestamps, not Sigstore/Rekor. Both were evaluated:
- OpenTimestamps — one optional dependency (
pip install forward-qpop[anchor], wrapsopentimestamps-client), stdlib-only integration (subprocess), and reuses this repo's existing localots_stampscaffolding. Honest caveat: a fresh stamp is "submitted," not yet "confirmed" — the Bitcoin attestation completes over hours, and the receipt is upgradeable later withots upgrade. - Sigstore/Rekor (
hashedrekord) — would give an immediate inclusion proof + log index, but needs an ephemeral-key-signed entry, pulling incryptography/sigstorefor a payload this project otherwise has no use for (no code signing here). Passed on for now given the dependency weight; may revisit if Rekor's immediacy becomes worth the tradeoff.
Degradation is always loud. No ots binary on PATH, a network failure, or an unreachable
calendar server all exit non-zero with a specific message — anchor external never writes a
sidecar claiming success it didn't get. The unit tests exercise this against a faked backend (no
network needed for pytest); a true-network round-trip test exists but only runs when you
explicitly opt in (QPOP_TEST_LIVE_OTS=1) — see repro/ and
tests/test_external_anchor.py for both.
- The method — the 7-step admission ladder (falsifiable claim → deterministic gates → bear-case-first → source tiers → forward pre-registration → forward-not-backtest → on-balance). Auto-applied by the
auditable-researchskill. - Verify the released mechanics —
repro/: tests, tamper demo, schema validation, the anchor round-trip, and the paper build — each with expected output. (For the paper's empirical results, a reader can inspect the released prompts and recompute the reported aggregate arithmetic; the individual empirical decisions are not auditable or re-runnable from this repo — see the paper's Reproducibility and Release section.) - Data contracts —
schemas/: JSON Schema for candidate cards, ledger entries, evidence, exit triggers, and run manifests. - Python library —
pip install forward-qpop(or runscripts/qpop.pyfrom a clone): the dependency-free ledger + anchor. API in the package README. - The paper — PDF / source: theory, pilot evidence, and the over-admission-rate (OAR) benchmark. If you build on it, please cite
CITATION.cff. - Worked examples —
examples/: the AI-supply-chain funnel, a portable template, and a non-finance ML-benchmark pre-registration. - Other agents & roadmap — the discipline is portable markdown in
skills/(Codex / other agents can adopt it). Next: Codex/Cursor adapters, an MCP server, a LangChain/LangGraph wrapper. (The external timestamp anchor, previously roadmapped as "turnkey Sigstore/Rekor," shipped via OpenTimestamps instead — see above for the tradeoff.)
Not investment advice, not a stock-picker, not a claim to beat the market. The finance domain is a
deliberately adversarial testbed (markets punish wishful thinking); qpop is a research
discipline. See DISCLAIMER.md and ETHICS.md.
MIT. Implements the Forward-QPOP protocol — see research/ and
CITATION.cff.