esgcalc 0.2.0.dev0 — Offline engine handbook

esgcalc — Offline ESG calculation engine

Local development candidate 0.2.0.dev0, Python >=3.10, no runtime dependencies. The calculation core implements 47 independently callable capabilities across nine domains. Native R is the subsequent implementation; nothing is published.

from esgcalc import social, data, rating, Context

assert social.pay_gap(20, 18).value == 10
company = data.load_reference_data("synthetic_companies.json").value["companies"][1]
result = rating.rate_company(company["observations"], company["findings"],
                             context=Context(as_of="2026-06-30"))
print(result.value, result.details["coverage"])

The declared scope includes 23 edition-pinned quantitative taxonomy clauses, six-goal assessment combination, nonfinancial turnover/CapEx/OpEx calculations including plan restatement, all 18 Table1 PAI numerical contracts, selected ENV/SOC/GOV indicators, portfolio metrics, listed-company financed emissions, a configurable transparent ESG performance rating, and local data interfaces. All runtime work is offline on supplied structured inputs. There is no network, AI extraction, service, database or mandatory whole-company data graph.

The enumerated contract defines completeness. This is not every ESG law, every taxonomy activity, complete DNSH investigation or statutory reporting. External assessments carry source/date/method/scope. The reference rating uses explicit model choices, not an alleged statutory universal ESG grade. Coverage, unknown findings and missing observations stay visible. PAI18’s source ambiguity requires an explicit interpretation; it is never silently reversed.

21 original source PDFs underpin the local selected-edition review. Public resources retain provenance and hashes, not the private regulations archive. 120 reconciled annual cases for 60 fictional companies in six archetypes have independent numerical expectations; an executable mixed portfolio links the upstream metrics, PAI, ratings and financed emissions. Synthetic data are not empirical calibration, and future-year observations are labelled scenarios.

Start with contracts, API, taxonomy catalog, rating, casebook, method issues and verification.

Local install: python -m pip install --no-index --no-deps <local wheel>. Offline example: python examples/realistic_pipeline.py output.json. Build with local tools: python -m build --no-isolation. No publisher identity or licence has been invented; those decisions and any external publication remain separate from the local engine implementation.

Engine contracts v2

Common conventions

Public functions return Result: value, unit, status, function_id, profile_id, source_ids, method_id, assessment, mode, input_hash, details, limitations. to_dict() is JSON serializable. CalculationError exposes code, field, message and to_dict(). Malformed inputs raise; well-formed missing observations return MISSING_INPUT, PARTIAL or UNRATED. NOT_APPLICABLE is distinct from a zero result. Never coerce None to zero.

Use finite JSON numbers (not booleans), ISO dates, explicit string IDs and unique row IDs. Units are exact, case-sensitive identifiers. Unless percent is stated, proportions use 0..1. Scores use 0..100; higher means stronger performance according to the selected model. No decision-time rounding. Amounts are nonnegative net rows; signed accounting corrections must be reconciled before input. Money is in currency units, never implicit millions.

Context(profile_id="esgcalc-methods-v1", as_of=None, period_start=None, period_end=None, currency="EUR", mode="REFERENCE", scenario_reason=None) is immutable. Scenarios require a reason. Legal profiles require explicit dates within their declared interval: eu-taxonomy-2025-v1 (taxonomy only), eu-taxonomy-2026-v1 (taxonomy/KPI), sfdr-pai-v1 (2023–2026). Profiles never determine the caller’s legal applicability. They expose bounded methods, not an attestation of complete/current regulation.

External assessment schema: {status, origin, evidence, source, method, scope, assessed_on, reason?}. All fields except reason are required; reason is required for NOT_APPLICABLE. Assessments after the knowledge date are rejected. Status is MET/NOT_MET/UNKNOWN/NOT_APPLICABLE; origin is COMPUTED, EXTERNAL_ASSESSMENT or DECLARED_INPUT. Evidence is a nonempty source reference. NOT_APPLICABLE requires a reason. Metadata is structurally checked; neither source truth nor assessor competence is independently established.

Taxonomy

Goal codes: CCM, CCA, WTR, CE, PPC, BIO. combine_taxonomy_assessments takes eligibility, minimum safeguards and goal-keyed SC, DNSH, technical-screening assessments. A goal requires eligibility, SC, technical screening, safeguards and DNSH for the other five goals. Any known failure dominates unknowns; a single satisfied goal is sufficient for the combined result. Missing goals are unknown. Validated NOT_APPLICABLE DNSH is an external scope decision, not an automatic exemption. validate_activity_assessment adds unique activity_id rows with eligible/sc/dnsh/minimum_safeguards/technical_screening fields.

check_quantitative_criterion computes the 23 explicitly enumerated numerical clauses in TAXONOMY_CRITERIA.md. That catalog specifies all field names, fixed units, formulas, boundaries and exact edition locators. The 2025/legacy profiles support only the two unchanged original clauses (new-building PED and light-vehicle manufacturing CO2). The expanded catalog is pinned to the 2026 consolidated editions. Unsupported criteria are rejected. A passed numerical clause is not full activity alignment; the other named conditions, DNSH and minimum safeguards remain separate inputs.

Allocation input: amounts {amount_id, amount}; allocations {allocation_id, amount_id, fraction, goals:[...]}. Slices are disjoint and fractions for one amount cannot exceed one. Each slice counts once in the union, while contributing to multiple goal totals. Goal totals are not additive.

Nonfinancial taxonomy KPIs

Every row: amount_id, amount, currency, period_end, accounting_category, eligible and alignment assessments. Currency must equal Context; row period_end equals Context.period_end (or as_of when omitted). as_of is the knowledge/review date, not automatically the reporting date. Input amounts already represent disjoint economic slices; allocation_fraction, if present, must be one. reconcile_kpi_allocations checks fractions separately.

Turnover includes net_revenue, excludes other_income. Adapted-only turnover is excluded unless adaptation_enabling or otherwise_aligned is explicitly true. CapEx includes IAS16, IAS38, IAS40_FAIR, IAS40_COST, IAS41, IFRS16_ROU, GAAP_EQUIVALENT additions; excludes fair_value_change, depreciation, non_rou_lease. National GAAP equivalence and the financial boundary are caller assessments. OpEx includes research_development, building_renovation, short_lease, maintenance_repair, day_to_day_servicing, gaap_noncapitalised_lease. The last requires accounting_basis=NATIONAL_GAAP_NO_ROU. capitalised, other_cost and already_in_capex=true are excluded. Categories use the regulatory direct-cost boundary; library users must provide correctly mapped accounting records.

CapEx/OpEx numerator_category: aligned (default), plan or measure. Plan: purpose=expand_aligned/upgrade_eligible, start/end, approved/disclosed assessments. Duration <=5 years; >5 and <=10 requires extended_justification. After end, completion_aligned is required. When criteria_amended_on applies, updated_on must be within two years and resets the start; overdue updates fail. Unknown pending updates suppress the complete ratio. taxonomy_capex accepts prior_periods: each record has period_end, optional period_start, rows and previously_reported_aligned_numerator. Historical plan rows identify plan_id; current rows contain the matching plan with plan_id and current review. The function recalculates historical numerators using those reviews and reports restated_periods, numerator_adjustment, plan_failures and restatement_required. It does not change the caller’s accounting ledger. Every plan requiring a historical review must be supplied; missing reviews are an error. Measure: measure_start, operational_on, measure_qualifies assessment. Completion may be planned within 18 months; the date is not proof of actual implementation.

non_material=true invokes the explicit 2026 unassessed option; combined omitted amount must be strictly below 10% of the respective KPI denominator. This remains in the denominator and is separately reported. OpEx immaterial_reason suppresses both eligibility/alignment output while retaining its denominator. Unknown non-omitted assessments suppress the quote; known aligned amounts remain visible. Rows may identify taxonomy_activity: 4.26–4.28 nuclear, 4.29–4.31 fossil gas, or another explicit activity code/other. A complete supplied breakdown produces separate aligned/eligible amounts and denominator shares; if any activity code is absent, nuclear_and_gas is None, never a guessed zero.

eu-taxonomy-legacy-fy2025-v2 implements the Article 4 election in 2026/73: period_start must be in 2025, period_end explicit; the new 10% unassessed option is unavailable. Applicability/election are chosen externally. The 2026 profile is a frozen edition, not a promise to incorporate future amendments automatically. Financial institutions’ GAR and specialist reporting are outside this profile.

PAI Table 1 numerical contract

All rows include position_id, issuer_id, value (EUR), currency=EUR, as_of and asset_class=company/sovereign/real_estate/cash/other. Include the entire declared portfolio; do not silently filter cash or unknown assets to improve coverage. Money inputs EVIC/revenue/GDP are EUR; emissions tCO2e, water/waste tonnes. Company scope2 is one already-selected compatible measurement, never the sum of location and market alternatives. Caller supplies measurement boundaries.

Indicator Additional input Calculation
1 evic, scope1/2/3 Sum value/EVIC * emissions, each scope and total
2 same Attributed total divided by all investments in million EUR
3 scope1/2/3, revenue_eur Investment-weighted emissions per million EUR revenue
4 fossil_fuel boolean Positive company exposure / all investments
5 nonrenewable_consumption_mwh, consumption_mwh, nonrenewable_production_mwh, production_mwh Two separate average fractions
6 sector (NACE section), energy_gwh, revenue_eur Average GWh/million EUR revenue per section A–H or L
7 biodiversity_adverse boolean Positive company exposure / all investments
8 evic, water_emissions_t Attributed tonnes / all investments in million EUR
9 evic, hazardous_radioactive_waste_t Same attribution with waste tonnes
10 ungc_oecd_violation boolean Positive company exposure / all investments
11 missing_compliance_process boolean Positive company exposure / all investments
12 male_hourly_pay, female_hourly_pay Average signed (male-female)/male *100
13 female_board, total_board Average female/all-members fraction
14 controversial_weapons boolean Positive company exposure / all investments
15 country_emissions_tco2e, gdp_eur Investment-weighted emissions/million EUR GDP
16 country_id, social_violation boolean Distinct violating countries and fraction of all investee countries
17 fossil_real_estate boolean Positive real-estate exposure / all real-estate investments
18 energy_inefficient, epc_nzeb_applicable booleans Inefficient exposure / applicable real-estate exposure

