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

TestReferencePlatform v1.0.37Status
C1.1 Discount rate monotonicityMonotonic 0%→20%; NPV(0%)=$16.95bn=ΣCFs; NPV(8%)=$7.45bnMonotonic 0%→20%; NPV(0%)=$21.27bn=ΣCFs; NPV(8%)=$9.81bnBoth PASS structurally; both fail tight reconciliation tolerance
C1.2 Commodity price monotonicityMonotonic $1.50→$5.00/lb across 36 incrementsMonotonic $1.50→$5.00/lb across 36 incrementsBoth PASS
C1.3 OPEX monotonicityMonotonic $0.30→$2.00/lb across 18 incrementsMonotonic $0.30→$2.00/lb across 18 incrementsBoth PASS

Category 2 — Boundary tests

TestReferencePlatform v1.0.37Status
C2.1 Zero-discount-rateNPV(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 ≈ $0Both PASS structurally
C2.3 −$1bn shock in year 5Decrease = $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; sensibleBoth PASS

Category 3 — Sensitivity tests

TestReferencePlatform v1.0.37Status
C3.1 Price elasticity ($3.10→$3.13/lb)1.36 at $0.48 LoM literal; 1.85 at $1.06 reconciled1.27 at $0.48 LoM literalReference: 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% range7.7% — just below 8% lower boundReference: PASS. Platform: FAIL by 0.3 pp

Category 4 — Reconciliation tests

TestTargetToleranceReference @ $0.48 literalPlatform @ $0.48 literalStatus
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

TestReferencePlatform v1.0.37Implication
C5.1 Missing by-product creditsReference 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 mishandlingTax 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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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

ImplementationReconciling 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-recoveredDerived
Platform DCF v1.0.37 (no ramp, no sustaining, no shield)$1.51/lb-recoveredDerived

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)

  1. 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.
  2. Solve C4.2, C4.3, C4.4 reconciling LoM costs in cycle 2. Each study has different scope/grade/throughput.
  3. 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)

  1. 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.
  2. Add ramp-up profile input. Even a 2-year ramp [50%/100%] would meaningfully improve realism without UX bloat.
  3. Add sustaining capex input. Single $/year line item separate from initial capex.
  4. 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.
  5. 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:

Cycle 2 should reduce the absence list, not add to it.

Why this is published

Three reasons:

  1. 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.
  2. 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.
  3. 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).