Methodology
How PreDealCheck checks claims against source data, and what it does and does not do.
What this is
You get a tool that checks seller claims against the source tables they cite.
PreDealCheck matches claims (active customers, annualized MRR) to source tables (customer schedule, revenue schedule). It finds material, source-linked discrepancies and flags them for your review. It is a diligence aid, not legal or accounting certification, fraud allegation, or investment advice.
The pipeline
You upload a manifest and get a report with four possible states per claim.
manifest (claims + source tables) → per-claim recomputation against the cited table → one of four honest states per claim → report (contradicted / supported / questions / unverified)
Entry point: whynot review <manifest.json> or whynot.engine.review(dict).
The four states
Every claim gets one state. A contradiction requires a present, readable table and matching arithmetic.
| State | Meaning |
|---|---|
SUPPORTED |
The table reproduces the claim within agreed tolerance. |
CONTRADICTED |
The table is present and readable and materially disagrees at the same period and basis. |
DEFINITION_MISMATCH |
The numbers differ, and so do the period, basis, or units — an open question, not a contradiction. |
UNVERIFIED |
The cited table is absent, unreadable, or no reconciler exists. |
A contradiction emerges only when a present, readable table was recomputed and arithmetic disagrees like-for-like at the same period and basis. Missing supporting tables yield UNVERIFIED. This control is in tests/test_engine.py: inflate a claim against a present table; you get a contradiction. Delete that table; the same claim becomes UNVERIFIED.
Metric contracts
Two metrics are implemented. All others return UNVERIFIED.
active_customers
Count of customers with live status. Counts unique customer_id with at least one row in [active, live] and no other status. Mixed live and lost rows are excluded as ambiguous.
annualized_mrr
Sum of explicit recurring live MRR, multiplied by 12. Formula: sum(mrr for LIVE rows where recurring=true) × 12. This is ChartMogul's Annual Run Rate: trailing MRR projected to 12 months. It is not revenue recognized for a year.
LIVE rows with missing, blank, unparseable mrr, or no explicit recurring=true flag are excluded. If all LIVE rows are excluded, the metric is uncomputable.
Snapshot churn — why it is not checked
You get an honest answer: logo churn from snapshots always returns UNVERIFIED.
Period churn requires two things: a starting cohort and period events (joins, reactivations, churns). The ratio lost rows / all rows from an arbitrary snapshot shows the current state, not the flow. A missing row may have joined after the snapshot started, or may still be active on another contract. Reporting UNVERIFIED is the honest answer, because the snapshot alone cannot measure churn.
Materiality and definitions
Small gaps are absorbed. Large gaps work around period mismatches only when they are genuine. You get a clear question, not a hidden escape.
- Materiality (default 1%,
--materiality): gaps of 1% or less are absorbed. This absorbs rounding and presentation differences. - Definition guard: a material gap where claim and table describe different periods or bases (calendar 2024 vs fiscal 2025, MRR vs ARR) is
DEFINITION_MISMATCH— an open question, not a contradiction. This protects a consistent seller from a manufactured flag. The mismatch is reported as an open question you must close. - Large gaps persist: gaps of 10% or more remain material even if period or basis differ. Labels cannot be used to escape a real discrepancy.
Reproducibility
Every finding carries the details you need to verify it by hand.
Each finding includes: source name, SHA-256 digest of exact bytes loaded, source version, contributing row numbers, as-of date, currency, claimed value, recomputed value, relative gap, and a reason string stating arithmetic in words.
The engine is deterministic and stdlib-only. No clock, no randomness, no network, no LLM call in the core path. Arithmetic uses decimal.Decimal, never binary floats. The same manifest yields byte-identical output, always.
Limits
You know exactly what is checked and what is not.
- Only two metrics are checked:
active_customersandannualized_mrr. Unregistered metrics (includinglogo_churn) returnUNVERIFIED. These are reported as open questions. - The engine aligns rows and sums fields. It does not read tables as prose or detect intent. A qualified reviewer keeps responsibility for legal, technical, and business materiality.
- Findings are as good as supplied sources. Scope omissions, unreadable documents, parsing defects, and ambiguous rows are reported as defects and excluded. Missing data is never guessed.
- Demo and sample material is labelled synthetic. Production runs carry
Data: SYNTHETIC — not a real company or client result, or clear source attribution.