The recommended sfdr-pai-2023-esa2025-v2 profile fixes value-weighted applicable-company means for 5/6/12/13 (per sector for 6). This is a declared method convention where the legal text says average, not a claimed uniquely mandated weighting. The older sfdr-pai-v1 analytical interface requires an explicit value_weighted or issuer_mean selection. Its presence is not evidence that every alternative is accepted for statutory reporting.

The v2 profile requires valuation_basis=FISCAL_YEAR_END_PRICES, valuation_date, fx_date, fiscal_year_end (all equal), and lookthrough_complete=true. Holdings are already resolved to underlying positions. Optional position_units, unit_price, unit_price_currency and fx_rate (EUR per pricing currency) derive the value; a separately supplied inconsistent value is rejected. Optional EVIC components common_equity_market_value, preferred_equity_market_value, book_debt and noncontrolling_interests derive EVIC without subtracting cash. All four must be supplied together. EUR conversion of company financial statements remains an explicit upstream step, supported by convert_currency.

Missing required fields suppress an indicator rather than deleting a position. EVIC/revenue/GDP must be positive. PAI5 zero total consumption/production with zero corresponding nonrenewable quantity is NOT_APPLICABLE for that component; positive nonrenewable quantity over zero total is invalid. Zero-production companies are excluded only from that component’s applicable mean. Company attributes must agree for repeated issuers; real-estate asset_id, when supplied, is the classification key so distinct buildings of one owner can differ.

PAI18 accepts either explicit external booleans or structured building inputs: epc_nzeb_applicable; built_on; for older buildings epc A..G, with C..G inefficient; for newer buildings ped, nzeb_threshold, unit=kWh/m2/year, ped_interpretation. The archived corrected formula literally uses PED below NZEB. Therefore RTS_LITERAL_BELOW follows that wording. Reversing it requires PERFORMANCE_NONCOMPLIANCE_SCENARIO and Context.mode=SCENARIO. There is no silent interpretation. Exactly 2020-12-31 requires cutoff_policy=year_end_inclusive. Unknown applicability is missing; explicitly inapplicable assets are outside the PAI18 denominator. See METHOD_ISSUES.md.

aggregate_pai_observations requires March31, June30, September30 and December31 of one year, not later than the knowledge date. Each scalar observation has as_of, indicator_id, component, unit, method_id and value; metadata must agree. The default averages exactly four observations. Additional dates require sampling=all_observations_equal and all mandatory quarter ends remain necessary. Missing values suppress the mean; a ratio of pooled yearly numerators is not a substitute for the mean of reference-date ratios. Multi-component indicators are split into separate component series explicitly. aggregate_pai_portfolio calculates the selected indicators on the same full investment universe.

Relevant optional Table2/3 positions are mapped, with exact coverage and remaining steps, in optional_pai_catalog.json. Reusable company metrics and exposure functions are not advertised as a complete optional-PAI statement. Product classification, reporting materiality decisions and entity disclosures are outside this calculation engine.

Environment, social and governance

Emissions rows: activity_id, quantity, activity_unit, factor, factor_activity_unit, factor_emission_unit, factor_source. Multiplication converts to tCO2e. Factors already include the chosen GWP boundary; there is no factor lookup or estimation. Inventory rows: emission_id, scope=1/2_location/2_market/3, value, unit. Explicitly choose scope2_method and required_scopes. Absent required scopes are missing. Units: kgCO2e/tCO2e; kg/t; kWh/MWh/GWh/MJ/GJ; litre/m3; m/km; unit. Cross-dimensional conversion is rejected. Intensity inputs are emissions in tCO2e and an explicitly named denominator. Zero denominator returns NOT_APPLICABLE. Energy rows: energy_id, category=renewable/fossil/nuclear, value, unit, flow=consumption (default) or production. Production and consumption never sum into one denominator. Optional revenues rows {sector,net_revenue,currency} plus energy sector codes enable A–H/L consumption divided by corresponding revenue. Every high-impact revenue sector needs an explicit energy row (including zero), and every high-impact energy sector needs its revenue. Water: withdrawn/discharged/reused in one unit on a common boundary; consumption =withdrawn-discharged, own analytical reuse share=reused/(withdrawn+reused). Alternatively supply facilities {facility_id,withdrawn,discharged,reused,unit,water_risk}; this also calculates water-risk-area consumption. Unknown risk does not silently become false. stored_start/stored_end report storage and change separately; net_revenue_eur enables m3/million EUR intensity. Storage is not deducted twice from consumption. Waste rows: waste_id,value,unit,destination=reused/recycled/other_recovery/disposal, hazardous boolean. Material rows: material_id,value,unit,secondary boolean, optional reused boolean (a subset of secondary). Recycling, reuse/recycling and recovery shares are distinct. Change requires comparable=true; relative change only for a positive baseline. Optional restatement={restated_baseline,reason} retains both original and restated baseline.

Workforce rows: group_id,headcount,fte, disjoint groups. Diversity rows: category,count, disjoint and exhaustive including unknown if relevant. Pay gap uses gross hourly male/female averages and returns signed percent. Turnover uses leavers / turnover_denominator; denominator_method is mandatory when turnover is requested. Workforce groups are disjoint and the turnover population must be comparable. No individual employee records are required. Health/safety: recordable_injuries, hours_worked, optional fatalities, fatalities_from_ill_health, recordable_ill_health_cases, days_lost. Missing optional counts remain None. Injury and illness fatalities are separate subsets. Rates use one million hours; severity-days rate is an analytical convention. Optional covered_workers,total_workers,audited_covered_workers produce management-system coverage. Day-count convention must match the intended disclosure (ESRS/SFDR workday and calendar-day populations are not interchangeable). Board denominator includes female,male,other,unknown_gender; independent is a separate optional count. Governance findings are finding_id + assessment records. Conflicting states for one supplied topic and scope are rejected. Definitions are selected metrics, not the complete ESRS disclosure requirements.

Portfolios and local data

Positions: position_id,issuer_id,value>=0,currency,as_of if Context is dated. No leverage/shorts, look-through or market-data retrieval. Weights normalize over all positions; coverage counts nonmissing field values; exposure requires a boolean field. HHI is sum of squared grouped weights. FX rate is target currency units per source unit, with matching explicit dates and rate_source. Financed emissions accepts listed_equity/market_value and listed_company_bond/book_value, positive evic, emissions_tco2e and emissions_scope. One compatible emission boundary is required. No other PCAF asset classes or quality scores are asserted. Issuer grouping requires equal issuer_data values.

validate_inputs takes rows and schema={unique_key?,fields:{name:{type, required?,minimum?,maximum?,enum?,unit_field?,unit?,schema?}}}; types number/integer/text/boolean/date/object/array. Nested object/array schemas describe mapping rows recursively. additional_fields=allow (default) or reject is explicit. Missing required values produce a missing report. load_local_data accepts JSON or CSV; without schema CSV stays strings. With schema, declared numeric and true/false fields are parsed and validated; no guessed units/dates or nested CSV encoding. UTF-8 BOM is supported; nonfinite JSON, duplicate headers and inconsistent row widths are rejected. Resource names are manifest-registered JSON basenames; hashes are checked. Export requires an explicit local destination and rejects existing targets. No implicit file writes, networking, secrets, global caches or automatic resource updates occur.

Selected quantitative taxonomy clauses

Version taxonomy-clauses-2; frozen 2026 consolidated editions. Exactly 23 numerical clauses; full activity alignment is a separate result.

Missing required fields return UNKNOWN. Malformed values, incompatible supplied units, duplicate component IDs and zero ratio denominators raise CalculationError. All thresholds are compared without decision-time rounding. Quantities use the units in the field names/catalog; optional units={field:unit} is checked, never converted silently.

For baseline reductions supply works_start and exactly the three preceding calendar-year intensity rows. These are annual intensities whose arithmetic mean defines the baseline; no hidden energy/output pooling. Qualitative evidence and any additional conjunctive conditions remain necessary.

building_new_ped

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 7.1(1). Goal CCM.

Inputs: ped (kWh/m2/year), nzeb_threshold (kWh/m2/year).

Algorithm: ped; comparison <= with threshold 0.9.

{}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

light_vehicle_manufacture_co2

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 3.3(f). Goal CCM.

Inputs: co2 (gCO2/km), category (M1 or N1_LIGHT).

Algorithm: vehicle; comparison == with threshold 0.

