Menu ▾ ▴

#209 Materiality is the literal 0.005 in 33 places; the group root already declares a tolerance the models could read

open
nobody
None
2026-09-17
2026-09-15
Anonymous
No

Originally created by: grynn-in

What happened

Materiality is a literal in the models that post and reconcile intercompany and deal journals. abs(...) >= 0.005 appears throughout the elimination rule table:

{# models/gold/gold_ic_eliminations.sql:143-148 #}
('group', 'matched',    ..., 'abs(post_m_a) >= 0.005'),
('group', 'nci',        ..., 'abs(post_n_a) >= 0.005'),
('group', 'difference', ..., "ic_difference_account != '' and abs(post_d_a) >= 0.005"),

with the same literal repeated in gold_ic_reconciliation, gold_ic_unmatched, gold_business_combination_journal, gold_business_disposal_journal, gold_goodwill_amortisation_journal and gold_equity_method_associates — 33 occurrences across eight models by a rough count.

Half a cent is a sensible rounding epsilon and is probably right as a default. The problem is that it is the only answer available, and it is not the same kind of number in every currency or at every scale: half a unit of a currency quoted in thousands per USD is not half a cent, and a group consolidating in the billions has a different idea of immaterial from one consolidating in the millions.

konsol#180 records the same class of problem from the other end: the trial-balance balance check is an absolute ±0.01 and rejects good files at large-currency scale.

Root cause

The epsilon was written inline at each site as the models were built, and there was no declared tolerance to read at the time.

Suggested fix

There is already a precedent for this exact thing, one field away: ic_difference_tolerance on the Consolidation Group root, declared in konsol, synced to epm_gold.consolidation_groups, validated non-negative, and already read by the elimination models.

  1. Introduce one macro — materiality_floor() — as the single definition, so the literal appears once rather than 33 times. That alone is worth doing and changes no behaviour.
  2. Have it resolve from the group root where a group declares a floor, falling back to 0.005.
  3. Consider whether the floor should scale with the reporting currency's magnitude, since usd_log10 on ISO Currency already exists for exactly that purpose in the FX guard (macros/fx_magnitude.sql). Relative rather than absolute is the same fix konsol#180 needs.

Step 1 is mechanical and makes 2 and 3 a one-line change later.

Repro

Not a failure with the current data — it is a single-scale assumption. Visible as 33 copies of one number that nothing can configure.

🤖 Generated with Claude Code

https://claude.ai/code/session_01P3Pf9835FeLeXjRrYTTZ1M

Discussion

  • Anonymous

    Anonymous - 2026-09-15

    Originally posted by: grynn-in

    Step 1 (one materiality_floor() macro) shipped in PR [#211]. Steps 2-3 (a group-declared or currency-scaled floor) stay open.

     

    Related

    Tickets: #211

  • Anonymous

    Anonymous - 2026-09-17

    Originally posted by: grynn-in

    Decision: the materiality floor is group-declared, with a currency-scaled default

    Decided by Deepak Pai, 18 September 2026.

    First, a correction to this issue's premise

    Measured on main before deciding. The issue says "the literal 0.005 in 33
    places"
    . That is not the current state:

    macros/materiality.sql   {% macro materiality_floor() %}0.005{% endmacro %}
    used by                  18 files
    guarded by               tests/test_materiality_literal.py
    strays remaining          3, all in assert_hierarchy_node_as_of.sql
    

    The macro already exists and its own comment anticipates exactly this decision:
    "One definition, so a later change (a group-declared floor, a currency-scaled
    floor) is one edit."
    So the work is far smaller than the issue implies.

    The decision

    materiality_floor() stops returning a constant.

    1. A new field on the Consolidation Group root: materiality_floor (Float).
      It does not overload ic_difference_tolerance, which already exists and
      means something different — the intercompany difference tolerance. One field,
      one meaning.

    2. When the field is blank, the floor is derived from the currency's declared
      minor unit
      — half the smallest representable unit, read from
      ISO Currency.minor_unit:

      minor_unit 2 → 0.005 76 currencies (reproduces today's constant)
      minor_unit 0 → 0.5 8 currencies
      minor_unit 3 → 0.0005 4 currencies

    3. The effective floor is written into the build's vars and log, so it is
      inspectable rather than computed invisibly inside SQL. A declared default is
      only compatible with konsol#247 if someone can see what it resolved to.

    4. The 3 strays in assert_hierarchy_node_as_of.sql route through the
      macro.
      tests/test_materiality_literal.py has a hole that let them in;
      that gap is part of this work.

    Why a derived default rather than a hard one

    The literal 0.005 is correct only for two-decimal currencies. On a JPY balance
    it is a hundred times smaller than the smallest representable amount, and on a
    three-decimal currency it is ten times too coarse. Deriving it from
    ISO Currency.minor_unit makes it right across all 15 currencies in play
    without asking anyone, and the rule is declared rather than folklore.

    Options rejected

    • Keep the constant. A hard-coded materiality judgement is indefensible at
      audit, and the constant is the silent fallback konsol#247 forbids.
    • Overload ic_difference_tolerance. One field meaning two things: a change
      to the IC tolerance would silently move materiality. Classic overload.
    • Group-declared with a flat 0.005 default. This was the initially
      recommended option, chosen partly because currency-scaling "could turn
      assertions red on upgrade". That objection does not apply — there are zero
      customers as of 18 September 2026
      , so there is no installed base to protect
      and no reason to ship the less correct default first.

    Sequencing

    Step 2 needs a currency on the row, which is konsol#253 (now decided: the
    trial balance carries its currency and every read names one). Land that column
    first; the rest of this issue does not depend on it.

    Explicitly out of scope

    The 55 occurrences of 0.01 across the assertion suite are not the
    materiality floor — they are tie-out tolerances (abs(a - b) > 0.01) in
    individual assertions, plus 4 float-equality comparisons at 0.000001, 3 at
    0.0001 and 1 at 0.001. They are absolute and therefore carry the same defect
    as konsol#180: meaningless for a zero-decimal currency, too loose for a
    three-decimal one. Separate issue, not covered by this decision.

     

Log in to post a comment.