Accounting estimates are the most frequent audit inspection finding in the world, and the deficiency is rarely arithmetic. It is that the file shows agreement documented after the fact instead of challenge performed before the conclusion. Estimate Challenger is the auditor's side of that file, built the way I draw the trust boundary everywhere: code independently recomputes management's ECL from the raw loan tape and tests every assumption against the tape's own default history, the model may only phrase a challenge memo from cited computations, and the sign-off stays locked until a named partner dispositions every flag. The ledger's ordering is the deliverable: challenge first, conclusion last, hashes in between.
Real loan tape (UCI Statlog German Credit) · management's model layer seeded synthetic, and labelled as such in-app · simplified 12-month ECL
IFIAR's 2025 survey of inspection findings reported that 35% of inspected engagements had at least one deficiency, and auditing of accounting estimates sat at the top of the finding categories, as it has for years across PCAOB and international regimes (IFIAR, 2025). The pattern behind the number is familiar to anyone who has sat through a quality review. Management delivers an ECL model in Excel with the assumptions embedded in cells. The team ties a sample of inputs, "assesses reasonableness" by discussion, and drafts the challenge memo from that discussion, often after the conclusion has already been reached. Sensitivity analysis is a single alternative scenario if it exists at all.
Partners know the memo was written the night before archiving. Inspectors increasingly can tell. What the file cannot show is the one thing the standards actually ask for: evidence that the team considered information that contradicts management's assumptions, at a point in time when the conclusion could still have moved.
The obvious AI move here is the worst one. Hand the workpapers to a language model and ask it to draft the challenge-of-management memo, and you get fluent, confident scepticism on demand. The memo will say the team evaluated contrary evidence whether or not anyone did, because the model's job is to produce the document, and the document was always the easy part. That is the after-the-fact memo again, produced faster and with better prose. An inspector who asks "show me the computation this sentence rests on" gets the same silence as before.
There is a second failure underneath the first. Models agree with the premise they are handed. Ask one whether a 19% PD assumption is reasonable and it will find reasons, because manufacturing comfort is what unconstrained generation does when the data is silent. The one thing a challenge process cannot tolerate is a component that fills evidential gaps with plausible language. So this demo inverts the assignment. The model never assesses anything. Code does the assessing, and the model is confined to phrasing sentences around numbers the engine has already computed, with a forced abstention wherever the tape holds no evidence.
The engine recomputes management's estimate segment by segment (PD x LGD x EAD, cure-adjusted, macro overlay on top) directly from the raw loan tape, and issues a tolerance verdict against the booked figure. It then runs a sensitivity sweep for every assumption and, for each segment PD, derives the same parameter from the tape itself: the observed default rate and its 95% Wilson interval. An assumption sitting outside its own data's interval raises a contradiction flag. None of this involves a model.
Here is the deterministic decision at the centre, from the shipped file. The flag is a comparison against an interval the code derived from the entity's own data, and no one can phrase their way past it:
// 95% Wilson score interval for the observed default rate function wilson(k, n) { var z = 1.96, p = k / n, d = 1 + z * z / n; var c = (p + z * z / (2 * n)) / d; var w = z * Math.sqrt(p * (1 - p) / n + z * z / (4 * n * n)) / d; return [c - w, c + w]; } // THE DETERMINISTIC CHALLENGE: each segment PD vs the tape's own history. // A flag fires only when the assumption sits outside the 95% interval of the // segment's observed default rate. Code decides; nobody phrases this away. function contradictionChecks(stats, segs, asm) { return segs.map(function (s) { var x = stats[s], pd = asm.pd[s]; var flagged = pd < x.lo || pd > x.hi; return { seg: s, assumed: pd, obs: x.obs, n: x.n, k: x.k, lo: x.lo, hi: x.hi, flagged: flagged, dir: pd < x.lo ? "below" : (pd > x.hi ? "above" : "inside") }; }); }
The tape is the UCI Statlog German Credit dataset: 1,000 real consumer loans with default outcomes, grouped into four purpose segments. Management's synthetic model layer books an ECL of DM 366,800. The engine's independent recompute from the raw tape lands at DM 364,765, a difference of DM 2,035 (0.55%), inside the stated 2.0% tolerance. So the arithmetic holds, which is exactly why arithmetic-only testing would have closed this file quietly.
The assumptions do not hold. The household segment is assumed at a 19.0% PD while its own 473 loans show an observed default rate of 26.2% (95% interval 22.5% to 30.4%). The business segment is assumed at 24.0% against an observed 35.1% on 97 loans (interval 26.3% to 45.0%). Both assumptions sit below the bottom of their own data's interval, so both flags fire. Re-anchoring just those two PDs to their observed rates moves the estimate to DM 424,332, which is DM 57,532 and 15.7% above the booked figure. That is the size of the gap a discussion-based "reasonableness assessment" would have signed past.
Everything on the tiles is computed live in the browser from the shipped data: the 0.55% recompute difference, the 2 of 4 flags, the +15.7% re-anchored gap, and the ledger ordering. The run is deterministic, so Replay reproduces identical figures and identical hashes on every visit. This is what a governed challenge control plane looks like. It is not a client outcome and I make no claim that any engagement's numbers move by these amounts.
The limits. The German Credit tape is 1970s to 1980s German retail lending, small, dated, denominated in Deutsche Mark, and stratified at 300 bad in 1,000, so its default rates are far above any live book; the app labels this and the point is the pattern, not the parameters. The ECL is a simplified 12-month PD x LGD x EAD, without IFRS 9 staging, lifetime PD term structures or probability-weighted macro scenarios. Forward-looking overlays genuinely cannot be tested against historical data alone, and the app abstains there by design rather than pretending otherwise. Above all, professional judgment remains the auditor's. The engine evidences the challenge; it does not perform the judgment, and a partner could still disposition both flags badly. What the file would then show is that they did so knowingly, in writing, with the contrary evidence in front of them, which is precisely the accountability the standards intend.
Sweep the household PD across its own confidence interval, watch the tornado re-rank, read the memo's abstentions, then try the sign-off button before dispositioning the flags. Tamper with the ledger and watch Verify locate the exact entry. The whole run is client-side and reproducible.
I have drafted challenge-of-management memos the honest way and the other way, and the difference was never the prose. It was whether a computation existed underneath each sentence at the moment the sentence was written. That is why the scribe in this demo is the least powerful component I could build: it cannot assess, cannot conclude, and cannot speak where the data is silent, because the one skill language models bring to an audit file, producing conviction without computation, is the exact skill that creates estimates findings. The engineering effort went into the boring half, the recompute, the intervals and the chain, so that when the partner's name goes on the file, the timestamps already tell the story an inspector wants told.