{
  "before_2026": 50
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

building_renovation_ped

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 7.2 and footnote 335. Goal CCM.

Inputs: baseline_ped (kWh/m2/year), current_ped_excluding_renewable_supply_reduction (kWh/m2/year).

Algorithm: reduction; comparison >= with threshold 0.3.

{
  "numerator": "current_ped_excluding_renewable_supply_reduction",
  "denominator": "baseline_ped"
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

building_existing_top15

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 7.7(1). Goal CCM.

Inputs: stock_percentile (fraction), built_on (ISO date).

Algorithm: percentile; comparison <= with threshold 0.15.

{}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

hydro_power_density

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 4.5(b). Goal CCM.

Inputs: installed_capacity_w (W), reservoir_area_m2 (m2).

Algorithm: ratio; comparison > with threshold 5.

{
  "numerator": "installed_capacity_w",
  "denominator": "reservoir_area_m2"
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

hydro_lifecycle_emissions

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 4.5(c). Goal CCM.

Inputs: lifecycle_emissions_tco2e (tCO2e), energy_output_kwh (kWh).

Algorithm: ratio; comparison < with threshold 100.

{
  "numerator": "lifecycle_emissions_tco2e",
  "denominator": "energy_output_kwh",
  "scale": 1000000
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

geothermal_electricity_lifecycle

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 4.6. Goal CCM.

Inputs: lifecycle_emissions_tco2e (tCO2e), energy_output_kwh (kWh).

Algorithm: ratio; comparison < with threshold 100.

{
  "numerator": "lifecycle_emissions_tco2e",
  "denominator": "energy_output_kwh",
  "scale": 1000000
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

geothermal_heat_lifecycle

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 4.22. Goal CCM.

Inputs: lifecycle_emissions_tco2e (tCO2e), energy_output_kwh (kWh).

Algorithm: ratio; comparison < with threshold 100.

{
  "numerator": "lifecycle_emissions_tco2e",
  "denominator": "energy_output_kwh",
  "scale": 1000000
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

water_supply_energy

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 5.1(a). Goal CCM.

Inputs: net_energy_kwh (kWh), water_m3 (m3).

Algorithm: ratio; comparison <= with threshold 0.5.

{
  "numerator": "net_energy_kwh",
  "denominator": "water_m3"
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

water_supply_leakage

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 5.1(b) and footnote 227. Goal CCM.

Inputs: annual_real_losses_m3 (m3), unavoidable_losses_m3 (m3).

Algorithm: ratio; comparison <= with threshold 1.5.

{
  "numerator": "annual_real_losses_m3",
  "denominator": "unavoidable_losses_m3"
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

water_supply_renewal_energy

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 5.2(a). Goal CCM.

Inputs: works_start (ISO date), baseline (3 annual {year, intensity} rows), current_intensity (kWh/m3).

Algorithm: baseline_reduction; comparison >= with threshold 0.2.

{}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

water_supply_renewal_leakage

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 5.2(b). Goal CCM.

Inputs: works_start (ISO date), baseline (3 annual {year, intensity} ILI rows), current_intensity (ILI).

Algorithm: leakage_gap; comparison >= with threshold 0.2.

{
  "target": 1.5
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

wastewater_treatment_energy

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 5.3(1). Goal CCM.

Inputs: capacity_pe (p.e.), served_pe (p.e.), net_energy_kwh (kWh/year).

Algorithm: wastewater; comparison <= with threshold None.

{
  "capacity_thresholds": [
    10000,
    100000
  ],
  "energy_thresholds": [
    35,
    25,
    20
  ]
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

wastewater_renewal_energy

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 5.4(2)-(3). Goal CCM.

Inputs: works_start (ISO date), baseline (3 annual {year, intensity} rows), current_intensity (kWh/p.e./year).

Algorithm: baseline_reduction; comparison >= with threshold 0.2.

{}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

steel_eaf_high_alloy_scrap

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 3.9(b)(i). Goal CCM.

Inputs: scrap_input_t (t), product_output_t (t).

Algorithm: ratio; comparison >= with threshold 0.7.

{
  "numerator": "scrap_input_t",
  "denominator": "product_output_t"
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

steel_eaf_carbon_scrap

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 3.9(b)(ii). Goal CCM.

Inputs: scrap_input_t (t), product_output_t (t).

Algorithm: ratio; comparison >= with threshold 0.9.

{
  "numerator": "scrap_input_t",
  "denominator": "product_output_t"
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

building_water_fittings

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 7.1 DNSH(3). Goal WTR.

Inputs: fittings (rows {fitting_id,type,flow_l_min?,full_flush_l?,average_flush_l?,litres_bowl_hour?}).

Algorithm: fittings; comparison None with threshold None.

{
  "limits": {
    "tap": 6,
    "shower": 8,
    "toilet_full": 6,
    "toilet_average": 3.5,
    "urinal_hour": 2,
    "urinal_flush": 1
  }
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

construction_waste_dnsh

Source: EU_TAX_CLIMATE_CONSOLIDATED_20260101, Annex I 7.1 DNSH(4). Goal CE.

Inputs: waste (rows {waste_id,mass_kg,hazardous,natural_170504,destination,mineral?}).

Algorithm: waste; comparison >= with threshold 0.7.

{
  "allow_backfill": true
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

circular_new_building_waste

Source: EU_TAX_ENV_CONSOLIDATED_20260101, Annex II 3.1(1). Goal CE.

Inputs: waste (disjoint mass rows; hazardous/natural excluded).

Algorithm: waste; comparison >= with threshold 0.9.

{
  "allow_backfill": false
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

circular_renovation_waste

Source: EU_TAX_ENV_CONSOLIDATED_20260101, Annex II 3.2(1). Goal CE.

Inputs: waste (disjoint mass rows; hazardous/natural excluded).

Algorithm: waste; comparison >= with threshold 0.7.

{
  "allow_backfill": false
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

circular_demolition_waste

Source: EU_TAX_ENV_CONSOLIDATED_20260101, Annex II 3.3(4). Goal CE.

Inputs: waste (mass rows with mineral flag).

Algorithm: demolition; comparison >= with threshold 0.9.

{
  "mineral_threshold": 0.95,
  "nonmineral_threshold": 0.7
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

circular_renovation_retained_area

Source: EU_TAX_ENV_CONSOLIDATED_20260101, Annex II 3.2(4). Goal CE.

Inputs: retained_gross_external_m2 (m2), original_gross_external_m2 (m2).

Algorithm: ratio; comparison >= with threshold 0.5.

{
  "numerator": "retained_gross_external_m2",
  "denominator": "original_gross_external_m2",
  "bounded_fraction": true
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

circular_new_building_primary_material

Source: EU_TAX_ENV_CONSOLIDATED_20260101, Annex II 3.1(4). Goal CE.

Inputs: materials (rows {material_id,category,mass_kg,secondary_fraction?,reused}), selected_categories (three heaviest if boundary ties).

Algorithm: materials; comparison None with threshold None.

{
  "limits": {
    "concrete_stone": 0.7,
    "brick_ceramic": 0.7,
    "biobased": 0.8,
    "glass_insulation": 0.7,
    "plastic_nonbio": 0.5,
    "metals": 0.3,
    "gypsum": 0.65
  }
}

Activity classification, named qualitative evidence, other SC clauses, DNSH and minimum safeguards remain separate requirements.

Shared algorithm definitions

ratio = numerator / denominator times scale; reduction = 1 − current/baseline. baseline_reduction = 1 − current/mean(baseline); leakage_gap = (mean(baseline) − current)/(mean(baseline) − 1.5). No positive baseline/gap means NOT_APPLICABLE. Waste shares use disjoint nonhazardous, non-natural-17-05-04 mass only. construction_waste_dnsh allows other material recovery including qualifying backfill; other_recovery here never means energy recovery. Circular-contribution waste clauses count only reuse/recycling and exclude backfill. Demolition supports either 90% overall or separate mineral/nonmineral 95%/70%; separate collection is external evidence.

Water fittings check every specified appliance. An unknown measurement produces UNKNOWN unless another appliance is already known to fail. Material clauses group by category and test the three heaviest groups; tied selection at third place requires selected_categories. Missing recycled-content information counts as primary material as prescribed; reused products count as zero primary. Lifecycle emissions are supplied on the prescribed lifecycle boundary; this is not a lifecycle modelling engine. PED renovation still requires the underlying survey/EPC evidence and a qualifying succession of measures within three years; the selected clause calculates the reduction fraction.

The 2025/legacy profiles permit only building_new_ped and light_vehicle_manufacture_co2. Unsupported profile/criterion combinations fail explicitly. Vehicle emissions are <50 gCO2/km before 2026 and exactly zero from 2026; the activity is manufacturing, not every transport use.

Reference rating esgcalc-reference-1

This is a normative, non-calibrated model of selected structured ESG performance for nonfinancial companies. It is neither a legal-compliance grade, a prediction of financial risk, a measure of causal impact nor a validated agency rating. Every scale, weight, class and cap is MODEL_CHOICE, not a legal threshold.

Six indicators receive equal weights within three equally weighted pillars:

Pillar Indicator / input unit Piecewise-linear scale Rationale and limits
E renewable_energy / fraction 0→0, 1→100 Renewable energy share is a transparent energy-mix proxy; ignores absolute consumption
E recycled_waste / fraction 0→0, 1→100 Recovery proxy; prevention and hazardous content remain separate metrics
S pay_gap / percent -100→0, 0→100, 100→0 Symmetric distance from equal gross hourly averages; not proof of equal pay for equal work
S injury_rate / per_million_hours 0→100, 20→0, above20→0 Zero injuries is the performance target; 20 is an illustrative saturation point, not a statutory or empirical benchmark
G board_female_share / fraction 0→0, 0.5→100, 1→0 Balanced representation proxy; deliberately nonmonotonic; other diversity dimensions not covered
G board_independence / fraction 0→0, 1→100 Independence proxy, not evidence of governance effectiveness

Fractions outside 0..1, pay gaps outside -100..100 and negative injury rates are outside the reference-model domain and rejected. Negative pay gaps below -100 can exist mathematically; the underlying pay-gap function supports them, while this deliberately bounded reference model does not. Unadjusted composition and sector effects make cross-sector comparisons especially limited.

Scores interpolate linearly between knots and saturate outside the knot domain within permitted input bounds. No rounding occurs before class assignment. A >=80, B >=60, C >=40, D >=20, E >=0. Equal weights avoid pretending to have empirical estimates of relative importance. The equally spaced classes are presentation choices. They do not carry probabilities or statutory meaning.

All six indicators are required by the reference model (minimum coverage=1). Missing/stale/non-applicable indicators are distinguished in missing_reasons, but each suppresses the overall score. There is no free weight redistribution for non-applicability: use a separately identified applicable model instead. Observations need value, unit, as_of and source; future dates are rejected; maximum age is 366 days relative to the explicit rating date. This annual-age convention is another model choice. Source content is not authenticated.

An externally assessed severe_violation=true caps the overall score at 20. The cap expresses an illustrative dominance rule over compensating strengths; it is not a legislated penalty. A missing required finding yields UNRATED. No violation is inferred from silence. Score and coverage remain separate.

Custom models have an ID/version, purpose, entity_type, parameter_origin, max_age_days, minimum_coverage, pillar_weights, indicators, limits and classes. Use rating_reference.json as the complete schema/example. Each indicator has id/pillar/unit/weight/minimum/maximum/knots. Each limit has id/required and action=cap (default) with cap, or action=exclude without a cap. A triggered exclusion produces EXCLUDED, score/class=None. Required unknown findings suppress a rating. Weights are positive and sum to one per pillar and overall (tolerance 1e-12). Coverage thresholds use the same arithmetic tolerance, never for legal limits. A distinct model hash identifies every parameter variant, even if a caller reuses an ID. Changes in population, time, boundaries or model invalidate naive comparisons. Models are copied and reference resources are never mutated.

normalize_indicator, pillar_scores, apply_rating_limits and overall_rating are independently callable. rate_company composes them and retains normalized scores, effective weights, contributions, input provenance, missing reasons and caps. explain_rating returns this stored evidence. rating_sensitivity supports explicit indicator/pillar weights, observations and findings overrides. Each variant carries a reason and SCENARIO mode, model hash, score delta, class_changed and pillar_contribution_deltas before caps. Changed caps/exclusions can explain a final-score delta beyond those raw contributions; all original inputs remain unchanged.

portfolio_rating_summary accepts serialized rate_company results. Models, as_of dates and modes must match; repeated issuer ratings must agree. The mean score is conditioned on rated investment value. Coverage, unrated value and class exposure are separate; ordinal class labels are never averaged.

120 reconciled annual cases for 60 entities in six archetypes and an executable mixed-portfolio pipeline supplement the older deterministic fixtures. Independent arithmetic checks every annual result; missing/stale inputs, exclusions and scenario class changes have explicit tests. See SYNTHETIC_CASEBOOK.md. They cannot establish predictive validity, industry calibration or representativeness. Offering published ratings of real companies requires a separate regulatory assessment; library/offline status alone does not establish an exemption from the EU ESG rating framework.

Deliberate method decisions and source boundaries

The local source freeze contains 21 full PDFs, including the 2026/73 amendment, 2026 consolidated taxonomy acts, the corrected/consolidated SFDR RTS and the archived ESA consolidated questions and answers. Original PDFs stay outside public packages. Packaged source_status.json contains provenance and hashes; source_contracts.json maps each function to its actual edition and locator.

PAI18: explicit interpretation instead of a hidden correction

The archived consolidated 2022/1288 Annex I formula for buildings after 31 December 2020 uses PED below the NZEB threshold. That wording conflicts with the intuitive interpretation of energy inefficiency as exceeding a consumption limit. The engine does not quietly replace the statutory comparison. A caller must select RTS_LITERAL_BELOW or, only in a named scenario, PERFORMANCE_NONCOMPLIANCE_SCENARIO. The exact cutoff date also requires an explicit year-end-inclusive convention. A missing selection raises an error. External asset applicability and national threshold selection remain supplied assessments. This resolves software behavior, not the underlying legal ambiguity.

Averaging, valuation and populations

For PAI5/6/12/13 the v2 profile fixes investment-value-weighted averages over applicable companies. The choice is documented as a method convention, not as a uniquely prescribed weighting in the legislation. Values/FX follow the declared FYE convention supported by the archived ESA Q&A; holdings can vary by reference date. Inputs must already resolve look-through and compatible issuer data.

ESRS/SFDR and a performance rating can reuse arithmetic without sharing every population or denominator. In particular, GOV-001 female/all-board is suitable for the selected SFDR component; it is not automatically the ESRS female/male ratio. Water reuse/(withdrawal+reuse) and days-lost severity per million hours are labelled analytical conventions. A date, unit and numerical result alone do not establish interchangeable disclosure definitions.

Frozen editions and reporting periods

as_of means knowledge/review date; period_start/period_end identify reporting. A pinned profile does not find later laws, decide legal applicability or certify that a reporting election is available to the caller. The 2025 legacy KPI option requires a fiscal year beginning in 2025 and uses the pre-2026 option set. The expanded numerical taxonomy catalog uses the 2026 consolidated editions; legacy profiles only support the two expressly retained unchanged clauses.

Qualitative evidence and selected coverage

External assessments require source, assessed_on, method, scope, status and evidence. A supplied positive result is never counted as an implemented technical criterion. The 23 numerical clauses are the complete selected catalog, not the complete EU taxonomy. Other activities/clauses and complete DNSH assessments are outside that catalog. The six environmental objectives remain supported in the combination model. Financial-institution KPIs, full SFDR/ESRS reports, legal applicability, lifecycle models and other sector engines are outside scope.

Rating interpretation

All rating scales, weights, exclusions and classes are model choices. The reference model covers six selected performance proxies; it omits absolute GHG emissions, many social outcomes and most sector-specific effects. A high score cannot establish climate alignment, legal compliance or low financial risk. Synthetic examples are designed for reproducibility and plausible accounting relationships, not empirical calibration or validation of predictive power.

Reconciled synthetic casebook

synthetic_realistic_cases.json contains 120 annual cases for 60 fictional nonfinancial entities, ten variants each of six archetypes, for 2025 and 2026:

Archetype Main contrast
Steel Electric-arc scrap threshold, substantial purchased electricity and upstream emissions
Services Low taxonomy eligibility with potentially good selected performance metrics
Cement/process industry High process emissions, eligible but largely unaligned expenditure
Water utility Computed pumping intensity, water-risk facilities and operating costs
Renovation builder PED reduction, material/waste flows and accident frequency
Hydropower Computed power density; energy production separate from own consumption

All numbers, companies, assessments and emission factors are fictional design assumptions. They are internally reconciled, not statistically representative. Full-year 2026 observations and their 2027 knowledge dates are explicit future scenarios. A fixed random seed 20261007 controls size variants. Dataset ID, schema, generator hash and seed accompany the JSON; the development source has frozen CSV financial, workforce and expectation tables as well.

Assets equal liabilities plus book equity. EVIC uses market equity, preferred equity, debt and minority interests without deducting cash. Opening employees plus hires minus leavers equal closing employees. Revenue/CapEx/OpEx rows reconcile to company totals. Waste destinations reconcile to generated mass; hazardous waste is a subset. Scope2 market/location figures are alternatives. Fictional qualitative assessments carry date, scope, method and source.

synthetic_realistic_expected.json contains independently calculated Decimal expectations for emissions, energy/waste fractions, pay, injuries, turnover, water, KPIs and the reference score/class. Its generator does not import the engine. Every annual case is executed against those expectations in the tests. The mixed-portfolio example includes split company positions, cash, a sovereign and real estate; PAI denominators, financed emissions and rating coverage are reconciled independently.

examples/realistic_pipeline.py OUTPUT.json executes six representative annual cases and the mixed portfolio using only installed resources. Results preserve their calculations, source references and rating explanations. Individual functions remain usable without running this example.

Adverse scenarios are catalogued in synthetic_scenarios.json and exercised in the shipped tests: unknown DNSH, missing/stale rating inputs, severe violation, explicit exclusion, changed weights/inputs, successful/failed CapEx plans and historical restatement, duplicate positions, missing FX, PAI18 interpretation, and scope2 overlap. These include expected rejections, not only successful cases. The older 120 index-formula fixtures remain as regression data; they are not presented as realistic sector cases or independent empirical evidence.

Public API v0.2.0.dev0

All functions return Result. Common and row contracts: CONTRACTS.md. Rating model schema and interpretation: RATING.md. The 47 entries describe implemented capabilities, not complete ESG legislation coverage.

TAX-001 — esgcalc.taxonomy.combine_taxonomy_assessments

(eligible, substantial_contribution, dnsh, minimum_safeguards, technical_screening, *, context=None)

Combine explicitly sourced assessments for six goals; never infer missing evidence.

Source/method: EU_TAX_BASE_2020 — Articles 3, 9, 17, 18. Kind: LEGAL_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_taxonomy_goal_and_activity_contracts, test_unknown_alignment_does_not_pass, test_bad_baseline_unit_and_provenance.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "eligible": {
      "status": "MET",
      "origin": "EXTERNAL_ASSESSMENT",
      "source": "synthetic-evidence-v1",
      "method": "explicit fixture classification",
      "scope": "declared synthetic activity",
      "assessed_on": "2025-01-01",
      "evidence": "synthetic reviewed finding"
    },
    "substantial_contribution": {
      "CCM": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "CCA": {
        "status": "NOT_MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "WTR": {
        "status": "NOT_MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "CE": {
        "status": "NOT_MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "PPC": {
        "status": "NOT_MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "BIO": {
        "status": "NOT_MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      }
    },
    "dnsh": {
      "CCM": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "CCA": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "WTR": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "CE": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "PPC": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "BIO": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      }
    },
    "minimum_safeguards": {
      "status": "MET",
      "origin": "EXTERNAL_ASSESSMENT",
      "source": "synthetic-evidence-v1",
      "method": "explicit fixture classification",
      "scope": "declared synthetic activity",
      "assessed_on": "2025-01-01",
      "evidence": "synthetic reviewed finding"
    },
    "technical_screening": {
      "CCM": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "CCA": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "WTR": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "CE": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "PPC": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      },
      "BIO": {
        "status": "MET",
        "origin": "EXTERNAL_ASSESSMENT",
        "source": "synthetic-evidence-v1",
        "method": "explicit fixture classification",
        "scope": "declared synthetic activity",
        "assessed_on": "2025-01-01",
        "evidence": "synthetic reviewed finding"
      }
    }
  },
  "context": {
    "profile_id": "eu-taxonomy-2026-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": "MET"
  }
}

TAX-002 — esgcalc.taxonomy.check_quantitative_criterion

(criterion_id, values, *, context=None)

Compute a selected clause from the edition-pinned numerical criterion catalog.

Source/method: EU_TAX_CLIMATE_CONSOLIDATED_20260101 — Per-clause locators in criteria.json; circular clauses use EU_TAX_ENV_CONSOLIDATED_20260101. Kind: LEGAL_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_numeric_boundaries, test_wastewater_capacity_steps, test_waste_boundaries, test_waste_alternatives_and_exclusions, test_fitting_limits, test_primary_material_boundaries, test_material_selection_and_reuse, test_bad_baseline_unit_and_provenance, test_vehicle_date_threshold, test_taxonomy_goal_and_activity_contracts.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "criterion_id": "building_new_ped",
    "values": {
      "ped": 90,
      "nzeb_threshold": 100,
      "unit": "kWh/m2/year"
    }
  },
  "context": {
    "profile_id": "eu-taxonomy-2026-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": "MET"
  }
}

TAX-003 — esgcalc.taxonomy.validate_activity_assessment

(activities, *, context=None)

Validate distinct activities and their assessment inputs without classifying NACE codes.

Source/method: EU_TAX_BASE_2020 — Articles 3, 9, 17, 18. Kind: LEGAL_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_taxonomy_goal_and_activity_contracts.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "activities": [
      {
        "activity_id": "a",
        "eligible": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        },
        "sc": {
          "CCM": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "CCA": {
            "status": "NOT_MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "WTR": {
            "status": "NOT_MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "CE": {
            "status": "NOT_MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "PPC": {
            "status": "NOT_MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "BIO": {
            "status": "NOT_MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          }
        },
        "dnsh": {
          "CCM": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "CCA": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "WTR": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "CE": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "PPC": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "BIO": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          }
        },
        "minimum_safeguards": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        },
        "technical_screening": {
          "CCM": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "CCA": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "WTR": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "CE": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "PPC": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          },
          "BIO": {
            "status": "MET",
            "origin": "EXTERNAL_ASSESSMENT",
            "source": "synthetic-evidence-v1",
            "method": "explicit fixture classification",
            "scope": "declared synthetic activity",
            "assessed_on": "2025-01-01",
            "evidence": "synthetic reviewed finding"
          }
        }
      }
    ]
  },
  "context": {
    "profile_id": "eu-taxonomy-2026-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value.0.assessment": "MET"
  }
}

TAX-004 — esgcalc.taxonomy.allocate_multi_objective_contributions

(amounts, allocations, *, context=None)

Allocate disjoint slices once in total; each slice may contribute to multiple goals.

Source/method: EU_TAX_KPI_CONSOLIDATED_20260101 — Annex I 1.2.2; Annex II: avoid double counting. Kind: LEGAL_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_double_counting_rejected, test_kpi_denominators_and_allocation_edges.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "amounts": [
      {
        "amount_id": "a",
        "amount": 100
      }
    ],
    "allocations": [
      {
        "allocation_id": "x",
        "amount_id": "a",
        "fraction": 0.6,
        "goals": [
          "CCM",
          "CE"
        ]
      }
    ]
  },
  "context": {
    "profile_id": "eu-taxonomy-2026-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": 60,
    "details.by_goal.CCM": 60,
    "details.by_goal.CE": 60
  }
}

KPI-001 — esgcalc.kpi.taxonomy_turnover

(rows, *, context=None)

Compute nonfinancial turnover KPI from net-revenue rows and explicit assessments.

Source/method: EU_TAX_KPI_CONSOLIDATED_20260101 — Article 2(1a); Article 8(6)-(7); Annex I 1.1.1. Kind: LEGAL_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_turnover_adaptation_restriction, test_materiality_boundary_and_missing_amount, test_kpi_denominators_and_allocation_edges, test_capex_plan_failure_history_and_legacy.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "amount_id": "a",
        "amount": 40,
        "accounting_category": "net_revenue",
        "currency": "EUR",
        "period_end": "2026-06-30",
        "eligible": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        },
        "alignment": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        }
      },
      {
        "amount_id": "b",
        "amount": 60,
        "accounting_category": "net_revenue",
        "currency": "EUR",
        "period_end": "2026-06-30",
        "eligible": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        },
        "alignment": {
          "status": "NOT_MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        }
      }
    ]
  },
  "context": {
    "profile_id": "eu-taxonomy-2026-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": 0.4,
    "details.denominator": 100
  }
}

