Skip to content

🐛 Plan ratio lookup never consults the documented default key #4025

Description

@PierreBrisorgueil

Problem

unitsFromCosts resolves a per-feature multiplier with a direct key lookup and falls back to a hardcoded 1:

// modules/billing/services/billing.meter.service.js:80
const ratio = typeof ratios[key] === 'number' && ratios[key] >= 0 ? ratios[key] : 1;

The default key is never consulted, even though the config schema documents it as part of the contract:

// modules/billing/config/billing.config.zod.js:12
 *   ratios      feature multiplier map (e.g. { default: 1, autofix: 2 })

Impact

A consumer configuring ratios: { default: 2, autofix: 3 } gets 2 applied to nothing: every cost key that is not literally autofix bills at 1, not at the configured default. The result is silent under-billing with no warning and no error.

This is invisible in practice today because the shipped example value is default: 1, which happens to equal the hardcoded fallback. Any consumer setting a default other than 1 is silently ignored.

Reproduction

  1. Configure a plan with ratios: { default: 2 }.
  2. Call BillingMeterService.attribute() with a cost map whose keys are not present in ratios (e.g. { someFeature: 1.0 }).
  3. Observed: units computed with ratio = 1. Expected: ratio = 2.

Options

  • (a) Honour the documented key — consult ratios.default before the hardcoded 1. Smallest change, matches the documented contract.
  • (b) Drop default from the schema docs — keep the hardcoded fallback and stop advertising a key the code ignores.
  • (c) Refuse unknown keys — when a plan snapshot resolved with a non-empty ratio map, a cost key absent from it is not a billable feature of that plan; throw instead of billing it at an implicit 1.

(c) is the strongest: besides fixing the default contract, it stops the meter from billing an arbitrary object of positive numbers whose keys have no relationship to the plan's feature taxonomy. Today any such object is accepted and charged at flat rate. It is also a behaviour change for consumers that rely on the implicit 1, so it needs a MIGRATIONS entry.

Acceptance

  • The documented contract and the implemented behaviour agree.
  • A cost key that is not a feature of the resolved plan cannot be silently billed at an unintended rate.
  • Unit test covering a plan whose default differs from 1.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions