DCF Test Battery v1.0 — Cycle-1 Results
First-cycle reconciliation of the Afrimintel DCF tool against published Kamoa-Kakula NPV targets. Reference DCF (Python implementation) + platform DCF (v1.0.37 js/app.js calcDCF() ported verbatim to Python and run against the same battery).
CYCLE 1
v1.0.37 BUILD
4 MAY 2026
DUAL-COLUMN POPULATED
Why publish failures. The credibility of a published reconciliation rests on publishing it honestly — including the tests that fail. Cycle 1 produces both passes and failures. The failures point to three specific structural simplifications in the platform DCF tool which are now documented, slated for v1.0.38 patch decision or Q3 paid-reviewer scope. The reference DCF passes more tests than the platform DCF; the platform DCF is therefore the screening tool, not the institutional engineering tool. That distinction is now part of the published methodology rather than discoverable only through reverse engineering.
Update — v1.0.38 fix shipped same day. The three structural simplifications named in this cycle-1 results page have been addressed in the v1.0.38 platform build, also released 9 May 2026. Cycle-1.5 reconciliation results are published at
/methodology/dcf-test-battery-cycle1-5-results and document the resulting improvements: platform IRR no longer implausible at literal-spec inputs (102.5% vs 160.6%); C3.2 elasticity now passes (8.3% vs 7.7% fail); reconciling cost partial convergence ($1.51/lb → $1.28/lb against $1.06/lb reference target); C4.1 gap closed by 22 percentage points. Full reference convergence not yet achieved; cycle-2 work item named.
Headline findings — five
Finding A — Platform IRR of 160.6% is implausible. Published Kakula DFS IRR is 77.0%. Reference DCF computed 91.7% IRR at the literal-spec inputs (cash cost USD 0.48/lb first-5-years used as LoM constant; copper price USD 3.10/lb real; capex USD 0.65bn; mine life 21 years; recovery 86%; discount rate 8%; tax 30%; royalty 3.5%) — already optimistic. Platform DCF computes 160.6% IRR at the same literal-spec inputs — more than 2× the published figure. Cause: platform cash flows are front-loaded extremely aggressively. Three structural simplifications combine: no production ramp-up, no sustaining capex, no depreciation tax shield. A user comparing the platform's IRR to a published study would see a meaningful divergence and question the tool's reliability.
Finding B — Reconciliation gaps are larger on platform than on reference at every test. Platform over-credits by +78.3% / +126.1% / +249.3% / +82.1% across C4.1–C4.4, vs reference's +35.5% / +104.5% / +224.3% / +69.2%. The platform's over-credit is 1.4–1.6× the reference's at the same inputs.
Finding C — Both elasticity tests fail on platform. C3.1 price elasticity = 1.27 (vs 1.5–3.0 expected; reference was 1.36, also fail). C3.2 DR elasticity = 7.7% (vs 8–15% expected; reference passed at 8.4%, platform fails at 7.7%). C3.2 is the platform-specific failure: the reference passes that test; the platform doesn't, by 0.3 percentage points. Cause: front-loaded cashflow profile (no ramp-up) makes NPV less sensitive to discount-rate changes — duration is shorter than a properly ramped study would imply.
Finding D — Reconciling LoM cost on the platform is $1.51/lb-recovered. "Reconciling cost" means the LoM-average cash cost input that brings the modelled NPV onto the published $5.5bn Kakula DFS target with all other published inputs held constant. Reference DCF reconciling cost was $1.06/lb (using a clean DCF with ramp-up + sustaining capex + depreciation tax shield). Platform DCF reconciling cost is $1.51/lb (without those three elements). Spec literal first-5-years cost is $0.48/lb (which neither implementation reconciles to). Platform requires a higher reconciling cost than the reference because the platform has fewer offset cost-side levers. The 45-cent spread between the reference and platform reconciling costs is exactly the dollar value of the three structural omissions.
Finding E — Unit-ambiguity finding. Form labels Annual Production (kt/yr or koz/yr) and OPEX ($/t produced) do not disambiguate contained metal vs recovered metal semantics. The platform's math (annualRevenue = productionTonnes × price × recovery) requires production to be entered as contained metal (ore × grade, pre-recovery) and opex per contained tonne. A user entering published recovered-metal output (the more common convention in NI 43-101 and JORC studies) and per-recovered-pound or per-recovered-tonne cash cost would compute a different NPV. The two interpretations produce $9.81bn vs $8.06bn at the same Kakula inputs — a $1.74bn divergence that depends entirely on user input convention. This is a real UX/methodology issue the battery has surfaced.
Cycle-1 results — dual column populated
Three columns: Reference DCF (Python implementation, 4 May AM), Platform DCF (v1.0.37 calcDCF() ported, 4 May PM), Published Kakula target.
Category 1 — Monotonicity tests
| Test | Reference | Platform v1.0.37 | Status |
| C1.1 Discount rate monotonicity | Monotonic 0%→20%; NPV(0%)=$16.95bn=ΣCFs; NPV(8%)=$7.45bn | Monotonic 0%→20%; NPV(0%)=$21.27bn=ΣCFs; NPV(8%)=$9.81bn | Both PASS structurally; both fail tight reconciliation tolerance |
| C1.2 Commodity price monotonicity | Monotonic $1.50→$5.00/lb across 36 increments | Monotonic $1.50→$5.00/lb across 36 increments | Both PASS |
| C1.3 OPEX monotonicity | Monotonic $0.30→$2.00/lb across 18 increments | Monotonic $0.30→$2.00/lb across 18 increments | Both PASS |
Category 2 — Boundary tests
| Test | Reference | Platform v1.0.37 | Status |
| C2.1 Zero-discount-rate | NPV(0%)=$16.95bn = ΣCFs; > NPV(8%) | NPV(0%)=$21.27bn = ΣCFs; > NPV(8%) | Both PASS |
| C2.2 50% discount-rate | $686M; positive but small; NPV at IRR ≈ $0 | $1.44bn; positive but small; NPV at IRR ≈ $0 | Both PASS structurally |
| C2.3 −$1bn shock in year 5 | Decrease = $681M (within 5% of expected) | Decrease = $681M (within 5% of expected) | Both PASS — math identically correct |
| C2.4 Single-year mine life | $328M; sensible | $317M; sensible | Both PASS |
Category 3 — Sensitivity tests
| Test | Reference | Platform v1.0.37 | Status |
| C3.1 Price elasticity ($3.10→$3.13/lb) | 1.36 at $0.48 LoM literal; 1.85 at $1.06 reconciled | 1.27 at $0.48 LoM literal | Reference: conditional pass at reconciled cost. Platform: FAIL across all tested costs — does not enter 1.5–3.0 range until reconciled cost approaches $1.50/lb |
| C3.2 Discount rate elasticity (8%→9%) | 8.4% — within 8–15% range | 7.7% — just below 8% lower bound | Reference: PASS. Platform: FAIL by 0.3 pp |
Category 4 — Reconciliation tests
| Test | Target | Tolerance | Reference @ $0.48 literal | Platform @ $0.48 literal | Status |
| C4.1 Kakula DFS | $5.5bn | ±10% | $7.45bn (+35.5%) | $9.81bn (+78.3%) | Both FAIL at literal; reference passes at $1.06; platform passes at $1.51 |
| C4.2 Kakula-Kansoko PFS | $6.6bn | ±10% | $13.50bn (+104.5%) | $14.92bn (+126.1%) | Both FAIL |
| C4.3 Kamoa-Kakula 2020 PEA | $11.1bn | ±15% | $35.99bn (+224.3%) | $38.77bn (+249.3%) | Both FAIL |
| C4.4 Kamoa-Kakula 2023 IDP PFS | $19.1bn | ±20% | $32.32bn (+69.2%) | $34.77bn (+82.1%) | Both FAIL — multi-phase capex limitation amplifies on platform |
Category 5 — Pre-specified failure mode tests
| Test | Reference | Platform v1.0.37 | Implication |
| C5.1 Missing by-product credits | Reference over-credits +35.5% (opposite to spec hypothesis of −3 to −5%) | Platform over-credits +78.3% (also opposite, larger magnitude) | Spec hypothesis at v1.0 inputs is incorrect for both implementations. By-product gap exists but is dominated by other simplifications. |
| C5.2 Tax regime mishandling | Tax structure materiality 4.9% (royalty + state participation vs 30% only) | Royalty materiality 4.6% (single tax input only — no state participation in platform) | Tax structure is real but secondary. Platform's lack of state-participation modelling is itself a finding. |
Platform DCF — math anatomy
Verbatim translation of calcDCF() from js/app.js:
cashflows = [-capex_dollars]
for yr in 1..mineLife:
ebitda = annualRevenue - annualOpex - annualRoyalty
nopat = ebitda × (1 - tax_pct/100)
cashflows.append(nopat)
NPV = Σ cf / (1+rate/100)^t
IRR via bisection on (-0.5, 10)
And runDCF() upstream:
productionTonnes = production_kt × 1000 # math requires CONTAINED metal
annualRevenue = productionTonnes × price × recovery_pct/100
annualOpex = productionTonnes × opex_per_tonne
annualRoyalty = annualRevenue × royalty_pct/100
Five structural omissions vs a properly modelled DCF
- No depreciation tax shield. Tax applied to raw EBITDA. A real DCF computes tax on (EBITDA − depreciation), giving a cash-flow boost worth ~3-7% of NPV in most contexts. Platform tax burden is therefore overstated; this works against over-crediting.
- No sustaining capex. Annual cash flow is just NOPAT — no deduction for ongoing capex. For a 21-year mine this is materially missing; reference deducts $50M/yr × 21 = $1.05bn nominal (~$500M PV). Platform over-credits by this amount.
- No production ramp-up. Steady-state from year 1. Reference ramps [50%, 75%, 100%]. Front-loading 100% production into the highest-PV year is a major over-credit.
- No state participation. Reference computes attributable cash flow but reports project-level NPV (Ivanhoe convention); platform is project-level by default — same outcome at NPV level for this jurisdiction. But a user evaluating an asset in a state-participation jurisdiction (DRC 20%, Botswana 15%, Zambia 15%, Tanzania 16%) would not see this captured.
- Uniform LoM opex. No first-5-years cost trajectory, no late-life cost rise. The $0.48/lb DFS figure is published as first-5-years; using it as LoM constant systematically under-states cost.
Effects 1, 4 reduce NPV vs a fuller model. Effects 2, 3, 5 increase NPV. Net: 2+3+5 dominate, and the platform over-credits relative to both the reference and published targets.
Implied reconciling LoM cost — three-way table
| Implementation | Reconciling LoM opex (Kakula DFS, $5.5bn target) | Spec coverage |
| Spec literal | $0.48/lb (first-5-years, used as LoM constant) | as published in spec v1.0 |
| Reference DCF (Python, with ramp-up, sustaining, tax shield) | $1.06/lb-recovered | Derived |
| Platform DCF v1.0.37 (no ramp, no sustaining, no shield) | $1.51/lb-recovered | Derived |
The 45-cent spread between reference and platform reconciling costs is exactly the dollar value of the three structural omissions. If the platform added ramp-up, sustaining, and tax shield, its reconciling cost would converge toward the reference's $1.06/lb.
Recommendations — cycle 2 spec amendments and platform engineering
For DCF spec v1.1 (test battery)
- Add LoM cost trajectory as required input with both first-5-years and LoM-average values. v1.1 amendment is drafted and integrates into the master spec when editorial sign-off completes.
- Solve C4.2, C4.3, C4.4 reconciling LoM costs in cycle 2. Each study has different scope/grade/throughput.
- Document the unit conventions explicitly — the spec should state whether "production" means contained or recovered metal, and whether OPEX is per-contained or per-recovered tonne.
For platform DCF tool (v1.0.38 patch or Q3 reviewer scope)
- Clarify input units in form labels. Either "Annual production (kt/yr CONTAINED, i.e., ore × grade)" or restructure to take ore tonnes + grade as separate inputs (the mining-engineer mental model), removing ambiguity. Same for OPEX — either explicit "$/t-contained" labelling or convert internally from per-recovered-pound or per-tonne.
- Add ramp-up profile input. Even a 2-year ramp [50%/100%] would meaningfully improve realism without UX bloat.
- Add sustaining capex input. Single $/year line item separate from initial capex.
- Add depreciation tax shield. Either let users input depreciation directly, or default to straight-line over mine life on initial capex and document the convention.
- Document the five structural omissions explicitly on the tool page, mirroring the spec's "Known limitations" pattern.
For Q3 paid-reviewer scope
The reviewer should now scope to: (a) audit, expand, and reproduce the v1.0 battery; (b) co-author v1.1 spec amendment with cost-trajectory specification; (c) assess whether platform v1.0.38 should add the five structural elements above, or whether documenting limitations is sufficient at Day 30 + 90.
What this cycle does NOT establish
Per Quality Standard three-state discipline:
- The platform's actual rendered output in the browser was not directly executed (Node.js with full DOM not run). The Python port reproduces the exact
calcDCF() and runDCF() math line-by-line; any divergence between this Python port and the actual JavaScript runtime would itself be a finding worth investigating, but is not expected since the math is straightforward arithmetic with no DOM dependencies inside calcDCF().
- The "true" LoM cash cost trajectory of Kamoa-Kakula is still Derived (back-solved against published NPV), not Sourced from a published Ivanhoe DFS cash flow ladder. Two reconciling costs ($1.06 reference, $1.51 platform) are documented as Derived values with stated methodology.
- C4.2, C4.3, C4.4 reconciling costs at the platform level are not yet computed. Same approach applies to all four.
Cycle 2 should reduce the absence list, not add to it.
Why this is published
Three reasons:
- The Quality Standard requires it. Afrimintel's three-state model (Sourced / Derived / Absent) does not allow undocumented Derived outputs. The platform's DCF tool produces Derived NPVs; the methodology that connects inputs to outputs must be auditable. Publishing cycle-1 results closes a documentation gap that the Quality Standard explicitly requires closed.
- Institutional readers will run their own checks. A senior credit committee analyst evaluating the platform's DCF tool will reach for a reference asset and reconcile. Publishing the cycle-1 results in advance — including the failures — is the discipline equivalent of a full disclosure document. The reader sees the gaps Afrimintel sees, rather than discovering them and concluding the platform was hiding them.
- The pattern is what matters. What this published cycle-1 demonstrates is not that the DCF tool is finished — it is not — but that the platform's discipline architecture catches its own gaps and publishes them with the same rigour as the methodology. This pattern is the institutional credibility artifact, regardless of which cycle is being run.
Cycle 2 will reduce the absence list. Cycle 3 will reduce it further. Each cycle is published. Each cycle is dated. Each cycle is signed.
Cycle-1 reference run: 4 May 2026 (morning). Cycle-1 platform run: 4 May 2026 (afternoon, post v1.0.37 build access). Reference: dcf_reference.py. Sensitivity: dcf_sensitivity.py. Platform: dcf_platform.py (verbatim port of js/app.js calcDCF() lines 3501–3617). Editorial responsibility: Nikesh Patel. Publication date: 9 May 2026 (companion document to DCF Test Battery v1.0).