KPI-002 — esgcalc.kpi.taxonomy_capex

(rows, *, prior_periods=(), context=None)

Compute CapEx KPI with accounting exclusions, plan and individual-measure routes.

Source/method: EU_TAX_KPI_CONSOLIDATED_20260101 — Article 2(1b); Annex I 1.1.2.1-1.1.2.2; 2026/73 Article 4 election. Kind: LEGAL_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_kpi_denominators_and_allocation_edges, test_capex_plan_term_boundaries, test_capex_plan_unknown_and_updated_criteria, test_individual_measure_18_months, test_capex_plan_failure_history_and_legacy.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "amount_id": "a",
        "amount": 40,
        "accounting_category": "IAS16",
        "currency": "EUR",
        "period_end": "2026-06-30",
        "eligible": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        },
        "alignment": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        }
      },
      {
        "amount_id": "b",
        "amount": 60,
        "accounting_category": "IAS16",
        "currency": "EUR",
        "period_end": "2026-06-30",
        "eligible": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        },
        "alignment": {
          "status": "NOT_MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        }
      }
    ]
  },
  "context": {
    "profile_id": "eu-taxonomy-2026-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": 0.4,
    "details.denominator": 100
  }
}

KPI-003 — esgcalc.kpi.taxonomy_opex

(rows, *, immaterial_reason=None, context=None)

Compute OpEx KPI, excluding capitalised/double-counted expenditure.

Source/method: EU_TAX_KPI_CONSOLIDATED_20260101 — Article 2(1c); Annex I 1.1.3.1-1.1.3.2. Kind: LEGAL_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_opex_exclusion_and_immateriality, test_kpi_denominators_and_allocation_edges, test_opex_plan_and_measure.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "amount_id": "a",
        "amount": 40,
        "accounting_category": "maintenance_repair",
        "currency": "EUR",
        "period_end": "2026-06-30",
        "eligible": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        },
        "alignment": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        }
      },
      {
        "amount_id": "b",
        "amount": 60,
        "accounting_category": "maintenance_repair",
        "currency": "EUR",
        "period_end": "2026-06-30",
        "eligible": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        },
        "alignment": {
          "status": "NOT_MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        }
      }
    ]
  },
  "context": {
    "profile_id": "eu-taxonomy-2026-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": 0.4,
    "details.denominator": 100
  }
}

KPI-004 — esgcalc.kpi.reconcile_kpi_allocations

(amounts, allocations, *, context=None)

Reconcile disjoint allocation fractions against original amount IDs.

Source/method: EU_TAX_KPI_CONSOLIDATED_20260101 — Annex I 1.2.2; Annex II: avoid double counting. Kind: LEGAL_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_kpi_denominators_and_allocation_edges.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "amounts": [
      {
        "amount_id": "a",
        "amount": 100
      }
    ],
    "allocations": [
      {
        "allocation_id": "x",
        "amount_id": "a",
        "fraction": 1,
        "goals": [
          "CCM",
          "CE"
        ]
      }
    ]
  },
  "context": {
    "profile_id": "eu-taxonomy-2026-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": true,
    "details.unallocated.a": 0
  }
}

PAI-001 — esgcalc.pai.calculate_pai_indicator

(indicator_id, rows, *, averaging=None, context=None)

Calculate an enumerated Table 1 component; averaging conventions must be explicitly selected.

Source/method: EU_SFDR_RTS_CONSOLIDATED_20230220 — Annex I Table 1 indicators 1-18 and definitions; ESA JC2023_18 Section IV questions 10,11,17,27,28. Kind: LEGAL_DEFINITION.

Contract boundary: Weighted means for 5/6/12/13 are declared method choices; PAI18 PED ambiguity has explicit interpretations.

Edge/reference tests: test_all_pai_independent_hand_values, test_each_company_pai_component, test_sovereign_and_real_estate_pai, test_pai_averaging_method_is_explicit_and_split_invariant, test_pai_price_evic_and_building_interpretation, test_pai_adverse_field_and_universe_contracts.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "indicator_id": 1,
    "rows": [
      {
        "position_id": "a",
        "issuer_id": "a",
        "value": 200000,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "asset_class": "company",
        "evic": 1000000,
        "scope1": 20,
        "scope2": 30,
        "scope3": 50
      },
      {
        "position_id": "b",
        "issuer_id": "b",
        "value": 800000,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "asset_class": "cash"
      }
    ]
  },
  "context": {
    "profile_id": "sfdr-pai-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value.scope1": 4,
    "value.scope2": 6,
    "value.scope3": 10,
    "value.total": 20
  }
}

PAI-002 — esgcalc.pai.aggregate_pai_observations

(observations, *, sampling='quarter_ends', context=None)

Mean of complete quarter-end observations, or explicitly selected additional observations.

Source/method: EU_SFDR_RTS_CONSOLIDATED_20230220 — Article 6(3); ESA JC2023_18 Section IV: quarter-end mean, optional additional observations. Kind: LEGAL_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_pai_annual_schedule, test_incomplete_quarters_rejected, test_pai_adverse_field_and_universe_contracts.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "observations": [
      {
        "as_of": "2026-03-31",
        "indicator_id": 2,
        "component": "footprint",
        "unit": "tCO2e/million_EUR_invested",
        "method_id": "v1",
        "value": 10
      },
      {
        "as_of": "2026-06-30",
        "indicator_id": 2,
        "component": "footprint",
        "unit": "tCO2e/million_EUR_invested",
        "method_id": "v1",
        "value": 20
      },
      {
        "as_of": "2026-09-30",
        "indicator_id": 2,
        "component": "footprint",
        "unit": "tCO2e/million_EUR_invested",
        "method_id": "v1",
        "value": 30
      },
      {
        "as_of": "2026-12-31",
        "indicator_id": 2,
        "component": "footprint",
        "unit": "tCO2e/million_EUR_invested",
        "method_id": "v1",
        "value": 40
      }
    ]
  },
  "context": {
    "profile_id": "sfdr-pai-v1",
    "as_of": "2026-12-31"
  },
  "expected_checks": {
    "value": 25
  }
}

PAI-003 — esgcalc.pai.aggregate_pai_portfolio

(rows, indicator_ids, *, averaging=None, context=None)

Calculate selected PAI components on one unchanged portfolio universe.

Source/method: EU_SFDR_RTS_CONSOLIDATED_20230220 — Annex I Table 1 indicators 1-18 and definitions; ESA JC2023_18 Section IV questions 10,11,17,27,28. Kind: LEGAL_DEFINITION.

Contract boundary: Weighted means for 5/6/12/13 are declared method choices; PAI18 PED ambiguity has explicit interpretations.

Edge/reference tests: test_mixed_portfolio_reconciles, test_pai_price_evic_and_building_interpretation, test_pai_adverse_field_and_universe_contracts.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "position_id": "a",
        "issuer_id": "a",
        "value": 200000,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "asset_class": "company",
        "evic": 1000000,
        "scope1": 20,
        "scope2": 30,
        "scope3": 50
      },
      {
        "position_id": "b",
        "issuer_id": "b",
        "value": 800000,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "asset_class": "cash"
      }
    ],
    "indicator_ids": [
      1,
      2
    ]
  },
  "context": {
    "profile_id": "sfdr-pai-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value.2.value": 20
  }
}

PAI-004 — esgcalc.pai.validate_pai_inputs

(indicator_id, rows, *, context=None)

Check an indicator’s declared universe, consistent issuer data and required typed fields.

Source/method: EU_SFDR_RTS_CONSOLIDATED_20230220 — Annex I Table 1 indicators 1-18 and definitions; ESA JC2023_18 Section IV questions 10,11,17,27,28. Kind: LEGAL_DEFINITION.

Contract boundary: Weighted means for 5/6/12/13 are declared method choices; PAI18 PED ambiguity has explicit interpretations.

Edge/reference tests: test_pai_adverse_field_and_universe_contracts, test_missing_and_zero_denominators, test_pai_price_evic_and_building_interpretation.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "indicator_id": 1,
    "rows": [
      {
        "position_id": "a",
        "issuer_id": "a",
        "value": 200000,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "asset_class": "company",
        "evic": 1000000,
        "scope1": 20,
        "scope2": 30,
        "scope3": 50
      },
      {
        "position_id": "b",
        "issuer_id": "b",
        "value": 800000,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "asset_class": "cash"
      }
    ]
  },
  "context": {
    "profile_id": "sfdr-pai-v1",
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": true
  }
}

ENV-001 — esgcalc.environment.emissions_from_activity

(rows, *, context=None)

Multiply explicit activity quantities by sourced factors, output tonnes CO2e.

Source/method: GHG_CORPORATE_REVISED_2004 — Chapter 6: identifying and calculating GHG emissions. Kind: ACCOUNTING_METHOD.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_environment_boundaries_not_silent_defaults, test_consistent_balances_and_independent_results.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "activity_id": "a",
        "quantity": 2000,
        "activity_unit": "kWh",
        "factor": 0.4,
        "factor_activity_unit": "kWh",
        "factor_emission_unit": "kgCO2e",
        "factor_source": "synthetic"
      }
    ]
  },
  "context": {},
  "expected_checks": {
    "value": 0.8
  }
}

ENV-002 — esgcalc.environment.aggregate_ghg_inventory

(rows, *, scope2_method, required_scopes=('1', '2', '3'), context=None)

Aggregate the declared scopes; market/location alternatives are never added.

Source/method: EU_ESRS_2023 — ESRS E1-6 paragraphs 44-55; AR39-55. Kind: METRIC_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_scope2_and_scaling, test_environment_boundaries_not_silent_defaults, test_missing_and_zero_denominators.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "emission_id": "1",
        "scope": "1",
        "value": 10,
        "unit": "tCO2e"
      },
      {
        "emission_id": "2_location",
        "scope": "2_location",
        "value": 20,
        "unit": "tCO2e"
      },
      {
        "emission_id": "2_market",
        "scope": "2_market",
        "value": 3,
        "unit": "tCO2e"
      },
      {
        "emission_id": "3",
        "scope": "3",
        "value": 40,
        "unit": "tCO2e"
      }
    ],
    "scope2_method": "market"
  },
  "context": {},
  "expected_checks": {
    "value": 53
  }
}

ENV-003 — esgcalc.environment.ghg_intensity

(emissions, denominator, *, denominator_unit, context=None)

Divide tonnes CO2e by an explicitly named positive physical/economic basis.

Source/method: EU_ESRS_2023 — ESRS E1-6 paragraphs 44-55; AR39-55. Kind: METRIC_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_missing_and_zero_denominators, test_invalid_numbers.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "emissions": 100,
    "denominator": 20,
    "denominator_unit": "product_t"
  },
  "context": {},
  "expected_checks": {
    "value": 5
  }
}

ENV-004 — esgcalc.environment.energy_metrics

(rows, *, revenues=(), context=None)

Separate consumption/production and compute high-impact-sector revenue intensity.

Source/method: EU_ESRS_2023 — ESRS E1-5 paragraphs 37-43; AR32-38. Kind: METRIC_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_energy_water_social_restatement, test_environment_boundaries_not_silent_defaults, test_consistent_balances_and_independent_results.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "energy_id": "r",
        "category": "renewable",
        "value": 1000,
        "unit": "kWh"
      },
      {
        "energy_id": "n",
        "category": "nuclear",
        "value": 3,
        "unit": "MWh"
      }
    ]
  },
  "context": {},
  "expected_checks": {
    "value.total_mwh": 4,
    "value.renewable_fraction": 0.25
  }
}

ENV-005 — esgcalc.environment.water_metrics

(withdrawn=None, discharged=None, reused=None, *, unit='m3', facilities=None, stored_start=None, stored_end=None, net_revenue_eur=None, context=None)

Common-boundary consumption, risk-area consumption, storage and revenue intensity.

Source/method: EU_ESRS_2023 — ESRS E3-4 paragraphs 28-29; AR28-32. Kind: METRIC_DEFINITION.

Contract boundary: Reuse/withdrawal-plus-reuse share is a separate analytical convention.

Edge/reference tests: test_energy_water_social_restatement, test_environment_boundaries_not_silent_defaults.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "withdrawn": 100,
    "discharged": 60,
    "reused": 25
  },
  "context": {},
  "expected_checks": {
    "value.consumption_m3": 40,
    "value.reuse_fraction_of_withdrawal_plus_reuse": 0.2
  }
}

ENV-006 — esgcalc.environment.waste_resource_metrics

(waste_rows, *, material_inputs=(), context=None)

Separate waste destinations/hazardous totals and secondary material input shares.

Source/method: EU_ESRS_2023 — ESRS E5-4 paragraph31; E5-5 paragraphs35-40. Kind: METRIC_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_environment_boundaries_not_silent_defaults, test_consistent_balances_and_independent_results.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "waste_rows": [
      {
        "waste_id": "a",
        "value": 1000,
        "unit": "kg",
        "destination": "recycled",
        "hazardous": false
      },
      {
        "waste_id": "b",
        "value": 1,
        "unit": "t",
        "destination": "disposal",
        "hazardous": true
      }
    ],
    "material_inputs": [
      {
        "material_id": "a",
        "value": 5,
        "unit": "t",
        "secondary": true
      }
    ]
  },
  "context": {},
  "expected_checks": {
    "value.waste_t": 2,
    "value.recycled_fraction": 0.5,
    "value.secondary_input_fraction": 1
  }
}

ENV-007 — esgcalc.environment.metric_change

(baseline, current, *, comparable, unit='value', restatement=None, context=None)

Calculate change only on a declared comparable boundary/method; negative bases have no relative rate.

Source/method: EU_ESRS_2023 — ESRS 1 section7.1 comparatives; explicit arithmetic change. Kind: ANALYTICAL_METHOD.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_energy_water_social_restatement, test_environment_boundaries_not_silent_defaults.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "baseline": 100,
    "current": 80,
    "comparable": true
  },
  "context": {},
  "expected_checks": {
    "value.absolute": -20,
    "value.relative": -0.2
  }
}

SOC-001 — esgcalc.social.workforce_metrics

(groups, *, leavers=None, turnover_denominator=None, denominator_method=None, context=None)

Aggregate disjoint workforce groups in headcount and FTE separately.

Source/method: EU_ESRS_2023 — ESRS S1-6 paragraphs50-52; AR59. Kind: METRIC_DEFINITION.

Contract boundary: Turnover denominator methodology explicitly selected and disclosed.

Edge/reference tests: test_energy_water_social_restatement, test_social_governance_population_contracts, test_consistent_balances_and_independent_results.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "groups": [
      {
        "group_id": "a",
        "headcount": 10,
        "fte": 8
      },
      {
        "group_id": "b",
        "headcount": 5,
        "fte": 2
      }
    ]
  },
  "context": {},
  "expected_checks": {
    "value.headcount": 15,
    "value.fte": 10
  }
}

SOC-002 — esgcalc.social.diversity_metrics

(groups, *, context=None)

Compute disjoint category shares including an explicit unknown category where needed.

Source/method: EU_ESRS_2023 — ESRS S1-9 paragraphs64-66; caller defines exhaustive population. Kind: METRIC_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_social_governance_population_contracts.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "groups": [
      {
        "category": "female",
        "count": 4
      },
      {
        "category": "male",
        "count": 5
      },
      {
        "category": "unknown",
        "count": 1
      }
    ]
  },
  "context": {},
  "expected_checks": {
    "value.fractions.female": 0.4,
    "value.fractions.unknown": 0.1
  }
}

SOC-003 — esgcalc.social.pay_gap

(male_gross_hourly_pay, female_gross_hourly_pay, *, context=None)

Unadjusted pay gap (male minus female)/male, in percentage points; signed.

Source/method: EU_ESRS_2023 — ESRS S1-16 paragraph97(a); AR98. Kind: METRIC_DEFINITION.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_social_governance_population_contracts, test_consistent_balances_and_independent_results.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "male_gross_hourly_pay": 20,
    "female_gross_hourly_pay": 18
  },
  "context": {},
  "expected_checks": {
    "value": 10
  }
}

SOC-004 — esgcalc.social.health_safety_metrics

(recordable_injuries, hours_worked, *, fatalities=None, fatalities_from_ill_health=None, recordable_ill_health_cases=None, days_lost=None, covered_workers=None, total_workers=None, audited_covered_workers=None, context=None)

Accident frequency, separately classified fatalities, days lost and coverage.

Source/method: EU_ESRS_2023 — ESRS S1-14 paragraph88; AR80-95. Kind: METRIC_DEFINITION.

Contract boundary: Severity per million hours is analytical; fatalities and illness remain separate counts.

Edge/reference tests: test_energy_water_social_restatement, test_social_governance_population_contracts.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "recordable_injuries": 3,
    "hours_worked": 2000000,
    "fatalities": 1
  },
  "context": {},
  "expected_checks": {
    "value.injury_rate_per_million_hours": 1.5
  }
}

GOV-001 — esgcalc.governance.board_metrics

(female, male, *, other=0, unknown_gender=0, independent=None, context=None)

Female representation uses all board members, including declared other/unknown gender.

