|
| 1 | +--- |
| 2 | +title: Verify this review |
| 3 | +description: Re-run LoopOver's published backtest corpus yourself — download a checksummed snapshot, replay the same scorer, and compare against the published numbers. |
| 4 | +eyebrow: Core concepts |
| 5 | +--- |
| 6 | + |
| 7 | +## Why this page exists |
| 8 | + |
| 9 | +LoopOver publishes measured per-rule precision on the [fairness report](/fairness). Numbers on a |
| 10 | +website only build trust if a skeptic can check them without asking anyone's permission. This |
| 11 | +page is the end-to-end walkthrough: export the same corpus snapshot the numbers come from, verify |
| 12 | +its checksum, replay the same scorer over it, and compare what you get against what is published. |
| 13 | + |
| 14 | +Everything below runs read-only against a database export and pure functions from |
| 15 | +`@loopover/engine`. Nothing posts anywhere, nothing needs an API key. |
| 16 | + |
| 17 | +## 1. Export the corpus snapshot |
| 18 | + |
| 19 | +Every rule's fired/override history exports as a versioned, checksummed JSON snapshot |
| 20 | +([backtest & calibration](/docs/backtest-calibration) explains how that history is recorded): |
| 21 | + |
| 22 | +```bash |
| 23 | +npx tsx scripts/backtest-corpus-export.ts --rule-id linked_issue_scope_mismatch --output corpus.json --remote |
| 24 | +``` |
| 25 | + |
| 26 | +On a self-host deployment, point the same CLI at your own Postgres instead: |
| 27 | + |
| 28 | +```bash |
| 29 | +npx tsx scripts/backtest-corpus-export.ts --rule-id linked_issue_scope_mismatch --output corpus.json --pg "$DATABASE_URL" |
| 30 | +``` |
| 31 | + |
| 32 | +The snapshot's `checksum` field is a SHA-256 over the canonicalized cases (keys sorted, so |
| 33 | +property order can never change the hash). The fairness report's *reproducibility freeze point* |
| 34 | +shows the checksum of the corpus behind the latest persisted backtest run — an export of the same |
| 35 | +window reproduces the same checksum, byte for byte. |
| 36 | + |
| 37 | +## 2. Verify the checksum |
| 38 | + |
| 39 | +The manifest is self-verifying: recompute the hash over its own `cases` array and compare it to |
| 40 | +the recorded `checksum`. The canonicalization lives in `scripts/backtest-corpus-export-core.ts` |
| 41 | +(`buildBacktestCorpusManifest`), so the check is one short script: |
| 42 | + |
| 43 | +```bash |
| 44 | +node --experimental-strip-types -e ' |
| 45 | +import { readFileSync } from "node:fs"; |
| 46 | +import { buildBacktestCorpusManifest } from "./scripts/backtest-corpus-export-core.ts"; |
| 47 | +const saved = JSON.parse(readFileSync("corpus.json", "utf8")); |
| 48 | +const recomputed = buildBacktestCorpusManifest(saved.ruleId, saved.cases); |
| 49 | +console.log(recomputed.checksum === saved.checksum ? "checksum OK" : "CHECKSUM MISMATCH"); |
| 50 | +' |
| 51 | +``` |
| 52 | + |
| 53 | +## 3. Replay the scorer |
| 54 | + |
| 55 | +The published precision comes from the same pure functions any Node script can import: |
| 56 | +`scoreBacktest` replays a classifier over the labeled cases, and `compareBacktestScores` applies |
| 57 | +the Pareto-floor verdict between two scores. Replaying the shipped confidence floor over your |
| 58 | +verified snapshot: |
| 59 | + |
| 60 | +```bash |
| 61 | +node --experimental-strip-types -e ' |
| 62 | +import { readFileSync } from "node:fs"; |
| 63 | +import { buildConfidenceThresholdClassifier, scoreBacktest } from "@loopover/engine"; |
| 64 | +const saved = JSON.parse(readFileSync("corpus.json", "utf8")); |
| 65 | +const report = scoreBacktest(saved.ruleId, saved.cases, buildConfidenceThresholdClassifier(0.5)); |
| 66 | +console.log(report); |
| 67 | +' |
| 68 | +``` |
| 69 | + |
| 70 | +- **"Reversed" is the positive class** — a prediction of `reversed` says the rule's original |
| 71 | + firing was wrong, and it is scored against what a human actually decided. |
| 72 | +- **`null` is never `0`.** Precision and recall stay `null` below the decided-sample floor; |
| 73 | + the fairness report renders that as *insufficient data*, never as a zero. |
| 74 | + |
| 75 | +## 4. Compare against the published numbers |
| 76 | + |
| 77 | +The [fairness report](/fairness) renders each rule's decided-case count and measured precision |
| 78 | +from the public stats endpoint (`/v1/public/stats`, the `rulePrecision` block). The aggregated |
| 79 | +run history is also readable directly: |
| 80 | + |
| 81 | +```bash |
| 82 | +npx tsx scripts/backtest-track-record.ts --db loopover --remote |
| 83 | +``` |
| 84 | + |
| 85 | +Your replayed `confirmed / decided` for a rule should match the published precision for the same |
| 86 | +window; the freeze-point checksum ties the published numbers to the exact corpus you just |
| 87 | +verified. |
| 88 | + |
| 89 | +## What this proves — and what it does not |
| 90 | + |
| 91 | +<Callout variant="note"> |
| 92 | + **Proved:** the published scores are real computations over a real, checksummed, replayable |
| 93 | + corpus — not hand-entered numbers. Anyone can independently reproduce them from the snapshot. |
| 94 | + |
| 95 | + **Not proved:** that the live gate *ran this exact code* when it made its decisions. Verifying |
| 96 | + the runtime itself is an attestation problem — a trusted-execution boundary, not a replay |
| 97 | + boundary — and is tracked as its own explicitly-scoped decision in |
| 98 | + [#8136](https://github.com/JSONbored/loopover/issues/8136) and |
| 99 | + [#8137](https://github.com/JSONbored/loopover/issues/8137). This walkthrough is honest about |
| 100 | + stopping at the reproducibility line rather than implying the stronger guarantee. |
| 101 | +</Callout> |
0 commit comments