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.

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.

  1. Only two metrics are checked: active_customers and annualized_mrr. Unregistered metrics (including logo_churn) return UNVERIFIED. These are reported as open questions.
  2. 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.
  3. 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.
  4. Demo and sample material is labelled synthetic. Production runs carry Data: SYNTHETIC — not a real company or client result, or clear source attribution.