Maturity Ramp
When a new offer goes live, its propensity posterior is wide — we don’t yet know its true acceptance rate. Showing it at full ranking confidence is reckless (we might over-spend on a dud) but suppressing it entirely is also wrong (we’ll never learn). The maturity ramp is the gating decision that controls cold-start exposure as evidence accumulates. Kaireon ships two maturity ramps. The default — Bayesian Confidence-Bound Maturity Ramp (BCB-MR) — is the better one and the rest of this page covers it. A legacy linear-count ramp is kept for back-compat with tenants who tuned around a fixed interaction threshold.Why BCB-MR exists
Count-based maturity gates — “treat the model as immature until it has accumulated N responses” — are the conventional choice in next-best-action systems. The approach is intuitive but has two real failure modes:- Low-volume offers are trapped. A campaign for 100 high-value enterprise customers will never accumulate a 5,000-response gate. The model is forever immature even when its posterior is already tight — useful signal is thrown away.
- High-volume volatile offers are prematurely “done”. An offer with thousands of trials at a seasonal volatile rate still has a wide posterior. A pure count gate declares it mature even when it deserves continued exploration.
The algorithm
For each candidate offer at decision time:-
Aggregate the offer-scope
ModelAdaptationposterior for this offer — itspositives+negativessummed across all active models. The maturity ramp keys off the offer-scope Beta posterior specifically, not the full scope hierarchy that propensity scoring walks — maturity is a property of the offer, not of a channel/direction cell. (Apausedoffer-scope row bypasses the ramp entirely.) -
Compute the Wilson score interval for the Bernoulli proportion
p̂ = positives / (positives + negatives)at the configured confidence level (default 95%, z = 1.96). Wilson 1927 is the canonical closed-form CI; well-behaved at boundaries (no blow-up at p̂ = 0 or 1, unlike the Wald interval). -
Let
width = upper − lower. Ifwidth ≤ widthThreshold(default 0.20), the offer is mature →exposureProbability = 1.0. -
Otherwise the offer is immature. Compute a decaying cold-start floor:
At n=0 the floor is
baseFloor(default 0.50). At n=100 with default decay it’s ≈ 0.15. The decay mirrors UCB1’s√(2 ln t / n)shrinkage but applied to the floor rather than an exploration bonus. -
Return
exposureProbability = max(floor(n), wilsonLower). Themaxensures we never penalize evidence we already have — an offer with strong early positives (8/10 → Wilson lower ≈ 0.49) gets at least that lower-bound exposure even if the decay-floor shrank below it.
hash(customerId + offerId + today) ≤ exposureProbability decides whether THIS impression includes the offer.
Observability. A ramp exclusion is never silent: every withheld candidate is recorded in the request’s rejectedOffers[] (stage maturity_ramp, reason "Below cold-start exposure floor — maturity ramp withheld this offer for this customer today (exposure probability N%, mode …)") when the caller sets explain=true, and on the persisted DecisionTrace.rejectedOffers for sampled traces. See Recommend and Decision Traces.
Worked examples
The table below shows real exposure decisions at default config (widthThreshold = 0.20, baseFloor = 0.50, z = 1.96, decayHalfLife = 10).
The 8/10 row is the interesting one: the offer is clearly persuasive (μ̂ = 0.80) but the CI is still wide, so it’s
ci_gated — exposure is the Wilson lower bound 0.49. As soon as evidence tightens the CI below 0.20 width, the offer flips to mature.
Configuration
Per-tenant settings (Settings → Models → Maturity Ramp):Why this design works
- Low-volume offers aren’t trapped. A campaign for 100 enterprise customers can mature once its posterior tightens, regardless of raw response count.
- High-volume volatile offers stay in exploration. An offer with thousands of trials but a still-wide CI keeps getting exploration traffic instead of being declared “done” by a count threshold.
- The threshold is principled. A 0.20 width at 95% means “the true rate is known to within ±0.10” — a tunable that business owners can reason about, with a clear posterior interpretation rather than a chosen convention.
- The maturity event is observable. Operators see a single interpretable number — “this offer’s posterior CI width is 0.18, just below the 0.20 threshold” — instead of “evidence count is 4837 of 5000”. Combined with direction-scoped adaptations, this surfaces directly in the Model Health UI.
References
- Wilson, E.B. (1927). “Probable inference, the law of succession, and statistical inference.” JASA 22:209–212. The canonical Bernoulli proportion CI.
- Auer, Cesa-Bianchi, Fischer (2002). “Finite-time Analysis of the Multiarmed Bandit Problem.” Machine Learning 47:235–256. UCB1 — origin of the √(ln t / n) shrinkage form adapted here as a decaying floor.
- Chapelle & Li (2011). “An Empirical Evaluation of Thompson Sampling.” NeurIPS. Empirical justification for Bayesian bandit-style exposure.
Code
- Library:
platform/src/lib/ml/maturity.ts - Tests:
platform/src/lib/ml/__tests__/maturity.test.ts - Call site:
platform/src/lib/pipeline-runner.ts(applyMaturityRamp)