Source/method: EU_ESRS_2023 — ESRS 2 GOV-1 paragraph21; SFDR Annex I Table1 indicator13. Kind: METRIC_DEFINITION.

Contract boundary: Female/all-member denominator follows SFDR; do not label it ESRS female/male ratio.

Edge/reference tests: test_social_governance_population_contracts.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "female": 4,
    "male": 5,
    "unknown_gender": 1,
    "independent": 6
  },
  "context": {},
  "expected_checks": {
    "value.female_fraction": 0.4,
    "value.independent_fraction": 0.6
  }
}

GOV-002 — esgcalc.governance.validate_governance_findings

(findings, *, context=None)

Check provenance/status of structured findings, without investigating their truth.

Source/method: EU_ESRS_2023 — ESRS G1 structured supplied findings. Kind: INPUT_CONTRACT.

Contract boundary: No independent investigation or legal determination.

Edge/reference tests: test_social_governance_population_contracts, test_governance_future_assessment.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "findings": [
      {
        "finding_id": "a",
        "assessment": {
          "status": "MET",
          "origin": "EXTERNAL_ASSESSMENT",
          "source": "synthetic-evidence-v1",
          "method": "explicit fixture classification",
          "scope": "declared synthetic activity",
          "assessed_on": "2025-01-01",
          "evidence": "synthetic reviewed finding"
        }
      }
    ]
  },
  "context": {},
  "expected_checks": {
    "value.a": "MET"
  }
}

PORT-001 — esgcalc.portfolio.position_weights

(rows, *, context=None)

Long-only weights over the entire declared portfolio; no missing-position deletion.

Source/method: ESGCALC_METHODS_V2 — CONTRACTS.md portfolio methods. Kind: ANALYTICAL_METHOD.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_portfolio_full_denominators_and_errors, test_duplicate_positions_and_conflicting_issuers, test_currency_and_date_mismatch.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "position_id": "a",
        "issuer_id": "a",
        "value": 20,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": true,
        "metric": 5
      },
      {
        "position_id": "b",
        "issuer_id": "b",
        "value": 80,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": false
      }
    ]
  },
  "context": {
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value.a": 0.2
  }
}

PORT-002 — esgcalc.portfolio.portfolio_coverage

(rows, *, field, context=None)

Amount-weighted nonmissing field coverage with numerator/denominator retained.

Source/method: ESGCALC_METHODS_V2 — CONTRACTS.md portfolio methods. Kind: ANALYTICAL_METHOD.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_portfolio_full_denominators_and_errors, test_portfolio_empty_coverage.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "position_id": "a",
        "issuer_id": "a",
        "value": 20,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": true,
        "metric": 5
      },
      {
        "position_id": "b",
        "issuer_id": "b",
        "value": 80,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": false
      }
    ],
    "field": "metric"
  },
  "context": {
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": 0.2
  }
}

PORT-003 — esgcalc.portfolio.portfolio_exposure

(rows, *, field, context=None)

Known positive exposure and unknown share; incomplete data do not become false.

Source/method: ESGCALC_METHODS_V2 — CONTRACTS.md portfolio methods. Kind: ANALYTICAL_METHOD.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_portfolio_full_denominators_and_errors.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "position_id": "a",
        "issuer_id": "a",
        "value": 20,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": true,
        "metric": 5
      },
      {
        "position_id": "b",
        "issuer_id": "b",
        "value": 80,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": false
      }
    ],
    "field": "flag"
  },
  "context": {
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": 0.2
  }
}

PORT-004 — esgcalc.portfolio.portfolio_concentration

(rows, *, group_field='issuer_id', context=None)

Herfindahl index and largest group weight for nonnegative exposures.

Source/method: ESGCALC_METHODS_V2 — CONTRACTS.md portfolio methods. Kind: ANALYTICAL_METHOD.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_portfolio_full_denominators_and_errors.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "position_id": "a",
        "issuer_id": "a",
        "value": 20,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": true,
        "metric": 5
      },
      {
        "position_id": "b",
        "issuer_id": "b",
        "value": 80,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": false
      }
    ]
  },
  "context": {
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value.hhi": 0.68,
    "value.largest_fraction": 0.8
  }
}

PORT-005 — esgcalc.portfolio.convert_currency

(amount, *, source_currency, target_currency, rate, rate_date, as_of, rate_source, context=None)

Use explicit target-currency units per one source-currency unit; never fetch or invert.

Source/method: ESGCALC_METHODS_V2 — CONTRACTS.md portfolio methods. Kind: ANALYTICAL_METHOD.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_portfolio_full_denominators_and_errors, test_currency_and_date_mismatch, test_fx_exact_dates.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "amount": 100,
    "source_currency": "USD",
    "target_currency": "EUR",
    "rate": 0.9,
    "rate_date": "2026-06-30",
    "as_of": "2026-06-30",
    "rate_source": "synthetic"
  },
  "context": {
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": 90
  }
}

PORT-006 — esgcalc.portfolio.financed_emissions

(rows, *, context=None)

Declared listed-equity/listed-company-bond attribution using positive EVIC and sourced emissions.

Source/method: PCAF_PART_A_2025 — Section5.1 listed equity and corporate bonds. Kind: ACCOUNTING_METHOD.

Contract boundary: Listed-company equities at market value and bonds at book value; EVIC, no cash subtraction; other PCAF asset classes excluded.

Edge/reference tests: test_financed_emissions_reject_inconsistent_issuer_data, test_portfolio_full_denominators_and_errors, test_mixed_portfolio_reconciles, test_financed_scope_and_zero_evic.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "position_id": "a",
        "issuer_id": "a",
        "value": 20,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": true,
        "metric": 5,
        "asset_class": "listed_equity",
        "value_basis": "market_value",
        "evic": 100,
        "emissions_tco2e": 1000,
        "emissions_scope": "1+2+3"
      }
    ]
  },
  "context": {
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value": 200
  }
}

PORT-007 — esgcalc.portfolio.aggregate_issuer_positions

(rows, *, context=None)

Aggregate position values while requiring consistent duplicated issuer attributes.

Source/method: ESGCALC_METHODS_V2 — CONTRACTS.md portfolio methods. Kind: ANALYTICAL_METHOD.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_portfolio_full_denominators_and_errors, test_duplicate_positions_and_conflicting_issuers.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "position_id": "a",
        "issuer_id": "a",
        "value": 20,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": true,
        "metric": 5
      },
      {
        "position_id": "a2",
        "issuer_id": "a",
        "value": 30,
        "currency": "EUR",
        "as_of": "2026-06-30",
        "flag": true,
        "metric": 5
      }
    ]
  },
  "context": {
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value.0.value": 50
  }
}

RATE-001 — esgcalc.rating.validate_rating_model

(model=None, *, context=None)

Validate bounded piecewise scales, complete pillars, weights, classes and caps.

Source/method: ESGCALC_REFERENCE_MODEL_V1 — RATING.md; rating_reference.json. Kind: MODEL_CHOICE.

Contract boundary: Normative performance model, not a statutory rating or empirically calibrated financial-risk model.

Edge/reference tests: test_model_invalid_weights_and_unit, test_rating_model_edges_and_effective_weights, test_rating_exclusions_and_input_findings_sensitivity.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {},
  "context": {},
  "expected_checks": {
    "value": true
  }
}

RATE-002 — esgcalc.rating.normalize_indicator

(indicator_id, value, *, unit, model=None, context=None)

Interpolate an explicit model scale; reject incompatible units and out-of-domain inputs.

Source/method: ESGCALC_REFERENCE_MODEL_V1 — RATING.md; rating_reference.json. Kind: MODEL_CHOICE.

Contract boundary: Normative performance model, not a statutory rating or empirically calibrated financial-risk model.

Edge/reference tests: test_normalization_monotonic, test_model_invalid_weights_and_unit, test_rating_model_edges_and_effective_weights.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "indicator_id": "renewable_energy",
    "value": 0.7,
    "unit": "fraction"
  },
  "context": {},
  "expected_checks": {
    "value": 70
  }
}

RATE-003 — esgcalc.rating.pillar_scores

(scores, *, model=None, context=None)

Weight normalized indicator scores; report coverage separately and enforce minima.

Source/method: ESGCALC_REFERENCE_MODEL_V1 — RATING.md; rating_reference.json. Kind: MODEL_CHOICE.

Contract boundary: Normative performance model, not a statutory rating or empirically calibrated financial-risk model.

Edge/reference tests: test_rating_model_edges_and_effective_weights, test_rating_data_removal_never_rewards.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "scores": {
      "renewable_energy": 20,
      "recycled_waste": 80,
      "pay_gap": 60,
      "injury_rate": 80,
      "board_female_share": 100,
      "board_independence": 60
    }
  },
  "context": {},
  "expected_checks": {
    "value.E.score": 50,
    "value.S.score": 70,
    "value.G.score": 80
  }
}

RATE-004 — esgcalc.rating.apply_rating_limits

(score, findings, *, model=None, context=None)

Apply most restrictive triggered cap; unknown required findings suppress the score.

Source/method: ESGCALC_REFERENCE_MODEL_V1 — RATING.md; rating_reference.json. Kind: MODEL_CHOICE.

Contract boundary: Normative performance model, not a statutory rating or empirically calibrated financial-risk model.

Edge/reference tests: test_rating_model_edges_and_effective_weights, test_rating_exclusions_and_input_findings_sensitivity, test_unknown_critical_finding_and_stale_data.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "score": 90,
    "findings": {
      "severe_violation": true
    }
  },
  "context": {},
  "expected_checks": {
    "value": 20
  }
}

RATE-005 — esgcalc.rating.overall_rating

(pillars, findings, *, model=None, context=None)

Combine E/S/G pillar records, check coverage, then apply caps and class thresholds.

Source/method: ESGCALC_REFERENCE_MODEL_V1 — RATING.md; rating_reference.json. Kind: MODEL_CHOICE.

Contract boundary: Normative performance model, not a statutory rating or empirically calibrated financial-risk model.

