When an assurer signs a GHG number, they are signing that it can be re-derived from evidence: the activity data, a named emission factor, a stated method, all inside a defined boundary. This workbench does exactly that. It recomputes every disclosed figure independently, runs a tolerance test, and assigns Pass · Exception · Cannot-Verify. The arithmetic is the easy part. The hard part is the factor and method judgement, so the tool makes that judgement visible and interactive, and never lets a language model near a single number.
From financial year 2025 the EU's Corporate Sustainability Reporting Directive requires limited assurance over the ESRS sustainability statement. The IAASB's ISSA 5000, effective for periods beginning on or after 15 December 2026, becomes the global baseline for how that assurance is done. The criteria are explicit: a reported emissions figure must be supported by evidence and based on proper methods, and an assurer must be able to reproduce it.
In practice they can't. A GHG number is a chain of activity × emission factor × unit conversion × estimation method, summed inside a Scope 1/2/3 boundary. Companies disclose the total but rarely the defensible trail behind it: which DEFRA fuel factor, which IEA grid vintage, which Scope-3 method, sourced and versioned. So the assurer either burns days rebuilding the calculation by hand, or issues a modified (qualified) opinion because the figure cannot be verified. This is the work, and it is almost entirely deterministic.
Figures from a synthetic suite (Helios Manufacturing SE, FY2025). They demonstrate the mechanism, not a real-world outcome.
Point a language model at a sustainability statement and ask whether the numbers are right, and it will pattern-match to a comforting yes: fluent, confident, and untethered to any factor, any activity row, any source. That is the precise opposite of assurance. Several things break on contact with the real task:
So the real job is a recompute against a cited, versioned factor library, with a tolerance test and an honest Cannot-Verify, plus a clear rule about where deterministic code stops and a human's judgement begins.
This is the load-bearing decision of the whole system. Every number, every tolerance test, every verdict is computed deterministically from the activity rows and a cited factor: reproducible to the gram, re-runnable, hashable. The language model sits strictly downstream and read-only. It drafts the negative-form limited-assurance conclusion, the standard "nothing has come to our attention…" wording, about verdicts the engine already produced. It never selects a factor, never sets materiality, never decides a verdict, never touches a number. If the teeth are anywhere, they are in the code.
The decision function is small on purpose, small enough to read, audit, and re-run. It resolves the cited factor (honouring the live version/method swap), recomputes from the activity rows, runs the tolerance test, and abstains when the evidence isn't there:
function verdict(m) {
const rc = recompute(m); // qty × cited factor → tCO2e
if (rc === null) { // no cited factor id / source…
return { code: "CANNOT_VERIFY", // …so we ABSTAIN, we do not
reason: "no cited factor id / source on the disclosure" };
} // assert correctness.
const devPct = (m.reported_tco2e - rc.tco2e) / rc.tco2e * 100;
const code = Math.abs(devPct) <= m.tolerance_pct ? "PASS" : "EXCEPTION";
return { code, recomputed: rc.tco2e, devPct, factorId: rc.factorId };
}
Take the German grid electricity, metric M06. The engine joins its two activity rows, Munich and Leipzig, to the cited IEA grid factor, sums to tonnes, and compares to the reported figure. Under the 2024 vintage (0.381 kgCO₂e/kWh) it recomputes to 1,733.6 t against 1,759.6 reported: +1.5%, inside the ±5% band → Pass. Switch the assurer's control to the 2023 vintage (0.425) and the same metric recomputes to 1,933.8 t: −9.0% → Exception. Nothing about the company changed. Only the factor judgement did.
| Metric | Cited factor | Reported | Recomputed | Deviation | Verdict |
|---|---|---|---|---|---|
| M06 · DE grid | F-IEA-GRID-DE-2024 | 1,759.6 | 1,733.6 | +1.5% | Pass |
| M06 · DE grid | F-IEA-GRID-DE-2023 | 1,759.6 | 1,933.8 | −9.0% | Exception |
| M20 · APAC elec. | none disclosed | 847.0 | n/a | abstained | Cannot-Verify |
On the default (2024) configuration the engine recomputed 18 of 20 disclosed metrics within the ±5% tolerance. Two are exceptions, upstream road freight (M14, +10.1%) and modelled commuting (M19, +11.5%), and one is Cannot-Verify (M20, no cited factor). Together those three drive a modified (qualified) opinion, and the template drafts only that conclusion's wording. A full replay recomputes all twenty figures from scratch and reproduces them to the gram, with the ledger hash chain intact.
Where these come from, honestly. Helios Manufacturing SE is a synthetic company. The activity data, the factor library, and the seeded exceptions are all generated by a fixed-seed script (gen_data.py), so a third party can re-run the seed and obtain byte-identical data, identical verdicts, and identical hashes. That makes the figures a clean demonstration of the mechanism (recompute, tolerance, abstain, replay), not a finding about any real company's inventory.
The honest limit, stated plainly. Reproducibility is not the same as correctness of the factor or method choice. This workbench automates the part that should never have been manual, the arithmetic, and it makes the genuinely hard part visible and interactive: the factor vintage, the Scope-3 method, the unverifiable line. But it cannot adjudicate the contested judgements themselves: which grid vintage is appropriate for this entity, where a Scope-3 boundary should be drawn, whether a supplier-specific method is better evidenced than a spend-based one. Those remain the assurer's call. And the language model must stay strictly on conclusion wording. The moment it drifts toward factor selection or materiality, the control loses its teeth. The tool's job is to put the right question in front of a human, computed correctly, with the evidence attached.
seq : 8 action : recompute_metric subject : M06 (Purchased electricity , DE plants, location-based) verdict : PASS reported : 1759.6 t recomputed : 1733.6 t dev : +1.50% factor : F-IEA-GRID-DE-2024 (IEA Emission Factors 2024) prev_hash : cf131ea7 hash : 0a9d4e6b ← = fnv1a(prev_hash + canonical(entry))
| ISSA 5000 / CSRD expectation | How this workbench meets it |
|---|---|
| Figures supported by evidence | Every recomputed number traces to its activity row id(s) + factor id/version + source, all shown in the trace. |
| Based on proper, reproducible methods | Seeded generator + deterministic engine; replay reproduces all 20 figures to the gram. |
| Honest treatment of insufficient evidence | Missing factor → Cannot-Verify → proposes a modified opinion, rather than asserting correctness. |
| Judgement made by a qualified human | Factor/method choice is surfaced as a control; the engagement lead signs off the opinion before it is recorded. |
| Conclusion in the prescribed negative form | The template drafts only the "nothing has come to our attention…" wording, and nothing more. |
Sustainability assurance is where a lot of AI hope is about to meet a lot of regulatory reality, and the tempting shortcut, asking a model whether the emissions look right, is exactly the move that destroys the assurance. So I drew the trust boundary first. The calculation lives in code, reproducible to the gram, and the model is confined to the one thing it is genuinely good at: phrasing a standard conclusion, with citations, after the verdict is already decided. Then I refused to hide the part that's actually difficult. The factor vintage and the Scope-3 method are judgements, not facts, so I made them a dial the assurer turns and watches the opinion move. A demo that buried that choice would be lying about where the difficulty is. The real work is naming that difficulty and keeping the model's hands off every number.
The recompute register, the live factor-version and method swap, the Cannot-Verify trace, the modified-opinion draft, and the tamper-and-replay ledger all run as a working, offline application over the synthetic statement.