Edge/reference tests: test_class_boundaries, test_rating_data_removal_never_rewards, test_rating_model_edges_and_effective_weights.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "pillars": {
      "E": {
        "score": 60,
        "coverage": 1
      },
      "S": {
        "score": 60,
        "coverage": 1
      },
      "G": {
        "score": 60,
        "coverage": 1
      }
    },
    "findings": {
      "severe_violation": false
    }
  },
  "context": {},
  "expected_checks": {
    "value.score": 60,
    "value.class": "B"
  }
}

RATE-006 — esgcalc.rating.rate_company

(observations, findings, *, entity_type='nonfinancial', model=None, context=None)

Rate dated sourced observations with coverage, provenance, contributions and binding caps.

Source/method: ESGCALC_REFERENCE_MODEL_V1 — RATING.md; rating_reference.json. Kind: MODEL_CHOICE.

Contract boundary: Normative performance model, not a statutory rating or empirically calibrated financial-risk model.

Edge/reference tests: test_unknown_critical_finding_and_stale_data, test_rating_model_edges_and_effective_weights, test_consistent_balances_and_independent_results, test_rating_exclusions_and_input_findings_sensitivity.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "observations": {
      "renewable_energy": {
        "value": 0.6,
        "unit": "fraction",
        "as_of": "2026-06-30",
        "source": "synthetic"
      },
      "recycled_waste": {
        "value": 0.6,
        "unit": "fraction",
        "as_of": "2026-06-30",
        "source": "synthetic"
      },
      "pay_gap": {
        "value": 40,
        "unit": "percent",
        "as_of": "2026-06-30",
        "source": "synthetic"
      },
      "injury_rate": {
        "value": 8,
        "unit": "per_million_hours",
        "as_of": "2026-06-30",
        "source": "synthetic"
      },
      "board_female_share": {
        "value": 0.3,
        "unit": "fraction",
        "as_of": "2026-06-30",
        "source": "synthetic"
      },
      "board_independence": {
        "value": 0.6,
        "unit": "fraction",
        "as_of": "2026-06-30",
        "source": "synthetic"
      }
    },
    "findings": {
      "severe_violation": false
    }
  },
  "context": {
    "as_of": "2026-06-30"
  },
  "expected_checks": {
    "value.score": 60,
    "value.class": "B",
    "details.coverage": 1
  }
}

RATE-007 — esgcalc.rating.explain_rating

(rating, *, context=None)

Return stored rating contributions and reasons, without inventing narrative conclusions.

Source/method: ESGCALC_REFERENCE_MODEL_V1 — RATING.md; rating_reference.json. Kind: MODEL_CHOICE.

Contract boundary: Normative performance model, not a statutory rating or empirically calibrated financial-risk model.

Edge/reference tests: test_rating_portfolio_incomparability, test_remaining_api_and_io.

Composed/local-I/O example and independent assertions: tests/test_contracts.py::test_remaining_api_and_io.

RATE-008 — esgcalc.rating.rating_sensitivity

(observations, findings, scenarios, *, model=None, context=None)

Recalculate explicit indicator-weight scenarios; preserve original model and observations.

Source/method: ESGCALC_REFERENCE_MODEL_V1 — RATING.md; rating_reference.json. Kind: MODEL_CHOICE.

Contract boundary: Normative performance model, not a statutory rating or empirically calibrated financial-risk model.

Edge/reference tests: test_remaining_api_and_io, test_rating_exclusions_and_input_findings_sensitivity, test_weight_sensitivity_changes_class.

Composed/local-I/O example and independent assertions: tests/test_contracts.py::test_remaining_api_and_io.

RATE-009 — esgcalc.rating.portfolio_rating_summary

(rows, *, context=None)

Value-weight compatible company scores; report classes and unrated exposure separately.

Source/method: ESGCALC_REFERENCE_MODEL_V1 — RATING.md; rating_reference.json. Kind: MODEL_CHOICE.

Contract boundary: Normative performance model, not a statutory rating or empirically calibrated financial-risk model.

Edge/reference tests: test_rating_portfolio_incomparability, test_remaining_api_and_io, test_mixed_portfolio_reconciles.

Composed/local-I/O example and independent assertions: tests/test_contracts.py::test_remaining_api_and_io.

DATA-001 — esgcalc.data.validate_inputs

(rows, schema, *, context=None)

Validate explicit nested table schemas, types, enums, units, bounds and unique IDs.

Source/method: ESGCALC_DATA_CONTRACT_V2 — CONTRACTS.md; API.md. Kind: INPUT_CONTRACT.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_nested_schema_csv_and_unknown_fields, test_data_local_and_integrity_contracts.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "rows": [
      {
        "id": "a",
        "value": 5
      }
    ],
    "schema": {
      "unique_key": "id",
      "fields": {
        "value": {
          "type": "number",
          "minimum": 0
        }
      }
    }
  },
  "context": {},
  "expected_checks": {
    "value": true
  }
}

DATA-002 — esgcalc.data.load_local_data

(path, *, format=None, schema=None, context=None)

Read UTF-8 JSON or CSV; CSV fields stay strings, with no guessed units or types.

Source/method: ESGCALC_DATA_CONTRACT_V2 — CONTRACTS.md; API.md. Kind: INPUT_CONTRACT.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_nested_schema_csv_and_unknown_fields, test_data_local_and_integrity_contracts, test_remaining_api_and_io, test_utf8_csv.

Composed/local-I/O example and independent assertions: tests/test_contracts.py::test_remaining_api_and_io.

DATA-003 — esgcalc.data.list_resources

(*, context=None)

List packaged resource names and checksums, without any remote registry.

Source/method: ESGCALC_DATA_CONTRACT_V2 — CONTRACTS.md; API.md. Kind: INPUT_CONTRACT.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_data_local_and_integrity_contracts, test_result_hash_default_context_equivalent.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {},
  "context": {},
  "expected_checks": {
    "value.version": "esgcalc-resources-1"
  }
}

DATA-004 — esgcalc.data.load_profile

(profile_id, *, context=None)

Return an explicitly requested profile; never infer legal applicability.

Source/method: ESGCALC_DATA_CONTRACT_V2 — CONTRACTS.md; API.md. Kind: INPUT_CONTRACT.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_explicit_profiles, test_capex_plan_failure_history_and_legacy, test_data_local_and_integrity_contracts.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "profile_id": "eu-taxonomy-2026-v1"
  },
  "context": {},
  "expected_checks": {
    "value.id": "eu-taxonomy-2026-v1"
  }
}

DATA-005 — esgcalc.data.load_reference_data

(name, *, context=None)

Read and integrity-check a packaged JSON reference resource.

Source/method: ESGCALC_DATA_CONTRACT_V2 — CONTRACTS.md; API.md. Kind: INPUT_CONTRACT.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_resource_integrity_guard, test_data_local_and_integrity_contracts, test_consistent_balances_and_independent_results.

Executable input (pass kwargs and Context(**context) to the function):

{
  "kwargs": {
    "name": "rating_reference.json"
  },
  "context": {},
  "expected_checks": {
    "value.id": "esgcalc-reference-1"
  }
}

DATA-006 — esgcalc.data.export_resources

(destination, *, names, context=None)

Export selected resources to an explicit local directory; never overwrite existing files.

Source/method: ESGCALC_DATA_CONTRACT_V2 — CONTRACTS.md; API.md. Kind: INPUT_CONTRACT.

Contract boundary: Structured scope/applicability supplied by the caller.

Edge/reference tests: test_data_local_and_integrity_contracts, test_remaining_api_and_io.

Composed/local-I/O example and independent assertions: tests/test_contracts.py::test_remaining_api_and_io.

Verification of the local engine candidate

The v2 suite combines independently authored hand calculations, boundary and adverse-input cases, and 120 complete realistic annual pipelines checked against separately generated Decimal expectations. All 18 mandatory Table1 PAI positions have an independent mixed-universe example. All 23 selected numerical taxonomy clauses have threshold/alternative tests. Model tests cover missing/stale data, class thresholds, effective weights, caps, exclusions, input/finding/weight variants and portfolio comparability.

The source/requirement review links each enumerated obligation to a source edition and locator, implementation and named executed tests. Traceability is not a mathematical proof or independent legal opinion. The complete set of all ESG laws and taxonomy activities is not the test universe. Explicit boundaries are in CONTRACTS.md, TAXONOMY_CRITERIA.md and METHOD_ISSUES.md.

Run from the unpacked source archive using locally available tools:

PYTHONDONTWRITEBYTECODE=1 python -B -m pytest tests -p no:cacheprovider
python -m build --no-isolation
python examples/realistic_pipeline.py output.json

The package has no runtime dependency or network client. The local verification also installs the exact wheel and the wheel rebuilt from sdist into separate locations. tools/smoke_installed.py uses isolated Python and a Linux seccomp network prohibition to validate every packaged resource and the portable hand cases, and scans the runtime import surface. Originals, scaffold and prior reference projects are not installed. Resources can be read from a write-protected installation; intentional exports target a user-chosen external directory.

The older v0.1.0.dev0 reports apply only to that historical candidate. Current release/test counts and artifact hashes are recorded in the local v2 acceptance report. Native R parity is a subsequent gate; no R implementation, licence selection, publisher identity or external publication is implied here.

Subsequent native R implementation

The Python engine is the source of the stabilized public contract, not an R runtime dependency. Port the 47 capability IDs and signatures using native R functions and structured result/condition classes. Input tables may use native data.frames but must preserve explicit null/unknown/NA distinctions, booleans, unique identifiers, units, dates and method/profile selection.

Shared portable resources include reference_cases.json (42 hand cases), synthetic_realistic_cases.json and synthetic_realistic_expected.json (120 independent annual expectations), taxonomy parameters, profiles, source locators and the reference rating model. Export them using DATA-006. The original law archive and old reference libraries are unnecessary at runtime.

Numerical equality uses stated floating-point tolerances for computed totals; legal threshold comparisons, class decisions, statuses and universe selection must match exactly. Do not transfer missing quantities to zero, average ordinal classes, combine scope2 alternatives or silently change PAI18 interpretation. R must independently execute the portable cases before Python/R comparisons. Canonical hashing and serialization need an explicit cross-language contract; a matching rounded numerical output alone is not full parity.

Current status: R has not been implemented or verified. No reticulate, Python subprocess, shared engine server or universal code generator is required.